Model Context Protocol
An open protocol created by Anthropic that standardizes how AI applications connect to external tools, data sources, and prompts. MCP defines a JSON-RPC 2.0 based client-server protocol where hosts embed clients that talk to servers exposing tools, resources, and prompts.
- Steward
anthropic
- Layer
Tool Layer
- Specification URL
- https://modelcontextprotocol.io/specification
- Created Date
- Nov 25, 2024
Conventions
transport
MCP is a JSON-RPC 2.0 protocol carried over a small, well-defined set of transports
| Text | Rationale | |
|---|---|---|
| # | All MCP messages MUST be framed as JSON-RPC 2.0 requests, responses, or notifications with the 'jsonrpc' field set to the string '2.0' | Reusing JSON-RPC 2.0 gives MCP a battle-tested message shape, standard error envelope, and free library support across every language an agent host is likely to be implemented in |
| # | Implementations MUST use one of the standardized MCP transports (stdio, Streamable HTTP, or the legacy HTTP+SSE transport) rather than inventing a bespoke transport | A closed set of transports lets any compliant client talk to any compliant server without negotiation over transport details |
| Text | Rationale | |
|---|---|---|
| # | Individual JSON-RPC messages sent over the stdio transport MUST NOT contain embedded newlines in the serialized form - each message is a single line terminated by a newline | The stdio transport frames messages by newline; embedding literal newlines inside a message would corrupt the frame boundary |
initialize-request
A well-formed JSON-RPC 2.0 initialize request with the mandatory jsonrpc field and the MCP method name
- Value
- {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"ExampleClient","version":"1.0.0"}}}
missing-json-rpc-field
Invalid - missing the required jsonrpc field, so this is not a JSON-RPC 2.0 message
- Value
- {"id":1,"method":"initialize","params":{}}
lifecycle
Every MCP session begins with an initialize handshake that negotiates protocol version and capabilities before any other request is permitted
| Text | Rationale | |
|---|---|---|
| # | A client MUST send the 'initialize' request as its first message after establishing the transport, and MUST NOT issue any other requests until it has received the initialize response and sent the 'notifications/initialized' notification | A fixed handshake prevents servers from having to guess which client capabilities are available when answering early requests |
| # | The server MUST echo the negotiated protocol version back in its initialize response; if it cannot support any version the client offers, it MUST fail the request rather than proceeding | Explicit version negotiation avoids silent incompatibility where two peers keep talking while interpreting the same method name differently |
initialized-notification
The notification a client sends after receiving a successful initialize response - it has no id because it is a JSON-RPC notification, not a request
- Value
- {"jsonrpc":"2.0","method":"notifications/initialized"}
tool-call-before-initialize
Invalid - a client must not call any method other than initialize until the handshake has completed
- Value
- client sends tools/call as its first message on a fresh connection
capabilities
Clients and servers advertise optional feature groups during initialize so peers can avoid calling unsupported methods
| Text | Rationale | |
|---|---|---|
| # | A server MUST NOT respond successfully to a tools/* request unless it advertised the 'tools' capability in its initialize response, and similarly for 'resources', 'prompts', and 'logging' | Capability advertisement is the contract that lets a client skip calling methods that will never work on this server, avoiding useless round-trips and cryptic errors |
| Text | Rationale | |
|---|---|---|
| # | A server SHOULD declare the 'listChanged' sub-capability under tools, resources, or prompts when it is capable of emitting the corresponding notifications/*/list_changed notification | Clients that know a server will emit list_changed can cache listings and refresh reactively instead of polling |
server-capabilities-object
A server capabilities object declaring tools with listChanged, resources with subscribe and listChanged, and prompts with no sub-capabilities
- Value
- {"capabilities":{"tools":{"listChanged":true},"resources":{"subscribe":true,"listChanged":true},"prompts":{}}}
undeclared-tools-call
Invalid - a server must only answer method groups it has advertised
- Value
- server accepts tools/call without having declared the tools capability in initialize
tools
Tools are callable server-side operations that a client can invoke on behalf of the model
| Text | Rationale | |
|---|---|---|
| # | Every tool declared in a tools/list response MUST carry a JSON Schema object under 'inputSchema' describing the accepted parameters, even if the schema is an empty object | A machine-readable input schema is what lets the host validate arguments, surface them to the model, and generate UI - without it the tool is unusable programmatically |
| # | Tool names returned by a single server MUST be unique within that server's namespace | Clients identify tools by name when calling tools/call; duplicate names would make dispatch ambiguous |
| Text | Rationale | |
|---|---|---|
| # | Tools SHOULD provide a natural-language description aimed at the model, explaining when the tool should be used and what it returns | The description is what the model reads to decide whether to call the tool; terse or missing descriptions lead to either never-called or incorrectly-called tools |
read-file-tool
A well-formed tool declaration with a name, description, and JSON Schema for its single required string parameter
- Value
- {"name":"read_file","description":"Read the contents of a UTF-8 text file from the local filesystem","inputSchema":{"type":"object","properties":{"path":{"type":"string"}},"required":["path"]}}
tool-missing-input-schema
Invalid - inputSchema is required, even for tools that take no parameters (an empty object schema is acceptable)
- Value
- {"name":"read_file","description":"Read a file"}
resources
Resources are readable, URI-addressable pieces of context that a server exposes to the client
| Text | Rationale | |
|---|---|---|
| # | Every resource exposed through resources/list MUST be identified by a URI string, and clients MUST read its content via resources/read using that exact URI | A URI is the only stable identifier a client can hand back when asking for content; names or descriptions are human-readable and may change |
| # | A resource's content MUST be returned as either a text payload with a declared mimeType or as a binary blob encoded with base64 and a declared mimeType | Without a declared mimeType a client cannot decide whether to show the content as text, render it in a UI, or hand it to the model as raw bytes |
| Text | Rationale | |
|---|---|---|
| # | Servers exposing mutable resources SHOULD support the resources/subscribe method so clients can receive updates instead of re-reading on a timer | Push updates reduce latency and avoid wasted round-trips, which matters when a resource backs a live UI element |
file-resource
A resource listing entry using a file URI, a readable name, and a declared mimeType
- Value
- {"uri":"file:///project/README.md","name":"Project README","description":"Top-level project README","mimeType":"text/markdown"}
resource-without-uri
Invalid - resources must be addressable by a URI, not just by name
- Value
- {"name":"README","mimeType":"text/markdown"}
prompts
Prompts are parameterized templates a server exposes that a client can instantiate to produce a model-ready message sequence
| Text | Rationale | |
|---|---|---|
| # | Every prompt returned from prompts/list MUST declare its arguments (if any) with a name and a required/optional flag so clients can prompt the user for values before calling prompts/get | Clients cannot present a prompt to an end user without knowing which variables need to be filled in - hidden arguments break the authoring UX |
review-prompt
A prompt declaration with two required arguments and human-readable descriptions
- Value
- {"name":"review_code","description":"Review a snippet of code","arguments":[{"name":"language","description":"The programming language","required":true},{"name":"code","description":"The code to review","required":true}]}
prompt-missing-argument-names
Invalid - arguments must be objects with name and required flag, not bare strings
- Value
- {"name":"review_code","arguments":["language","code"]}
sampling
Sampling is the server-to-client path that lets a server ask the client to run an LLM inference on its behalf
| Text | Rationale | |
|---|---|---|
| # | A server MUST NOT issue a sampling/createMessage request unless the client advertised the 'sampling' capability in its initialize response | Sampling is the reverse direction of the usual flow and puts load on the client's model quota, so clients opt in explicitly |
| Text | Rationale | |
|---|---|---|
| # | Clients SHOULD give the user an opportunity to review and approve or modify sampling requests before forwarding them to the model, especially for sensitive contexts | Sampling lets a server indirectly instruct the client's model; human review protects against prompt injection and quota abuse |
sampling-create-message
A sampling/createMessage request with a single user turn and a maxTokens budget
- Value
- {"jsonrpc":"2.0","id":42,"method":"sampling/createMessage","params":{"messages":[{"role":"user","content":{"type":"text","text":"Summarize the attached file"}}],"maxTokens":500}}
sampling-without-capability
Invalid - sampling is opt-in on the client side and must not be used unilaterally
- Value
- server issues sampling/createMessage to a client that did not declare the sampling capability
error-handling
MCP reuses JSON-RPC 2.0 error semantics and adds its own small set of application error codes
| Text | Rationale | |
|---|---|---|
| # | Error responses MUST use the JSON-RPC 2.0 error envelope with numeric 'code', human-readable 'message', and optional 'data' fields, and MUST NOT encode errors as successful results with an error flag | Treating application errors as successful results breaks every generic JSON-RPC client library and defeats the purpose of a standard error shape |
method-not-found-error
A well-formed error response reusing the JSON-RPC 2.0 method-not-found code
- Value
- {"jsonrpc":"2.0","id":7,"error":{"code":-32601,"message":"Method not found","data":{"method":"tools/nonexistent"}}}
error-in-result
Invalid - errors must use the error envelope, not a successful result with a flag
- Value
- {"jsonrpc":"2.0","id":7,"result":{"ok":false,"error":"Method not found"}}
notifications
MCP uses JSON-RPC notifications (no id, no response) for change events and out-of-band updates
| Text | Rationale | |
|---|---|---|
| # | Recipients MUST NOT send any response to a JSON-RPC notification, including error responses | Notifications are fire-and-forget by design; responding to them breaks the JSON-RPC contract and can put the transport into an inconsistent state |
list-changed-notification
The notification a server emits when its advertised tool set has changed
- Value
- {"jsonrpc":"2.0","method":"notifications/tools/list_changed"}
response-to-notification
Invalid - a notification has no id and must receive no response at all
- Value
- {"jsonrpc":"2.0","id":null,"result":{}}