Ir al contenido

STDIO y configuración MCP

STDIO conecta un cliente con un proceso local de Orionis: el cliente escribe peticiones en stdin y lee las respuestas en stdout. Utiliza el mismo servidor compilado, schemas, hooks y contenedor que HTTP, sin abrir un puerto HTTP.

En el archivo AI configurado de tu aplicación, normalmente routes/ai.py, registra un handle explícito:

from app.mcp.servers.demo_server import DemoServer
from orionis.support.facades import Mcp
Mcp.local("demo", DemoServer)

Si ya añadiste este registro en Servidores y rutas, consérvalo una sola vez. El archivo se carga con ai="routes/ai.py" en la llamada existente app.withRouting(...) de bootstrap/app.py, antes de app.create(); conserva sus demás argumentos.

Mcp.local() recibe el handle y una clase que hereda Server. No acepta una instancia ni una ruta de módulo recibida desde el cliente. El handle debe ser único y cumplir [A-Za-z0-9][A-Za-z0-9_.-]{0,127}: entre 1 y 128 caracteres, primero letra o dígito. demo y support.internal son válidos; un nombre con espacios no lo es.

El handle local es independiente de Server.name y de la dirección HTTP. Puedes registrar la misma clase con Mcp.web("/mcp/demo", DemoServer) y Mcp.local("demo", DemoServer). Cada registro publica su propio transporte; crear uno no crea automáticamente el otro.

Desde la raíz del proyecto, cualquiera de estas alternativas lista los servidores HTTP y STDIO registrados:

Ventana de terminal
orionis mcp:list
python -B reactor mcp:list

La tabla muestra transporte, endpoint o handle y clase del servidor. No abre puertos ni inicia el transporte. Para ejecutar el servidor local, utiliza una de estas alternativas:

Ventana de terminal
orionis mcp:start demo
Ventana de terminal
python -B reactor mcp:start demo
ComandoArgumentos propiosResultado
mcp:listNinguno.Muestra el inventario registrado.
mcp:start namename obligatorio: handle local.Ejecuta STDIO hasta que se cierre la entrada o finalice el transporte.
mcp:start --help--help.Describe el argumento local; no inicia un servidor.

Un handle desconocido escribe el diagnóstico en stderr y termina con código 1. mcp:start no descubre clases a partir de nombres arbitrarios. Los generadores de componentes crean archivos; no sustituyen el registro Mcp.local().

Cada mensaje es un objeto JSON-RPC UTF-8 en una sola línea, terminado con un salto de línea. No envíes un documento JSON con formato en varias líneas ni un array de llamadas. Este mensaje consulta el servidor:

{"jsonrpc":"2.0","id":"discover-1","method":"server/discover","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}

La respuesta usa el mismo id y devuelve el mismo contrato de server/discover que HTTP. No se envían cabeceras HTTP por stdin: la versión y las capacidades están en params._meta. Tampoco necesitas initialize; se admite exclusivamente MCP 2026-07-28.

El lector continúa atendiendo entrada mientras se ejecutan solicitudes dentro del presupuesto configurado. Cada solicitud tiene su scope; las respuestas concurrentes se correlacionan mediante id y no debes depender de su orden. Una notificación notifications/cancelled con params.requestId permite cancelar una solicitud STDIO activa. Al recibir EOF o fallar la E/S del transporte, se cancelan las solicitudes activas y se espera su limpieza; no se garantiza la entrega de respuestas pendientes. Un error JSON-RPC ordinario devuelve su error sin terminar las demás solicitudes.

stdout está reservado para mensajes del protocolo. Orionis separa su salida de consola del canal MCP, pero tus componentes también deben evitar escribir diagnósticos o datos ajenos a JSON-RPC allí. Utiliza logging o stderr para diagnósticos. Esto es especialmente relevante al convertir un ejemplo que utiliza print() en una herramienta MCP.

sequenceDiagram
    participant C as Cliente local
    participant S as Proceso mcp:start
    participant D as Dispatcher MCP
    C->>S: JSON-RPC + salto de línea por stdin
    S->>S: Lectura acotada y admisión
    S->>D: Petición dentro de su scope
    D-->>S: Resultado o mensajes de progreso
    S-->>C: JSON-RPC por línea en stdout
    C->>S: Otra petición o cancelación
    C->>S: EOF al cerrar la entrada
    S->>S: Finalizar tareas y liberar el transporte

Usa McpRequest para consultar método, argumentos y metadata de la llamada. Los servicios se inyectan en constructores y métodos como en los ejemplos de herramientas. Un objeto HTTP Request solo existe en el transporte HTTP; no lo solicites para una herramienta que también deba funcionar por STDIO.

Los middleware HTTP y una cabecera Authorization no se ejecutan por stdin. Los hooks shouldRegister y authorize de las primitivas siguen aplicándose. Define la política de confianza del proceso local y no interpretes la metadata enviada por el cliente como una identidad autenticada.

La entidad McpConfig valida las opciones. Puedes configurarlas desde bootstrap/app.py con la instancia app existente, antes de app.create():

app.withConfigMcp(
max_request_size=1024 * 1024,
max_concurrent_requests=32,
default_page_size=50,
max_page_size=100,
allowed_origins=["https://agent.example.com"],
)

Solo necesitas especificar las opciones que cambies. Los campos restantes utilizan los valores predeterminados de la entidad y sus variables de entorno. Si ya tienes configuración MCP del proyecto, integra los valores allí en lugar de mantener dos fuentes distintas. Si utilizas bootstrap compilado, sigue el procedimiento de caché y reinicio al cambiarlo.

CampoVariable de entornoPredeterminado
max_request_sizeMCP_MAX_REQUEST_SIZEHTTP_MAX_BODY_SIZE o 1 MiB; bytes máximos de entrada.
max_concurrent_requestsMCP_MAX_CONCURRENT_REQUESTSHTTP_MAX_CONCURRENT_REQUESTS o 32 solicitudes activas.
max_response_sizeMCP_MAX_RESPONSE_SIZE4 MiB; presupuesto de respuesta serializada.
max_metadata_sizeMCP_MAX_METADATA_SIZE64 KiB de metadata serializada.
default_page_sizeMCP_DEFAULT_PAGE_SIZE50 entradas por página.
max_page_sizeMCP_MAX_PAGE_SIZE100; límite de validación de default_page_size.

La paginación MCP acepta un cursor opaco, no un tamaño de página proporcionado por el cliente. max_page_size no habilita un argumento RPC limit. El límite de concurrencia se aplica a cada instancia de transporte; cada endpoint HTTP tiene la suya. El kernel HTTP puede imponer además su propio límite de peticiones.

CampoVariable de entornoPredeterminado
subscription_buffer_sizeMCP_SUBSCRIPTION_BUFFER_SIZE64 eventos en el buffer de cada suscripción.
max_subscriptionsMCP_MAX_SUBSCRIPTIONS1024 listeners en el bus de eventos.
max_resource_subscriptionsMCP_MAX_RESOURCE_SUBSCRIPTIONS64 URI de recurso por escucha.
subscription_keepaliveMCP_SUBSCRIPTION_KEEPALIVE15 segundos entre keepalives.
tool_search_max_resultsMCP_TOOL_SEARCH_MAX_RESULTS20 resultados por búsqueda de catálogo.
tool_search_max_callsMCP_TOOL_SEARCH_MAX_CALLS5 llamadas por ejecución de catálogo.
tool_search_max_output_bytesMCP_TOOL_SEARCH_MAX_OUTPUT_BYTES256 KiB de salida de catálogo.

Los streams abiertos conservan una plaza de admisión hasta terminar. Los límites y el bus en memoria pertenecen al proceso, no son una cuota distribuida entre workers. Consulta Flujos avanzados para suscripciones, herramientas agrupadas y sus requisitos.

CampoVariable de entornoPredeterminado
allowed_originsCORS_ALLOW_ORIGINSLista vacía.

Una petición sin Origin está permitida. Si la cabecera está presente, su valor debe coincidir exactamente con un origen HTTP(S) configurado, como https://agent.example.com. No se aceptan comodines, rutas ni una excepción implícita para el mismo origen. Una lista vacía rechaza cualquier Origin presente.

Esta opción valida el origen para MCP; si conectas desde un navegador, configura también el CORS del flujo HTTP. Un agente que se ejecuta como proceso remoto no es necesariamente un navegador ni tiene por qué enviar Origin.

Los presupuestos enteros deben ser positivos; subscription_keepalive debe ser finito y mayor que cero, y default_page_size no puede superar max_page_size. La configuración incorrecta se rechaza al crear la entidad, antes de servir las peticiones.