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ón | Era | Estado |
|---|---|---|
2026-07-28 | Stateless, sin handshake — descubrimiento por server/discover | Soportada |
2025-06-18 | Handshake por initialize — es la versión en la que el initialize cierra por defecto | Soportada |
2025-03-26 | Handshake por initialize | Soportada |
La era se decide por el cuerpo de la solicitud, con los encabezados como verificación:
params._metatraeio.modelcontextprotocol/protocolVersion→ era2026-07-28(ver abajo).- Sin ese envoltorio, con
MCP-Protocol-Versionde 2025 o sin el encabezado → era 2025, coninitialize. MCP-Protocol-Version: 2026-07-28sin el envoltorio →400con-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._meta | Obligatoria | Significado |
|---|---|---|
io.modelcontextprotocol/protocolVersion | sí | 2026-07-28 |
io.modelcontextprotocol/clientCapabilities | sí | Capacidades del cliente (un objeto; {} sirve). Sin elicitation de formulario, las tools destructivas se rechazan — vea confirmación de tools destructivas |
io.modelcontextprotocol/clientInfo | recomendada | name 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:
| Encabezado | Tiene que ser igual a |
|---|---|
MCP-Protocol-Version | La versión del envoltorio |
Mcp-Method | El method del mensaje |
Mcp-Name | Solo 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
| Encabezado | Obligatorio | Significado |
|---|---|---|
Authorization | sí | Bearer <access token> emitido para el MCP — ver Autenticación |
X-Workspace-Id | no | Workspace 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-Type | sí | application/json |
Accept | recomendado | application/json |
MCP-Protocol-Version | sí en la era 2026-07-28; recomendado en la de 2025 | Revisión del protocolo |
Mcp-Method | sí en la era 2026-07-28 | El método del mensaje |
Mcp-Name | sí en el tools/call de la era 2026-07-28 | El nombre de la tool |
Origin | condicional | Lo envían los clientes de navegador; se valida contra una allowlist |
Encabezados de la respuesta
| Encabezado | Significado |
|---|---|
MCP-Protocol-Version | Revisión en uso |
Cache-Control | Siempre no-store |
Retry-After | En 429 — ver Límite de solicitudes — y en 503 |
Métodos
| Método | Era 2026-07-28 | Era 2025 | Para qué |
|---|---|---|---|
server/discover | sí | — | 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/list | sí | sí | Las tools que este llamador puede ejecutar, con JSON Schema y anotaciones |
tools/call | sí | sí | Ejecuta una tool |
resources/list | sí | sí | Los documentos de URI fija que este llamador puede leer |
resources/templates/list | sí | sí | Los templates de URI (kobana://finance/payables/{id}, …) que este llamador puede leer |
resources/read | sí | sí | Lee un documento por la URI |
prompts/list | sí | sí | Los guiones listos que este llamador puede usar |
prompts/get | sí | sí | Arma un guion con los argumentos dados |
completion/complete | sí | 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ón | Significado |
|---|---|
readOnlyHint | La tool solo lee |
destructiveHint | La tool borra, cancela o mueve dinero — solo se ejecuta con la confirmación del servidor |
idempotentHint | Repetir la llamada tiene el mismo efecto |
openWorldHint | Siempre 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_downloadno devuelve los bytes: un PDF en base64 en el contexto de un modelo es caro e inútil. El resultado trae elcontent_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_uploadrecibe el archivo encontent_base64(hasta 10 MB) y lo envía al endpoint comomultipart/form-data. Después,finance_attachments_linklo adjunta a un título o movimiento.