La mayor parte del contenido sobre MCP es teórico: diagramas de un cliente hablando con un servidor, una analogía con USB-C, un “hello world” de cinco líneas que corre en localhost y nunca sale de tu portátil. Nada de eso te prepara para el trabajo real: tomar una herramienta que construiste localmente y ponerla en un servidor real, detrás de un proxy inverso real, accesible por un cliente real a través del internet público. Ese es un problema distinto, y es del que nadie escribe.
Yo desplegué uno. Un servidor MCP local, movido a una instancia EC2, servido sobre el transporte streamable HTTP con autenticación por bearer token delante de él mediante nginx. Funcionó. Y luego, casi tan pronto como me acostumbré a esa configuración, el propio protocolo cambió por debajo: la mayor revisión de la especificación MCP desde su lanzamiento pasó de release candidate a final el 28 de julio de 2026, y reescribe el núcleo del protocolo para que sea stateless. La mayoría de la gente trata “añadir soporte MCP” como una sola casilla en una lista. No lo es. Es una decisión de transporte, una decisión de autenticación y ahora, con esta actualización de la especificación, una decisión de escalado; equivocarte en cualquiera de ellas marca la diferencia entre una herramienta que funciona en una demo y una en la que realmente puedes confiar en producción.
Aprenderás:
- Qué estandariza realmente MCP y qué deja deliberadamente en tus manos
- Cómo funciona el transporte streamable HTTP y por qué reemplazó al enfoque anterior basado en SSE
- Cómo expuse un servidor MCP local en EC2 con nginx y autenticación por bearer token
- Qué cambia a nivel de protocolo con la reescritura stateless del 28 de julio de 2026
- Por qué un bearer token estático cumple con el mínimo de “tener autenticación”, pero no con el de “autenticación conforme a la especificación”
- Qué tuve que cambiar realmente en mi propio despliegue para mantenerme al día con la actualización
Tabla de contenidos
- Conceptos básicos
- La arquitectura completa
- Capas principales explicadas
- Recorrido de extremo a extremo
- Casos especiales
- Desafíos de escalado y producción
- Ejemplos de código
- Errores comunes
- Buenas prácticas de producción
Conceptos básicos
Qué cubre realmente MCP
El Model Context Protocol estandariza una cosa específica: cómo una aplicación de IA descubre y llama herramientas, y lee recursos, expuestos por un proceso de servidor separado, usando un formato de mensajes definido (JSON-RPC) sobre un transporte definido. No te dice cómo escribir la lógica de tus herramientas, cómo diseñar tu agente ni cómo desplegar nada. Es un estándar de conexión, no un framework. Antes de que existiera, toda aplicación de IA que quisiera hablar, por ejemplo, con tu API interna necesitaba una integración personalizada y aislada. MCP significa que construyes el servidor una vez y cualquier cliente compatible puede comunicarse con él.
Ese enfoque importa porque también explica exactamente por qué el despliegue es la parte difícil. El protocolo te da un contrato para cómo se estructuran los mensajes; no dice nada sobre si tu servidor es accesible solo desde tu portátil o desde internet, si está protegido por algo o si puede sobrevivir a que más de un cliente lo golpee al mismo tiempo. Todo eso te corresponde a ti.
Por qué los detalles del despliegue son un problema de alto valor
- Un servidor MCP ejecutándose localmente no tiene una superficie de ataque real. Uno desplegado públicamente sí. En el momento en que tu servidor tiene una URL pública, es una API autenticada como cualquier otra, con todo lo que eso implica sobre manejo de tokens y validación de entradas.
- El comportamiento de sesión y escalado no era obvio bajo la especificación anterior. Equivocarse aquí significaba o bien un servidor que se rompía silenciosamente bajo carga concurrente, o uno sobrediseñado con sticky sessions que no necesitaba.
- El propio protocolo sigue moviéndose. Un despliegue construido sobre supuestos de hace seis meses puede dejar silenciosamente de cumplir la especificación sin lanzar un solo error; los clientes simplemente empiezan a negociar hacia una versión más antigua del protocolo.
La arquitectura completa
AI Client ── HTTPS POST ──▶ nginx (TLS termination, auth check, reverse proxy)
│
▼
MCP Server Process
(Streamable HTTP transport)
│
┌────────────┴────────────┐
Tools Resources
El principio rector: una vez que tu servidor MCP tiene una URL pública, trátalo exactamente como cualquier otra API autenticada que expondrías desde EC2, porque estructuralmente eso es lo que es. La parte “MCP” es el formato de mensajes que viaja por encima; las consideraciones de seguridad y escalado por debajo son las mismas que aplicarías a cualquier servicio backend.
Capas principales explicadas
1. Transporte: Streamable HTTP
Qué es: Un único endpoint HTTP que acepta solicitudes POST que transportan mensajes JSON-RPC. Reemplazó al transporte anterior HTTP+SSE, que dependía de un flujo persistente de server-sent events junto con llamadas HTTP regulares.
Por qué importa: Un único endpoint POST es algo que todo proxy inverso, load balancer y API gateway ya sabe manejar. No estás peleando con tu infraestructura para mantener vivo un flujo de larga duración a través de nginx o un ALB; se comporta como una API HTTP normal.
location /mcp {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header Authorization $http_authorization;
proxy_http_version 1.1;
proxy_read_timeout 300s;
}
Consejo de producción: Incluso con un transporte de endpoint único, mantén generoso el timeout de lectura del proxy. Las llamadas individuales a herramientas pueden tardar legítimamente más que una solicitud típica de API, y un timeout demasiado estricto terminará llamadas válidas de larga duración, no solo conexiones colgadas.
2. Autenticación: Bearer Tokens (un punto de partida, no un destino)
Qué es: Un token estático en la cabecera Authorization, verificado antes de que una solicitud pueda llegar al proceso del servidor MCP, ya sea en la capa de nginx o en middleware de la aplicación.
Por qué importa: Es la seguridad mínima viable para cualquier cosa con una URL pública, y es exactamente por donde empecé. Pero un bearer token estático no tiene semántica de expiración, no está vinculado a un servidor específico y no ofrece protección si se filtra en algún log. Responde a la pregunta “¿esta solicitud está autenticada en absoluto?”, y poco más.
from fastapi import Request, HTTPException
VALID_TOKEN = "your-rotated-secret-token"
async def verify_bearer(request: Request):
auth = request.headers.get("authorization", "")
if auth != f"Bearer {VALID_TOKEN}":
raise HTTPException(status_code=401, detail="Unauthorized")
Consejo de producción: Nunca permitas que la cabecera Authorization sin procesar llegue a tus access logs. Es algo fácil de pasar por alto en un formato por defecto de logs de nginx o de la app, y convierte un archivo de log en una fuga de credenciales.
3. Statelessness: el núcleo de la reescritura de julio de 2026
Qué es: La especificación del 2026-07-28 elimina por completo las sesiones a nivel de protocolo. Ya no existe la cabecera Mcp-Session-Id, y el transporte elimina el endpoint GET de flujo mantenido durante mucho tiempo que las versiones anteriores usaban para mantener sincronizados a cliente y servidor a lo largo de una conversación. Ahora cada solicitud es autocontenida.
Por qué importa: Bajo el modelo anterior, un servidor a menudo necesitaba recordar algo sobre un cliente entre llamadas, que es exactamente el tipo de estado que te obliga a usar sticky sessions cuando tienes más de una instancia de servidor detrás de un load balancer. La statelessness a nivel de protocolo significa que cualquier instancia puede manejar legítimamente cualquier solicitud: verdadero escalado horizontal, sin fijar un cliente a una caja específica.
Consejo de producción: Si tu servidor dependía de estado en memoria para recordar algo sobre un cliente entre llamadas, ese patrón se rompe con la nueva especificación. Mueve el estado importante al propio payload de la solicitud o a un almacén externo como Redis que cualquier instancia pueda leer.
4. Solicitudes de múltiples viajes de ida y vuelta (preguntar algo al usuario a mitad de llamada)
Qué es: Un protocolo stateless sigue necesitando una forma de que un servidor pida datos a un cliente en mitad de una llamada de herramienta: una confirmación, un parámetro faltante. La nueva especificación lo resuelve con lo que llama Multi Round-Trip Requests: en lugar de mantener un flujo abierto, el servidor devuelve un InputRequiredResult que contiene las preguntas y un blob opaco requestState. El cliente recopila las respuestas y reemite la llamada original con tanto las respuestas como el estado reflejado adjuntos.
Por qué importa: Esta es la pieza que hace viable la verdadera statelessness. Como todo lo que el servidor necesita para reanudar está contenido en el payload que el cliente envía de vuelta, cualquier instancia del servidor, no necesariamente la que inició la llamada, puede retomar el reintento y finalizarlo.
Consejo de producción: Trata requestState como opaco del lado del cliente. No lo inspecciones ni lo modifiques; simplemente almacénalo y devuélvelo exactamente como lo recibiste, o te arriesgas a romper una reanudación que depende de que permanezca intacto.
5. Endurecimiento de autorización: OAuth 2.1, PKCE e indicadores de recurso
Qué es: La especificación actualizada empuja a los servidores MCP remotos hacia OAuth 2.1 con PKCE para autorización, y exige indicadores de recurso (según RFC 8707) que vinculen un token emitido al servidor específico para el que fue destinado.
Por qué importa: Un bearer token estático puede reutilizarse contra cualquier servidor que lo acepte, si alguna vez se filtra. Vincular un token a un recurso específico cierra exactamente esa brecha: un token emitido para un servidor MCP ya no puede usarse contra otro distinto, incluso si ambos confían en el mismo proveedor de identidad.
Consejo de producción: Una configuración con bearer token como la que la mayoría usamos al principio satisface “este endpoint requiere autenticación”. No satisface el nivel de interoperabilidad y protección contra replay que la nueva especificación establece para un servidor remoto correctamente compatible, y vale la pena cerrar esa brecha antes de entregar la URL a alguien fuera de tu equipo.
6. Política de deprecación y framework de extensiones
Qué es: La especificación ahora mueve las capacidades a través de un ciclo de vida formal Active → Deprecated → Removed, con una separación mínima de doce meses entre deprecación y eliminación. Las nuevas capacidades —cosas como UI renderizada por el servidor o soporte para tareas de larga duración— se publican primero como Extensions optativas en lugar de entrar directamente en el núcleo del protocolo. Un puñado de capacidades más antiguas, incluidas roots, sampling y logging tal como se especificaron originalmente, están avanzando por esta ruta de deprecación a favor de sus reemplazos.
Por qué importa: Significa que una integración funcional no se romperá de la noche a la mañana por una revisión futura, y significa que puedes adoptar nuevas capacidades deliberadamente en lugar de verte obligado a seguir el ritmo de todo lo que la especificación añada.
Recorrido de extremo a extremo
Sigue la ruta real desde un prototipo local hasta un despliegue actual de EC2 conforme a la especificación:
- Pruebas locales. El servidor MCP se ejecuta sobre stdio contra un cliente local: sin red, sin autenticación, iteración rápida.
- Mover a streamable HTTP. El servidor se reconfigura para servir el transporte streamable HTTP en lugar de stdio, y se despliega en una instancia EC2.
- nginx delante. La terminación TLS ocurre en nginx, que también verifica el bearer token antes de que nada llegue al proceso MCP, y luego hace reverse proxy de la solicitud.
- Una llamada de herramienta normal. El cliente envía una solicitud JSON-RPC como un único POST; el servidor la procesa y devuelve un resultado en el mismo ciclo solicitud-respuesta.
- Negociación de versión del protocolo. Con la especificación 2026-07-28 ya final, el cliente y el servidor negocian qué revisión del protocolo usar. Un cliente que aún no se ha actualizado negocia automáticamente hacia 2025-11-25 en lugar de fallar por completo.
- Una herramienta necesita más información. En lugar de mantener abierta la conexión, el servidor devuelve un
InputRequiredResultcon las preguntas que necesita que se respondan. - El cliente reanuda. Reemite la llamada con las respuestas y el
requestStatereflejado. Como nada de esa reanudación depende de alcanzar la misma instancia del servidor, puede aterrizar en cualquier caja detrás de nginx. - Ruta de fallo de autenticación. Un token ausente o inválido es rechazado en la capa de nginx, antes de que siquiera llegue al proceso MCP; el código de la aplicación ni siquiera ve una solicitud malformada o no autenticada.
Casos especiales
Herramientas de larga duración. Todo lo que antes dependía de un flujo mantenido abierto para una operación lenta debería pasar al patrón de extensión Tasks en lugar de intentar recrear una conexión persistente sobre un transporte que ya no la soporta.
Herramientas sin intención de volverse remotas. Si un servidor solo necesita ejecutarse localmente junto a su cliente, el transporte stdio sigue siendo la opción más simple, y casi ninguna de las preocupaciones de despliegue aquí aplica.
Clientes de versiones mixtas durante la transición. Dado que los clientes antiguos hacen fallback a 2025-11-25 automáticamente, es realista ejecutar durante un tiempo un único despliegue que sirva correctamente ambas revisiones del protocolo, en lugar de forzar a todos los clientes a actualizarse en lockstep con el servidor.
Desafíos de escalado y producción
Múltiples instancias detrás de un load balancer. Bajo el antiguo modelo basado en sesiones, esto significaba sticky sessions o almacenamiento compartido de sesiones para que un cliente permaneciera vinculado a la instancia que lo conocía. La statelessness elimina ese requisito por completo: cualquier instancia puede servir cualquier solicitud, y ese es el verdadero desbloqueo detrás de esta reescritura para cualquiera que ejecute más de una caja.
TLS y gestión de certificados a medida que crece el número de instancias. Gestionar un certificado por instancia EC2 no escala bien; colocar un load balancer administrado con un certificado respaldado por ACM delante de una flota de instancias supone mucha menos sobrecarga operativa que configuraciones TLS de nginx por instancia.
Emisión y rotación de tokens a escala. Los bearer tokens estáticos no solo tienen un techo de seguridad: también son un dolor operativo una vez que tienes más de un par de clientes, ya que cada rotación es un problema de coordinación manual. Pasar a una emisión basada en OAuth 2.1 resuelve de una vez la brecha de seguridad y el dolor de la rotación.
Trazabilidad de interacciones de múltiples llamadas sin estado de sesión del lado del servidor. Sin una sesión que una una secuencia de llamadas en el servidor, los correlation IDs transmitidos a través del payload de la solicitud se convierten en la única forma fiable de reconstruir qué hizo realmente una sola interacción de agente a través de múltiples llamadas.
Ejemplos de código
La configuración de nginx y el middleware de verificación de bearer anteriores cubren las capas de transporte y autenticación base. Aquí tienes un patrón mínimo para manejar el lado de reanudación de una Multi Round-Trip Request en el cliente:
async def call_tool_with_resume(client, tool_name, params):
result = await client.call_tool(tool_name, params)
if result.get("type") == "InputRequiredResult":
answers = collect_answers(result["questions"])
return await client.call_tool(
tool_name,
{**params, "answers": answers, "requestState": result["requestState"]},
)
return result
El detalle importante es la última línea: requestState se pasa directamente, intacto, exactamente como lo envió el servidor.
Errores comunes
Error: tratar “está desplegado” como “está listo para producción”. Poner nginx y un bearer token delante de un servidor MCP supera el listón más bajo, no todo el listón. Solución: exige a tu endpoint MCP el mismo estándar que exigirías a cualquier otra API pública: autenticación adecuada, disciplina de logging y rate limiting antes de compartir ampliamente la URL.
Error: depender de la memoria del servidor entre llamadas. Algunos despliegues tempranos dependían silenciosamente de que el servidor recordara algo sobre un cliente de una solicitud anterior. Solución: trata cada solicitud como si pudiera aterrizar en una instancia distinta de la anterior, porque bajo la especificación actual, puede hacerlo.
Error: codificar de forma rígida una única versión del protocolo en el cliente. Un cliente fijado a una revisión exacta se rompe en el momento en que un servidor se actualiza. Solución: implementa negociación de versión con un fallback razonable, para que una actualización de un lado no tumbe al otro.
Error: subestimar el listón de autenticación para una herramienta “solo interna”. Lo que hoy es solo interno a menudo mañana se comparte con un equipo socio, momento en el que un token estático deja de ser suficiente. Solución: construye desde el principio con OAuth 2.1 e indicadores de recurso si existe alguna posibilidad realista de que el servidor salga de tu propio portátil o VPC.
Buenas prácticas de producción
- No mantengas estado que tu infraestructura no pueda escalar. Si un cliente necesita que se recuerde algo entre llamadas, ponlo en el payload o en un almacén externo, no en la memoria del servidor.
- Vincula los tokens al recurso para el que fueron emitidos. Un token que funciona contra cualquier servidor en el que confíes es un token que eventualmente se usará en algún lugar donde no lo pretendías.
- Negocia versiones del protocolo; no asumas una sola. Tus clientes y tu servidor no siempre se actualizarán el mismo día; diseña deliberadamente para esa brecha.
- Mantén las comprobaciones de autenticación en el borde. Rechazar una mala solicitud en nginx, antes de que llegue al código de tu aplicación, mantiene tu proceso MCP más simple y tu superficie de ataque más pequeña.
- Trata como opaco todo campo opaco del protocolo.
requestStatey campos similares existen para que el servidor pueda confiar en lo que regresa; no los inspecciones ni los alteres del lado del cliente.
Cierre
La teoría alrededor de MCP es fácil; el despliegue es donde viven las decisiones reales, y esas decisiones acaban de cambiar por debajo de todos con la actualización de la especificación del 28 de julio de 2026. Streamable HTTP se volvió más simple de ejecutar detrás de infraestructura estándar, la statelessness convirtió “añadir otra instancia EC2” en una opción real en lugar de un proyecto de gestión de sesiones, y el modelo de autorización por fin se puso al día con lo que necesita un servidor expuesto públicamente. Nada de esto es complicado una vez que lo has visto; simplemente no es la parte que cubren la mayoría de los textos teóricos.
Si has desplegado un servidor MCP en algún lugar distinto de localhost, ¿qué tuviste que cambiar una vez que aparecieron clientes reales y carga real?
