Last updated: September 2026 · Spec 2026-07-28

The Model Context Protocol,
explained the way agents actually use it.

A practical, practitioner-grade reference for the 2026-07-28 specification: what MCP is, how the new stateless core works, how to build a server well, and how to keep it secure - with animated diagrams of every flow.

~500M
SDK downloads / month
1B+
Total TS & Python downloads
0
Protocol-level sessions
M+N
Replaces M x N integration
// 00 - What changed

What's new in 2026-07-28

The largest breaking change since MCP launched. MCP is now a stateless request/response protocol. If you last read about MCP before August 2026, read this section first - several things you learned are no longer true.

The one-line version -> The initialize handshake, the Mcp-Session-Id header, server-initiated requests, and SSE stream resumability are all gone. Any request can now land on any instance behind a plain round-robin load balancer.
∅

No sessions, no handshake

Protocol-level sessions and initialize/notifications/initialized are removed. Every request carries its protocol version, client identity and capabilities in _meta.

SEP-2567 · SEP-2575
?

server/discover

One mandatory RPC advertising supported versions, capabilities and identity. Clients may call it up front, or just fire a request and handle UnsupportedProtocolVersion.

SEP-2575
⇄

Multi Round-Trip Requests

Servers no longer call clients. They return resultType: "input_required"; the client answers and retries the original request. Elicitation now survives a stateless core.

SEP-2322
⌁

Header-based routing

Mcp-Method and Mcp-Name are required on Streamable HTTP POSTs, so gateways, rate limiters and WAFs can route and meter without parsing JSON.

SEP-2243
⧗

Cacheable list results

ttlMs and cacheScope are required on list and read results. Clients cache instead of re-listing every turn.

SEP-2549
⌫

Deprecations

Roots, Sampling, Logging and OAuth Dynamic Client Registration are deprecated - still functional, removable no sooner than 2027-07-28.

SEP-2577 · SEP-2596

Also in this release

Not urgent -> Deprecated is not removed. Roots, Sampling, Logging and DCR keep working, and the earliest any can disappear is the first revision released on or after 2027-07-28. Don't add new dependencies on them; don't panic-rewrite working code this quarter. There's a full migration checklist in section 12.
// 01 - Definition

What is MCP?

An open JSON-RPC 2.0 standard that gives any LLM a single, uniform way to call tools, read data, and use reusable prompts from any compliant external system. The "USB-C for AI" - one connector, every device.

The M x N -> M+N integration collapse
BEFORE MCP - M x N adapters Host A Host B Host C Tool 1 Tool 2 Tool 3 9 custom integrations -> MCP WITH MCP - M + N connectors Host A Host B Host C M C P B U S Tool 1 Tool 2 Tool 3 6 standard connectors

A short history

At the 2026-07-28 release the maintainers reported close to half a billion downloads a month across the Tier 1 SDKs, with the TypeScript and Python SDKs each crossing 1 billion total downloads.

Protocol revisions at a glance

Revision
Era
Headline change
2024-11-05
Legacy
Initial spec; stdio and HTTP+SSE transports
2025-03-26
Legacy
Streamable HTTP; OAuth 2.1; HTTP+SSE deprecated
2025-06-18
Legacy
Elicitation; structured tool output; Resource Indicators
2025-11-25
Legacy
Experimental tasks; URL-mode elicitation
2026-07-28
Modern
Stateless core; no handshake, no session IDs; MRTR; extensions

The spec calls 2025-11-25 and earlier legacy (they establish a session with an initialize handshake) and 2026-07-28 and later modern (version, identity and capabilities travel as per-request metadata). An implementation supporting both is dual-era.

The mental model -> A Host (Claude Desktop, an IDE, your agent) runs one Client per Server. The server is the program that exposes capabilities from some external system. One host, many clients, many servers - all speaking the same protocol.
// 02 - Primitives

The building blocks

MCP organizes capabilities into a small set of named primitives. Three live on the server, three used to live on the client (two are now deprecated), and a handful are protocol-level. Know them cold.

Server-side primitives what your server exposes

T

Tools

Functions the model can invoke - query a DB, send an email, create a GitHub issue. Each has a name, description and JSON Schema. Since 2026-07-28, schemas may use any JSON Schema 2020-12 keywords.

model-controlled
R

Resources

Read-only data identified by URI - file contents, DB rows, API responses. Pulled into context by the host or user.

application-controlled
P

Prompts

Reusable, parameterized templates the server exposes - slash commands, workflows. The user picks them from a menu.

user-controlled

Client-side primitives what the server can ask the host to do

These no longer flow as server-initiated requests -> As of 2026-07-28 a server cannot call the client out of band. It returns an interim result saying "I need this input", and the client answers and retries. That's Multi Round-Trip Requests.
?

Elicitation active

Server asks the user a structured follow-up question mid-flow - "Which environment do you want to deploy to?" The only client feature the modern spec still lists as active.

elicitation/create
S

Sampling deprecated

Server asks the host's LLM to run a completion. Still functional through the deprecation window; new servers should integrate directly with an LLM provider API instead.

removable from 2027-07-28
⌂

Roots deprecated

Client tells the server "you may operate within these URIs". Migration path: pass directories or files via tool parameters, resource URIs, or server configuration.

removable from 2027-07-28

Protocol-level primitives

// 03 - Mechanics

How MCP works, end-to-end

Built on JSON-RPC 2.0. Every message is a request, response, or notification - and since 2026-07-28, every request is self-describing. Two standardized transports: stdio for local, Streamable HTTP for remote.

JSON-RPC 2.0 Stateless core stdio transport Streamable HTTP MRTR Cacheable lists No sessions No handshake

There is no session, and no handshake

This is the defining change of 2026-07-28. No initialize exchange, no Mcp-Session-Id header, no per-connection list results, no SSE resumability, no ping. The practical consequence: any request can land on any instance behind a plain round-robin load balancer - no sticky routing, no shared session store, no drained sessions on redeploy. A server that genuinely needs cross-call state mints an explicit handle and hands it back as an ordinary tool argument.

Modern lifecycle - discover (optional) -> list -> call -> resolve -> subscribe (optional)
MCP Client (in Host) MCP Server any instance 0 server/discover (OPTIONAL) supportedVersions · capabilities · ttlMs 1 tools/list _meta: { protocolVersion, ... } resultType: "complete" · ttlMs · cacheScope 2 tools/call Mcp-Method · Mcp-Name 3 resultType: "input_required" inputRequests · requestState 4 RETRY: same params + inputResponses new request id · may hit a different instance 5 resultType: "complete" 6 subscriptions/listen (OPTIONAL) ack, then notifications/tools/list_changed ...

Multi Round-Trip Requests (MRTR)

Server-initiated requests needed an open, sticky stream, so they couldn't survive a stateless core. MRTR inverts them. When a server needs an elicitation, a sampling completion or a roots list, it does not call the client - it returns an interim result:

↩

Server returns

resultType: "input_required" plus an inputRequests map (keys are server-assigned ids; values are elicitation/create, sampling/createMessage or roots/list request objects) and an opaque requestState.

↪

Client retries

The same request, a new request id, now carrying inputResponses keyed by the same ids plus the echoed requestState. Because the state travels with the retry, the second call can land on a different instance.

Header-based routing

Streamable HTTP POSTs must now carry standard headers so gateways, rate limiters and WAFs can route and meter without parsing the body. A mismatch between a header and the body is a HeaderMismatch error (-32020).

Header
Source
Required on
MCP-Protocol-Version
the request's protocol version
all requests
Mcp-Method
method
all requests
Mcp-Name
params.name or params.uri
tools/call, resources/read, prompts/get

Tools may additionally promote individual parameters into HTTP headers with an x-mcp-header annotation in the input schema - useful for routing on a tenant or region without cracking the payload open.

Error codes

-32000..-32019 legacy, do not allocate -32020 HeaderMismatch -32021 MissingRequiredClientCapability -32022 UnsupportedProtocolVersion resource not found: -32002 → -32602

The two transports, side by side

⌥

stdio

Host spawns the server as a subprocess. Talks over stdin/stdout. Best for local, single-user integrations - IDE plugins, desktop apps. Trust the host process.

⌘

Streamable HTTP

Server runs as a remote service. Client POSTs JSON-RPC; the server may stream request-scoped notifications over SSE on that response. This is how multi-tenant production servers run - and since 2026-07-28 it's just an ordinary HTTP workload.

Don't build on HTTP+SSE -> The legacy HTTP+SSE transport, deprecated since 2025-03-26, is now formally Deprecated under the lifecycle policy and eligible for removal three months after SEP-2596 reaches Final. Migrate to Streamable HTTP.
// 04 - Build Checklist

What to consider when building a server

A "hello world" MCP server is 50 lines. A production-grade one is a product. Here's the checklist that separates toys from tools agents love.

  1. Define the agent story first. Write the user-and-agent workflows you want to support, then design the minimum tools to satisfy them in one or two calls - not seven.
  2. Pick the right transport. stdio for local; Streamable HTTP for remote multi-tenant.
  3. Design for statelessness - it's now the protocol's default. Not a deployment preference any more. Every handler must be independently servable by any instance; mint explicit handles for anything spanning calls.
  4. Budget your tool surface. 5-8 tools per server is the sweet spot. Past ~12, agent performance degrades.
  5. Write tool descriptions for the LLM. Action-first, disambiguating, under ~200 tokens. They go straight into the context window.
  6. Design payloads for context economy. Return only what the agent needs for the next decision. Offer a verbose flag if power users need more.
  7. Set caching hints honestly. A generous ttlMs on a stable catalog saves a round trip every turn. The same on a per-user tool list is a correctness bug - and cacheScope: "public" on anything user-specific is a data-leak bug. When in doubt: private, short TTL.
  8. Errors are guidance, not just failures. "Permission denied" is bad. "Reauthenticate with repo:write scope" is useful. Use the allocated code ranges; never invent codes in -32020..-32099.
  9. Version gracefully. Tool names and schemas are a public API. Add fields as optional; never repurpose existing ones. Return a deterministic tool order so client and prompt caches stay warm.
  10. Plan your dual-era story. You'll serve legacy and modern callers together for a while. Let server/discover and UnsupportedProtocolVersionError do the negotiating.
  11. Observability from day one. Structured logs, per-tool latency, audit trail of who called what. Propagate traceparent from _meta.
// 05 - Comparison

Why MCP and not just APIs?

MCP and REST solve different problems for different consumers. The fastest way to misuse MCP is to think of it as "an API with extra steps." It isn't.

Dimension
REST API
MCP
consumer
Human developer writing code
LLM agent at runtime
discovery
Docs at build time
server/discover + tools/list at runtime
state
Stateless request/response
Stateless request/response (since 2026-07-28)
cross-call state
Your own IDs and cursors
Explicit server-minted handles as tool arguments
direction
Client -> server only
Client -> server, plus MRTR interim results and opt-in streams
schema
OpenAPI (for humans)
JSON Schema 2020-12 + natural-language
errors
HTTP status codes
Guidance text agents can act on
caching
HTTP Cache-Control
ttlMs + cacheScope on list and read results
auth
Many flavors, ad hoc
OAuth 2.1 + PKCE + Resource Indicators + CIMD
best for
Deterministic system-to-system
Open-ended agent workflows

Note how much of that table converged on REST in 2026-07-28. That's the point: a remote MCP server is now, operationally, just another HTTP workload. What still distinguishes MCP is who the consumer is and how the contract is described.

When to reach for each

✓

Use MCP when...

An agent dynamically chooses what to do · three+ tools combine in one chat · you want the same integration to work across Claude, ChatGPT, Cursor, your in-house agent · destructive actions need user-consent flow.

◷

Keep REST when...

A scheduled job calls a known endpoint with known params · mobile app needs deterministic CRUD with strict latency · the caller is human-written code, not a model.

The honest framing -> MCP sits on top of your APIs, it doesn't replace them. Your REST API is still the system of record. The MCP server is the agent-friendly facade in front of it.
// 06 - Agent Interaction

How agents interact with MCP

From the agent's point of view, MCP tools look identical to natively defined functions - but richer, with semantic descriptions and live discovery.

Animated tool invocation flow - user prompt to system response
User prompt Agent LLM picks tool Client JSON-RPC Server executes tool API DB · SaaS "refund this" stripe_refund tools/call REST POST

The five phases of agent ↔ MCP

  1. Connection - host spawns or connects each configured server. There's no handshake to run; it may call server/discover to pin a version and read capabilities.
  2. Discovery - client calls tools/list; descriptions land in the agent's system prompt. Because results carry ttlMs, a well-behaved host caches them rather than re-listing every turn.
  3. Decision - LLM, given the catalog, decides whether to answer or call a tool. MCP's richer descriptions improve tool-selection accuracy materially.
  4. Invocation - chosen call goes out as tools/call with _meta and the routing headers; server executes; result feeds back as a tool message.
  5. Mid-flight input & notifications - long tools stream notifications/progress on their own response. If the server needs input it returns input_required and the client retries (MRTR). Genuinely long work returns a task handle the client polls. Catalog changes arrive on an opt-in subscriptions/listen stream.
Termination -> There's no session to close. The client simply stops sending requests and cancels any open subscriptions/listen stream. Nothing on the server needs tearing down.
// 07 - Security

Security designs for your MCP server

Public MCP deployments have already shown familiar security failures: leaked credentials, broad tokens, weak audit trails, and prompt-injection exposure. Treat the bar as high from day one.

The five recurring attack classes

⚠

Prompt injection

Malicious instructions hidden in data the agent reads - an issue title, an email body - that hijack the agent into calling tools it shouldn't.

indirect injection
☠

Tool poisoning / rug pulls

Hosted server ships benign tools, then mutates them with hidden directives later. Defend with pinning and signature verification.

MCPTox vector
⚡

Over-privileged tokens

User grants a broad scope once; every agent action runs with the union. Least-privilege per tool is essential.

scope sprawl
⌬

Credential sprawl

Every dev spins up their own server with their own keys, stored insecurely in source or env files. Leaked keys turn a small server into a large incident.

secret hygiene
◑

Audit blind spots

No record of which agent called which tool, on whose behalf, with what arguments. Compliance fails when you can't answer.

governance gap
⤬

Cross-server token replay

Token issued for server A reused at server B. Mitigated by RFC 8707 Resource Indicators binding tokens to a server URI.

RFC 8707
OAuth 2.1 + PKCE + Resource Indicators flow for protected HTTP servers
Agent / Client Auth Server MCP Server 1 authorize + PKCE challenge + resource=mcp.example.com 2 authorization code (user consents) 3 code + PKCE verifier -> exchange 4 access_token { aud: mcp.example.com, exp: 30m } 5 tools/call + Bearer token 6 validates aud + scope -> result

What changed in 2026-07-28 authorization hardening

New risk created by the stateless core -> Anything you hand back for the client to pass into a later call - a cursor, a job ID, an MRTR requestState blob - is now a token travelling through the model's context. Assume it will be logged, summarized and possibly replayed. Sign or encrypt it, bind it to the authenticated principal, re-check that binding on use, give it an expiry, and put nothing sensitive in it in plaintext.

The compact security rule

The seven commandments -> Authenticate every request. Authorize every tool call. Validate every input. Sanitize every output. Encrypt every connection. Sign every handle. Log every action.

"Every request" is now literal. With no session there is no connection-scoped "already authorized at connection time" - each request must be independently authenticated and authorized.

Security baseline for HTTP deployments

// 08 - Wrapping Existing APIs

Designing MCP when you already have rich APIs

The most common starting point - and the most commonly bungled. The trap: one MCP tool per REST endpoint. It works. It's frustrating. Simple goals take five calls.

The rule -> Do not mirror your API surface. Your REST API was designed for human developers with a debugger and docs open. Your agent has neither. MCP design is a product exercise, not an export job.

Patterns that work

⇶

Workflow tools, not CRUD tools

Instead of list_issues + get_issue + update_issue + list_comments + add_comment, expose triage_issue that does the obvious orchestrated thing.

⌗

Service-prefixed names

Pattern: {service}_{verb}_{noun} - slack_send_message, linear_create_issue, stripe_refund_charge. Avoids collisions across loaded servers.

⊞

Domain-sharded servers

A 400-endpoint API != one MCP server with 400 tools. It's 10-20 small servers, each scoped to a coherent domain. Hosts load only what they need.

≋

Generated scaffold, hand-tuned surface

Stainless, FastMCP, Azure APIM auto-generate stubs from OpenAPI. Treat as starting point. Hand-curate names, collapse endpoints, prune fields.

≣

Server-side ranking + pagination

500 results is fine for REST. For MCP it's broken. Default to 5-20, rank by relevance server-side, let the model ask for more.

✕

Anti-patterns to avoid

Every endpoint "for completeness" · raw API JSON unchanged · HTTP status codes as errors · agent forced to know internal IDs.

A migration path

  1. Stand up a thin generated server from your OpenAPI spec to validate plumbing.
  2. Instrument it. Log which tools the agent actually uses for real workflows.
  3. Identify the top 3-5 workflows by frequency.
  4. Replace each cluster of fine-grained tools with one workflow tool.
  5. Retire unused fine-grained tools, or move behind a "power user" flag.
  6. Re-evaluate quarterly - usage shifts as agents and users learn what's possible.
// 09 - State Management

State management - life after protocol-level sessions

The stateful-vs-stateless debate that dominated MCP architecture through 2025 and early 2026 has been settled by the spec: stateless won. The protocol no longer has sessions. State didn't disappear - it became explicit.

This section changed substantially in 2026-07-28. Under 2025-11-25 and earlier, a session ran from initialize to shutdown and was pinned by an Mcp-Session-Id header. That was the single biggest obstacle to running MCP servers like ordinary web services. SEP-2567 and SEP-2575 removed the session and the handshake together.

What was removed, and what replaced it

Concern
Old mechanism (legacy)
Modern replacement
setup
initialize / notifications/initialized
Per-request _meta
capabilities
Learned at handshake
server/discover (optional, cacheable)
identity
Mcp-Session-Id header
Nothing - requests are self-describing
catalogs
Per-connection list results
Connection-independent, cacheable
server asks client
Server-initiated requests
MRTR interim result + client retry
change events
resources/subscribe + HTTP GET stream
subscriptions/listen
dropped stream
SSE resumability (Last-Event-ID)
Nothing - re-issue with a new request id
slow work
Long-blocking calls
Tasks extension (durable handles, polling)

Where state actually lives now

1

Cursors & opaque tokens

Pagination, a partially built query, a draft. The server mints an identifier, returns it, and the agent passes it to the next call. Backed by Redis or your database, readable by any instance.

2

requestState

Scoped to one logical request that needed user input. The server encodes what it needs; the client echoes it back verbatim on the retry. Sign it.

3

Task handles

For work that outlives a single HTTP exchange. Durable by definition - the client can crash, restart, and resume polling tasks/get with the same taskId.

The design test -> Could a second, freshly started instance of my server serve this request correctly, given only the request itself and shared storage? If not, you have hidden state. If you still need sticky routing, you have hidden state.
The modern pattern - stateless instances behind plain round-robin, state in a shared store
User A · Agent handle: a1 User B · Agent handle: b1 User C · Agent handle: c1 Load balancer round-robin MCP Server #1 stateless MCP Server #2 stateless MCP Server #3 stateless Shared State Store Redis · DynamoDB signed handles · tasks cursors · checkpoints

The practical recipe

Per-agent isolation is still your job

⚠

The risk moved, it didn't vanish watch this

Everything that used to be dangerous about a shared session is now dangerous about a shared handle: cross-tenant leakage if a handle isn't bound to its principal · auth confusion about whose scopes apply · concurrent-modification bugs in whatever the handle points at.

★

Share the resource, not the identity the rule

A warmed index in a process-level cache is fine. A handle any caller can present is not. Bind every handle to its authenticated principal and re-check that binding on use.

If you still support legacy clients -> Dual-era servers keep the old session machinery alive for 2025-11-25 and earlier callers while serving modern requests statelessly. Don't let a legacy session's cached state leak into the modern path, and remember that sticky routing a legacy client needs is a property of that one path - not of your whole deployment. Route on the era, not on the service.
// 10 - Architecture Best Practices

Architectural best practices

Consolidated from the 2026-07-28 specification, the August 2026 roadmap, and what production teams have learned the hard way.

Scope & structure

One server, one domain 5-8 tools sweet spot Service-prefixed names Workflow over CRUD No "and" in elevator pitch

Schema & contracts

Strict JSON Schema LLM-readable descriptions Versioned, additive changes Structured content returns JSON Schema 2020-12 Deterministic tool order

Transport & deployment

Stateless by default Externalized state, keyed by handle Emit the routing headers Deliberate ttlMs + cacheScope Health + readiness probes Plain round-robin scaling No HTTP+SSE No sticky routing

Security

OAuth 2.1 + PKCE Resource Indicators Validate iss (RFC 9207) CIMD over DCR Issuer-bound credentials Short-lived tokens Least-privilege scopes, re-checked per request Sign + scope + expire every handle Human confirm destructive ops Allow-list egress

Reliability

Idempotency keys Timeouts + circuit breakers Tasks for anything slow Classified errors with guidance Never leak stack traces

Idempotency matters more now, not less: there's no stream resumability, so a dropped response means the client will re-issue the request.

Observability

Structured logs per call p50/p95/p99 latency per tool traceparent from _meta Audit trail for compliance Tool-definition pinning Not the Logging primitive

Developer experience

// 11 - Extensions & Roadmap

The extension ecosystem

2026-07-28 formalized extensions (SEP-2133) as the way to add capability without destabilizing the core. Understanding this is now part of understanding MCP, because several things that used to be core features live here.

How extensions work

⧗

Tasks io.modelcontextprotocol/tasks

Experimental tasks left the core and were reshaped around statelessness. A supported request returns a CreateTaskResult (resultType: "task") with a taskId, status, TTL and suggested polling interval - durably created before the response is sent. The client polls tasks/get; statuses are working, input_required, completed, failed, cancelled. Mid-flight input is answered with tasks/update. tasks/result and tasks/list are gone.

SEP-2663 · roadmap: fold back into core
▣

MCP Apps ext-apps

Servers ship interactive HTML - charts, forms, dashboards - that the host renders inline in a sandboxed iframe. A tool declares a UI resource via _meta.ui.resourceUri; the host preloads and renders it in place of a text result. The app can call tools, delegate actions to the host and reuse the user's connected integrations - and because actions flow through the same JSON-RPC path, the audit trail stays consistent.

SEP-1865
🔑

Authorization ext-auth

OAuth Client Credentials for machine-to-machine callers with no human in the loop. Enterprise-Managed Authorization (stable since June 2026) for ID-JAG token exchange during SSO, so an organization provisions MCP access centrally through its IdP.

zero-touch OAuth
◈

Skills over MCP ext-skills

Discover Agent Skills - workflow instructions and their supporting files - through MCP resources, so a server can hand an agent structured know-how rather than only callable functions.

ext-skills

The road ahead August 2026 roadmap

Five priority areas guide the next release cycle. SEPs inside them get expedited review.

  1. Agentic messaging primitives - server-initiated events (webhooks and channels) so clients stop polling for results; a composition review across the Agents, Transports and Triggers & Events Working Groups; maturing Tasks toward core inclusion.
  2. HTTP-native transport unification - stretching the "an MCP server is just an HTTP workload" model to every deployment mode, including local servers speaking Streamable HTTP over stdio.
  3. Agent identity & enterprise security - moving past "a person approves in a browser" to workloads with their own identity: DPoP (RFC 9449), Workload Identity Federation, the ID-JAG grant, and standard token exchange, with continued engagement in the IETF OAuth and WIMSE working groups.
  4. Improved primitives - one clear contract for tool result handling, plus progressive discovery so a server can offer a small entry point and reveal more of its catalog as the conversation narrows.
  5. Improved SDK developer experience - ergonomics, conformance testing and documentation, which matter more now that many developers build MCP servers by pointing an agent at the libraries.

Separately, the Server Card Working Group is defining .well-known metadata conventions so a server can be discovered and reasoned about without connecting to it.

// 12 - Migration

Migrating 2025-11-25 → 2026-07-28

A practical checklist. Budget real time for this - it's a breaking change, though the SDKs absorb much of it.

Server checklist

Client checklist

✓

Things you can stop doing

Provisioning sticky sessions at the load balancer · running a shared session store just to survive a redeploy · holding an SSE stream open to receive elicitations · polling tools/list on every turn.

◷

Things not to rush

Roots, Sampling, Logging, DCR and the includeContext values "thisServer" / "allServers" are deprecated, not removed. Earliest removal is the first revision released on or after 2027-07-28. Don't add new dependencies; do plan the migration; don't panic-rewrite working code this quarter.