Son las 2:14 a. m. Laura, el asistente de TI (cliente MCP), recibe por Slack:
- “Mi laptop no enciende y un deadline en pocas horas.” — María, Ingeniería

Detras de Laura hay varios servidores MCP:
• Directorio (STDIO): empleados, activos, relaciones.
• Mesa de servicios (HTTP + SSE): incidentes y flujos ITSM.
• Correo (HTTP): notificaciones por email.
El reto: hablar con todos a la vez sin mezclar respuestas. Aquí es donde JSON-RPC brilla con su correlación por ID, mensajes estrictos y manejo de errores
El pacto de sesion (estado sobre un núcleo sin estado)
Aunque JSON-RPC es “sin estado”, MCP construye una sesión encima. Laura inicia el “apretón de manos” con initialize (petición con ID) y lo cierra con initialized (notificación sin ID).
Request (initialize):
{
"id": "init-dir-1",
"method": "initialize",
"params": {
"protocolVersion": "2025-09-22",
"capabilities": {},
"clientInfo": { "name": "Laura", "version": "3.4.1" }
}
}
Response (mismo id):
{
"id": "init-dir-1",
"result": {
"serverInfo": { "name": "Directory", "version": "2.1.0" },
"capabilities": { "tools": ["get_user", "get_devices"] }
}
}
Notification (no espera respuesta):
{
"method": "initialized",
"params": { "ts": "2025-09-22T07:14:13Z" }
}
Parte difícil: la notificación no tiene id → no hay confirmación. Úsala solo para eventos donde no necesitas garantía de entrega.
Quien es María? (paralelismo + correlación por ID)
Laura consulta en paralelo al servidor Directorio (vía STDIO, un JSON completo por línea):
Requests (en paralelo):
{"jsonrpc":"2.0","id":"dir-1001","method":"tools/call","params":{"name":"get_user","arguments":{"email":"maria@empresa.com"}}}
{"jsonrpc":"2.0","id":"dir-1002","method":"tools/call","params":{"name":"get_devices","arguments":{"owner_email":"maria@empresa.com"}}}
Responses (pueden llegar desordenadas):
{"jsonrpc":"2.0","id":"dir-1002","result":{"devices":[{"id":"LAP-4471","model":"ThinkPad Z13","serial":"ZX9-1137"}]}}
{"jsonrpc":"2.0","id":"dir-1001","result":{"user":{"id":"u-784","name":"María Gomez","dept":"Engineering"}}}
Parte difícil: nunca confíes en el orden de llegada. La unión petición <-> respuesta siempre es por id único por sesión.
Crear el incidente (¿error de protocolo o resultado de dominio?)
Laura intenta abrir un ticket en Mesa de servicios (HTTP + SSE).
Request (falta un parámetro obligatorio):
{
"id": "sd-2001",
"method": "tools/call",
"params": {
"name": "create_incident",
"arguments": {
"title": "La laptop no enciende",
"priority": "high",
"requester": "u-784",
"asset_id": "LAP-4471"
}
}
}
Error (invocación inválida → código estándar JSON-RPC):
{
"id": "sd-2001",
"error": {
"code": -32602,
"message": "Parámetros inválidos",
"data": {
"missing": ["impact"],
"hint": "Usa: 'user-blocked'|'team-blocked'|'org-critical'"
}
}
}
Retry correcto:
{
"id": "sd-2002",
"method": "tools/call",
"params": {
"name": "create_incident",
"arguments": {
"title": "La laptop no enciende",
"priority": "high",
"requester": "u-784",
"asset_id": "LAP-4471",
"impact": "user-blocked"
}
}
}
Éxito:
{
"id": "sd-2002",
"result": {
"incident_id": "INC-90271",
"status": "open",
"assigned_group": "Endpoint Support"
}
}
Parte difícil: decidir cuándo devolver error JSON-RPC (p. ej., faltan parámetros → -32602) y cuándo un resultado de dominio (p. ej., “no se encontró el activo” -> podría ser {“found”: false} dentro de result). Esta convención debe ser consistente en todo MCP.
Telemetría “dispara y olvida” (notificaciones)
Laura registra un evento no crítico como notificación:
{
"jsonrpc": "2.0",
"method": "logEvent",
"params": {
"category": "helpdesk",
"message": "INC-90271 creado para maria@empresa.com"
}
}
Parte difícil: si el servidor lo pierde, nadie responde. No uses notificaciones para acciones que requieran garantías (pagos, cambios de estado críticos).
Lote para acelerar (batching sin orden garantizado)
Para anticipar contexto, Laura agrupa peticiones al Directorio:
[
{
"id": "dir-2010",
"method": "tools/call",
"params": { "name": "get_team", "arguments": { "user_id": "u-784" } }
},
{
"id": "dir-2011",
"method": "tools/call",
"params": { "name": "get_manager", "arguments": { "user_id": "u-784" } }
},
{
"method": "logEvent",
"params": { "category": "perf", "message": "prefetch posterior al incidente" }
}
]
Batch response (sin orden, sin respuesta a la notificación):
[
{ "jsonrpc": "2.0", "id": "dir-2011", "result": { "manager": "u-055" } },
{ "jsonrpc": "2.0", "id": "dir-2010", "result": { "team": ["u-101","u-102","u-784"] } }
]
Parte difícil: en lote, las respuestas no respetan el orden del array original; debes reensamblar solo con id. La notificación del lote no genera respuesta.
Cancelación y contrapresión (códigos MCP)
María desenchufa y vuelve a enchufar el cargador: problema resuelto. Laura decide cancelar un despacho pendiente:
{
"id": "sd-3001",
"method": "tools/call",
"params": { "name": "dispatch_technician", "arguments": { "incident_id": "INC-90271" } }
}
Respuesta de cancelación (código MCP específico):
{
"id": "sd-3001",
"error": {
"code": -32800,
"message": "Solicitud cancelada",
"data": { "at": "queued", "reason": "user_resolved" }
}
}
Parte difícil: la cooperación del servidor para honrar cancelaciones y aplicar backpressure (no seguir procesando trabajo que el cliente ya no quiere).
Email humano (exclusión estricta resultado/error)
Laura confirma por correo el ticket (si fallara, no puede enviar result y error juntos).
{
"id": "mail-4001",
"method": "send_mail",
"params": {
"to": "maria@empresa.com",
"subject": "Tu incidente INC-90271",
"body": "Hemos abierto el ticket. Avísanos si persiste el problema."
}
}
Response (éxito):
{
"jsonrpc": "2.0",
"id": "mail-4001",
"result": { "queued": true, "message_id": "MSG-77821" }
}
Rasgos especiales de JSON-RPC (lo que realmente importa)
1. Correlación determinística por id: base del paralelismo seguro.
2. Exclusión mutua: una respuesta tiene o result o error, nunca ambos.
3. Agnóstico al transporte: mismo formato JSON via STDIO, HTTP/SSE o WebSocket; tú manejas encuadre y flujo.
4. Errores estandarizados:
• -32700 Parse error
• -32600 Invalid request
• -32601 Method not found
• -32602 Invalid params
• -32603 Internal error
Extensiones MCP:
• -32800 Request Cancelled
• -32801 Content Too Large
5. Batching: menos viajes de red, más complejidad de correlación.
6. Notificaciones: “dispara y olvida”; sin garantías.
7. Higiene de IO: protocolo por stdout, logs por stderr (crítico en STDIO).
Checklist técnico (para no tropezar)
• Genera id únicos por sesión (UUID o contador + prefijo).
• Valida estrictamente el esquema (jsonrpc: “2.0”, method, id cuando aplica).
• Define reglas claras: cuándo es error JSON-RPC vs. resultado de dominio.
• En STDIO: una línea = un JSON completo; logs a stderr.
• Soporta respuestas fuera de orden (especialmente con batch).
• Usa notificaciones solo si puedes tolerar pérdida.
• Implementa cancelación y respeta backpressure.
Curso de MCP con Microsoft Azure
COMPARTE ESTE ARTÍCULO Y MUESTRA LO QUE APRENDISTE




