MCP Adapterο
Overviewο
Zyra exposes an MCP-compatible endpoint for discovery and invocation.
Discoveryο
GET /v1/mcp
or OPTIONS /v1/mcp
returns a spec-shaped payload suitable for MCP clients:
{
"mcp_version": "0.1",
"name": "zyra",
"description": "Zyra MCP server for domain-specific data visualization",
"capabilities": {
"commands": [
{ "name": "process-decode-grib2", "description": "...",
"parameters": { "type": "object", "properties": { ... } } }
]
}
}
JSON-RPC Methods (POST /v1/mcp
)ο
initialize
: MCP handshake. - Result:{ protocolVersion: '2025-06-18', serverInfo: { name, version }, capabilities: { tools: true } }
tools/list
: returns{ tools: [ { name, description, inputSchema } ] }
tools/call
: invokes a tool by namespaced name (e.g.,process-decode-grib2
) - Params (MCP shape):{ name: str, arguments?: object, mode?: 'sync'|'async' }
- Params (legacy aliascallTool
):{ stage: str, command: str, args?: object, mode?: 'sync'|'async' }
- Result (sync):{ status: 'ok', stdout?, stderr?, exit_code? }
- Result (async):{ status: 'accepted', job_id, poll, ws, download, manifest }
listTools
: alias oftools/list
(returns the same shape)statusReport
(alias:status/report
): minimal status with version
Authenticationο
If an API key is configured (ZYRA_API_KEY
), include it in the
X-API-Key
header (or the value of API_KEY_HEADER
).
Feature Flagsο
ZYRA_ENABLE_MCP
: enable/disable the MCP endpoint (default: enabled)ZYRA_MCP_MAX_BODY_BYTES
: optional request body limit in bytes; requests exceeding the limit are rejected with a JSON-RPC error
Examplesο
Curl status report:
curl -sS -H 'Content-Type: application/json' -H 'X-API-Key: $ZYRA_API_KEY' \
-d '{"jsonrpc":"2.0","method":"statusReport","id":1}' \
http://127.0.0.1:8000/v1/mcp
Initialize and list tools:
curl -sS -H 'Content-Type: application/json' -H 'X-API-Key: $ZYRA_API_KEY' \
-d '{"jsonrpc":"2.0","method":"initialize","id":2}' \
http://127.0.0.1:8000/v1/mcp
curl -sS -H 'Content-Type: application/json' -H 'X-API-Key: $ZYRA_API_KEY' \
-d '{"jsonrpc":"2.0","method":"tools/list","id":3}' \
http://127.0.0.1:8000/v1/mcp
Call a tool (sync):
curl -sS -H 'Content-Type: application/json' -H 'X-API-Key: $ZYRA_API_KEY' \
-d '{"jsonrpc":"2.0","method":"callTool","params":{"stage":"visualize","command":"heatmap","args":{"input":"samples/demo.npy","output":"/tmp/heatmap.png"},"mode":"sync"},"id":3}' \
http://127.0.0.1:8000/v1/mcp
Error Mappingο
Validation errors β JSON-RPC error
-32602
with an explanation of invalid params.Execution failures (non-zero exit codes) β JSON-RPC error
-32000
withdata
:{ exit_code, stderr?, stdout?, stage, command }
.
Progress Streamingο
MCP clients can observe async job progress via WebSocket or Server-Sent Events (SSE):
WebSocket:
/ws/jobs/{job_id}
(supports?stream=stdout,stderr,progress
)SSE:
/v1/mcp/progress/{job_id}?interval_ms=200&max_ms=10000
SSE emits JSON events as data: {...}\n\n
until a terminal status (succeeded
, failed
, or canceled
) or the optional max_ms
timeout elapses.
Quickstartο
Prerequisitesο
Install with API extras:
poetry install --with dev -E api
Run the API locally:
poetry run uvicorn zyra.api.server:app --reload --host 0.0.0.0 --port 8000
Optional: set an API key and include it in requests:
export ZYRA_API_KEY=devkey # Header to include in requests: X-API-Key: devkey
Discover tools (HTTP)ο
Fetch Zyraβs capabilities in MCP format:
curl -sS -H "X-API-Key: $ZYRA_API_KEY" http://127.0.0.1:8000/v1/mcp | jq .
Or via OPTIONS:
curl -sS -X OPTIONS -H "X-API-Key: $ZYRA_API_KEY" http://127.0.0.1:8000/v1/mcp | jq .
JSON-RPC examplesο
Status:
curl -sS -H 'Content-Type: application/json' -H "X-API-Key: $ZYRA_API_KEY" \
-d '{"jsonrpc":"2.0","method":"statusReport","id":1}' \
http://127.0.0.1:8000/v1/mcp | jq .
Initialize and list tools:
curl -sS -H 'Content-Type: application/json' -H "X-API-Key: $ZYRA_API_KEY" \
-d '{"jsonrpc":"2.0","method":"initialize","id":2}' \
http://127.0.0.1:8000/v1/mcp | jq .
curl -sS -H 'Content-Type: application/json' -H "X-API-Key: $ZYRA_API_KEY" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":3}' \
http://127.0.0.1:8000/v1/mcp | jq .
Call a tool (sync) via tools/call
:
curl -sS -H 'Content-Type: application/json' -H "X-API-Key: $ZYRA_API_KEY" \
-d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"visualize-heatmap","arguments":{"input":"samples/demo.npy","output":"/tmp/heatmap.png"},"mode":"sync"},"id":4}' \
http://127.0.0.1:8000/v1/mcp | jq .
WebSocket Transportο
Zyra also provides a native WebSocket MCP endpoint for persistent connections and server-initiated notifications.
Endpoint:
/v1/ws/mcp
(query paramapi_key
when enabled)Protocol: JSON-RPC 2.0 frames over WebSocket
Handshake: 1. Client sends
initialize
2. Server replies with result then emitsnotifications/initialized
Discovery: send
tools/list
and receive{ tools: [...] }
Invocation: send
tools/call
with{ name, arguments, mode }
Progress: when mode=``async``, Zyra emits progress notifications on the same socket: -
{"jsonrpc":"2.0","method":"notifications/progress","params":{ "job_id": "...", ... }}
- Params mirror the job progress stream (e.g.,progress
,stdout
,stderr
,status
,exit_code
,output_file
)
Example (WS frames):
{ "jsonrpc":"2.0", "id": 1, "method": "initialize", "params": {} }
{ "jsonrpc":"2.0", "id": 1, "result": { "protocolVersion": "2025-06-18", "serverInfo": {"name":"zyra","version":"..."}, "capabilities": {"tools": {"listChanged": true}} } }
{ "jsonrpc":"2.0", "method": "notifications/initialized", "params": {} }
{ "jsonrpc":"2.0", "id": 2, "method": "tools/list", "params": {} }
{ "jsonrpc":"2.0", "id": 2, "result": { "tools": [ {"name": "process-decode-grib2", "inputSchema": {"type":"object", ...} } ] } }
{ "jsonrpc":"2.0", "id": 3, "method": "tools/call", "params": { "name": "process-decode-grib2", "arguments": {"file_or_url": "s3://..."}, "mode": "async" } }
{ "jsonrpc":"2.0", "id": 3, "result": { "status": "accepted", "job_id": "abc123", "poll": "/jobs/abc123", "ws": "/ws/jobs/abc123", "download": "/jobs/abc123/download", "manifest": "/jobs/abc123/manifest" } }
{ "jsonrpc":"2.0", "method": "notifications/progress", "params": { "job_id": "abc123", "progress": 0.0 } }
{ "jsonrpc":"2.0", "method": "notifications/progress", "params": { "job_id": "abc123", "stdout": "..." } }
{ "jsonrpc":"2.0", "method": "notifications/progress", "params": { "job_id": "abc123", "status": "succeeded", "exit_code": 0, "output_file": "/v1/jobs/abc123/download" } }
Observe progress (async):
# After submitting an async callTool and capturing the job_id
curl -N -H "X-API-Key: $ZYRA_API_KEY" \
"http://127.0.0.1:8000/v1/mcp/progress/$JOB_ID?interval_ms=200"
IDE integration notesο
Claude Desktop / Cursor / VS Code MCP clients typically probe
GET /mcp
. Ensure Zyra is running and accessible (default port 8000).If an API key is set, configure the client to send
X-API-Key: <value>
with requests.The MCP discovery response uses names like
process-decode-grib2
and includes JSON Schema parameters for each tool.
See alsoο
Example client script:
scripts/mcp_client_example.py
MCP spec: https://modelcontextprotocol.io/docs