Saltar al contenido
26/30Capítulo 26 de 30

MCP explicado desde la especificación: qué es realmente un servidor

Una línea de JSON a un subproceso y vuelven trece definiciones de herramientas, según la revisión de 2026-07-28.

En esta página

Instala un servidor MCP publicado, envíale una línea de JSON y lee lo que devuelve.

terminalBASH
npm i @modelcontextprotocol/server-everything@2026.8.31
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \
  | npx @modelcontextprotocol/server-everything stdio
TEXT
{"result":{"tools":[{"name":"echo","title":"Echo Tool","description":"Echoes
back the input string","inputSchema":{"$schema":"http://json-schema.org/draft-07/
schema#","type":"object","properties":{"message":{"type":"string","description":
"Message to echo"}},"required":["message"]},"annotations":{"readOnlyHint":true,
…                                        … 7,663 bytes on one line …
"jsonrpc":"2.0","id":1}

Trece definiciones de herramientas, en una sola línea, desde un proceso que leyó una línea de su entrada estándar. Acabas de hablar el Model Context Protocol, sin SDK, sin biblioteca de cliente y sin framework. Eso es todo: un transporte, un formato de mensaje y un pequeño conjunto de métodos con nombre.

El capítulo 18 definió una herramienta como dos cosas: un JSON Schema que ve el modelo y un endpoint en tu código que el modelo nunca ve. El capítulo 23 construyó un harness que contiene un catálogo de ellas. Ninguno respondió a la pregunta que decide si algo de esto es reutilizable: quién escribe el esquema y cómo llega desde quien lo escribió hasta tu prompt. MCP es una respuesta a esa pregunta, y merece la pena leerlo en el original, porque casi todo lo que se ha escrito sobre él describe una revisión que ya no existe.

Hay tres cosas incorrectas en el comando que acabas de ejecutar, y cada una es una sección de este capítulo. No llevaba versión del protocolo, así que un servidor conforme lo habría rechazado. Aun así obtuvo una respuesta, por una razón que la especificación llama peligro y no función. Y pidió una de tres primitivas sin descubrir nunca que existen las otras dos.

El problema que resuelve y la analogía que hace la propia especificación

Enlace a la sección: El problema que resuelve y la analogía que hace la propia especificación

Antes del cable, la aritmética. Tienes NN aplicaciones de IA y MM cosas a las que deberían poder llegar: un calendario, un gestor de tickets, una base de datos de almacén, una herramienta de diseño. Sin un contrato compartido, alguien escribe N×MN \times M integraciones, y cada una de ellas es un esquema más un endpoint más una historia de autenticación más una carga de mantenimiento. Con uno, el proveedor de la herramienta escribe un servidor, el proveedor de la aplicación escribe un cliente, y el total es N+MN + M.

No es una observación nueva, y la especificación dice de quién tomó la idea:

MCP takes some inspiration from the Language Server Protocol, which standardizes how to add support for programming languages across a whole ecosystem of development tools. In a similar way, MCP standardizes how to integrate additional context and tools into the ecosystem of AI applications.1

Tómate esa comparación literalmente, no como un cumplido. Antes de ese protocolo, dar soporte a un lenguaje en un editor significaba un plugin por editor; después, el equipo del lenguaje distribuía un servidor y todos los editores lo obtenían. La medida del éxito no era la elegancia, sino que el número de integraciones dejaba de multiplicarse. Aquí pasa lo mismo: el valor está en el número de implementaciones, no en el diseño. Un protocolo que hablan dos productos es un formato de datos con ceremonia extra.

Los mensajes MCP son JSON-RPC 2.0. Una petición es un objeto con jsonrpc, un id, un method y params opcional; una respuesta lleva el mismo id y result o error; una notificación es una petición sin id y no recibe respuesta. La especificación añade tres restricciones encima: el id debe ser una cadena o un número y no debe ser null, no debe colisionar con otra petición aún en curso, y todo resultado debe llevar un campo resultType.2

En el transporte stdio —el que usó el comando anterior— la regla de framing es una línea por mensaje:

Messages are delimited by newlines, and MUST NOT contain embedded newlines. […] The server MUST NOT write anything to its stdout that is not a valid MCP message.3

Esa última cláusula es la forma más habitual en que se rompe un servidor casero, y se rompe en silencio: un console.log perdido, una barra de progreso, una advertencia de deprecación de una dependencia, y el parser de líneas del cliente encuentra algo que no es JSON. La vía de escape está en la misma sección: el servidor puede escribir lo que quiera en stderr, y el cliente no debería tratarlo como un error. El servidor de referencia anterior imprime Starting default (STDIO) server... en cada arranque, en stderr, por eso la tubería siguió funcionando.

El otro transporte estándar es Streamable HTTP: cada mensaje es un POST a un único endpoint, y la respuesta es o bien un objeto JSON o bien un flujo de Server-Sent Events con alcance de petición: el formato de cable que el capítulo 14 parseó a mano. La semántica es idéntica en ambos, porque un transporte es un binding: define framing y entrega, no significado.4

El comando anterior envió tools/list y nada más. Bajo la revisión actual, esa petición está mal formada, y un servidor conforme debe rechazarla.

Desde el 2026-07-28, MCP es un protocolo sin estado, y la especificación lo afirma sin matices:

The Model Context Protocol (MCP) is a stateless protocol: all the information needed to process a request is contained in the request itself. A server processes each request independently; no state should be inferred from previous requests, even those on the same connection or stream.2

Así que cada petición lleva su propia versión del protocolo y sus propias capacidades de cliente, en un objeto reservado _meta dentro de params. Dos de esos campos son obligatorios en todas y cada una de las peticiones; una petición a la que le falte cualquiera de ellos está mal formada y el servidor debe responder -32602:2

clave _metaobligatoriaqué es
io.modelcontextprotocol/protocolVersionla revisión que habla esta petición, por ejemplo "2026-07-28"
io.modelcontextprotocol/clientCapabilitiesqué puede hacer el cliente por el servidor en esta petición
io.modelcontextprotocol/clientInfono (pero debería)nombre y versión del cliente, solo para mostrar y logs
io.modelcontextprotocol/logLevelnoel nivel mínimo de log que el servidor debería emitir para esta petición

Escrita completa, una tools/list correcta es esta, y es la última vez que este capítulo muestra los metadatos completos, porque aparecen en todas las peticiones a partir de aquí:

one line, split for the pageTEXT
{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{"_meta":{
  "io.modelcontextprotocol/protocolVersion":"2026-07-28",
  "io.modelcontextprotocol/clientCapabilities":{"elicitation":{"form":{}}},
  "io.modelcontextprotocol/clientInfo":{"name":"bare-hands","version":"0.0.1"}}}}

El objeto de capacidades es la negociación. Ya no hay un paso de negociación separado: el cliente declara qué puede hacer en cada petición, el servidor declara qué puede hacer en el resultado, y ninguna parte puede usar una función que la otra no haya declarado. Un servidor que necesite una capacidad que el cliente no declaró debe responder -32021 y nombrar la capacidad que falta en data.requiredCapabilities. Un servidor que no hable la versión solicitada debe responder -32022 y listar las versiones que sí habla.2

Los clientes que quieren la respuesta por adelantado pueden pedirla: server/discover es una RPC obligatoria que devuelve versiones soportadas, capacidades, identidad y un bloque opcional de instructions en un solo viaje de ida y vuelta.5 Llamarla es opcional. Implementarla no.

Lo segundo que estaba mal: el servidor era legacy

Enlace a la sección: Lo segundo que estaba mal: el servidor era legacy

El comando funcionó. Bajo la revisión actual no debería haberlo hecho, y la razón merece una medición más que un párrafo, porque es el estado de todo el ecosistema en una línea.

Sondea el servidor de referencia como la especificación indica que debe hacerlo un cliente moderno:

terminalBASH
echo '{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{"_meta":{
  "io.modelcontextprotocol/protocolVersion":"2026-07-28",
  "io.modelcontextprotocol/clientCapabilities":{}}}}' \
  | npx @modelcontextprotocol/server-everything stdio
TEXT
{"jsonrpc":"2.0","id":1,"error":{"code":-32601,"message":"Method not found"}}

Esa es la tercera rama de la regla de compatibilidad: un DiscoverResult significa moderno, un error moderno reconocido significa moderno-pero-versión-incorrecta, y cualquier otra cosa —incluido -32601— significa legacy, vuelve al handshake initialize.3 Así que hazlo, pidiendo la revisión actual:

TEXT
→ {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2026-07-28",
   "capabilities":{},"clientInfo":{"name":"bare-hands","version":"0.0.1"}}}

← {"result":{"protocolVersion":"2025-11-25","capabilities":{"tools":{"listChanged":true},
   "prompts":{"listChanged":true},"resources":{"subscribe":true,"listChanged":true},
   "logging":{},"tasks":{…},"completions":{}},"serverInfo":{"name":"mcp-servers/everything",
   "title":"Everything Reference Server","version":"2.0.0"},"instructions":"…"}}

El cliente pidió 2026-07-28 y el servidor respondió 2025-11-25. El 7 de septiembre de 2026, el servidor de referencia oficial —paquete npm @modelcontextprotocol/server-everything, versión 2026.8.31, publicado el 31 de agosto de 2026— no implementa la revisión actual. Tampoco, por las fechas, lo hace el SDK de TypeScript sobre el que está construido: la release 1.30.0 salió el 27 de julio de 2026, el día antes de la revisión.

Lee la consecuencia, no el cotilleo. Casi todo lo escrito sobre MCP describe un protocolo con un handshake initialize, una sesión, una petición roots/list que el servidor envía al cliente y un transporte HTTP+SSE. Las cuatro cosas han desaparecido o van camino de hacerlo. Cuando leas cualquier cosa sobre MCP, incluida esta página, lo primero que debes buscar es un número de revisión.

Y la razón por la que funcionó el primerísimo comando aparece en la especificación como un peligro, no como una función:

some legacy servers do not validate that a request arrives after initialize and would process an era-ambiguous method (such as tools/call) under legacy semantics. Probing yields a deterministic failure instead.3

Medido: enviar tools/list a ese servidor sin ningún handshake devuelve el catálogo completo. Un método que debería haberse rechazado fue atendido, que es exactamente por lo que la especificación dice que primero hay que sondear con server/discover aunque solo soportes versiones modernas.

Tres roles y la frase que hay que citar de todo el documento

Enlace a la sección: Tres roles y la frase que hay que citar de todo el documento

MCP tiene tres partes, y la distinción entre las dos primeras es la que la gente suele confundir:

Host. La aplicación: el producto de chat, el editor, el agent. Posee la conversación, el modelo, las credenciales y el consentimiento del usuario. Crea clientes y aplica el límite de seguridad entre ellos.

Cliente. Un conector dentro del host. Cada cliente habla con exactamente un servidor —una relación estricta 1:1— y adjunta la versión del protocolo y las capacidades a cada petición que enruta.

Servidor. Un proceso o servicio que expone recursos, herramientas y prompts. Puede ser local o remoto, opera de forma independiente, y todo su trabajo es un área concreta.6

Esa regla de «exactamente un servidor» no es contabilidad. Es lo que hace implementable el principio de diseño de abajo, y esta es la frase que debes llevarte de la especificación si solo te llevas una:

Servers should not be able to read the whole conversation, nor "see into" other servers. Servers receive only necessary contextual information. Full conversation history stays with the host. Each server maintains isolation. Cross-server interactions are controlled by the host.6

Eso invierte el modelo mental con el que llega la mayoría. Un servidor del tiempo que conectas a tu asistente no ve lo que preguntaste. Ve un tools/call con los argumentos que eligió el modelo, y nada más: ni los turnos anteriores, ni tu system prompt, ni los resultados que devolvió el servidor de calendario hace un momento. Si dos servidores necesitan cooperar, el host lleva un valor de uno a otro, deliberadamente, porque el modelo se lo pidió. Por eso el aislamiento es la propiedad de seguridad en la que se apoya el capítulo 30: un servidor comprometido tiene un radio de explosión pequeño y definido, y ampliarlo exige que el host coopere.

La tercera cosa: tres primitivas, ordenadas por quién manda

Enlace a la sección: La tercera cosa: tres primitivas, ordenadas por quién manda

El primer comando pidió herramientas a ese servidor y obtuvo trece. Hazle las otras dos preguntas y también las responde: resources/list devuelve siete, prompts/list devuelve cuatro. Ninguna apareció, porque nadie preguntó. Esto nos lleva a la columna vertebral pedagógica de MCP, presente en la especificación como una tabla que casi nadie cita:

PrimitivaControlDescripciónEjemplo
PromptsControlado por el usuarioPlantillas interactivas invocadas por elección del usuarioComandos slash, opciones de menú
RecursosControlado por la aplicaciónDatos contextuales adjuntados y gestionados por el clienteContenido de archivos, historial de git
HerramientasControlado por el modeloFunciones expuestas al LLM para realizar accionesPeticiones API POST, escritura de archivos

No son «tres formas de exponer una capacidad». Son tres respuestas a quién decide que esto ocurra. El modelo decide llamar a una herramienta. La aplicación decide adjuntar un recurso. La persona decide ejecutar un prompt. Si te equivocas ahí, la función sigue funcionando, pero funciona en el momento equivocado y por la razón equivocada.

La forma más clara de sentirlo es un calendario. Aquí hay un servidor que expone el mismo calendario tres veces, una como cada primitiva, en cien líneas de Node puro sin dependencias:

calendar.mjs — the parts that matterJS
const TOOL = {
  name: "create_event",
  description: "Create a calendar event. Writes to the calendar.",
  inputSchema: {
    type: "object",
    properties: {
      title:    { type: "string", description: "Event title." },
      startsAt: { type: "string", format: "date-time", description: "Start, ISO 8601 UTC." },
    },
    required: ["title"],
  },
};

switch (method) {
  case "resources/read":                                          
    return ok(id, { contents: [{ uri: "calendar://week",
      mimeType: "application/json", text: JSON.stringify(EVENTS) }],
      ttlMs: 60000, cacheScope: "private" });

  case "prompts/get":                                             
    return ok(id, { description: PROMPT.description, messages: [{ role: "user",
      content: { type: "text", text: `Read calendar://week and draft a plan. ` +
        `Focus: ${params.arguments?.focus ?? "balance"}.` } }] });

  case "tools/list":                                              
    return ok(id, { tools: [TOOL], ttlMs: 300000, cacheScope: "public" });
}

Ejecútalo y pregúntale de las tres maneras. Salida real, un mensaje por línea en el cable, ajustada aquí para la página, con el _meta de la petición y el bloque de identidad del servidor omitidos:

TEXT
→ resources/read  {"uri":"calendar://week"}
← {"resultType":"complete","contents":[{"uri":"calendar://week",
   "mimeType":"application/json","text":"[{\"id\":\"e1\",\"title\":\"Standup\",
   \"startsAt\":\"2026-09-07T09:00:00Z\"},{\"id\":\"e2\",\"title\":\"Design review\",
   \"startsAt\":\"2026-09-09T15:00:00Z\"}]"}],"ttlMs":60000,"cacheScope":"private"}

→ prompts/get    {"name":"prepare_week","arguments":{"focus":"deep work"}}
← {"resultType":"complete","description":"Read the week and draft a plan.",
   "messages":[{"role":"user","content":{"type":"text",
   "text":"Read calendar://week and draft a plan. Focus: deep work."}}]}

→ tools/call     {"name":"create_event","arguments":{"title":"Dentist",
                  "startsAt":"2026-09-10T08:30:00Z"}}
← {"resultType":"complete","content":[{"type":"text",
   "text":"Created e3: Dentist at 2026-09-10T08:30:00Z"}],
   "structuredContent":{"id":"e3","title":"Dentist","startsAt":"2026-09-10T08:30:00Z"},
   "isError":false}

Tres métodos, tres formas, un calendario. Ahora, la idea:

Se direcciona mediante una URI, es inerte y la aplicación decide si adjuntarlo a la conversación. Nada en el protocolo permite que el modelo lo coja por su cuenta. El resultado lleva ttlMs y cacheScope, nuevos en esta revisión, para que el cliente pueda cachear la semana durante un minuto en lugar de hacer polling.

Tiene un esquema, tiene efectos secundarios, y el modelo decide cuándo llamarla. Su resultado lleva isError, que es el campo que defendía el capítulo 18: un fallo de validación vuelve como resultado de herramienta que el modelo puede leer y corregir, no como error de protocolo.

Es una plantilla con nombre y argumentos que la persona invoca: el comando slash en el menú. Devuelve mensajes, no una respuesta. Es una forma de que quien escribe el servidor distribuya la formulación que funciona con sus propias herramientas, que es exactamente el conocimiento que tiene quien escribe el servidor y que no tiene el usuario.

Casi todo el mundo convierte las tres cosas en herramientas. El resultado es un catálogo donde una lectura que la aplicación debería haber adjuntado en silencio compite por la attention del modelo con una escritura que necesita aprobación, y donde lo único para lo que una persona quería un botón queda enterrado en un esquema. Hacerlo bien no cuesta nada, y se decide antes de escribir una línea.

La herramienta de calendario tiene un argumento obligatorio, title, y uno opcional, startsAt. Pídele que cree un evento sin fecha y vuelve algo interesante:

TEXT
→ tools/call {"name":"create_event","arguments":{"title":"Dentist"}}

← {"resultType":"input_required",
   "inputRequests":{"when":{"method":"elicitation/create","params":{"mode":"form",
     "message":"When should \"Dentist\" start?",
     "requestedSchema":{"type":"object",
       "properties":{"startsAt":{"type":"string","format":"date-time"}},
       "required":["startsAt"]}}}},
   "requestState":"eyJ0aXRsZSI6IkRlbnRpc3QifQ=="}

El servidor no envió una petición. Respondió a la que recibió, con resultType: "input_required" y una descripción de lo que todavía necesita. El cliente recoge la respuesta de la persona y luego reenvía la llamada original, con un nuevo id, llevando inputResponses y devolviendo el requestState opaco:

TEXT
→ tools/call {"name":"create_event","arguments":{"title":"Dentist"},
   "inputResponses":{"when":{"action":"accept",
     "content":{"startsAt":"2026-09-10T08:30:00Z"}}},
   "requestState":"eyJ0aXRsZSI6IkRlbnRpc3QifQ=="}

← {"resultType":"complete","content":[{"type":"text",
   "text":"Created e3: Dentist at 2026-09-10T08:30:00Z"}],"isError":false}

Esto es Multi Round-Trip Requests, introducido en la revisión actual, y sustituyó al diseño anterior en el que los servidores enviaban peticiones JSON-RPC de vuelta a los clientes. La especificación de transporte ahora enuncia la regla sin rodeos: «servers do not initiate JSON-RPC requests and clients do not send JSON-RPC responses».4 Hay una sola dirección de iniciativa, y pertenece al host.

Dos funciones del lado del cliente se apoyan en ese mecanismo, y una de ellas tiene un nombre que te hará tropezar.

Elicitation es que el servidor pida algo a la persona: un formulario con un JSON Schema deliberadamente restringido —objetos planos, propiedades primitivas, sin anidamiento— para que cualquier cliente pueda renderizarlo sin un motor de layout. Lleva una regla estricta: los servidores no deben usar el modo formulario para pedir «passwords, API keys, access tokens, or payment credentials», y deben usar el modo URL para eso, que envía al usuario a una página que el cliente nunca lee.7

Sampling es que el servidor pida una generación al modelo del host, para que un servidor pueda ser inteligente sin tener una API key. Y aquí va la advertencia de vocabulario, porque esta palabra ya significa otra cosa en este curso: esto no es el sampling del capítulo 17. Aquí nada va de temperatura, top-p ni de la forma de una distribución de probabilidad. Es una llamada anidada al modelo que viaja hacia atrás por un protocolo.

Hay una segunda razón para no recurrir a ello: en esta revisión, sampling está deprecated, junto con roots y logging, bajo SEP-2577, con una migración sugerida sin rodeos: «integrate directly with LLM provider APIs instead of Sampling».8 La idea no falló técnicamente; no logró justificar su superficie, y un protocolo que puede quitar cosas es más sano que uno que no puede.

Rómpelo a propósito: las conexiones no son sesiones

Enlace a la sección: Rómpelo a propósito: las conexiones no son sesiones

La ausencia de estado suena a detalle de formato de cable hasta que la pruebas. Toma el intercambio de tres mensajes anterior y ejecuta cada mensaje en un proceso separado: un node calendar.mjs nuevo, sin memoria compartida, sin nada trasladado:

TEXT
process A   tools/call (no date)   → resultType: input_required
                                     requestState: eyJ0aXRsZSI6IkRlbnRpc3QifQ==
process B   tools/call (with the answer, same requestState)
                                   → resultType: complete
                                     "Created e3: Dentist at 2026-09-10T08:30:00Z"
process C   resources/read calendar://week
                                   → events: 2  (Standup, Design review)

El proceso B, que nunca vio la pregunta, completó una llamada multi-round-trip que empezó el proceso A. Ese es el sentido de requestState: la continuación viaja en el mensaje, así que nada depende de que el proceso sea el mismo.

El proceso C es el fallo. El evento se creó y no está ahí, porque el servidor de juguete guarda EVENTS en un array a nivel de módulo, y un array a nivel de módulo es estado de conexión. La nota de la especificación nombra el error con precisión:

an open connection, such as a STDIO process, is not a conversation or session: clients may interleave unrelated requests on the same transport, and a server must not treat connection or process identity as a proxy for conversation or session continuity.2

La solución prescrita no es una sesión. Es un handle explícito: una herramienta de creación devuelve un identificador opaco, y cada llamada posterior lo toma como argumento ordinario. El protocolo no tiene ningún concepto de él: «from the wire's perspective a handle is an ordinary string in a tool result and an ordinary argument to subsequent tool calls».9 Eso pone al modelo a cargo de llevarlo, y al servidor a cargo de validar que quien llama tiene permiso para usarlo en cada llamada, porque un handle es un nombre y no un permiso.

Cada herramienta que expone un servidor es un esquema que entra en tu prompt en cada petición, y el capítulo 24 midió lo que eso le hace a una ventana. MCP añade una segunda partida fácil de pasar por alto, así que conviene contar ambas en el servidor de referencia anterior.

o200k_base tokensTEXT
13 tool definitions (name + description + inputSchema):  1,307 tokens
  cheapest tool, get-tiny-image                              52
  costliest tool, gzip-file-as-resource                     235
server `instructions`, returned by discovery:               312 tokens
                                                          ------
one server, connected, before it is used:                 1,619 tokens

Dos observaciones. La primera es aritmética: conecta cinco servidores de este tamaño y unos ocho mil tokens de tu ventana quedan ocupados en cada turno, para siempre, use o no el modelo alguno de ellos; ese es el mecanismo detrás de la reducción de 150.000 a 2.000 que citaba el capítulo 24, y la razón por la que existe el descubrimiento de herramientas justo a tiempo.

La segunda es una nota de seguridad disfrazada de contabilidad. instructions es texto en lenguaje natural, escrito por quien creó el servidor, que acaba en el prompt del host, y las descripciones de las herramientas que lo acompañan son lo mismo. La especificación dice qué hacer con eso en sus propios principios de seguridad: las anotaciones y descripciones de herramientas «should be considered untrusted, unless obtained from a trusted server», y los hosts «must obtain explicit user consent before invoking any tool».1 Conectar un servidor MCP no es añadir una dependencia. Es conceder a un desconocido 1.619 tokens de tu system prompt y el derecho a ser llamado. El capítulo 30 trata de lo que ocurre cuando ese desconocido es hostil.

Sección fechada: la revisión 2026-07-28 y lo que rompe

Enlace a la sección: Sección fechada: la revisión 2026-07-28 y lo que rompe

Todo en esta sección es cierto para la revisión del protocolo 2026-07-28, la actual, leída el 7 de septiembre de 2026. Las revisiones se fechan como YYYY-MM-DD y la fecha es la última vez que se hizo un cambio incompatible hacia atrás.10 El documento normativo es un archivo TypeScript, schema/2026-07-28/schema.ts; el JSON Schema que lo acompaña se genera a partir de él, por eso aquí la especificación se lee en TypeScript y por eso enseñar MCP desde cualquier otra cosa es enseñar una traducción.

Qué cambióEraAhora esRompe
El handshakeinitialize + notifications/initialized, una vez por conexióneliminado; cada petición lleva versión y capacidades _metatodo cliente escrito antes de esta revisión
Sesionescabecera Mcp-Session-Id, estado con alcance de conexióneliminadas; el estado viaja en handles explícitos acuñados por el servidorendpoints de listado que variaban por conexión
Descubrimientoinferido del resultado de initializeserver/discover, que los servidores deben implementarnada, pero ahora implementarlo es obligatorio
Llamadas de servidor a clienteel servidor enviaba roots/list, sampling/createMessage, elicitation/createInputRequiredResult y reintento del clientetodo servidor que empujaba una petición a un cliente
Forma del resultadocualquier objetoresultType obligatorio: "complete" o "input_required"nada: un campo ausente debe leerse como "complete"
Suscripcionesstream HTTP GET, resources/subscribeun stream subscriptions/listen con tipos opt-inel endpoint GET desaparece
Reanudación de streamreplay Last-Event-ID en Streamable HTTPeliminado; un stream roto pierde la petición, vuelve a emitirla con un nuevo idclientes que dependían de la redelivery
Rootsuna función de cliente que los servidores podían pedirdeprecated (SEP-2577); pasa rutas como argumentos de herramienta o URI de recursonada aún: ventana de doce meses
Sampling y loggingfunciones de clientedeprecated (SEP-2577)nada aún: ventana de doce meses
Transporte HTTP+SSEdeprecated desde 2025-03-26Deprecated bajo la política de ciclo de vida (SEP-2596)migra a Streamable HTTP
Registro de clienteOAuth 2.0 Dynamic Client Registration, RFC 7591deprecated a favor de Client ID Metadata Documentsse mantiene para authorization servers sin ellos
Códigos de error-32002 para recurso no encontrado-32602; -32020-32099 reservados para la especificaciónnuevos códigos -32020, -32021, -32022

El cambio de gobernanza bajo esa tabla importa más que cualquier fila concreta. Esta revisión adoptó una política de ciclo de vida de funciones y deprecación: las funciones son Active, Deprecated o Removed, una función deprecated documenta su ruta de migración y permanece en la especificación al menos doce meses antes de ser elegible para eliminación, y hay un registro que lista todo lo que está actualmente en estado Deprecated.8 Antes de esa política, «deprecated» en un protocolo de IA significaba lo que dijera el último post del blog. Ahora significa una fecha.

Mostrar detalles

Extensiones, la parte sobre la que aún no ha escrito nadie.

Más allá del núcleo, MCP define extensiones opcionales: «always opt-in and require explicit support from both client and server», declaradas mediante un campo extensions en las capacidades del cliente y del servidor.1 Hay tres que conviene conocer por nombre:

  • Tasks (io.modelcontextprotocol/tasks), sacada del protocolo central y convertida en extensión oficial en esta revisión: ejecución asíncrona de operaciones largas, con polling mediante tasks/get, entrada en mitad del vuelo mediante tasks/update y handles duraderos. Es la respuesta a una herramienta que tarda veinte minutos, que el capítulo 23 resolvió con un evento de progreso y una señal que llega a la herramienta.
  • Skills over MCP, un grupo de trabajo que hace que las agent skills —tema del capítulo 28— sean descubribles y consumibles mediante el protocolo.
  • MCP Apps, UI interactiva renderizada inline en la conversación: gráficos, formularios, reproductores de vídeo.

Y fíjate en qué significa ahora «negociado»: no hay inicialización en la que negociar, así que una extensión se declara por petición como todo lo demás.

Dónde encaja MCP frente a todo aquello con lo que se confunde

Enlace a la sección: Dónde encaja MCP frente a todo aquello con lo que se confunde

Este es el vocabulario de todo el bloque en un solo lugar.

Qué esQuién habla con quiénCuándo es la respuesta
Una API normalUna interfaz para un programatu código ↔ un servicioEstás escribiendo quien llama. Controlas el esquema, la auth y el manejo de errores, y no hay ningún problema de descubrimiento que resolver.
MCPUn protocolo para exponer herramientas, datos y plantillas a una aplicación de IAhost ↔ servidor, un cliente cada unoOtra persona escribió la capacidad y muchos hosts deberían poder usarla sin una integración a medida.
RAGUna técnica para encontrar texto y meterlo en el prompttu código ↔ tu índiceEl modelo necesita saber algo. Capítulo 19. MCP es una forma de entregar un recuperador; no es un recuperador.
Agent skillsUna carpeta con un SKILL.md que el modelo leemodelo ↔ un documentoEl conocimiento es procedimental —cómo lo hacemos nosotros— y es prosa, no una función. Capítulo 28.
A2AUn protocolo para que los agents colaboren como paresagent ↔ agentEl otro lado razona, planifica y mantiene estado durante una tarea larga, en lugar de responder a una llamada.
ACPEra un protocolo separado de comunicación entre agentsYa no es una comparación viva. Ver abajo.

Dos de ellos merecen una frase cada uno, porque ahí es donde vive realmente la confusión.

MCP frente a A2A no es una rivalidad, y ambas especificaciones lo dicen. La documentación de A2A traza la línea por lo que hay al otro extremo: MCP «defines how an AI agent interacts with and utilizes individual tools and resources, such as a database or an API», donde una herramienta realiza «specific, often stateless, functions»; A2A aborda agents, «more autonomous systems» que «reason, plan, use multiple tools, maintain state over longer interactions, and engage in complex, often multi-turn dialogues». Su propio resumen es la frase que debes recordar: «A2A is about agents partnering on tasks, while MCP is more about agents using capabilities».11 Los dos se anidan: una aplicación usa A2A para llegar a otros agents, y cada agent usa MCP para llegar a sus propias herramientas. El capítulo 25 trazó esa línea dentro de un proceso, entre pedir a un sub-agent y entregarle la conversación; A2A la traza entre organizaciones.

MCP frente a ACP es una comparación con una premisa caduca, que es exactamente por lo que merece la pena responderla. Agent Communication Protocol era un estándar abierto separado para mensajería agent-to-agent. Su propia documentación ahora abre con el aviso: «ACP is now part of A2A under the Linux Foundation!»12 La respuesta honesta a «¿MCP o ACP?» en septiembre de 2026 es que la pregunta tiene una opción menos de lo que sugieren las páginas que posicionan para ella.

Y la comparación que más pide la gente, mcp vs api, tiene la respuesta menos interesante: MCP es una API. Lo que añade no es potencia, sino convenciones: un conjunto fijo de nombres de métodos, una llamada de descubrimiento, una jerarquía de control sobre las primitivas y un modelo de aislamiento. Renuncias a la libertad de diseñar tu propia interfaz y obtienes todos los hosts que hablan el protocolo, que es el intercambio que todo protocolo ha ofrecido siempre.

Ahora puedes leer la especificación sin traductor, distinguir un recurso de una herramienta y de un prompt por quién manda sobre él, escribir una petición a mano cuando una biblioteca de cliente te está mintiendo, y fechar cualquier artículo sobre MCP que leas por cuáles de las funciones deprecated sigue enseñando como actuales.

Lo que no has hecho es publicar uno. El capítulo 27 escribe el mismo servidor dos veces —TypeScript y Python, lado a lado—, porque MCP es el único territorio genuinamente bilingüe de este curso y los números lo dicen en ambas direcciones. Cubre bien los dos transportes vivos, el inspector, el empaquetado y la mitad del protocolo que este capítulo ha dejado deliberadamente aparte: autorización. Porque en el momento en que tu servidor es remoto y no un subproceso en tu propio portátil, el cliente de un desconocido presentará un token, y la regla de la especificación sobre qué puedes hacer con él es inusualmente estricta.

Lo que plantea la pregunta que tiene que responder el próximo capítulo, y no es amable: si llega un token a tu servidor y fue emitido para la audiencia de otra persona, ¿qué impide exactamente que lo reenvíes?


Cada cita, nombre de método, código de error y regla de este capítulo se leyó en la especificación de Model Context Protocol, revisión 2026-07-28, el 7 de septiembre de 2026. Cada traza se produjo localmente en Node 22: el servidor de calendario de juguete tiene 101 líneas y ninguna dependencia, y el servidor de referencia es el paquete npm publicado que se nombra abajo. No se llamó a ninguna API de pago para escribir este capítulo: nada aquí necesita un modelo, que es precisamente la idea.

Las mediciones: @modelcontextprotocol/server-everything@2026.8.31, publicado el 31 de agosto de 2026, construido sobre @modelcontextprotocol/sdk@1.30.0, publicado el 27 de julio de 2026, un día antes de la revisión que describe este capítulo. Responde a server/discover con -32601, negocia 2025-11-25 cuando se le pide 2026-07-28, y sirve tools/list sin ningún handshake. Su catálogo son 13 herramientas en 7.663 bytes; los recuentos de tokens son o200k_base mediante tiktoken, sobre name, description y inputSchema de cada definición, que es lo que un proveedor renderiza en tu prompt y no lo que pesa el frame JSON-RPC.

Anthropic, Code execution with MCP: building more efficient agents, 4 de noviembre de 2025, es la fuente de la cifra de 150.000 a 2.000, citada y usada en el capítulo 24 y solo referenciada aquí.

  1. Specification, modelcontextprotocol.io/specification/latest (redirige a /2026-07-28), leído el 7 de septiembre de 2026. Fuente de la comparación con Language Server Protocol; la afirmación de que la especificación está «based on the TypeScript schema in schema.ts»; el resumen del protocolo base («Stateless, self-contained requests», «Per-request capability negotiation»); la lista de extensiones (Tasks, Skills over MCP, MCP Apps) y la afirmación de que las extensiones «are always opt-in and require explicit support from both client and server»; y los principios de Security y Trust & Safety, incluidos «Hosts must obtain explicit user consent before invoking any tool» y el tratamiento de las anotaciones de herramientas como no confiables. 2 3

  2. Base Protocol, modelcontextprotocol.io/specification/2026-07-28/basic. Fuente de las restricciones JSON-RPC (id no null, no reutilizar id, resultType obligatorio); la sección Statelessness y su nota de que un proceso stdio abierto no es una sesión; la tabla de claves reservadas _meta y el estado obligatorio/opcional de cada campo por petición; la regla -32602 para un campo obligatorio ausente; la regla MissingRequiredClientCapability (-32021); y la política de asignación de códigos de error. 2 3 4 5

  3. stdio transport, modelcontextprotocol.io/specification/2026-07-28/basic/transports/stdio. Fuente de las reglas de framing delimitado por saltos de línea, el requisito de pureza de stdout, la autorización de stderr y el sondeo de compatibilidad hacia atrás con tres resultados, incluida la advertencia de que algunos servidores legacy procesan métodos ambiguos de época sin handshake, algo que reproduce la medición de este capítulo. 2 3

  4. Transports overview, modelcontextprotocol.io/specification/2026-07-28/basic/transports. Fuente del encuadre de «a transport is a binding» y de la afirmación de que los servidores no inician peticiones JSON-RPC y los clientes no envían respuestas JSON-RPC. 2

  5. Discovery, modelcontextprotocol.io/specification/2026-07-28/server/discover. Fuente del carácter obligatorio de server/discover, la forma de DiscoverResult y el campo instructions descrito como «optional natural-language guidance for LLMs on how to use this server effectively».

  6. Architecture, modelcontextprotocol.io/specification/2026-07-28/architecture. Fuente de las definiciones de host/cliente/servidor, la regla 1:1 de cliente a servidor, los cuatro principios de diseño, de los cuales aquí se cita el principio de aislamiento sin su quinta viñeta, «Host process enforces security boundaries», y la sección de negociación de capacidades. 2

  7. Elicitation, .../client/elicitation, y Sampling, .../client/sampling. Fuente de los dos modos de elicitation y su esquema restringido; la prohibición de pedir credenciales mediante modo formulario; la definición de sampling, su requisito human-in-the-loop y la advertencia de deprecación adjunta.

  8. Key Changes, modelcontextprotocol.io/specification/2026-07-28/changelog, y Feature lifecycle and deprecation policy, .../community/feature-lifecycle. Fuente de cada fila de la tabla de cambios: eliminación de sesiones y la cabecera Mcp-Session-Id (SEP-2567); ausencia de estado y eliminación de initialize (SEP-2575); server/discover (SEP-2575); subscriptions/listen (SEP-2575); Multi Round-Trip Requests y resultType (SEP-2322); eliminación de la capacidad de reanudar streams (SEP-2575); deprecación de Roots, Sampling y Logging (SEP-2577); reclasificación de HTTP+SSE (SEP-2596); deprecación de Dynamic Client Registration a favor de Client ID Metadata Documents; renumeración de códigos de error; y ventana de deprecación de doce meses. 2

  9. Tools, modelcontextprotocol.io/specification/2026-07-28/server/tools, y Server Features, .../server. Fuente de la tabla de jerarquía de control reproducida arriba; las formas de tools/list y tools/call; la distinción de isError entre errores de protocolo y errores de ejecución de herramienta; las reglas de nombres de herramientas y la nota de namespace que recomienda «prefixing tool names with a server identifier»; y la guía no normativa «Stateful Tools» sobre handles explícitos.

  10. Versioning, modelcontextprotocol.io/specification/versioning. Fuente del esquema YYYY-MM-DD, los estados de revisión Draft/Current/Final, la confirmación de que 2026-07-28 es la actual y las reglas de negociación por petición. La tabla de tiers del SDK en modelcontextprotocol.io/docs/sdk lista TypeScript, Python, C#, Go y Rust en Tier 1; Java y Ruby en Tier 2; y Swift, PHP y Kotlin en Tier 3.

  11. A2A Protocol, versión 1.0.0, a2a-protocol.org: la especificación y la página A2A and MCP: Relationship and Distinction, leídas el 7 de septiembre de 2026. Fuente de la distinción herramientas-frente-a-agents, la afirmación de que ambos protocolos «address distinct but highly complementary needs» y la formulación partnering/using.

  12. Agent Communication Protocol, agentcommunicationprotocol.dev, leído el 7 de septiembre de 2026: «ACP is now part of A2A under the Linux Foundation!», un banner añadido sobre una especificación que todavía se sirve completa: arquitectura, manifiesto de agent, descubrimiento de agent, estructura de mensajes, agents con estado, ciclo de vida de ejecución y lista de endpoints REST siguen respondiendo 200. La especificación no desapareció; el proyecto sí.

¿Listo para dejar que elija LIA?

Crea con todos los modelos de IA en un mismo sitio. Empieza gratis hoy.