Read MCP messages: JSON-RPC 2.0
Learn the four message shapes every MCP exchange is built from, and the standard error codes.
- Tell requests, results, errors and notifications apart
- Match a response to its request by id
- Choose the right JSON-RPC error code
Every MCP message is a JSON-RPC 2.0 object. There are just four shapes:
| Shape | Has id? | Has method? | Has… |
|---|---|---|---|
| Request | yes (string or integer, never null) | yes | optional params |
| Result response | same id as the request | no | result (with a resultType) |
| Error response | same id as the request | no | error with integer code and message |
| Notification | no | yes | optional params - and never gets a reply |
1{
2 "jsonrpc": "2.0",
3 "id": 1,
4 "method": "tools/list",
5 "params": {
6 "_meta": {
7 "io.modelcontextprotocol/protocolVersion": "2026-07-28",
8 "io.modelcontextprotocol/clientInfo": {
9 "name": "ExampleClient",
10 "version": "1.0.0"
11 },
12 "io.modelcontextprotocol/clientCapabilities": {}
13 }
14 }
15}
16
17{
18 "jsonrpc": "2.0",
19 "id": 1,
20 "result": {
21 "resultType": "complete",
22 "tools": [
23 {
24 "name": "get_weather",
25 "description": "Get current weather for a city",
26 "inputSchema": {
27 "type": "object",
28 "properties": {
29 "city": {
30 "type": "string"
31 }
32 },
33 "required": [
34 "city"
35 ]
36 }
37 }
38 ]
39 }
40}In the current revision every result carries a resultType: "complete" for a finished answer, or "input_required" when the server needs more input first (you will meet that in the multi round-trip lesson). The id ties the result to its request, so many requests can be in flight at once.
Error codes
MCP uses the standard JSON-RPC codes for protocol failures, plus a few of its own:
| Code | Meaning |
|---|---|
-32700 | Parse error: the message isn’t valid JSON |
-32600 | Invalid request |
-32601 | Method not found |
-32602 | Invalid params (also: unknown tool, missing required _meta, resource not found) |
-32603 | Internal error |
-32020 to -32022 | MCP-specific: header mismatch, missing client capability, unsupported protocol version |
Key takeaways
Four shapes: request (id + method), result (id + result), error (id + error), notification (method, no id).
Responses reuse the request’s id; request ids are never
null.Standard codes: -32700 parse, -32601 unknown method, -32602 invalid params, -32603 internal.
Lesson quiz
6 questions · pass with 5 correct · up to 50 XP
Passing this quiz completes the lesson and keeps your streak going. Questions you miss come back in review sessions later.
Practice: write Python
Write Python in the editor and run it against sample inputs. Python runs locally in your browser using a WebAssembly runtime.
Classify messages
Each input line is one JSON-RPC message. Print what it is: request (has method and a non-null id), notification (has method, no id), result (has id and result), error (has error) or invalid (anything else, including a request whose id is null).
- All four shapes
- Null id and junk
Python runs in a sandboxed browser worker with a 60 second time limit. Its runtime loads from the Pyodide CDN; your code stays in this browser.
Answer an unknown method
Read one JSON-RPC request. The server supports only tools/list and tools/call. If the method is supported print supported; otherwise print the error response as compact JSON: {"jsonrpc": "2.0", "id": ID, "error": {"code": -32601, "message": "Method not found"}} (use json.dumps on a dictionary with keys in that order).
- Unknown method
- String id
- Supported
Python runs in a sandboxed browser worker with a 60 second time limit. Its runtime loads from the Pyodide CDN; your code stays in this browser.
Questions about this lesson
Stuck? Ask. Figured something out? Share it. Explaining is one of the best ways to learn.
Loading posts…