Protocols & Standards
Model Context Protocol (MCP)
Protocol for connecting AI agents to external tools, data sources, and services
Last verified 2026-09-25
Summary
defines how agents discover and use tools, read resources, and invoke prompts. It is the baseline protocol for connecting AI applications to external systems. Current spec revision: 2026-07-28, with a stateless core, no initialization handshake, and no protocol sessions. All Tier 1 - TypeScript, Python, Go, and C# - support it. Transport options are stdio for local processes and Streamable HTTP for remote servers. Protected HTTP servers use OAuth-based authorization discovery.
Agent → connects via stdio/HTTP → MCP Server
├─ discovers tools
├─ reads resources (documents, data)
├─ invokes prompts (structured workflows)
└─ negotiates optional extensions such as async Tasks- Three core primitives: tools (model-controlled), resources (application-controlled), prompts (user-controlled)
- Stateless JSON-RPC core over stdio or Streamable HTTP
- Async Tasks are the official
io.modelcontextprotocol/tasksextension, not a core primitive - Authorization for protected HTTP servers via protected-resource metadata and authorization-server discovery
- Server advertises capabilities; agent selects which to use
- - the first official MCP extension (SEP-1865, Stable
2026-01-26) - add interactive UI surfaces inside compatible clients (ChatGPT, Claude, VS Code, Goose, Postman); see MCP Apps - Agent Skills complement MCP by packaging reusable instructions, scripts, references, and assets
MCP defines a standard wire format for an agent to discover what a server can do, call tools, read resources, use structured prompts, and negotiate extensions. Implement MCP first when you are exposing remote tools, data, or workflows to external agents. For page-scoped browser tools, see WebMCP.
Origin and Governance
Anthropic published the initial MCP specification in November 2024. Since then it has become the main cross-vendor protocol surface for connecting agents to tools, resources, prompts, and workflow primitives. Stewardship now sits with the Agentic AI Foundation (AAIF) under the Linux Foundation (founded 2025-12-09, with MCP, goose, and as anchor projects); the MCP steering group persists under that umbrella.
The current specification revision is 2026-07-28. It shipped on schedule, and all Tier 1 SDKs support it. Its predecessor, 2025-11-25, remains relevant only for compatibility with clients that have not migrated. Current semantics come first below; legacy examples are labeled.
Revision 2026-07-28 was the largest change since MCP launched. It introduced a stateless
protocol core (removing the initialize / notifications/initialized handshake and the
Mcp-Session-Id header), an optional server/discover method, a single subscriptions/listen
stream in place of the HTTP GET endpoint and resources/subscribe, multi round-trip requests in
place of server-initiated roots/list, sampling/createMessage, and elicitation/create, a
formal extensions framework, OAuth 2.0 / hardening, a deprecation lifecycle with a 12-month
minimum window, JSON Schema 2020-12 tool schemas, and W3C Trace Context. Build new servers against
2026-07-28; add predecessor compatibility only when a supported client requires it.
The MCP roadmap update (2026-08-22) sets direction for what lands next: server-initiated events ( and channels, replacing polling) alongside maturing the Tasks extension; a unified HTTP-native transport that extends Streamable HTTP to local servers currently reached over stdio; agent identity work ( adoption and for delegation without long-lived ); standardized tool-result handling and progressive discovery so a server can expose a small entry point and reveal more of a large tool catalogue as a conversation narrows; and SDK ergonomics and spec-conformance work. None of this is shipped spec behavior yet; build against shipped features only.
Core Primitives and Extensions
MCP exposes three core primitives plus negotiated extensions. Each primitive has a different controller - the entity that decides when and how it is used.
Tools (Model-Controlled)
Tools are functions the model calls autonomously to take action or retrieve information. The model receives a list of available tools, selects which to call based on its reasoning, provides arguments, and receives the result.
{
"type": "tool",
"name": "billing_create_invoice",
"description": "Creates a new invoice for a customer. Call this when the user wants to bill a customer for services. Requires a valid customer_id - use billing_list_customers first if you only have a name.",
"inputSchema": {
"type": "object",
"required": ["customer_id", "amount_cents", "currency"],
"properties": {
"customer_id": {
"type": "string",
"format": "uuid",
"description": "UUID of the customer to invoice"
},
"amount_cents": {
"type": "integer",
"minimum": 1,
"description": "Invoice amount in cents"
},
"currency": {
"type": "string",
"enum": ["usd", "eur", "gbp"],
"description": "ISO 4217 currency code"
},
"due_date": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "Due date in YYYY-MM-DD format"
}
}
}
}The description field is the primary routing signal. It should answer: when to call this tool, when not to, what it returns, and what preconditions are required. See Server Architecture for patterns on writing effective tool descriptions.
Resources (Application-Controlled)
Resources expose data that the application makes available for an agent to read. Unlike tools, resources are not called autonomously - the application controls which resources are exposed and when. Resources are identified by URI and can represent files, database records, responses, or any other data source.
{
"uri": "file:///workspace/reports/q4-2025.pdf",
"name": "Q4 2025 Financial Report",
"description": "Quarterly financial report including revenue, expenses, and projections",
"mimeType": "application/pdf"
}Resource URIs follow standard URI syntax. Custom URI schemes are common for application-specific resources:
{
"uri": "billing://customers/cus_01HV3K8MNP/invoices",
"name": "Alice Chen — Invoice History",
"mimeType": "application/json"
}Servers that implement resource subscriptions notify clients when resource content changes. In 2025-11-25, declare this capability at server construction time (see Capability Declaration); in the current 2026-07-28 revision, resources/subscribe is removed and a client opts into resourceSubscriptions notifications on the subscriptions/listen stream instead (see What Changed in 2026-07-28).
Prompts (User-Controlled)
Prompts are reusable message templates that users select to start or guide interactions. They are pre-defined conversation starters or structured workflows, chosen by the user rather than the model.
{
"name": "billing_monthly_review",
"description": "Start a guided review of this month's billing activity",
"arguments": [
{
"name": "month",
"description": "Month to review in YYYY-MM format",
"required": true
},
{
"name": "currency",
"description": "Filter to a specific currency (optional)",
"required": false
}
]
}Tasks Extension (Async Execution)
The official io.modelcontextprotocol/tasks extension allows long-running, asynchronous operations with explicit state management. Requestors poll with tasks/get; a receiver can request additional input with tasks/update; processing continues until a terminal state such as completed, failed, or cancelled. Negotiate the extension rather than assuming every MCP peer implements it.
{
"name": "billing_generate_report",
"description": "Generate a comprehensive billing report. Returns a task_id for async polling.",
"inputSchema": {
"type": "object",
"properties": {
"month": { "type": "string", "description": "YYYY-MM format" },
"format": { "enum": ["csv", "json", "pdf"] }
}
}
}Use tasks when an operation cannot complete in a single RPC roundtrip - report generation, data exports, compliance audits, or expensive batch work. Clients can poll task state and receivers may send optional task status notifications.
Compatibility note: 2025-11-25 treated Tasks as experimental core behavior and used
tasks/result and tasks/list. Those methods are not part of the current Tasks extension.
Wire Format
MCP uses JSON-RPC 2.0 as its message format. Every interaction is a request/response pair or a notification, encoded as JSON.
Current Per-Request Metadata
In 2026-07-28, each request carries its protocol version and client capabilities in _meta. A server may implement server/discover to advertise supported versions, capabilities, and identity before normal calls, but discovery is optional. Handlers must not depend on connection state established by an initialization exchange.
Legacy 2025-11-25 Initialization Handshake
When a client connects, it sends an initialize request to negotiate protocol version and exchange capability declarations:
// Client → Server
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-11-25",
"capabilities": {
"roots": { "listChanged": true },
"sampling": {}
},
"clientInfo": {
"name": "claude-desktop",
"version": "1.4.0"
}
}
}
// Server → Client
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-11-25",
"capabilities": {
"tools": {},
"resources": { "subscribe": true, "listChanged": true },
"prompts": {},
"logging": {}
},
"serverInfo": {
"name": "billing-mcp",
"version": "2.1.0"
}
}
}The server must respond with the same protocolVersion or a lower supported version. If the versions are incompatible, the client disconnects.
Current 2026-07-28 behavior (SEP-2575): the initialize request and the
notifications/initialized acknowledgement are removed and the protocol core becomes stateless.
Every request instead carries its own protocol version and client capabilities in _meta under
io.modelcontextprotocol/protocolVersion, io.modelcontextprotocol/clientCapabilities, and
io.modelcontextprotocol/clientInfo; servers identify themselves with
io.modelcontextprotocol/serverInfo in the result _meta, and a version mismatch returns
UnsupportedProtocolVersionError. Servers may implement server/discover to advertise supported
versions, capabilities, and identity, with a back-compatibility probe for stdio. Design new
servers so that no handler depends on state established at connect time.
Tool Call
// Client → Server: tool call request
{
"jsonrpc": "2.0",
"id": 42,
"method": "tools/call",
"params": {
"name": "billing_create_invoice",
"arguments": {
"customer_id": "cus_01HV3K8MNPQR",
"amount_cents": 149900,
"currency": "usd",
"due_date": "2025-02-15"
}
}
}
// Server → Client: tool result
{
"jsonrpc": "2.0",
"id": 42,
"result": {
"content": [
{
"type": "text",
"text": "Invoice inv_01HV3K8STUVW created for $1,499.00 USD, due 2025-02-15."
}
],
"isError": false
}
}For error conditions, set isError: true and include a recovery-oriented message in content:
{
"jsonrpc": "2.0",
"id": 42,
"result": {
"content": [
{
"type": "text",
"text": "Customer cus_01HV3K8MNPQR not found. Use billing_list_customers to find valid customer IDs."
}
],
"isError": true
}
}Do not return JSON-RPC error objects for business logic failures. Use isError: true in the
result instead. JSON-RPC errors (error instead of result) should only be used for
protocol-level failures like malformed requests or unknown methods.
Resource Read
// Client → Server
{
"jsonrpc": "2.0",
"id": 7,
"method": "resources/read",
"params": {
"uri": "billing://customers/cus_01HV3K8MNP/invoices"
}
}
// Server → Client
{
"jsonrpc": "2.0",
"id": 7,
"result": {
"contents": [
{
"uri": "billing://customers/cus_01HV3K8MNP/invoices",
"mimeType": "application/json",
"text": "[{\"id\":\"inv_01HV\",\"status\":\"paid\",\"amount_cents\":149900}]"
}
]
}
}Tool Annotations
Tool definitions support an annotations field. Annotations communicate behavioral characteristics to clients without them in the description text.
{
"name": "billing_void_invoice",
"description": "Voids an invoice, marking it as cancelled. This action cannot be undone.",
"inputSchema": { ... },
"annotations": {
"readOnlyHint": false,
"destructiveHint": true,
"idempotentHint": false,
"openWorldHint": false
}
}| Annotation | Type | Meaning |
|---|---|---|
readOnlyHint | boolean | Tool does not modify any state (safe to call freely) |
destructiveHint | boolean | Tool may delete or irreversibly modify data |
idempotentHint | boolean | Calling with the same arguments multiple times has the same effect as calling once |
openWorldHint | boolean | Tool interacts with external systems outside the MCP server's control |
Annotations are hints, not enforcement. Clients may use them to require user confirmation before destructive calls or to allow unrestricted use of read-only tools, but trusted server or workflow code must still enforce authorization, , tenant boundaries, and approval gates.
New in 2025-11-25
The November 2025 revision adds several important capabilities:
Tasks - Experimental durable requests with polling and deferred result retrieval.
- Recommended OAuth client registration mechanism for clients and authorization servers that do not have a prior relationship.
Authorization-server discovery - HTTP authorization now leans on OAuth protected-resource metadata and supports OAuth or OpenID Connect discovery for authorization servers.
URL Mode Elicitation - Servers can direct users to external URLs for sensitive out-of-band interactions that should not pass through the MCP client. Removed in 2026-07-28; see the multi round-trip replacement below.
Tool-call support in sampling - Sampling can include tools and toolChoice parameters.
What Changed in 2026-07-28
Beyond the stateless core, the removal of sessions, and the Tasks extension covered above, the 2026-07-28 revision made the following changes. Avoid reintroducing the predecessor behavior in new systems.
Sessions are gone (SEP-2567) - the Mcp-Session-Id header disappears from Streamable HTTP and list endpoints no longer vary per connection. Cross-call state moves to explicit server-minted handles passed as ordinary tool arguments.
subscriptions/listen replaces the HTTP GET stream (SEP-2575) - one long-lived POST-response stream carries toolsListChanged, promptsListChanged, resourcesListChanged, and resourceSubscriptions notifications, opted into per type and tagged with io.modelcontextprotocol/subscriptionId. resources/subscribe and resources/unsubscribe are removed. Request-scoped notifications such as progress and log messages stay on the originating request's response stream.
Multi round-trip requests replace server-initiated calls (SEP-2322) - instead of the server calling roots/list, sampling/createMessage, or elicitation/create, it returns an InputRequiredResult (resultType: "input_required") carrying inputRequests, and the client retries the original request with inputResponses. Every result now carries a required resultType of "complete" or "input_required"; results from older servers that omit it MUST be treated as "complete". URL-mode elicitation's notifications/elicitation/complete and elicitationId are removed - the outcome is learned by retrying and correlated through requestState.
Removed utilities - ping, logging/setLevel, and notifications/roots/list_changed all go away. Log level is set per request through io.modelcontextprotocol/logLevel in _meta. SSE resumability goes with them: there are no event IDs or Last-Event-ID replay, so a broken stream is re-issued as a new request with a new ID.
Deprecated with a 12-month minimum window (SEP-2577) - Roots, Sampling, and Logging are all Deprecated. Replace Roots with tool parameters, resource URIs, or server configuration; replace Sampling with a direct call to your own provider API; replace Logging with stderr on stdio or OpenTelemetry on HTTP. The includeContext values "thisServer" and "allServers" are Deprecated, and the legacy HTTP+SSE transport is formally Deprecated under the new lifecycle policy. New implementations should not adopt any of these.
Transport and caching requirements - Streamable HTTP POSTs must carry Mcp-Method and Mcp-Name headers, with custom headers namespaced under x-mcp-header (SEP-2243). List and read results must carry ttlMs and a cacheScope of "public" or "private" via CacheableResult (SEP-2549). tools/list SHOULD return a deterministic order so clients get prompt-cache hits.
Schemas and errors - tool inputSchema and outputSchema accept any JSON Schema 2020-12 keywords including $ref, and structuredContent may be any JSON value (SEP-2106). Resource-not-found moves from -32002 to -32602 for JSON-RPC alignment, and a new allocation policy reserves -32020..-32099 for the spec while leaving -32000..-32019 implementation-defined.
Extensions and - ClientCapabilities and ServerCapabilities gain an extensions field using reverse-DNS identifiers, and OpenTelemetry trace context (traceparent, tracestate, baggage) travels in _meta (SEP-414).
Authorization - see MCP Authorization for the six authorization changes, including iss validation per RFC 9207 and the deprecation of RFC 7591 dynamic client registration in favor of Client ID Metadata Documents.
Advanced Capabilities
Sampling
Sampling allows an MCP server to request that the client (the AI model) generate a completion. This inverts the normal flow and enables servers to use the model's reasoning as part of their own logic.
// Server → Client: sampling request
{
"jsonrpc": "2.0",
"id": 99,
"method": "sampling/createMessage",
"params": {
"messages": [
{
"role": "user",
"content": {
"type": "text",
"text": "Classify this invoice description as 'consulting', 'software', or 'hardware': Web development services for March 2025"
}
}
],
"maxTokens": 10,
"modelPreferences": {
"costPriority": 0.8
}
}
}Sampling requires the client to declare the sampling capability - during initialization in 2025-11-25, or per request in _meta under the stateless 2026-07-28 core. Not all clients support it.
Deprecated in 2026-07-28 (SEP-2577). Sampling stays available through the 12-month minimum
deprecation window, but new servers should call their own LLM provider API directly instead. Where
a server genuinely needs the client's model in the loop, the replacement path is a multi
round-trip request returning resultType: "input_required".
Roots
Roots tell the server which filesystem or resource locations the client has authorized it to access. A client running in a workspace might declare its project directory as a root:
{
"roots": [
{
"uri": "file:///Users/alice/projects/billing-app",
"name": "Billing App Source"
}
]
}Servers that respect roots will not access paths outside the declared roots, providing a scope boundary for filesystem operations.
Deprecated in 2026-07-28 (SEP-2577), along with the notifications/roots/list_changed
notification, which is removed outright. Express scope boundaries as explicit tool parameters,
resource URIs, or server configuration instead - all three survive the stateless core, whereas a
client-declared root list does not.
Elicitation
Elicitation allows a server to request additional information from the user during a tool execution - mid-call user input rather than pre-declared parameters. A server handling a file operation might elicit confirmation before overwriting an existing file.
{
"jsonrpc": "2.0",
"method": "elicitation/create",
"params": {
"message": "File report-2025.pdf already exists. Overwrite it?",
"requestedSchema": {
"type": "object",
"properties": {
"overwrite": {
"type": "boolean",
"description": "Confirm overwrite"
}
},
"required": ["overwrite"]
}
}
}Deprecated in 2026-07-28 (SEP-2577). Server-initiated elicitation/create stays available
through the 12-month minimum deprecation window, but the replacement path is a multi round-trip
request: the server returns resultType: "input_required" and the client retries with
inputResponses.
Progress Notifications
For long-running tool operations, servers send progress notifications to keep clients informed without blocking on the final result:
{
"jsonrpc": "2.0",
"method": "notifications/progress",
"params": {
"progressToken": "export-job-7821",
"progress": 45,
"total": 100,
"message": "Processing invoice 2,247 of 5,000..."
}
}The client includes a _meta.progressToken in the original tool call request to opt into progress notifications for that call.
Pagination
List operations (tools/list, resources/list, prompts/list) support cursor-based for servers with large numbers of items:
// Client request with cursor
{
"jsonrpc": "2.0",
"id": 5,
"method": "resources/list",
"params": {
"cursor": "eyJwYWdlIjozfQ=="
}
}
// Server response with next cursor
{
"jsonrpc": "2.0",
"id": 5,
"result": {
"resources": [ ... ],
"nextCursor": "eyJwYWdlIjo0fQ=="
}
}Omit nextCursor from the response to indicate the final page. Clients must not assume a specific page size.
Transport
stdio (Local Processes)
The stdio transport runs the MCP server as a subprocess. The client writes JSON-RPC messages to the process's stdin and reads responses from stdout. stderr is reserved for diagnostic logs.
This is the appropriate transport for:
- Desktop applications launching local tools
- Development tooling and IDE integrations
- Tools that require filesystem access on the user's machine
// claude_desktop_config.json
{
"mcpServers": {
"billing": {
"command": "npx",
"args": ["-y", "billing-mcp@latest"],
"env": {
"STRIPE_API_KEY": "sk_live_..."
}
}
}
}The process is isolated at the OS level. Each MCP client instance gets its own subprocess. This provides natural process isolation - a crash in one MCP server does not affect others.
Streamable HTTP (Remote Services)
The Streamable HTTP transport is the preferred non-stdio transport for MCP servers. It runs the server as an HTTP endpoint where clients send JSON-RPC messages as POST requests. Responses can be:
- A single JSON response for request/response operations
- A Server-Sent Events stream for operations that produce multiple messages (progress notifications, streaming tool results, task polling)
POST /mcp HTTP/1.1
Content-Type: application/json
Authorization: Bearer eyJ...
MCP-Protocol-Version: 2025-11-25
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{...}}Streamable HTTP replaced the earlier http+sse transport, which has been deprecated since the 2025-03-26 revision and is formally Deprecated under the 2026-07-28 lifecycle policy. The legacy SSE transport remains supported via fallback for backward compatibility with older clients, but should not be used in new implementations. Streamable HTTP scales horizontally since each request is stateless.
Current 2026-07-28 behavior: the Mcp-Session-Id header is removed, the GET endpoint that
carried the server-to-client stream is replaced by subscriptions/listen, and SSE resumability
via Last-Event-ID is gone. POST requests must instead carry Mcp-Method and Mcp-Name headers,
with any custom headers namespaced under x-mcp-header. See Transport
Selection for what this means for server code today.
Security
OAuth 2.1 for HTTP Transport
Authorization is optional in MCP, but protected HTTP MCP servers should follow the MCP authorization specification. The server acts as an OAuth resource server, advertises protected-resource metadata, and points clients to one or more authorization servers:
1. Client receives `401 Unauthorized` with a `WWW-Authenticate` `resource_metadata` URL, or discovers protected-resource metadata at a well-known URI.
2. Client reads the `authorization_servers` value from protected-resource metadata.
3. Client discovers authorization server metadata using OAuth Authorization Server Metadata or OpenID Connect Discovery.
4. Client completes the supported OAuth flow and obtains an access token for the MCP server resource.
5. Client includes the bearer token in every protected MCP HTTP request.The MCP server exposes protected-resource metadata, not necessarily authorization-server metadata:
GET /.well-known/oauth-protected-resourceTokens must be validated on every request - not cached across sessions. Validate the audience (aud) claim to ensure tokens issued for other services are rejected.
Process Isolation for stdio
Each MCP server launched via stdio runs as its own OS process. This provides:
- Memory isolation between MCP servers
- Separate environment variable namespaces
- OS-level resource limits per server
- Clean failure containment - a crashing server does not cascade
Do not share credentials between MCP servers. Each server should have its own scoped credentials with the minimum permissions required for its domain.
Defense in Depth
┌─────────────────────────────────────┐
│ Network Layer │
│ TLS, rate limiting, IP allowlists │
├─────────────────────────────────────┤
│ Authentication Layer │
│ Bearer token / OAuth 2.1 with PKCE │
├─────────────────────────────────────┤
│ Authorization Layer │
│ Scope validation per tool, │
│ resource-level access control │
├─────────────────────────────────────┤
│ Input Validation Layer │
│ JSON Schema on every tool call, │
│ reject unknown fields │
├─────────────────────────────────────┤
│ Business Logic Layer │
│ Domain invariants, idempotency, │
│ per-customer rate limits │
└─────────────────────────────────────┘Each layer fails closed. A request that cannot be authenticated is rejected, not downgraded to reduced permissions.
Capability Declaration
Declare capabilities at server construction time. Only declare what you actually implement - undeclared capabilities are simply absent from the server's offerings, which is always preferable to declaring a capability and failing to implement its handler.
{
"capabilities": {
"tools": {},
"resources": {
"subscribe": true,
"listChanged": true
},
"prompts": {},
"logging": {}
}
}Declaring resources.subscribe: true without implementing resources/subscribe will produce protocol errors that are difficult to trace back to the missing handler.
Current 2026-07-28 behavior: capabilities travel per request rather than being negotiated once,
the logging capability is Deprecated, and resources.subscribe gives way to opting into
resourceSubscriptions on the subscriptions/listen stream. Capability objects also gain an
extensions field carrying reverse-DNS extension identifiers such as
io.modelcontextprotocol/tasks.
Spec & References
- MCP Specification 2025-11-25 - the predecessor revision, for compatibility work with clients that have not migrated
- MCP 2026-07-28 specification - current normative specification
- MCP 2026-07-28 release - migration summary and SDK support status
- modelcontextprotocol.io - official docs and server registry
Related Pages
- A2A Protocol - agent-to-agent delegation, the horizontal complement to MCP
- Protocol Comparison - when to use MCP vs vs