Index Ai Academy
Phase 9 · Model Context ProtocolProtocol foundations

JSON-RPC 2.0 in depth

About 35 minLooking ahead: some prerequisites aren't done yet

Why this matters

Every MCP message is JSON-RPC 2.0, so this is the floor everything else in the phase stands on. The spec fits on one page, but the details decide whether a server works with every client or only with yours. Notifications get no answer, ids must match, and a parse error has no id. Bugs here show up as hangs and mysterious timeouts in someone else's app, which makes them painful to debug.

  • Tell requests, results, errors and notifications apart, and know which get answers
  • Use the standard error codes correctly, from -32700 to -32603
  • Follow MCP's extra rules on ids, params and batches
  • Build a JSON-RPC dispatcher that never crashes and never leaks internals

Four kinds of message

You met JSON-RPC briefly in Phase 1. Here's the full picture, and a busy restaurant is a good way to see it.

A waiter writes an order on a numbered ticket. The kitchen sends back a dish with that number, or a note saying "we're out of that", also with the number on it. Now and then someone calls out "table six is leaving", and nobody answers, because nobody asked anything.

JSON-RPC works the same way. Every message is a JSON object with "jsonrpc": "2.0", and there are exactly four shapes:

messagehas id?has method?has result or error?gets a reply?
requestyesyesnoyes, exactly one
notificationnoyesnonever
result responseyes (same as the request)noresultno
error responseyes (or null if unknown)noerrorno
{"jsonrpc": "2.0", "id": 7, "method": "tools/call", "params": {"name": "get_campaign", "arguments": {"id": "cmp_103"}}}
{"jsonrpc": "2.0", "id": 7, "result": {"resultType": "complete", "content": [{"type": "text", "text": "..."}]}}
{"jsonrpc": "2.0", "id": 8, "error": {"code": -32601, "message": "Method not found: tools/delete"}}
{"jsonrpc": "2.0", "method": "notifications/progress", "params": {"progressToken": "t1", "progress": 40, "total": 100}}

The receiver tells them apart by which keys are present. A message with method and id is a request. With method and no id, it's a notification, and the receiver must not reply, not even with an error. Answering a notification is a classic bug: the other side gets a response for an id it never sent.

The id is how answers find their questions. Like tickets in a kitchen, a client can have many requests in flight at once, over one connection, and replies can come back in any order. So the id is the only link.

In plain words: an id means "someone is waiting for an answer to this". No id means nobody is, so stay quiet.