Overview
The Model Context Protocol (MCP) is a JSON-RPC 2.0 framing layer that lets an LLM client call external integrations through a stable, typed interface. The spec is published at modelcontextprotocol.io. Every MCP interaction is a JSON-RPC request-response pair or notification. The protocol defines three capability primitives: tools, resources, and prompts. Understanding the framing is the prerequisite for designing servers that work reliably across clients such as Claude Code and Claude Desktop.
Speak JSON-RPC 2.0; every message has an id, method, and params
MCP is not a custom wire format. It reuses JSON-RPC 2.0 exactly: every request carries an id, a method string, and a params object. Every response carries the same id and either a result or an error object. Notifications omit id because they expect no response.
{ "jsonrpc": "2.0", "id": 1, "method": "tools/call",
"params": { "name": "search_issues", "arguments": { "q": "is:open label:bug" } } }
{ "jsonrpc": "2.0", "id": 1, "result": { "content": [{ "type": "text", "text": "..." }] } }Reusing JSON-RPC means any JSON-RPC 2.0 library handles framing. The MCP-specific logic is entirely in the method names and payload shapes.
Capabilities are negotiated at session start with initialize/initialized
When a client connects, it sends initialize with its declared capabilities. The server responds with its own capability advertisement. The client then sends initialized to confirm the handshake. Neither side calls methods the peer did not advertise.
Capabilities include: tools, resources, prompts, logging, and experimental. Declaring a capability in initialize signals that the peer supports the matching method family. A server that declares resources but not tools will receive resource fetch requests but no tool calls.
Design servers to declare exactly the capabilities they implement. Advertising capabilities the server cannot fulfil causes the client to send methods that return errors.
Tools are callable actions; use them for writes and side effects
A tool is a named function with a JSON Schema input definition. The client calls tools/list to discover what tools are available and tools/call to invoke one. Tools are the correct primitive for anything that performs an action: creating a record, running a query, sending a message. See mcp-tool-design for schema design rules.
The model reads every tool’s description field before deciding whether to call it. Tool descriptions are context budget. Keep them accurate and concise.
Resources are addressable data; use them for read-only context
A resource is a URI-addressed piece of content the client can fetch without triggering side effects. The client calls resources/list to enumerate available resources and resources/read to fetch one by URI. Resources support subscriptions: the server can push notifications/resources/updated when a resource changes.
github://org/repo/issues/401
notion://workspace/page/abc123Resources keep the tool list short and give the model a stable reference scheme. See mcp-resources for the resource design pattern.
Prompts are reusable templates the user can invoke by name
A prompt is a named message template with optional arguments. The client calls prompts/list to discover them and prompts/get to render one with arguments. Prompts are useful for recurring patterns: a code-review template, a release-notes skeleton, a triage prompt. Clients like Claude Desktop surface prompts as slash commands.
Prompts are lower-friction than tools for operations that are primarily compositional rather than computational. If an operation is mostly “fill in a template and present to the user,” a prompt is correct. If it calls an API or modifies state, a tool is correct.
Client and server roles are asymmetric; clients drive, servers respond
The client (Claude Code, Claude Desktop) initiates all requests. The server advertises capabilities and responds. The exception is server-sent notifications (logging, resource updates, progress), which flow server-to-client without a preceding client request. The server never initiates a tool call or resource read.
This asymmetry matters for mcp-security: the attack surface is on the server side because the server holds credentials and executes code. The client side must validate that it is talking to the intended server, especially over HTTP transports.