Conectarse a un server MCP existente
Objetivo
Al terminar esta lección sabrás levantar y consumir un server MCP oficial (filesystem, fetch, time, everything) desde la línea de comandos con npx, ejecutar el handshake initialize → notifications/initialized → tools/list a mano para entender el wire protocol, y usar esos servers desde Claude Code.
Concepto
El repo modelcontextprotocol/servers
Anthropic mantiene un monorepo con servers MCP de referencia:
@modelcontextprotocol/server-filesystem— lee/escribe archivos dentro de un directorio permitido.@modelcontextprotocol/server-fetch— hace HTTP requests.@modelcontextprotocol/server-time— tools de manejo de fecha/hora y timezones.@modelcontextprotocol/server-everything— server "kitchen sink" que demuestra todas las primitivas del protocolo. Útil para aprender.- Y varios más (git, memory, sequentialthinking).
Todos se levantan con npx sin instalación previa:
npx -y @modelcontextprotocol/server-filesystem /ruta/permitidaLa flag -y le dice a npx "descargá si hace falta, sin preguntar". Los servers TypeScript del repo corren sobre Node; los Python usan uv. Revisá el README del server específico al integrarlo.
Handshake a mano: tres mensajes
El mínimo indispensable para empezar a hablar con un server MCP por stdio son tres líneas JSON-RPC:
initialize— handshake inicial del client. DeclaraprotocolVersiony capabilities.notifications/initialized— notification (sinid, sin response esperada) que le dice al server "listo, arrancá".- Primera llamada real —
tools/list,resources/list, etc.
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"demo","version":"0.1"}}}
{"jsonrpc":"2.0","method":"notifications/initialized"}
{"jsonrpc":"2.0","id":1,"method":"tools/list"}Cada línea se envía por stdin del proceso server. El server responde por stdout.
Usar los servers oficiales desde Claude Code
Claude Code configura servers MCP via ~/.claude.json (o .claude.json en el repo) en la sección mcpServers. Un entry típico:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/Documents/GitHub"]
},
"fetch": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-fetch"]
}
}
}Claude Code lanza cada server al arrancar, hace el handshake, y expone las tools al modelo. El user no ve JSON-RPC — ve "tengo disponibles read_text_file, write_file, etc.".
Diferencia con MCP Connector
Esto es importante para no confundirse con la lección 7:
| MCP Connector (lección 7) | Cliente local (esta lección) | |
|---|---|---|
| Transporte | HTTP (streamable-http) | stdio |
| Quién es host | La API de Anthropic | Tu proceso (Claude Code o tu script) |
| Server corre en | Internet, accesible a Anthropic | Localhost, tu máquina |
| Ejemplo de servers | DeepWiki, SaaS remoto | @modelcontextprotocol/server-filesystem |
| Beta header | mcp-client-2025-04-04 | No aplica — no hay API Anthropic involucrada |
Para servers locales, el flujo /v1/messages con mcp_servers no aplica — ese parámetro solo acepta URLs HTTP accesibles desde la infra de Anthropic. Para consumirlos vía Messages API, necesitás tu propio client MCP (lo vemos en la lección 9 cuando construyamos un server propio).
El server everything: referencia de features
@modelcontextprotocol/server-everything es el más útil para aprender porque implementa todas las primitivas y declara todas las capabilities. Al conectarte ves:
{
"capabilities": {
"tools": {"listChanged": true},
"prompts": {"listChanged": true},
"resources": {"subscribe": true, "listChanged": true},
"logging": {},
"completions": {}
},
"serverInfo": {"name": "mcp-servers/everything", "version": "2.0.0"}
}Y entre sus tools figura desde echo y get-sum (obviamente didácticas) hasta trigger-sampling-request y trigger-elicitation-request (para que pruebes las capabilities inversas del Módulo 6). Es un terreno seguro para experimentar con cada feature antes de encararlas en producción.
Ejecución real
1. Handshake manual contra server-filesystem
Creá un archivo de prueba:
mkdir -p /private/tmp/mcp-demo
echo "hello from mcp demo" > /private/tmp/mcp-demo/note.txtArmá un script que hable stdio con el server:
cat > /tmp/mcp-fs-test.sh <<'EOF'
#!/bin/bash
{
printf '%s\n' '{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"demo","version":"0.1"}}}'
printf '%s\n' '{"jsonrpc":"2.0","method":"notifications/initialized"}'
printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
printf '%s\n' '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"read_text_file","arguments":{"path":"/private/tmp/mcp-demo/note.txt"}}}'
sleep 3
} | npx -y @modelcontextprotocol/server-filesystem /private/tmp/mcp-demo 2>/dev/null
EOF
chmod +x /tmp/mcp-fs-test.shEjecutá y filtrá la respuesta al tools/call (id=2):
/tmp/mcp-fs-test.sh | python3 -c '
import sys, json
for line in sys.stdin:
line = line.strip()
if not line: continue
try: m = json.loads(line)
except: continue
if m.get("id") == 2:
print(json.dumps(m, indent=2))
'Output real (ejecutado 2026-04-12 con @modelcontextprotocol/server-filesystem@0.2.0):
{
"result": {
"content": [
{ "type": "text", "text": "hello from mcp demo\n" }
],
"structuredContent": {
"content": "hello from mcp demo\n"
}
},
"jsonrpc": "2.0",
"id": 2
}El server respetó el scope (/private/tmp/mcp-demo), leyó el archivo, devolvió el texto. structuredContent es opcional — la spec moderna la agregó para respuestas tipadas acorde al outputSchema de la tool.
Nota: si probás con
/tmp/...en lugar de/private/tmp/..., recibísisError: truecon mensaje "Access denied - path outside allowed directories". Es la normalización de symlinks de macOS. Usá siempre el path resuelto.
2. server-everything para ver capabilities completas
echo '{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"demo","version":"0.1"}}}' | \
npx -y @modelcontextprotocol/server-everything stdio 2>/dev/nullRespuesta (abreviada):
{
"result": {
"protocolVersion": "2025-06-18",
"capabilities": {
"tools": {"listChanged": true},
"prompts": {"listChanged": true},
"resources": {"subscribe": true, "listChanged": true},
"logging": {},
"completions": {}
},
"serverInfo": {"name": "mcp-servers/everything", "title": "Everything Reference Server", "version": "2.0.0"}
}
}Las capabilities del server están acá — todo lo que podés invocar. Si una capability no aparece, el método correspondiente (resources/list, prompts/list) devolvería error.
3. Configurar en Claude Code
En ~/.claude.json (o .claude.json del proyecto):
{
"mcpServers": {
"fs-demo": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/private/tmp/mcp-demo"]
}
}
}Reiniciá Claude Code. Al arrancar, el proceso del server se levanta automáticamente; las tools (read_text_file, write_file, etc.) están disponibles para que Claude las invoque cuando le pidas tareas de filesystem en ese directorio.
Anti-patterns
- ❌ Levantar
server-filesystemcon/o$HOMEcomo directorio permitido. Le das al modelo acceso a todo tu FS. Scopeá al mínimo necesario (un directorio de proyecto, un staging dir). - ❌ Dejar el server mostrando logs en stdout. Cualquier
console.logdel server rompe el canal JSON-RPC. El repomodelcontextprotocol/serversrespeta esto por default — si escribís uno custom, usá stderr para logs. - ❌ Confundir stdio con MCP Connector. Los servers de este repo corren locales; no podés pasarlos como
mcp_serversde la Messages API sin armar un wrapper HTTP. - ❌ Compartir tu
.claude.jsoncon servers privados configurados. Si el server usa env vars con tokens (ej:GITHUB_TOKEN), esos env vars van en la config. Usá.claude.jsonfuera del repo si tiene secretos, o usá placeholders y un.env. - ❌ Invocar un server desactualizado. El repo oficial evoluciona; versiones viejas pueden usar
protocolVersionanterior y romper con clients nuevos. Pinear versiones exactas enargs(ej:@modelcontextprotocol/server-filesystem@0.2.0) es prudente en producción. - ❌ Olvidar
notifications/initialized. Sin esa notification, el server puede rechazar las llamadas subsiguientes o quedarse esperando. Es parte del handshake — obligatoria.
Recap
- El repo oficial
modelcontextprotocol/serverstrae servers listos (filesystem,fetch,time,everything,git,memory, etc.). - Se levantan con
npx -y @modelcontextprotocol/server-...sin instalación previa. - Hablan stdio: una línea NDJSON por mensaje JSON-RPC. stdin = client→server, stdout = server→client, stderr = logs.
- Handshake mínimo:
initialize→notifications/initialized→ tu primera llamada. - En Claude Code se configuran en
.claude.jsonbajomcpServers: { name: { command, args } }. - Servers locales no son consumibles via MCP Connector de la Messages API — ese requiere URLs HTTP.
- Usá
server-everythingpara explorar capabilities que no viste en producción (sampling, elicitation, logging).
Fuente oficial: github.com/modelcontextprotocol/servers y modelcontextprotocol.io/docsEjercicio: