Why JSON-RPC?
MCP needs a tiny, well-understood message format that runs over any transport — stdio, HTTP, WebSockets. JSON-RPC 2.0 is exactly that: a one-page spec, native JSON, and bidirectional support for requests, responses, and one-way notifications.
One-line version: JSON-RPC defines the envelope; MCP defines what goes inside.
The four message types
Request
Has an id. The receiver MUST reply with a Response or Error.
Response (success)
Echoes the request's id and carries a result.
Response (error)
Same id but carries an error with code & message.
Notification
NO id. Fire-and-forget — the receiver MUST NOT reply.
Request shape
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "search_docs",
"arguments": { "query": "MCP transports" },
"_meta": { "progressToken": "abc123" }
}
}
jsonrpcMUST be the literal string"2.0".idis a string or integer — unique across all in-flight requests.params._meta.progressTokenopts into progress notifications.
Response (success vs error)
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{ "type": "text", "text": "Found 3 hits." }
]
}
}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32601,
"message": "Method not found"
}
}
A response MUST contain exactly one of result or error — never both.
Standard error codes
| Code | Meaning | When you see it |
|---|---|---|
| -32700 | Parse error | Invalid JSON. |
| -32600 | Invalid Request | Missing required JSON-RPC field. |
| -32601 | Method not found | Unknown method name. |
| -32602 | Invalid params | Wrong types, missing required, etc. |
| -32603 | Internal error | Server-side bug. |
| -32000…-32099 | Server-defined | Reserve for your own implementation. |
Important: Tool execution failures (invalid input, business-rule violations) belong in CallToolResult.isError: true, NOT a JSON-RPC error. That distinction lets the LLM read the failure and self-correct.
Notifications
Notifications have no id and never get a reply. MCP uses them heavily — progress, cancellation, log messages, list-changed events, the initialized handshake.
{
"jsonrpc": "2.0",
"method": "notifications/progress",
"params": { "progressToken": "abc123", "progress": 50, "total": 100 }
}
Quick summary
- Every MCP message is JSON-RPC 2.0 — request, response (success or error), or notification
- Requests carry an id; notifications don't
- Standard error codes: -32600 invalid request, -32601 method not found, -32602 invalid params, -32603 internal
- Tool execution failures use
isError: trueinside the result, not a JSON-RPC error