Lesson 14 — Build a mini MCP-style tool server
Speaking the protocol
MCP messages use JSON-RPC 2.0: small JSON objects with a fixed shape. A request has "jsonrpc": "2.0", an id (a number the client picks) and a method:
{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}
{"jsonrpc": "2.0", "id": 2, "method": "tools/call",
"params": {"name": "add", "arguments": {"a": 2, "b": 3}}}
The response repeats the same id, so the client can match answers to questions, and holds either a result or an error — never both:
tools/list→{"tools": [{"name", "description", "inputSchema"}, ...]}tools/call→{"content": [{"type": "text", "text": "5"}], "isError": false}
MCP has two kinds of errors:
- Protocol errors — the request itself is wrong. They go in
errorwith a code:-32602(invalid params — the spec uses it for an unknown tool) or-32601(method not found, a standard JSON-RPC code). - Tool errors — the tool ran but failed. They go in a normal
resultwith"isError": true, so the model can read what went wrong and try again (like Lesson 21).
Why it matters: this handle() function is the heart of every MCP server. Real servers add a start-up handshake and a connection to send messages over, which we skip — the request and response shapes are the same.
Finish handle(): for "tools/call" return an unknown-tool error (-32602) or ok() with the tool's content and isError; for any other method return error -32601 "Method not found: <method>".
Run your code — when the output matches the task, you will see a green check. Then mark the lesson complete.
A quick 3-question quiz on this lesson — instant feedback.