OdyDocs

MCP protocol details

Transport, protocol versions, authentication, error codes and raw JSON-RPC examples for building your own MCP client.

Most clients handle all of this for you. Read on if you're writing your own client, or debugging one.

#Transport

EndpointPOST https://api.ody.co/v1/mcp
TransportStreamable HTTP, stateless
Request bodyOne JSON-RPC 2.0 message (arrays are accepted for older clients)
ResponseOne application/json JSON-RPC response. Never an SSE stream
NotificationsMessages without an id get 202 Accepted with no body
SessionsNone. No Mcp-Session-Id, so every request stands alone
GET405 Method Not Allowed (no server-initiated stream)
AuthAuthorization: Bearer ody_live_… or ody_test_… on every request
Rate limitShared with the REST API: 600 requests per minute per key
CORSAny origin

#Protocol versions

The server supports 2025-06-18 (latest), 2025-03-26 and 2024-11-05. It answers initialize with the version you requested if it supports it, otherwise with 2025-06-18.

Capabilities: { "tools": { "listChanged": false } }. Ody offers tools only, with no resources, prompts, sampling or logging. Server info: { "name": "ody", "title": "Ody", "version": "0.3.0" }.

Methods: initialize, notifications/initialized, ping, tools/list, tools/call. Other notifications are accepted and ignored.

#Raw JSON-RPC

curl https://api.ody.co/v1/mcp \
  -H "Authorization: Bearer $ODY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'
initialize response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2025-06-18",
    "capabilities": { "tools": { "listChanged": false } },
    "serverInfo": { "name": "ody", "title": "Ody", "version": "0.3.0" }
  }
}
tools/call response
{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "content": [{ "type": "text", "text": "{\"conversations\":[{\"id\":\"c4f1…\",\"status\":\"open\"}]}" }],
    "structuredContent": { "result": { "conversations": [{ "id": "c4f1…", "status": "open" }] } },
    "isError": false
  }
}

#Tool definitions

Each tools/list entry has name, description, inputSchema (JSON Schema) and annotations:

JSON
{
  "name": "buy_number",
  "description": "Buy a phone number for the workspace (spends real money — …)",
  "inputSchema": {
    "type": "object",
    "properties": {
      "phoneNumber": { "type": "string", "description": "E.164, e.g. +15125550123" },
      "type": { "type": "string", "enum": ["local", "toll_free"] }
    },
    "required": ["phoneNumber"]
  },
  "annotations": { "title": "Buy a number", "readOnlyHint": false, "destructiveHint": false, "idempotentHint": false, "openWorldHint": true }
}

#Errors

SituationWhat you get
Missing or invalid keyHTTP 401, JSON-RPC error -32001 "Unauthorized: invalid API key"
Body isn't JSONHTTP 400, -32700 "Parse error"
Unknown method-32601 "Method not found"
Missing or unknown tool name-32602
Tool not allowed by the key's scopesA result with isError: true naming the missing scope
Business rule (not found, 10DLC not ready, plan gate…)A result with isError: true and a readable message
Unexpected server errorA result with isError: true, "Internal error running tool"
Rate limitedHTTP 429 with Retry-After (see Rate limits)

Tool failures are returned as results rather than JSON-RPC errors on purpose: the model sees the message and can explain or adjust.

Last updated Wed Oct 14 2026 00:00:00 GMT+0000 (Coordinated Universal Time)Something wrong on this page? Tell us