El 28 de julio de 2026 el Model Context Protocol publicó una nueva revisión de su especificación. El registro de cambios empieza con dos puntos que cambian el funcionamiento de todo servidor remoto: las sesiones a nivel de protocolo desaparecen, y también el handshake initialize. Cada petición lleva ahora todo lo que el servidor necesita para responderla.

Para muchos servidores eso es una migración. Para un servidor que lee documentos de identidad se parece más a una confirmación, porque ese tipo de servidor ya tenía buenas razones para no guardar estado. Este artículo recorre la revisión desde el punto de vista de un servidor de herramientas: qué cambios puede ignorar, cuáles tiene que construir y cuáles conviene adoptar pronto. Los ejemplos vienen de un servidor que operamos, así que las carencias que se nombran abajo también son nuestras.

Este es el blog de doc.cheap, una API de OCR para pasaportes y documentos de identidad con un servidor MCP delante. Lea las partes sobre el producto teniendo eso en cuenta.

La versión corta

Cambio en 2026-07-28 Qué hace un servidor de herramientas sin estado
Sin Mcp-Session-Id, sin sesiones Nada, si nunca guardó una
Sin handshake initialize; versión y capacidades en el _meta de cada petición Leerlas en cada petición y comprobar que coinciden con la cabecera MCP-Protocol-Version
server/discover Implementarlo: es un MUST
Cabeceras Mcp-Method y Mcp-Name obligatorias Comprobar que coinciden con el cuerpo
resultType en cada resultado Enviar "complete"
ttlMs y cacheScope en los resultados de listado y lectura Elegir una duración de caché y un ámbito
Herramientas en un orden determinista Mantener estable el orden de registro
Se eliminan el stream HTTP GET y la reanudación Responder a GET y DELETE con 405
Roots, Sampling y Logging quedan obsoletos Recibir archivos como argumentos de herramienta, registrar en stderr u OpenTelemetry

El resto del artículo explica cada fila y por qué pesa más en un servidor que maneja documentos.

Las sesiones desaparecen, y eso le sienta bien a un servidor de documentos

El primer punto del registro de cambios elimina "protocol-level sessions and the Mcp-Session-Id header from the Streamable HTTP transport". Los endpoints de listado como tools/list "no longer vary per-connection", y los servidores que necesitan estado entre llamadas "use explicit, server-minted handles passed as ordinary tool arguments", es decir, identificadores explícitos emitidos por el servidor y pasados como argumentos normales de la herramienta.

Un servidor de documentos rara vez quiere una sesión. Piense en lo que guardaría: una clave de API, quizá una imagen pendiente de leer, quizá el último resultado. Todo eso son datos que no conviene mantener en la memoria de un proceso entre llamadas, donde un fallo puede entregárselos al siguiente cliente.

Nuestro servidor alojado en https://mcp.doc.cheap/mcp ya estaba construido así antes de esta revisión. El código dice que es "stateless on purpose": cada POST recibe un servidor nuevo y un transporte nuevo, responde y se descarta, de modo que "no session lives in memory, a restart loses nothing, and one caller's key can never be seen by another caller's request". El transporte se crea sin generador de id de sesión, que es lo que en el SDK de TypeScript lo hace sin estado, y responde con JSON simple en lugar de un stream.

El patrón de identificadores que describe la revisión también está ya ahí. Cuando un agente quiere recuperar un resultado guardado, no depende de que una sesión lo recuerde. scan_document devuelve un id de escaneo, y las herramientas get_scan y delete_scan reciben ese id como un argumento normal scan_id. El id es el identificador. Cualquier instancia del servidor puede responderlo, y también un cliente que se reconecte mañana.

Si su servidor guarda algo por sesión, la pregunta es la misma: ¿puede convertirse en un id que el servidor devuelve y el cliente vuelve a enviar? En el trabajo con documentos la respuesta suele ser sí, y suele ser también una ganancia de privacidad.

Sin handshake: la versión viaja con cada petición

El segundo punto hace que MCP no tenga estado a nivel de protocolo. Se elimina el intercambio initialize y notifications/initialized, y "every request now carries its protocol version and client capabilities in _meta". En HTTP la versión va también en una cabecera. La página de Streamable HTTP dice que cada POST "MUST include an MCP-Protocol-Version header", y que debe coincidir con el valor de _meta; si no, el servidor responde 400 con un error HeaderMismatch.

Así se ve una llamada a una herramienta de documentos con la nueva forma. Las cabeceras y las claves de _meta siguen el ejemplo de la propia especificación; la herramienta y sus argumentos son nuestros, con marcadores de posición:

POST /mcp HTTP/1.1
Content-Type: application/json
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: scan_document

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "scan_document",
    "arguments": { "image_base64": "<base64 image>", "retain_hours": 0 },
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientInfo": { "name": "<client>", "version": "<version>" },
      "io.modelcontextprotocol/clientCapabilities": {}
    }
  }
}

Para el servidor esto significa tres comprobaciones en cada petición en lugar de un único handshake:

  1. La versión. Si el servidor no admite la versión pedida, responde 400 con un UnsupportedProtocolVersionError que enumera las versiones que sí admite.
  2. Las cabeceras frente al cuerpo. Mcp-Method es obligatoria en cada petición, y Mcp-Name en tools/call, resources/read y prompts/get. Reflejan el método y el nombre de la herramienta, y una discrepancia es un error HeaderMismatch.
  3. Las capacidades. Lo que el servidor antes recordaba de initialize ahora lo lee de la petición que tiene delante.

Las cabeceras importan más de lo que parece en un servidor de documentos. Una llamada a scan_document lleva una imagen entera en base64; nuestro servidor alojado acepta cuerpos de petición de hasta 36 MiB. Con Mcp-Method y Mcp-Name en las cabeceras, un gateway puede enrutar, contar o limitar la herramienta costosa por separado de las baratas sin leer ese cuerpo. La especificación añade una advertencia para esos gateways: "SHOULD verify" que la cabecera de versión nombra una versión que exige validar cabeceras y cuerpo, y rechazar la petición en caso contrario, en lugar de fiarse de una cabecera que nadie comprobó.

server/discover es obligatorio

El tercer punto añade server/discover, y "servers MUST implement this RPC to advertise their supported protocol versions, capabilities, and identity". Los clientes pueden llamarlo primero para elegir una versión, o usarlo como sonda de compatibilidad en stdio.

Este es el único cambio que un servidor sin estado no puede saltarse. Nuestro servidor sigue exponiendo un pequeño documento de descubrimiento en GET / (nombre, versión, transporte y endpoint), pero es una página web corriente, no el método del protocolo. Un servidor que quiera hablar 2026-07-28 responde server/discover por sí mismo.

Resultados: resultType, caché y orden

Tres puntos menores cambian el aspecto de un resultado.

  • resultType. Ahora lo lleva cada resultado: "complete" para una respuesta normal, "input_required" para la respuesta intermedia de una petición de varias idas y vueltas. Los clientes "MUST treat results from earlier-protocol servers that omit the field as "complete"", así que un servidor antiguo sigue funcionando, pero uno nuevo lo envía.
  • ttlMs y cacheScope. Los resultados de tools/list, prompts/list, resources/list, resources/read y resources/templates/list deben llevar una indicación de vigencia en milisegundos y un ámbito, "public" o "private". Para un servidor cuya lista de herramientas solo cambia con cada despliegue y cuyos recursos son documentación empaquetada, un ttlMs largo con "public" encaja de forma natural. Un recurso que depende de la clave del cliente sería "private".
  • Orden determinista de las herramientas. Los servidores "SHOULD return tools from tools/list in a deterministic order", para ayudar a la caché del cliente y a los aciertos de caché del prompt. Si las herramientas se registran en el código en un orden fijo, manténgalo así; el nuestro registra seis siempre en el mismo orden.

Pedir más datos al usuario: peticiones de varias idas y vueltas

Las revisiones anteriores permitían que un servidor enviara sus propias peticiones al cliente en mitad de una llamada: elicitation/create para preguntar algo al usuario, sampling/createMessage para usar el modelo del cliente, roots/list para conocer las carpetas permitidas. La revisión lo sustituye por el patrón Multi Round-Trip Requests. El servidor responde con resultType: "input_required" y una lista de inputRequests, y el cliente repite la petición original con inputResponses.

Para una herramienta de documentos, este es el lugar adecuado para una pregunta como "la foto está cortada por la izquierda, ¿envía otra?". Como el reintento es la misma petición otra vez, no hay que guardar nada en el servidor entre las dos llamadas. Un servidor que no usaba ninguna de las peticiones iniciadas por el servidor, como el nuestro, no tiene nada que migrar aquí.

Qué queda obsoleto y qué usar en su lugar

Roots, Sampling y Logging quedan obsoletos. Siguen funcionando durante un periodo de retirada de al menos doce meses, pero "new implementations should not add support for them". Las alternativas propuestas encajan bien en un servidor de documentos:

  • En lugar de Roots, pase los archivos como argumentos de herramienta o como configuración. Nuestro servidor local solo lee una ruta de archivo cuando hay un directorio de imágenes definido en su configuración, y solo dentro de ese directorio; una ruta fuera de él se rechaza. El modelo elige el argumento; el operador elige el perímetro.
  • En lugar de Logging, registre en stderr u OpenTelemetry. La revisión también elimina logging/setLevel; el nivel de log viaja ahora por petición en _meta, y los servidores no deben emitir mensajes de log para peticiones que no los pidieron. El contexto de traza (traceparent) también puede ir en _meta.

Los clientes antiguos seguirán llamando

No todos los clientes cambian el mismo día, así que la página del transporte describe qué debe hacer con el tráfico antiguo un servidor que solo admite 2026-07-28:

  • GET o DELETE al endpoint MCP: 405 Method Not Allowed.
  • Una cabecera Mcp-Session-Id: ignorarla, y nunca generar ni devolver una.
  • Una cabecera Last-Event-ID: ignorarla, porque los streams ya no se pueden reanudar.

Nuestro servidor alojado ya responde a GET y DELETE en su endpoint con 405 y una cabecera Allow: POST, OPTIONS, y no genera ningún id de sesión. Un cliente que todavía habla una revisión anterior lo descubre por el cuerpo del error: la especificación le indica volver a initialize solo cuando el cuerpo del 400 no es un error moderno reconocido.

Dónde está nuestro servidor

Para ser exactos: el servidor alojado está construido sobre la versión 1.30.0 del SDK de TypeScript, y nuestro propio código todavía no menciona 2026-07-28 en ninguna parte. Lo que de lo anterior ya se cumple es lo que viene de su diseño: sin sesiones, ids como identificadores, 405 para GET y DELETE, archivos acotados por configuración. server/discover, las comprobaciones de cabeceras, resultType y los campos de caché son el trabajo pendiente. La página del servidor MCP de OCR de pasaportes y la guía del servidor MCP describen a qué se conectan hoy los clientes.

Si está eligiendo una API de documentos para agentes, la comparativa de APIs de OCR de pasaportes enumera los servicios; vale la pena preguntar a cada uno por la parte MCP por separado.

Una lista de comprobación para su propio servidor de herramientas

  • No vive ningún estado entre peticiones; lo que necesite una llamada posterior es un id que devuelve el servidor.
  • La versión de cada petición se comprueba contra la cabecera MCP-Protocol-Version, con UnsupportedProtocolVersionError para una versión que no admite.
  • Mcp-Method y Mcp-Name se comprueban contra el cuerpo.
  • server/discover responde con versiones, capacidades e identidad.
  • Los resultados llevan resultType; los de listado y lectura llevan ttlMs y cacheScope.
  • tools/list devuelve siempre el mismo orden.
  • GET y DELETE reciben 405; las cabeceras de sesión y de id de evento se ignoran.
  • Los archivos llegan como argumentos dentro de un perímetro configurado, no a través de Roots.
  • Los logs van a stderr u OpenTelemetry, no al cliente.

El texto completo está en modelcontextprotocol.io, y el artículo del lanzamiento da el contexto. Hay más sobre nuestro servidor en la página del servidor MCP de OCR de pasaportes.

Una pregunta para usted: ¿su servidor MCP guardaba algo en una sesión que ahora tenga que convertir en un identificador? Cuéntenoslo en los comentarios.