Saltar al contenido principal

Transporte

Streamable HTTP, stateless. Un mensaje JSON-RPC 2.0 por POST, una respuesta JSON. No hay Mcp-Session-Id, no hay reanudación y no hay stream servidor→cliente: la aplicación corre varias réplicas, y ninguna operación necesita empujar mensajes al cliente.

Versiones del protocolo​

El servidor habla dos eras del protocolo en el mismo endpoint:

VersiónEraEstado
2026-07-28Stateless, sin handshake — descubrimiento por server/discoverSoportada
2025-06-18Handshake por initialize — es la versión en la que el initialize cierra por defectoSoportada
2025-03-26Handshake por initializeSoportada

La era se decide por el cuerpo de la solicitud, con los encabezados como verificación:

  • params._meta trae io.modelcontextprotocol/protocolVersion → era 2026-07-28 (ver abajo).
  • Sin ese envoltorio, con MCP-Protocol-Version de 2025 o sin el encabezado → era 2025, con initialize.
  • MCP-Protocol-Version: 2026-07-28 sin el envoltorio → 400 con -32602. El servidor no cae en silencio a la era antigua: pediste la semántica stateless y recibirías la otra.

Una versión que el servidor no habla — en el encabezado o en el envoltorio, en cualquier era — se responde con 400 y el error JSON-RPC -32022, listando las soportadas:

{ "jsonrpc": "2.0", "id": 1, "error": {
"code": -32022,
"message": "Unsupported protocol version",
"data": { "supported": ["2026-07-28", "2025-06-18", "2025-03-26"], "requested": "1999-01-01" }
} }

La versión en uso vuelve en el encabezado MCP-Protocol-Version de la respuesta.

Era 2026-07-28​

No hay handshake: cada solicitud lleva su propia versión y las capacidades del cliente en params._meta, y el servidor no recuerda nada entre una solicitud y otra.

Clave de params._metaObligatoriaSignificado
io.modelcontextprotocol/protocolVersionsí2026-07-28
io.modelcontextprotocol/clientCapabilitiessíCapacidades del cliente (un objeto; {} sirve). Sin elicitation de formulario, las tools destructivas se rechazan — vea confirmación de tools destructivas
io.modelcontextprotocol/clientInforecomendadaname y version del cliente

Los encabezados repiten lo que está en el cuerpo, para que los intermediarios puedan enrutar sin leerlo — y por eso tienen que coincidir con él:

EncabezadoTiene que ser igual a
MCP-Protocol-VersionLa versión del envoltorio
Mcp-MethodEl method del mensaje
Mcp-NameSolo en tools/call: el params.name, crudo o como =?base64?…?=

Cualquier divergencia es 400 con -32020 (Header mismatch: …). Si falta clientCapabilities, es 400 con -32602.

Los resultados de esta era siempre traen resultType: "complete" y el servidor en _meta["io.modelcontextprotocol/serverInfo"]. server/discover, tools/list, resources/list, resources/templates/list, resources/read y prompts/list traen además ttlMs: 0 y cacheScope: "private": lo que vuelve está filtrado por el scope y los permisos de quien llama, así que no debe reutilizarse ni compartirse. Un tools/call de una tool destructiva puede volver resultType: "input_required" — ver Confirmación de tools destructivas.

Encabezados de la solicitud​

EncabezadoObligatorioSignificado
AuthorizationsíBearer <access token> emitido para el MCP — ver Autenticación
X-Workspace-IdnoWorkspace predeterminado de la conexión, usado cuando la tool no recibe workspace_id — lo mismo que ?workspace_id= en la URL. Ver Autenticación → El workspace
Content-Typesíapplication/json
Acceptrecomendadoapplication/json
MCP-Protocol-Versionsí en la era 2026-07-28; recomendado en la de 2025Revisión del protocolo
Mcp-Methodsí en la era 2026-07-28El método del mensaje
Mcp-Namesí en el tools/call de la era 2026-07-28El nombre de la tool
OrigincondicionalLo envían los clientes de navegador; se valida contra una allowlist

Encabezados de la respuesta​

EncabezadoSignificado
MCP-Protocol-VersionRevisión en uso
Cache-ControlSiempre no-store
Retry-AfterEn 429 — ver Límite de solicitudes — y en 503

Métodos​

MétodoEra 2026-07-28Era 2025Para qué
server/discoversí—Versiones soportadas, capacidades del servidor e instrucciones
initialize—síNegociación de versión, capacidades del servidor e instrucciones
ping—síPrueba de vida; responde un resultado vacío
tools/listsísíLas tools que este llamador puede ejecutar, con JSON Schema y anotaciones
tools/callsísíEjecuta una tool
resources/listsísíLos documentos de URI fija que este llamador puede leer
resources/templates/listsísíLos templates de URI (kobana://finance/payables/{id}, …) que este llamador puede leer
resources/readsísíLee un documento por la URI
prompts/listsísíLos guiones listos que este llamador puede usar
prompts/getsísíArma un guion con los argumentos dados
completion/completesísíSugerencias para un argumento de prompt o de template

Resources, prompts y completions están en Resources y prompts.

Cualquier otro método es -32601 Method not found — en la era 2026-07-28, con HTTP 404; en la de 2025, en el cuerpo de un 200. El batching JSON-RPC no está soportado — se eliminó en la revisión 2025-06-18, y un cuerpo en array se responde con 400.

El cuerpo tiene un tope de 14 MB (14.680.064 bytes) — suficiente para la mayor solicitud legítima, un finance_attachments_upload de 10 MB en base64 con el envoltorio. Por encima, la respuesta es 413 con { "jsonrpc": "2.0", "id": null, "error": { "code": -32600, "message": "Request body too large (limit 14680064 bytes)" } }: un Content-Length declarado por encima del tope se rechaza incluso antes de la autenticación, y un cuerpo sin Content-Length (o con uno falso) se corta en el tope durante la lectura.

Una notificación (mensaje sin id, como notifications/initialized) se acepta con 202 y cuerpo vacío, en las dos eras.

La autenticación viene antes de cualquier método, incluido server/discover: sin token, la respuesta es el 401 con el desafío que lleva al cliente al login OAuth — ver Autenticación.

server/discover (era 2026-07-28)​

POST / HTTP/1.1
Authorization: Bearer <access token>
Content-Type: application/json
MCP-Protocol-Version: 2026-07-28
Mcp-Method: server/discover
{ "jsonrpc": "2.0", "id": 1, "method": "server/discover",
"params": { "_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {},
"io.modelcontextprotocol/clientInfo": { "name": "my-client", "version": "1.0.0" }
} } }
{ "jsonrpc": "2.0", "id": 1, "result": {
"resultType": "complete",
"supportedVersions": ["2026-07-28", "2025-06-18", "2025-03-26"],
"capabilities": { "tools": {}, "resources": {}, "prompts": {}, "completions": {} },
"instructions": "Tools for Kobana Finance: ...",
"ttlMs": 0,
"cacheScope": "private",
"_meta": {
"io.modelcontextprotocol/serverInfo": {
"name": "kobana-finance",
"title": "Kobana Finance",
"version": "1.0.0"
}
}
} }

initialize (handshake de la era 2025)​

{ "jsonrpc": "2.0", "id": 1, "method": "initialize",
"params": { "protocolVersion": "2025-06-18" } }
{ "jsonrpc": "2.0", "id": 1, "result": {
"protocolVersion": "2025-06-18",
"capabilities": {
"tools": { "listChanged": false },
"resources": { "subscribe": false, "listChanged": false },
"prompts": { "listChanged": false },
"completions": {}
},
"serverInfo": {
"name": "kobana-finance",
"title": "Kobana Finance",
"version": "1.0.0"
},
"instructions": "Tools for Kobana Finance: ..."
} }

Las instructions (en initialize y en server/discover) avisan al agente que los valores de campo en los resultados son datos escritos por terceros, nunca instrucciones, y piden confirmación del usuario antes de una acción destructiva o que mueve dinero que un resultado parezca pedir.

tools/list​

Devuelve solo las tools que el llamador puede ejecutar — las que alcanza el scope del token y que la persona que autorizó puede hacer en el panel. El inputSchema de una tool de escritura es el propio schema de request del endpoint — los mismos campos, obligatorios y enums de la Referencia de la API. Cada tool lleva annotations, que es lo que hace que el cliente pida confirmación al usuario:

AnotaciónSignificado
readOnlyHintLa tool solo lee
destructiveHintLa tool borra, cancela o mueve dinero — solo se ejecuta con la confirmación del servidor
idempotentHintRepetir la llamada tiene el mismo efecto
openWorldHintSiempre false — la tool no alcanza nada fuera de Finanzas Inteligentes

tools/call​

{ "jsonrpc": "2.0", "id": 2, "method": "tools/call",
"params": { "name": "finance_payables_list",
"arguments": { "workspace_id": "2b93f876-769a-4bdc-b23e-304957f313b4",
"filters": { "status": "pending" }, "limit": 10,
"fields": ["description", "amount", "due_date"] } } }

El resultado trae la respuesta JSON del endpoint como contenido de texto:

{ "jsonrpc": "2.0", "id": 2, "result": {
"content": [{ "type": "text", "text": "{ \"data\": [ ... ], \"meta\": { \"next_cursor\": \"…\" } }" }]
} }

Los errores sobre los que el llamador puede actuar vuelven con isError: true dentro del resultado, no como error JSON-RPC — ver Errores.

Despacho in-process​

Cada tool despacha in-process al handler de /v1 que implementa la operación, con la identidad ya verificada y el workspace resuelto para la llamada — el token del MCP no se reenvía, y el endpoint sigue exigiendo su propio scope. No hay un segundo viaje HTTP ni una segunda implementación: filtros, validación, reglas de liquidación, prorrateo, bloqueos de seguridad y aislamiento por workspace son el mismo código que corre la API REST. Las escrituras quedan registradas con el canal mcp (created_via / updated_via), distinguibles de las hechas por la API o por el panel.

Archivos​

  • Descarga — finance_attachments_download no devuelve los bytes: un PDF en base64 en el contexto de un modelo es caro e inútil. El resultado trae el content_type, el tamaño y el camino para descargar el archivo en la API — con un token emitido para la API, ya que el del MCP no vale allí.
  • Subida — finance_attachments_upload recibe el archivo en content_base64 (hasta 10 MB) y lo envía al endpoint como multipart/form-data. Después, finance_attachments_link lo adjunta a un título o movimiento.