Saltar a contenido

MCP y WebSocket

Expone capacidades de tu plugin a agentes de IA (MCP) y a la UI en tiempo real (WebSocket) sin duplicar la lógica REST.

Cuándo leer esta página

  • Quieres que un agente invoque operaciones de tu dominio (helloworld_list_items, template_create, …).
  • Necesitas push bidireccional desde el panel (chat, notificaciones, progreso).
  • Ya tienes servicios o handlers REST y buscas una fachada adicional.

Beta

MCP HTTP y WebSocket están implementados en el monorepo. La autenticación OIDC en MCP sigue en diseño (ADR 005); en desarrollo local se usa el header Authorization como en REST.

Regla de oro

Los handlers MCP y los handlers WebSocket reutilizan la misma lógica que tus endpoints REST. No copies reglas de negocio en un archivo aparte.

Fachada Ruta Hook de bootstrap
MCP (Streamable HTTP) POST /mcp register_mcp_tools
WebSocket WS /api/v1/ws register_ws_namespaces

El servidor MCP implementa el ciclo de vida mínimo (initialize, notifications/initialized, tools/list, tools/call, ping) compatible con clientes como Cursor IDE. URL local en mcp.json: http://localhost:8000/mcp (API, no Vite).

Ver ADR 006 y ADR 010.

Leyenda: Handshake MCP Streamable HTTP y convención de nombres snake_case. Actores: cliente MCP, servidor FastAPI. Estado: Implementado (beta).

sequenceDiagram
  participant Client as Cliente MCP
  participant API as POST /mcp
  participant Reg as McpToolRegistry
  Client->>API: initialize
  API-->>Client: capabilities
  Client->>API: notifications/initialized
  Client->>API: tools/list
  API->>Reg: listar tools activas
  Reg-->>API: helloworld_list_items core_hello
  API-->>Client: tools
  Client->>API: tools/call helloworld_list_items
  API->>Reg: invocar handler
  Reg-->>API: resultado
  API-->>Client: JSON-RPC result

Leyenda: Hub WebSocket con namespaces registrados por plugins. Actores: panel React, WsHub, ws_handler. Estado: Implementado (beta).

flowchart TB
  subgraph panel [Panel React]
    Client[WebSocket client]
  end
  subgraph api [FastAPI]
    Hub["WS /api/v1/ws"]
    Registry[WsNamespaceRegistry]
  end
  subgraph plugins [Plugins]
    AI[ai-agents ws_handler]
    Demo[demo namespace]
  end
  Client -->|envelope namespace type payload| Hub
  Hub --> Registry
  Registry --> AI
  Registry --> Demo
  AI -->|chat.reply| Hub
  Hub --> Client

Broadcasting de dominio (Diseño)

WebSocket es el transporte. El broadcasting de hechos de dominio hacia la UI es una fachada prevista sobre io.ws (Eventos de dominio, ADR 021). No sustituye events entre plugins ni MCP.

MCP: registrar tools

  1. Crea mcp_tools.py en tu plugin.
  2. Exporta register_mcp_tools en __init__.py.
  3. Usa PluginMcpRegistrar para el prefijo {namespace}_{action} (guiones del plugin se normalizan a _).
from cortex_framework.io.mcp.helpers import PluginMcpRegistrar
from cortex_framework.io.mcp.registry import McpToolRegistry

async def _list_items(_arguments: dict) -> dict:
    return {"items": [...], "count": 0}

def register_mcp_tools(registry: McpToolRegistry) -> None:
    mcp = PluginMcpRegistrar("helloworld", registry)
    mcp.register("list_items", "Lista ítems de demostración.", _list_items)
    # Nombre final: helloworld_list_items

Referencia completa: plugins/helloworld/cortex_plugin_helloworld/mcp_tools.py (si existe) o plugins/booking/... en el producto de referencia.

Convención de nombres

Tool Equivalente REST típico
helloworld_list_items GET /api/v1/samples/todo
helloworld_create_item POST /api/v1/samples/todo

CRUD: verbos cortos (list, get, create, update, delete). Acciones de dominio: verbo_sujeto (p. ej. check_availability, add_payment). El framework registra core_hello como tool de prueba de plataforma.

Protocolo HTTP

Petición JSON-RPC 2.0:

{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {"name": "helloworld_list_items", "arguments": {}}
}

Header obligatorio en tests y dev: Authorization.

WebSocket: registrar namespaces

  1. Implementa un handler async (message, context) -> None.
  2. Regístralo con register_ws_namespaces.
  3. El cliente envía envelopes {namespace, type, payload}.
from cortex_framework.io.ws.registry import WsNamespaceRegistry

async def handle_demo_ws(message: dict, context) -> None:
    msg_type = str(message.get("type", "")).strip()
    if msg_type == "ping":
        await context.send("pong", {}, namespace="demo")

def register_ws_namespaces(registry: WsNamespaceRegistry) -> None:
    registry.register("demo", handle_demo_ws)

Referencia: plugins/ai-agents/cortex_plugin_ai_agents/ws_handler.py (namespace ai-agents, tipos chat y tools.list).

Envelope

Campo Descripción
namespace Id registrado en bootstrap (único por handler)
type Tipo de mensaje acordado entre cliente y plugin
payload Objeto JSON con datos del mensaje

Al conectar, el servidor envía {"type": "connected"}. Si el namespace no existe, responde con type: error y payload.code: UNKNOWN_NAMESPACE.

Errores frecuentes

Síntoma Causa probable
Tool no aparece en tools/list Plugin no está activo en /control/plugins o falta exportar register_mcp_tools
Unknown tool Nombre sin prefijo {namespace}_ o typo en tools/call
UNKNOWN_NAMESPACE en WS Namespace no registrado o plugin ai-agents desactivado
Lógica duplicada REST/MCP Handler MCP debería llamar al mismo servicio que el router FastAPI

Siguiente paso