MCP is a shared language between AI apps and tools.
Instead of building a custom connector for every pair,
you build once and connect many.
Each AI needed its own integration for each tool. 3 AIs × 4 tools = 12 bespoke connectors to build, test, and maintain. MCP collapses this to a single shared protocol.
MCP defines exactly who does what. The Host orchestrates, the Client connects, the Server acts.
Every single MCP message is one of three shapes. Understand these three and you understand the entire wire protocol.
id.id to match them up.id, no response expected. Fire and forget.code + message.Think of the id like a restaurant order ticket. Multiple requests can fly in parallel — when the server finishes one, it staples its answer to the same ticket number so you know which order just arrived.
id from the corresponding requestresult on success, or error on failure — never bothresult shape depends on the method called (tools/call returns content[], tools/list returns tools[], etc.)notifications/progress — server updates client on a long-running tasknotifications/initialized — client signals it's ready after handshakenotifications/tools/list_changed — server's tools just changed, please re-listnotifications/cancelled — cancel an in-progress requestMCP sessions are deterministic. They always start and end the same way. Click each phase to see the exact messages exchanged.
The very first exchange. Neither side can do any real work until this completes successfully. The client sends its supported protocol version; the server responds with what it can do.
A simple notification (no id) that tells the server "I received your capabilities, I'm ready." This is mandatory before any other request is sent.
Claude asks for the server's tools (and optionally resources and prompts). The server responds with JSON Schemas — structured descriptions that tell Claude exactly what arguments each tool accepts. This is how Claude knows what to call and how to call it.
Based on the user's message and the tool schemas it discovered, Claude decides to call a tool. It sends a tools/call request with structured arguments. Your server runs the real logic — hits an API, queries a database, whatever — and returns the result.
For stdio: the Host kills the child process when Claude Desktop closes or the conversation ends. For HTTP: the session token expires or the connection is dropped. No shutdown message is required — the transport closing is the signal.
MCP gives your server five building blocks. Each has a specific role and a specific controller — the AI, the app, or the user.
JSON-RPC is the format. Transports are the wire. Your choice depends on one thing: local tool or remote service?
console.log inserts text mid-stream and corrupts every subsequent message. Use console.error for logs — it goes to stderr, which the protocol ignores.claude_desktop_config.json or .mcp.json/mcp handles everything: POST for requests, optional GET for SSE streamingOrigin header to prevent DNS rebinding attacksThe official SDK handles all the protocol complexity. You define tools, connect a transport, and you're done.
npx @modelcontextprotocol/inspector node dist/index.js
These aren't preferences. Each one has a technical reason. Violate them and you get silent failures, crashes, or security holes.
console.log to stdoutconsole.error, which goes to stderr and is ignored by the protocol.registerTool() — not server.tool()server.tool() API is deprecated per the official Anthropic skill reference. registerTool() is the supported path: better type safety, automatic schema handling, and compatible with future SDK versions.NodeStreamableHTTPServerTransport for all new remote deployments.package.json.