Módulo 2: Arquitectura Host-Client-Server
El Client: El Conector de MCP
El Client: El Conector de MCP
Descripción de la cápsula
Ya conoces al Host — Claude Code como orquestador que decide qué servers usar y coordina todo. Pero entre el Host que decide y el Server que ejecuta hay un componente invisible: el MCP Client. Es el mesero del restaurante — no cocina ni decide el menú, pero sin él los pedidos no llegan a la cocina y los platos no llegan a la mesa.
En esta cápsula vas a entender qué es el MCP Client, cómo maneja el ciclo de vida de una conexión (desde el handshake inicial hasta la desconexión), qué protocolo usa para comunicarse, y cómo negocia capabilities con el Server. Aunque normalmente no construyes Clients (vienen integrados en el Host), entender cómo funcionan te permite diagnosticar problemas de conexión y escribir Servers que se comporten correctamente.
El Client es la capa que más se da por sentada — todo funciona bien hasta que falla. Entenderlo te prepara para esos momentos donde necesitas saber exactamente qué pasa entre el Host y tu Server.
¿Qué es un MCP Client?
Definición
Un MCP Client es el componente dentro del Host que establece y mantiene una conexión con un MCP Server usando el protocolo MCP (basado en JSON-RPC 2.0).
En términos concretos:
Claude Code (Host)
├── MCP Client 1 ←→ Filesystem Server
├── MCP Client 2 ←→ GitHub Server
└── MCP Client 3 ←→ PostgreSQL Server
Cada Client es una conexión independiente.
Cada Client mantiene su propio estado.
Cada Client negocia capabilities con SU Server.
Relación 1:1
Un detalle arquitectural importante: la relación Client-Server es 1:1. No hay un Client que habla con múltiples Servers. Cada conexión Server tiene su propio Client dedicado:
✅ Correcto:
Host
├── Client A → Server A
├── Client B → Server B
└── Client C → Server C
❌ Incorrecto (esto NO es cómo funciona):
Host
└── Client único → Server A
→ Server B
→ Server C
Esto garantiza aislamiento: si la conexión con Server B falla, Client A y Client C siguen funcionando normalmente.
El protocolo: JSON-RPC 2.0
Por qué JSON-RPC
MCP usa JSON-RPC 2.0 como formato de mensajes. Si has trabajado con APIs REST, JSON-RPC te parecerá familiar pero con estructura diferente:
REST:
GET /api/files/readme.md
→ { "content": "# Mi README..." }
JSON-RPC:
→ { "jsonrpc": "2.0", "method": "tools/call", "params": {...}, "id": 1 }
← { "jsonrpc": "2.0", "result": {...}, "id": 1 }
Anatomía de un mensaje JSON-RPC
Cada mensaje que el Client envía al Server tiene esta estructura:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "read_file",
"arguments": {
"path": "/Users/dev/projects/README.md"
}
}
}
| Campo | Descripción |
|---|---|
jsonrpc | Siempre "2.0" — versión del protocolo |
id | Identificador único del request (para emparejar con la respuesta) |
method | Qué operación ejecutar |
params | Parámetros de la operación |
Y la respuesta del Server:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "# Mi README\n\nEste es mi proyecto..."
}
]
}
}
El id: 1 en la respuesta coincide con el id: 1 del request — así el Client sabe qué respuesta corresponde a qué petición.
Tipos de mensajes
El protocolo MCP maneja 3 tipos de mensajes:
Tipos de mensajes MCP:
│
├── Request (Client → Server)
│ Tiene: method, params, id
│ Espera: Response
│ Ejemplo: "Ejecuta el tool read_file"
│
├── Response (Server → Client)
│ Tiene: result (éxito) o error (fallo), id
│ Responde a: un Request específico (mismo id)
│ Ejemplo: "Aquí está el contenido del archivo"
│
└── Notification (cualquier dirección, sin id)
Tiene: method, params (NO tiene id)
No espera respuesta
Ejemplo: "La lista de resources cambió" (Server → Client)
La diferencia entre Request y Notification es clave: un Request espera respuesta (tiene id), una Notification es fire-and-forget (sin id).
// Request (espera respuesta)
{
"jsonrpc": "2.0",
"id": 42,
"method": "tools/call",
"params": { "name": "read_file", "arguments": { "path": "README.md" } }
}
// Notification (no espera respuesta)
{
"jsonrpc": "2.0",
"method": "notifications/resources/updated",
"params": { "uri": "file:///Users/dev/data.json" }
}
Lifecycle de una conexión MCP
Vista general
El lifecycle de una conexión MCP tiene 4 fases:
Lifecycle de conexión:
│
├── 1. INITIALIZE
│ Client y Server negocian versión y capabilities
│
├── 2. INITIALIZED (notification)
│ Client confirma que la inicialización fue exitosa
│
├── 3. OPERATION (fase principal)
│ Client envía requests, Server responde
│ Puede durar minutos, horas, o toda la sesión
│
└── 4. SHUTDOWN
Client cierra la conexión
Cada fase tiene mensajes específicos. Veámoslas en detalle.
Fase 1: Initialize
Cuando Claude Code arranca un MCP server, lo primero que hace el Client es enviar un mensaje initialize:
// Client → Server: "Hola, quiero conectarme"
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-03-26",
"capabilities": {
"roots": {
"listChanged": true
}
},
"clientInfo": {
"name": "claude-code",
"version": "1.0.0"
}
}
}
Este mensaje dice:
- protocolVersion: Qué versión del protocolo MCP habla el Client
- capabilities: Qué features soporta el Client
- clientInfo: Quién es el Client (nombre y versión)
El Server responde con su propia información:
// Server → Client: "Hola, acepto la conexión"
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-03-26",
"capabilities": {
"tools": {
"listChanged": true
},
"resources": {
"subscribe": true,
"listChanged": true
}
},
"serverInfo": {
"name": "filesystem-server",
"version": "0.5.0"
}
}
}
Este response dice:
- protocolVersion: Versión del protocolo que el Server acepta
- capabilities: Qué features expone el Server (tools, resources, etc.)
- serverInfo: Quién es el Server
Negociación de capabilities
La fase de initialize es una negociación. No todos los Servers exponen las mismas capabilities:
Server A (filesystem):
capabilities:
tools: ✅ (tiene tools como read_file, write_file)
resources: ❌ (no expone resources)
prompts: ❌ (no expone prompts)
Server B (database):
capabilities:
tools: ✅ (tiene tools como query, list_tables)
resources: ✅ (expone resources como db://schema)
prompts: ✅ (tiene prompt templates como analyze_table)
Server C (weather):
capabilities:
tools: ✅ (tiene tools como get_forecast)
resources: ❌
prompts: ❌
El Client registra las capabilities de su Server y le dice al Host qué puede hacer. Por eso el Host sabe qué tools están disponibles — porque cada Client le reportó las capabilities de su Server.
Fase 2: Initialized (Notification)
Después de recibir la respuesta de initialize, el Client envía una notification confirmando que todo está listo:
// Client → Server: "Todo listo, empecemos"
{
"jsonrpc": "2.0",
"method": "notifications/initialized"
}
Esta es una notification (sin id), no espera respuesta. A partir de este momento, la conexión está activa y el Client puede enviar requests operacionales.
Fase 3: Operation
Esta es la fase donde el Client envía requests reales al Server. Los tipos más comunes:
Operaciones disponibles según capabilities:
│
├── Tools
│ ├── tools/list → Listar todos los tools disponibles
│ └── tools/call → Ejecutar un tool específico
│
├── Resources
│ ├── resources/list → Listar resources disponibles
│ ├── resources/read → Leer un resource específico
│ └── resources/subscribe → Suscribirse a cambios
│
└── Prompts
├── prompts/list → Listar prompts disponibles
└── prompts/get → Obtener un prompt específico
Ejemplo: Listar tools disponibles
// Client → Server
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list"
}
// Server → Client
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"tools": [
{
"name": "read_file",
"description": "Lee el contenido completo de un archivo",
"inputSchema": {
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "Ruta al archivo"
}
},
"required": ["path"]
}
},
{
"name": "write_file",
"description": "Escribe contenido a un archivo",
"inputSchema": {
"type": "object",
"properties": {
"path": { "type": "string" },
"content": { "type": "string" }
},
"required": ["path", "content"]
}
}
]
}
}
Nota cómo cada tool tiene un inputSchema — un JSON Schema que define exactamente qué parámetros acepta. Esto permite al Host saber cómo invocar cada tool correctamente.
Ejemplo: Ejecutar un tool
// Client → Server: "Ejecuta read_file"
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "read_file",
"arguments": {
"path": "/Users/dev/projects/README.md"
}
}
}
// Server → Client: "Aquí tienes el resultado"
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "# Mi Proyecto\n\nEste proyecto es..."
}
],
"isError": false
}
}
Ejemplo: Error en un tool
// Server → Client: "Hubo un error"
{
"jsonrpc": "2.0",
"id": 4,
"result": {
"content": [
{
"type": "text",
"text": "Error: File not found: /Users/dev/nonexistent.md"
}
],
"isError": true
}
}
El Server retorna errores dentro de result con isError: true, no como un error JSON-RPC. Esto permite que el Host presente el error de forma amigable al usuario.
Fase 4: Shutdown
Cuando Claude Code cierra sesión o remueve un MCP server, el Client cierra la conexión de forma ordenada:
Shutdown del Client:
├── Client deja de enviar requests
├── Client cierra el canal de comunicación
└── El proceso del Server termina
En la práctica con Claude Code, el shutdown ocurre cuando:
- Cierras la sesión de Claude Code (
Ctrl+C) - Remueves un server (
claude mcp remove) - Claude Code detecta que el proceso del server crasheó
Transports: cómo viajan los mensajes
¿Qué es un transport?
El protocolo MCP define qué mensajes se envían. El transport define cómo viajan esos mensajes. Piensa en el protocolo como el idioma y el transport como el medio de comunicación (teléfono, email, presencial):
Transports MCP:
│
├── stdio (Standard I/O)
│ ├── Los mensajes viajan por stdin/stdout del proceso
│ ├── El server es un proceso local
│ ├── Usado por: Claude Code, Claude Desktop
│ └── El más común para desarrollo local
│
├── HTTP con SSE (Server-Sent Events)
│ ├── Los mensajes viajan por HTTP
│ ├── El server puede estar en una máquina remota
│ ├── Client → Server: HTTP POST
│ ├── Server → Client: SSE stream
│ └── Usado para: servers remotos, cloud deployments
│
└── Streamable HTTP
├── Evolución del transport HTTP
├── Soporta streaming bidireccional
└── Usado para: comunicación avanzada
stdio en detalle
Cuando configuras un MCP server en Claude Code con npx, usas stdio:
claude mcp add filesystem -s user -- npx -y @modelcontextprotocol/server-filesystem /dir
Lo que pasa internamente:
Claude Code
│
├── Lanza proceso: npx server-filesystem /dir
│ └── Este proceso tiene stdin y stdout
│
├── Client escribe en stdin del proceso
│ → {"jsonrpc":"2.0","method":"initialize",...}\n
│
├── Server lee de stdin, procesa, escribe en stdout
│ → {"jsonrpc":"2.0","result":{...}}\n
│
└── Client lee de stdout del proceso
→ Parsea el JSON y entrega al Host
Flujo de datos con stdio:
Client ──stdin──→ Server Process
Client ←─stdout── Server Process
Client ←─stderr── Server Process (logs/errores, no MCP)
Cada mensaje JSON va en una línea, terminada con \n. El Client y el Server parsean JSON línea por línea.
¿Cuándo usar cada transport?
stdio:
├── ✅ Server local (tu máquina)
├── ✅ Desarrollo y testing
├── ✅ Claude Code, Claude Desktop
└── ❌ Server en la nube
HTTP/SSE:
├── ✅ Server remoto (en un servidor)
├── ✅ Múltiples Hosts conectados al mismo Server
├── ✅ Deployments en producción
└── ❌ Más complejo de configurar
Para esta guía, trabajarás casi siempre con stdio. HTTP/SSE se explorará cuando llegues a deployments en producción.
Capabilities del Client
¿Qué puede hacer un Client?
El Client también declara capabilities durante la inicialización. Estas son menos conocidas pero importantes:
{
"capabilities": {
"roots": {
"listChanged": true
},
"sampling": {}
}
}
Capabilities del Client:
│
├── roots
│ ├── Le dice al Server qué directorios/URIs base tiene acceso
│ └── listChanged: el Client puede notificar si las roots cambian
│
└── sampling
└── El Client puede pedir al Server que genere texto
(usado en flujos avanzados donde el Server necesita
que el modelo genere algo)
Roots: los directorios base
Cuando el Client envía sus roots al Server, le dice "estos son los directorios a los que tengo acceso":
// Client informa al Server sus roots
{
"roots": [
{
"uri": "file:///Users/dev/projects/my-app",
"name": "My App"
}
]
}
Esto permite al Server contextualizar sus operaciones. Un Filesystem server puede saber que solo debe operar dentro de esos directorios.
Manejo de errores en el protocolo
Errores JSON-RPC
Cuando algo falla a nivel de protocolo (no a nivel de operación), el Server retorna un error JSON-RPC:
// Error de protocolo: método no encontrado
{
"jsonrpc": "2.0",
"id": 5,
"error": {
"code": -32601,
"message": "Method not found: tools/nonexistent"
}
}
Códigos de error estándar JSON-RPC:
Códigos de error:
├── -32700 Parse error (JSON inválido)
├── -32600 Invalid request (estructura incorrecta)
├── -32601 Method not found (método no existe)
├── -32602 Invalid params (parámetros incorrectos)
└── -32603 Internal error (error interno del server)
Errores vs resultados con isError
Es importante distinguir entre errores de protocolo y errores de operación:
Error de protocolo (error JSON-RPC):
→ El Client no pudo comunicarse con el Server
→ Ejemplo: método no existe, JSON inválido
→ Se retorna como "error" en JSON-RPC
Error de operación (result con isError):
→ El Client se comunicó bien, pero la operación falló
→ Ejemplo: archivo no encontrado, query SQL inválida
→ Se retorna como "result" con isError: true
Troubleshooting
"El server se conecta pero no lista tools"
Causa probable: El Server no declara tools en sus capabilities durante initialize.
Solución: Verifica que el Server retorne capabilities correctas:
{
"capabilities": {
"tools": {}
}
}
Si falta "tools": {}, el Client no pedirá la lista de tools.
"Los mensajes no llegan al server"
Causa probable: Problema de transport (stdio). El Server no está leyendo de stdin correctamente.
Solución:
# Prueba enviar un mensaje manualmente al server
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | npx -y @modelcontextprotocol/server-filesystem /tmp
Si no obtienes respuesta, el Server no está procesando stdin correctamente.
"Error: Protocol versión mismatch"
Causa: El Client y el Server hablan versiones diferentes del protocolo MCP.
Solución: Actualiza el Server (o el Client) a una versión compatible:
# Actualizar el server
npm update -g @modelcontextprotocol/server-filesystem
# O reinstalar la última versión
npm install -g @modelcontextprotocol/server-filesystem@latest
"Connection timeout durante initialize"
Causa: El Server tarda demasiado en responder al initialize.
Solución:
# Verificar que el server arranca correctamente
npx -y @modelcontextprotocol/server-filesystem /tu/dir
# Si usa npx, instalar globalmente para arranque más rápido
npm install -g @modelcontextprotocol/server-filesystem
"El server se desconecta después de un rato"
Causa probable: El proceso del Server crashea. Puede ser por un error no manejado, memory leak, o timeout.
Solución: Revisa los logs del Server (stderr):
# Los errores del server van a stderr
# Claude Code puede mostrar estos errores en su output
# Busca mensajes de error en la sesión de Claude Code
Ejercicios
Ejercicio 1: Identificar mensajes MCP (Fácil)
Clasifica cada mensaje como Request, Response, o Notification:
// Mensaje A
{ "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "read_file" } }
// Mensaje B
{ "jsonrpc": "2.0", "id": 3, "result": { "content": [{ "type": "text", "text": "hola" }] } }
// Mensaje C
{ "jsonrpc": "2.0", "method": "notifications/resources/updated" }
// Mensaje D
{ "jsonrpc": "2.0", "id": 7, "error": { "code": -32601, "message": "Method not found" } }
Ver solución
- Mensaje A: Request — Tiene
method,params, yid. Espera una respuesta. - Mensaje B: Response (éxito) — Tiene
resulteid. Es la respuesta al Request con id 3. - Mensaje C: Notification — Tiene
methodpero NO tieneid. No espera respuesta. - Mensaje D: Response (error) — Tiene
erroreid. Es una respuesta de error al Request con id 7.
Regla rápida: Si tiene id + method → Request. Si tiene id + result/error → Response. Si tiene method pero no id → Notification.
Ejercicio 2: Escribir un mensaje initialize (Medio)
Escribe el mensaje initialize que un Client enviaría, y la respuesta que un Server de database retornaría. El Server expone tools y resources, pero no prompts.
Ver solución
Client → Server (initialize request):
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-03-26",
"capabilities": {
"roots": {
"listChanged": true
}
},
"clientInfo": {
"name": "claude-code",
"version": "1.0.0"
}
}
}
Server → Client (initialize response):
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-03-26",
"capabilities": {
"tools": {
"listChanged": true
},
"resources": {
"subscribe": true,
"listChanged": true
}
},
"serverInfo": {
"name": "database-server",
"version": "1.0.0"
}
}
}
Puntos clave:
- El Server NO incluye
promptsen capabilities (no las soporta) - Ambos usan la misma
protocolVersion - El
id: 1coincide en request y response
Ejercicio 3: Trazar el lifecycle completo (Medio)
Dibuja la secuencia completa de mensajes cuando Claude Code conecta con un Filesystem MCP server y el usuario pide "lee el archivo package.json". Incluye desde initialize hasta la respuesta final.
Ver solución
Fase 1: Initialize
───────────────────
Client → Server: initialize (id:1)
{ protocolVersion, capabilities, clientInfo }
Server → Client: response (id:1)
{ protocolVersion, capabilities: {tools: {}}, serverInfo }
Client → Server: notifications/initialized
(sin id, notification)
Fase 2: Discover
───────────────────
Client → Server: tools/list (id:2)
Server → Client: response (id:2)
{ tools: [read_file, write_file, ...] }
→ Host registra los tools disponibles
Fase 3: Operation (usuario pide "lee package.json")
───────────────────
Host decide: necesita read_file → Filesystem server
Client → Server: tools/call (id:3)
{ name: "read_file", arguments: { path: "package.json" } }
Server → Client: response (id:3)
{ content: [{ type: "text", text: "{ \"name\": \"my-app\"... }" }] }
→ Host presenta el contenido al usuario
Total: 6 mensajes (3 del Client, 3 del Server)
Ejercicio 4: Diagnosticar errores por el mensaje (Medio)
Dado este intercambio de mensajes, identifica qué salió mal y en qué fase del lifecycle:
// Client → Server
{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2025-03-26", "capabilities": {}, "clientInfo": { "name": "claude-code", "version": "1.0.0" } } }
// Server → Client
{ "jsonrpc": "2.0", "id": 1, "result": { "protocolVersion": "2024-11-05", "capabilities": { "tools": {} }, "serverInfo": { "name": "old-server", "version": "0.1.0" } } }
Ver solución
Problema: Posible mismatch de versión de protocolo.
El Client envía protocolVersion: "2025-03-26" pero el Server responde con protocolVersion: "2024-11-05".
Análisis:
- El Client quiere usar la versión más reciente del protocolo
- El Server solo soporta una versión anterior
- Dependiendo de la implementación, esto puede:
- Funcionar si las versiones son backward-compatible
- Fallar si hay breaking changes entre versiones
- Causar comportamiento inesperado si el Client usa features que el Server no soporta
Fase: Initialize (Fase 1 del lifecycle)
Solución: Actualizar el Server a una versión que soporte el protocolo más reciente:
npm update -g @modelcontextprotocol/server-old-server
Ejercicio 5: Comparar stdio vs HTTP/SSE (Difícil)
Describe 3 escenarios donde usarías stdio y 3 donde usarías HTTP/SSE para el transport de un MCP server. Justifica cada elección.
Ver solución
Escenarios para stdio:
-
Server de filesystem local
- Accede a archivos en tu máquina
- No necesita red
- stdio es directo y rápido
- Justificación: la operación es local, no hay beneficio en HTTP
-
Server de development/testing
- Estás desarrollando un MCP server nuevo
- Necesitas ciclo rápido de desarrollo
- stdio no requiere puerto ni configuración de red
- Justificación: simplicidad para desarrollo
-
Server con datos sensibles
- Accede a secrets locales o archivos privados
- No quieres exponer un endpoint HTTP
- stdio mantiene todo dentro de la máquina
- Justificación: seguridad — sin superficie de ataque de red
Escenarios para HTTP/SSE:
-
Server de database en la nube
- La database está en AWS/GCP/Azure
- El server necesita correr cerca de la database (menos latencia)
- Múltiples developers quieren usar el mismo server
- Justificación: el server no puede correr localmente
-
Server compartido por equipo
- Un MCP server de Jira que todo el equipo usa
- Un server centralizado evita que cada developer configure el suyo
- Justificación: compartir un server entre múltiples Hosts
-
Server con CI/CD integration
- El server es parte de un pipeline de deployment
- Se despliega como un servicio web
- Otros sistemas (no solo Claude Code) necesitan accederlo
- Justificación: integración con infraestructura existente
Ejercicio 6: Escribir una secuencia de tools/call (Difícil)
Escribe los mensajes JSON-RPC completos para esta secuencia: el usuario quiere "buscar archivos .py y leer el más grande". Necesitas 2 calls: primero search_files, luego read_file con el resultado.
Ver solución
// Call 1: Buscar archivos .py
// Client → Server
{
"jsonrpc": "2.0",
"id": 10,
"method": "tools/call",
"params": {
"name": "search_files",
"arguments": {
"path": "/Users/dev/project",
"pattern": "*.py"
}
}
}
// Server → Client
{
"jsonrpc": "2.0",
"id": 10,
"result": {
"content": [
{
"type": "text",
"text": "Found 3 files:\n- main.py (2.4 KB)\n- utils.py (5.1 KB)\n- test_main.py (1.8 KB)"
}
],
"isError": false
}
}
// Host procesa: utils.py es el más grande (5.1 KB)
// Call 2: Leer el archivo más grande
// Client → Server
{
"jsonrpc": "2.0",
"id": 11,
"method": "tools/call",
"params": {
"name": "read_file",
"arguments": {
"path": "/Users/dev/project/utils.py"
}
}
}
// Server → Client
{
"jsonrpc": "2.0",
"id": 11,
"result": {
"content": [
{
"type": "text",
"text": "import os\nimport json\n\ndef parse_config(path):\n ..."
}
],
"isError": false
}
}
Puntos clave:
- Los
idson secuenciales (10, 11) para distinguir cada request - El Host (no el Client) decide que utils.py es el más grande
- El segundo call usa información del resultado del primero
- Ambos calls usan el mismo Client → mismo Server
Resumen
En esta cápsula aprendiste:
- El MCP Client es el componente dentro del Host que maneja la comunicación con un Server específico
- La relación Client-Server es 1:1 — cada Server tiene su Client dedicado
- MCP usa JSON-RPC 2.0 como formato de mensajes, con 3 tipos: Request, Response, Notification
- El lifecycle tiene 4 fases: Initialize → Initialized → Operation → Shutdown
- Durante initialize, Client y Server negocian versión del protocolo y capabilities
- Los transports definen cómo viajan los mensajes: stdio (local) o HTTP/SSE (remoto)
- Los errores pueden ser de protocolo (error JSON-RPC) o de operación (result con isError)
- Entender el Client te permite diagnosticar problemas de conexión entre Host y Server
Próxima cápsula: El Server: el proveedor — la capa que tú vas a construir. Cómo un Server expone capabilities, responde requests, y maneja estado.
Recursos adicionales
- MCP Protocol Specification - Especificación completa del protocolo incluyendo todos los mensajes
- JSON-RPC 2.0 Specification - El protocolo base que MCP usa
- MCP Transports - Documentación oficial de transports (stdio, HTTP/SSE)
- MCP Connection Lifecycle - Documentación del lifecycle de conexión
- MCP TypeScript SDK — Client - Código fuente del Client en TypeScript
- MCP Inspector - Herramienta para visualizar mensajes MCP en tiempo real
Siguiente cápsula: El Server: el proveedor — cómo un MCP Server expone capabilities, responde a requests, y maneja su estado interno. Esta es la capa que construirás en los módulos 4-6.