Lesson 5 / 31
JSON-RPC 2.0 in Five Minutes
Read the three message shapes MCP is built on.
Requests, responses and notifications
MCP messages are JSON-RPC 2.0. A request has jsonrpc: "2.0", a method, optional params and an id, and expects a response with the same id containing either a result or an error (with a numeric code and a message). A notification has a method but no id and gets no reply. Standard error codes include -32700 (parse error), -32600 (invalid request), -32601 (method not found), -32602 (invalid params) and -32603 (internal error). Because messages are plain JSON, MCP is easy to log, replay and test with ordinary tools.
JSON-RPC underneath
MCP is JSON-RPC messages, a capability handshake and three server primitives.
A mini JSON-RPC dispatcher, run
I ran this with plain Python 3 (standard library only). The function returns a result for a valid call, None for a notification (no id, no reply), and the standard error objects for an unknown method (-32601), bad params (-32602) and unparseable text (-32700).
import json
def handle(raw, methods):
"""A tiny JSON-RPC 2.0 dispatcher showing request, notification and error shapes."""
try:
msg = json.loads(raw)
except json.JSONDecodeError:
return {"jsonrpc": "2.0", "id": None, "error": {"code": -32700, "message": "Parse error"}}
if "id" not in msg: # notification: do the work, send no reply
methods.get(msg["method"], lambda p: None)(msg.get("params"))
return None
if msg["method"] not in methods:
return {"jsonrpc": "2.0", "id": msg["id"], "error": {"code": -32601, "message": "Method not found"}}
try:
return {"jsonrpc": "2.0", "id": msg["id"], "result": methods[msg["method"]](msg.get("params"))}
except (KeyError, TypeError):
return {"jsonrpc": "2.0", "id": msg["id"], "error": {"code": -32602, "message": "Invalid params"}}
methods = {"add": lambda p: p["a"] + p["b"], "ping": lambda p: {}}
for raw in ('{"jsonrpc":"2.0","id":1,"method":"add","params":{"a":2,"b":3}}',
'{"jsonrpc":"2.0","method":"ping"}',
'{"jsonrpc":"2.0","id":2,"method":"mul","params":{}}',
'{"jsonrpc":"2.0","id":3,"method":"add","params":{"a":2}}',
'{not json'):
print(handle(raw, methods))
Output:
{'jsonrpc': '2.0', 'id': 1, 'result': 5}
None
{'jsonrpc': '2.0', 'id': 2, 'error': {'code': -32601, 'message': 'Method not found'}}
{'jsonrpc': '2.0', 'id': 3, 'error': {'code': -32602, 'message': 'Invalid params'}}
{'jsonrpc': '2.0', 'id': None, 'error': {'code': -32700, 'message': 'Parse error'}}Quick check: What marks a JSON-RPC message as a notification?
- It has a result field
- It has no id and expects no reply
- It uses XML
- It has error code -32601
Answer
It has no id and expects no reply — Only requests carry an id that a response must echo.