Saltar al contenido
NUEVOGuía práctica con 5 agentes Python para Odoo, verificada contra Odoo 20 →
Focuz/academy

agentes ia

MCP se vuelve stateless: qué cambia la versión candidata 2026-07-28

El 21 de mayo se cerró la versión candidata de la próxima especificación de MCP: sin handshake, sin sesiones y con extensiones oficiales. Qué cambia para tus servidores y cómo prepararte en Python con Odoo.

Focuz Academy · 29 de mayo de 2026 · 7 min

El 21 de mayo de 2026, los mantenedores del Model Context Protocol cerraron la versión candidata (RC) de la especificación 2026-07-28. Según su propio anuncio, es la revisión más grande del protocolo desde su lanzamiento e incluye cambios incompatibles. La versión final se publicará el 28 de julio de 2026. Las diez semanas intermedias sirven para que los mantenedores de SDK y los equipos que implementan clientes validen los cambios.

Si mantienes un servidor MCP (por ejemplo, uno que expone datos de Odoo a un agente), este es el momento de entender qué cambia.

El cambio central: el protocolo pierde el estado

Hasta la versión 2025-11-25, llamar a una herramienta por Streamable HTTP exigía primero un initialize. El servidor respondía con un Mcp-Session-Id que el cliente debía enviar en cada solicitud, lo que lo "amarraba" a la instancia que emitió la sesión.

En la RC, la misma llamada es una sola solicitud autocontenida (ejemplo tomado del anuncio oficial):

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

{"jsonrpc":"2.0","id":1,"method":"tools/call",
 "params":{"name":"search","arguments":{"q":"otters"},
           "_meta":{"io.modelcontextprotocol/clientInfo":{"name":"my-app","version":"1.0"}}}}

Lo que desaparece:

  • El intercambio initialize/initialized (SEP-2575). La versión del protocolo, los datos del cliente y sus capacidades viajan ahora en _meta en cada solicitud. Un nuevo método, server/discover, permite consultar las capacidades del servidor cuando se necesitan.
  • El encabezado Mcp-Session-Id y la sesión a nivel de protocolo (SEP-2567).

Consecuencia práctica: cualquier instancia de tu servidor puede atender cualquier solicitud. Ya no necesitas sesiones "pegajosas" ni un almacén compartido de sesiones; un balanceador round-robin basta.

¿Y si mi servidor necesita estado?

El anuncio lo resuelve con un patrón conocido en APIs HTTP: el handle explícito. Una herramienta genera un identificador (por ejemplo, un basket_id) y el modelo lo pasa como argumento en las llamadas siguientes. Los mantenedores sostienen que esto suele ser más potente que la sesión oculta, porque el estado queda visible para el modelo, que puede combinarlo entre herramientas.

En Odoo este patrón es natural: el handle es el id del registro. Una cotización en borrador (sale.order) ya es un estado persistente en la base de datos.

Otros cambios que debes revisar

  • Solicitudes de varios pasos sin conexión abierta (SEP-2322). Si el servidor necesita pedir algo al usuario a mitad de una llamada (por ejemplo, una confirmación), ya no mantiene abierto un stream SSE: responde con un resultado input_required y un requestState, y el cliente reenvía la llamada original con las respuestas.
  • Encabezados Mcp-Method y Mcp-Name obligatorios en Streamable HTTP (SEP-2243), para que balanceadores y gateways enruten sin leer el cuerpo. El servidor rechaza solicitudes cuyo encabezado y cuerpo no coincidan.
  • Caché: las respuestas de listas y lecturas de recursos traen ttlMs y cacheScope (SEP-2549).
  • Extensiones oficiales: MCP Apps (interfaces HTML que el host muestra en un iframe aislado) y Tasks, que deja de ser una función experimental del núcleo y pasa a ser una extensión. Si implementaste Tasks según 2025-11-25, tendrás que migrar.
  • Deprecaciones: Roots, Sampling y Logging quedan marcados como obsoletos, pero siguen funcionando en esta versión y en las que se publiquen durante el año siguiente. El reemplazo de Logging es stderr en stdio y OpenTelemetry para observabilidad estructurada.
  • Esquemas: inputSchema y outputSchema admiten JSON Schema 2020-12 completo (oneOf, anyOf, $ref, etc.).
  • Errores: el código de "recurso no encontrado" pasa de -32002 a -32602. Si tu cliente compara con -32002, actualízalo.
  • Autorización: los clientes deben validar el parámetro iss según el RFC 9207, entre otros ajustes alineados con OAuth y OpenID Connect.

Cómo prepararte hoy en Python

A la fecha, la versión estable del SDK oficial de Python es la 1.27.x y habla la versión 2025-11-25 del protocolo. La RC necesita SDK nuevos; según el anuncio, los SDK de nivel 1 deben incorporarla dentro de la ventana de validación. Pero ya puedes diseñar tu servidor para un mundo sin sesiones:

  1. Arranca el servidor en modo stateless. La documentación del SDK 1.27.1 recomienda stateless_http=True y json_response=True para producción. En ese modo, el SDK crea un transporte nuevo por solicitud, sin seguimiento de sesión.
  2. No guardes estado en memoria del proceso. Guárdalo en Odoo y devuelve el id.

Ejemplo con la API JSON-2 de Odoo 19:

import os
import requests
from mcp.server.fastmcp import FastMCP

ODOO = os.environ["ODOO_URL"] + "/json/2"
HEADERS = {
    "Authorization": f"bearer {os.environ['ODOO_API_KEY']}",
    "X-Odoo-Database": os.environ["ODOO_DB"],
}

mcp = FastMCP("cotizaciones", stateless_http=True, json_response=True)

def odoo(modelo: str, metodo: str, **cuerpo):
    r = requests.post(f"{ODOO}/{modelo}/{metodo}", headers=HEADERS,
                      json=cuerpo, timeout=30)
    r.raise_for_status()
    return r.json()

@mcp.tool()
def crear_cotizacion(partner_id: int) -> dict:
    """Crea una cotización en borrador y devuelve su id (el handle)."""
    ids = odoo("sale.order", "create", vals_list=[{"partner_id": partner_id}])
    return {"cotizacion_id": ids[0]}

@mcp.tool()
def agregar_linea(cotizacion_id: int, product_id: int, cantidad: float) -> dict:
    """Agrega una línea a la cotización indicada por su id."""
    ids = odoo("sale.order.line", "create", vals_list=[{
        "order_id": cotizacion_id,
        "product_id": product_id,
        "product_uom_qty": cantidad,
    }])
    return {"linea_id": ids[0], "cotizacion_id": cotizacion_id}

if __name__ == "__main__":
    mcp.run(transport="streamable-http")

Cada llamada JSON-2 se ejecuta en su propia transacción y el estado vive en PostgreSQL, no en el proceso. Puedes levantar varias réplicas del servidor sin cambiar nada. Recuerda que, según la documentación de Odoo, el acceso a la API externa requiere un plan Custom, y que las API keys duran como máximo tres meses.

Ejercicio

  1. Haz un inventario de tu servidor MCP actual: ¿usas Mcp-Session-Id, Roots, Sampling, Logging o Tasks? Cada "sí" es una tarea de migración.
  2. Identifica el estado que hoy guardas en memoria y conviértelo en un handle explícito (un id de Odoo o uno generado por ti).
  3. Activa stateless_http=True, levanta dos réplicas detrás de un balanceador y prueba un flujo completo de cotización.
  4. Lee la especificación en borrador y su changelog antes del 28 de julio, y reporta en el repositorio cualquier problema que encuentres.

Fuentes: Anuncio de la RC 2026-07-28 · Especificación en borrador · Ejemplo stateless del SDK de Python 1.27.1 · API JSON-2 de Odoo 19

Guía gratuita: 5 agentes Python para Odoo

Casos de Junior a Senior, con código verificado contra Odoo 20.

Descargar →

¿Empiezas desde cero? Lee qué es el agent loop y sigue las lecciones gratuitas.

Sigue leyendo