- Protocolo MCP: De cero a producción tras el salto a la arquitectura sin estado
- 1. Fundamentos: El conector USB-C de la Inteligencia Artificial
- 2. Mecanismos de transporte: stdio frente a HTTP remoto
- 3. Arquitectura de producción: La especificación 2026-07-28 y el fin del estado
- 4. Implementación real: Servidor MCP para diagnóstico de infraestructura
- 5. Gobernanza y seguridad en producción: Lo que omiten las demos
- El estándar que la infraestructura necesitaba
Conectar un modelo de lenguaje a las tripas de tus servidores siempre ha sido una tarea ingrata. Durante los primeros compases de la fiebre de los agentes, cada proveedor nos obligó a reinventar la rueda: un formato JSON propietario para OpenAI, otro esquema incompatible para Anthropic, envoltorios ad-hoc para Gemini y una maraña de pegamento en librerías intermedias que se rompía al menor cambio de versión.
El Model Context Protocol (MCP) nació a finales de 2024 para cortar esa sangría y establecer un estándar abierto. Sin embargo, su despliegue en entornos reales chocó pronto con la realidad de la infraestructura. La reciente especificación del 28 de julio de 2026 marca la mayor transformación técnica del protocolo desde su origen: abandona el modelo con estado y abraza una arquitectura puramente desacoplada que por fin encaja en arquitecturas de microservicios modernas.
Vamos a desglosar qué es MCP, cómo funciona desde sus conceptos más elementales y cómo llevarlo a producción asumiendo las lecciones de la nueva especificación.
1. Fundamentos: El conector USB-C de la Inteligencia Artificial
Para entender MCP sin adornos teóricos, conviene recordar la evolución de los puertos en los dispositivos electrónicos. Hace quince años, cada teléfono móvil venía con un cargador diferente. Si cambiabas de terminal, el cable quedaba inservible.
En el ecosistema de los modelos fundacionales ocurría exactamente eso. Si programabas una herramienta para consultar el inventario en PostgreSQL, tenías que empaquetar el esquema de llamada en el formato específico de la API del proveedor de turno. Si querías que esa misma base de datos la consultaran varios agentes en paralelo —como vimos al explorar la coordinación en equipos de agentes—, el código de integración se multiplicaba por cada variante de modelo.
MCP funciona como el puerto USB-C para la IA. Es un protocolo de comunicación abierto (estructurado sobre JSON-RPC 2.0) que desacopla a los clientes de las fuentes de datos.
+-------------------------------------------------------------+
| CLIENTE MCP (Host / Orquestador) |
| (Antigravity 2.0, Claude Desktop, Cursor, Agente Python) |
+-------------------------------------------------------------+
|
JSON-RPC 2.0 (stdio / HTTP)
|
v
+-------------------------------------------------------------+
| SERVIDOR MCP |
| (PostgreSQL, Git, Monitorización, APIs REST) |
+-------------------------------------------------------------+
El cliente (el orquestador de agentes o la aplicación donde corre el modelo, como Antigravity 2.0) no necesita saber cómo funciona internamente tu base de datos ni tu infraestructura. El servidor MCP expone un catálogo uniforme, y el cliente consume ese catálogo a través de un contrato predecible.
Las tres primitivas básicas
Cualquier servidor MCP, por sofisticado que sea, solo maneja tres primitivas fundamentales:
- Tools (Herramientas): Son verbos. Funciones ejecutables que admiten parámetros y devuelven una respuesta. Tienen efectos colaterales calculados: consultar una tabla SQL, reiniciar un contenedor o crear un ticket en Jira.
- Resources (Recursos): Son sustantivos pasivos. Datos estructurados identificados mediante esquemas URI (
postgres://metricas/cpu,file:///var/log/syslog). El cliente los lee para alimentar la ventana de contexto, pero no ejecutan lógica activa ni alteran el estado del sistema. - Prompts (Plantillas): Recetas predefinidas de interacción. Permiten al servidor sugerirle al cliente cómo estructurar peticiones complejas para aprovechar sus propias herramientas de forma idónea.
2. Mecanismos de transporte: stdio frente a HTTP remoto
Para que el cliente y el servidor se entiendan necesitan un canal físico de comunicación. MCP define dos vías principales de transporte, cada una pensada para un entorno operativo distinto.
Transporte por entrada/salida estándar (stdio)
Es el canal predeterminado cuando el servidor se ejecuta en la misma máquina física o contenedor que el cliente. El host arranca el servidor MCP como un subproceso del sistema operativo, escribiendo peticiones en su stdin y leyendo respuestas de su stdout.
Este enfoque tiene dos ventajas técnicas inmediatas: elimina cualquier latencia de red y permite confinar la ejecución dentro de las fronteras de permisos del usuario del sistema operativo, una práctica obligatoria cuando ejecutamos agentes autónomos en entornos Linux. Si el proceso intenta acceder a un recurso no autorizado, el propio kernel de Linux bloquea la llamada.
Transporte remoto (HTTP / Server-Sent Events)
Cuando el servidor MCP vive en una máquina remota, un clúster de Kubernetes o un servicio gestionado, el tráfico viaja sobre HTTP. El cliente envía comandos mediante peticiones POST y, tradicionalmente, escuchaba las respuestas asíncronas y eventos a través de flujos SSE (Server-Sent Events).
Fue en este punto donde la arquitectura original de MCP comenzó a mostrar sus costuras en entornos corporativos reales.
3. Arquitectura de producción: La especificación 2026-07-28 y el fin del estado
Entre noviembre de 2024 y mediados de 2026, la especificación de MCP arrastraba un problema de diseño: dependía de un modelo con estado (stateful session).
La pesadilla del estado heredado
En las versiones previas (2024-11-05 y 2025-11-25), antes de poder ejecutar cualquier acción, el cliente debía completar un apretón de manos forzoso (initialize y su correspondiente confirmación initialized). El servidor devolvía un identificador de sesión a través de la cabecera Mcp-Session-Id.
En entornos de laboratorio o en una aplicación de escritorio local esto funcionaba sin problemas. En producción provocaba un dolor de cabeza monumental:
- Incompatibilidad con balanceadores estándar: Forzaba el uso de sesiones pegajosas (sticky sessions) en Nginx, HAProxy o AWS ALB. Si el tráfico se distribuía mediante round-robin entre varios pods de backend, una petición con un
Mcp-Session-Idcaía en un nodo diferente que no tenía esa sesión en memoria y fallaba con un error de desconexión. - Sobrecarga de almacenamiento: Obligaba a persistir el estado de las sesiones en memorias intermedias como Redis, añadiendo puntos únicos de fallo y latencia de red innecesaria.
- Fricción en entornos Serverless y contenedores efímeros: Si un contenedor escalaba a cero o se reciclaba para liberar memoria, la sesión moría y el agente se quedaba bloqueado a mitad de una tarea.
El nuevo paradigma: Peticiones autocontenidas
La especificación publicada el 28 de julio de 2026 rompe con ese diseño y reescribe las reglas del juego. Los cambios arquitecturales clave son:
1. Eliminación del apretón de manos y de Mcp-Session-Id
Ya no existe una fase previa obligatoria para abrir una sesión. El servidor ya no recuerda quién eres entre llamada y llamada. Cada petición individual es completamente autónoma.
2. Metadatos directos en la carga (_meta)
En lugar de depender de una sesión negociada previamente, cada petición JSON-RPC transporta de forma explícita la versión del protocolo y las capacidades del cliente dentro de un campo estructurado _meta. Cualquier réplica del servidor puede procesar la petición de inmediato sin consultar un almacén central de estados.
{
"jsonrpc": "2.0",
"id": "req-4819",
"method": "tools/call",
"params": {
"name": "consultar_salud_cluster",
"arguments": {
"nodo": "prod-k8s-worker-03"
},
"_meta": {
"protocolVersion": "2026-07-28",
"clientInfo": {
"name": "antigravity-agent",
"version": "2.4.0"
}
}
}
}
3. Enrutamiento por cabeceras HTTP (Mcp-Method y Mcp-Name)
Hasta ahora, un proxy inverso o un API Gateway tenía que parsear el cuerpo del JSON completo para saber qué herramienta o método se estaba invocando.
La especificación 2026-07-28 estandariza las cabeceras HTTP Mcp-Method y Mcp-Name. Esto permite a herramientas de infraestructura como Envoy, Cloudflare Workers o Traefik tomar decisiones de enrutamiento, aplicar limitadores de tasa (rate limiting) o rechazar llamadas no autorizadas en la misma capa de transporte sin deserializar un solo byte del payload.
POST /mcp/v1 HTTP/1.1
Host: api.srdata.dev
Content-Type: application/json
Mcp-Method: tools/call
Mcp-Name: consultar_salud_cluster
Authorization: Bearer eyJhbGciOi...
4. Descubrimiento unificado (server/discover)
Se acabó realizar sondeos secuenciales dispersos (tools/list, resources/list, prompts/list). La nueva especificación introduce el método RPC server/discover, que devuelve en una única llamada atómica las capacidades completas del servidor, las versiones toleradas y el esquema de herramientas.
4. Implementación real: Servidor MCP para diagnóstico de infraestructura
Para ver cómo se aplica este enfoque, vamos a construir un servidor MCP real en Python utilizando el SDK oficial y el framework FastMCP.
Este servidor no sumará números de juguete. Expondrá una herramienta para auditar el uso de recursos y saturación de particiones críticas en Linux, un recurso de diagnóstico para consultar las últimas líneas del registro del sistema, y una plantilla para guiar la respuesta del modelo ante incidentes graves.
Código del servidor (infra_sentinel.py)
import os
import shutil
import subprocess
from mcp.server.fastmcp import FastMCP, Context
from pydantic import BaseModel, Field
# Inicialización del servidor con la metadata del servicio
mcp = FastMCP(
name="infra-sentinel",
description="Servidor MCP para inspección y triaje de infraestructura en servidores Linux"
)
# --- MODELOS DE ENTRADA Y VALIDACIÓN ESTRICTA ---
class DiskAuditInput(BaseModel):
umbral_alerta: int = Field(
default=85,
ge=10,
le=99,
description="Porcentaje de uso de disco a partir del cual disparar una advertencia crítica."
)
ruta_montaje: str = Field(
default="/",
description="Punto de montaje a inspeccionar (por defecto la raíz del sistema)."
)
class ServiceLogsInput(BaseModel):
nombre_servicio: str = Field(
description="Nombre del servicio systemd a auditar (ej. nginx, postgresql, docker)."
)
lineas: int = Field(
default=30,
ge=5,
le=200,
description="Cantidad de líneas recientes a extraer del journal."
)
# --- RECURSOS (DATOS PASIVOS) ---
@mcp.resource("system://kernel/version")
def obtener_version_kernel() -> str:
"""Devuelve la versión actual del kernel y arquitectura del host."""
return f"{os.uname().sysname} {os.uname().release} ({os.uname().machine})"
@mcp.resource("system://metrics/loadavg")
def obtener_carga_sistema() -> str:
"""Devuelve la carga media del sistema en 1, 5 y 15 minutos."""
load1, load5, load15 = os.getloadavg()
cpu_count = os.cpu_count() or 1
return (
f"Carga: 1min={load1:.2f}, 5min={load5:.2f}, 15min={load15:.2f} | "
f"CPUs lógicas disponibles={cpu_count}"
)
# --- HERRAMIENTAS (VERBOS CON EFECTO) ---
@mcp.tool()
def auditar_espacio_disco(params: DiskAuditInput) -> dict:
"""Inspecciona la capacidad de almacenamiento y detecta riesgos de saturación."""
try:
total, used, free = shutil.disk_usage(params.ruta_montaje)
porcentaje_uso = (used / total) * 100
estado = "CRITICO" if porcentaje_uso >= params.umbral_alerta else "OK"
return {
"punto_montaje": params.ruta_montaje,
"total_gb": round(total / (1024 ** 3), 2),
"usado_gb": round(used / (1024 ** 3), 2),
"libre_gb": round(free / (1024 ** 3), 2),
"porcentaje_uso": round(porcentaje_uso, 2),
"alerta_disparada": estado == "CRITICO",
"estado": estado
}
except FileNotFoundError:
return {"error": f"El punto de montaje '{params.ruta_montaje}' no existe."}
except Exception as exc:
return {"error": f"Fallo al consultar almacenamiento: {str(exc)}"}
@mcp.tool()
def extraer_logs_servicio(params: ServiceLogsInput, ctx: Context) -> str:
"""Extrae las entradas recientes de systemd-journald para un servicio específico."""
# Validación defensiva del nombre del servicio para evitar caracteres extraños
if not params.nombre_servicio.replace("-", "").replace("_", "").isalnum():
return "Error de seguridad: Nombre de servicio con caracteres no permitidos."
cmd = [
"journalctl",
f"-u={params.nombre_servicio}",
f"-n={params.lineas}",
"--no-pager",
"-p=err..emerg"
]
try:
resultado = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=5,
check=False
)
salida = resultado.stdout.strip()
if not salida:
return f"Sin incidencias graves en los últimos logs de '{params.nombre_servicio}'."
return salida
except subprocess.TimeoutExpired:
return "Error: Timeout superado al consultar los registros del sistema."
except Exception as e:
return f"Error al ejecutar journalctl: {str(e)}"
# --- PROMPTS (PLANTILLAS DE CONTEXTO) ---
@mcp.prompt()
def triaje_urgente_incidencia(servicio: str) -> str:
"""Genera una plantilla de instrucciones para analizar un fallo de servicio en el host."""
return (
f"Estás auditando un fallo en el servicio '{servicio}'. Sigue este orden riguroso:\n"
f"1. Consulta el recurso 'system://metrics/loadavg' para verificar la saturación del host.\n"
f"2. Invoca la herramienta 'auditar_espacio_disco' para descartar disco lleno en '/'.\n"
f"3. Llama a 'extraer_logs_servicio' para analizar los últimos errores en journald.\n"
f"4. Formula un plan de remediación priorizado evitando conjeturas."
)
if __name__ == "__main__":
# Ejecución por stdio (canal local estándar)
mcp.run()
Configuración en el cliente (Orquestador / Host)
Para conectar este servidor a un cliente moderno (por ejemplo, el entorno de subagentes en Antigravity 2.0 o Claude Desktop), agregamos la definición en el archivo de configuración JSON correspondiente:
{
"mcpServers": {
"infra-sentinel": {
"command": "python3",
"args": [
"/home/leif/dev/servicios/infra_sentinel.py"
],
"env": {
"PATH": "/usr/local/bin:/usr/bin:/bin"
}
}
}
}
Al levantar la sesión, el cliente invoca el subproceso, lanza la llamada de descubrimiento inicial server/discover y mapea las funciones automáticamente dentro de su árbol de herramientas ejecutables.
5. Gobernanza y seguridad en producción: Lo que omiten las demos
Integrar MCP en un entorno de desarrollo local con permisos de superusuario es muy cómodo, pero llevar este protocolo a redes corporativas exige precauciones adicionales.
1. El modelo mental de "Cero Confianza" (Zero-Trust)
Un servidor MCP no debe confiar ciegamente en los parámetros que envía el modelo. Los LLMs sufren alucinaciones y son vulnerables a ataques de inyección de prompt indirecta. Si un agente lee un archivo modificado por un atacante y este contiene instrucciones maliciosas ocultas, el modelo intentará llamar a tus herramientas con argumentos hostiles.
Toda herramienta debe validar sus entradas con esquemas rígidos (como Pydantic) y verificar rutas de archivos antes de tocarlas en disco. En operaciones que impliquen escrituras o destrucciones de datos, el servidor debe actuar como un cortafuegos y exigir confirmación humana explícita (Human-in-the-loop).
2. La trampa del envenenamiento de contexto
Cada herramienta expuesta en un servidor MCP traslada su esquema completo en formato JSON Schema al prompt de sistema del modelo. Si montas un servidor monolítico con 70 herramientas distintas, estás consumiendo miles de tokens de contexto antes incluso de que el usuario envíe su primera instrucción.
Además del gasto económico, saturar la atención del modelo degrada su precisión para seleccionar la herramienta correcta. La solución técnica consiste en diseñar servidores MCP pequeños y especializados, desplegados por dominio de negocio y activados de forma modular por el orquestador principal.
3. Aislamiento y control de gasto
Dotar a un modelo de capacidad de ejecución automatizada puede disparar bucles imprevistos de llamadas y peticiones en cadena. Al diseñar la topología de red de tus servidores MCP remotos, aplica los mismos límites estrictos que detallamos al configurar disyuntores de facturación en pipelines de CI/CD. Si un agente entra en una cascada de reintentos fallidos, el sistema debe cortar la conexión tras un umbral prudencial de iteraciones.
El estándar que la infraestructura necesitaba
El paso a un modelo puramente sin estado en la especificación de julio de 2026 rescata a MCP de quedarse como un protocolo restringido a herramientas de escritorio y lo habilita como una pieza válida de arquitectura empresarial. Al tratar cada invocación como una transacción atómica y delegar la autenticación en estándares abiertos como OAuth 2.0 y cabeceras HTTP limpias, los servidores MCP pasan a comportarse exactamente igual que cualquier otra API REST o servicio gRPC en tu infraestructura.
El valor del protocolo no radica en la magia del software, sino en la disciplina del contrato: separar limpiamente el razonamiento no determinista del modelo de la ejecución rígida y determinista de nuestros sistemas de producción.