Strip away the abstraction and MCP is a small set of real JSON objects sent back and forth — nothing more mysterious than that.
1Why JSON-RPC 2.0
MCP didn't invent a new wire format — it builds on JSON-RPC 2.0, a lightweight, well-established standard for calling remote methods with named parameters and getting a matching response back. Reusing an existing standard means MCP inherits decades of tooling and battle-tested conventions instead of reinventing request/response semantics from scratch.
2The Four Fields That Matter
Every request carries jsonrpc (the protocol version, always "2.0"), id (a value the client picks so it can match this request to its eventual response), method (which operation to perform, like tools/call or tools/list), and params (the arguments for that method). Miss the id and a client juggling several in-flight calls has no way to know which response belongs to which request.
3Step-by-Step Breakdown
The Anatomy of an MCP Message. Under the hood, MCP is JSON-RPC 2.0: every request has a jsonrpc version, an id, a method name, and a params object. When your future server wants to call the read_file tool, it isn't magic — it's one real JSON object sent over the wire, and your server sends back one real JSON object with the result.
Build a Real tools/call Request. Finish build_request so its params object includes the actual arguments the tool call needs, not just the tool's name. Without this field, your server would know which tool to run but never know what to run it on.
Why does every JSON-RPC request need an id field?
- →So the client can match each response back to the specific request that triggered it, since multiple calls can be in flight or arrive out of order.
- →It exists purely for internal logging and has no effect on how the protocol behaves.
One Message Down, One to Go. You just built a real tools/call request by hand — the exact message your server will receive. But before it can be called, a tool has to be described in a way the model can understand and choose correctly. Next: writing your first real tool schema.
Level Up 🚀
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Fully supported.
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1Keep Error Responses Structured, Not Just a String
Return errors as a real JSON-RPC error object with a code and message field rather than embedding the failure inside a normal result — clients and any UI reading the response can then render it consistently.
{ "error": { "code": -32602, "message": "Invalid params" } }SEO Implications
- 1
Target 'JSON-RPC 2.0 example' and 'MCP tools/call request format' separately
Developers debugging a real integration search for the underlying spec and the MCP-specific shape as distinct problems.
Best Practices
Always Generate request ids, Never Reuse Them
A stale or duplicate id makes it possible for a response to be matched to the wrong pending request, silently corrupting client state.
Frequent Bugs
Forgetting to pass the tool's actual arguments in the params.arguments field.
A tools/call request without arguments tells the server which tool to run but not what to run it on — the server has nothing to act on.
Real-World Examples
Claude Desktop's stdio Transport
Claude Desktop launches your MCP server as a subprocess and exchanges these exact JSON-RPC messages over its stdin/stdout — the same message shape you just built by hand.
process.stdout.write(JSON.stringify(response) + "\n")