MCP Stateless
The Model Context Protocol went stateless in revision 2026-07-28. It is the largest break since launch: the initialize handshake, the session id, the standalone SSE stream, and server-initiated requests are all gone. Every request now carries everything a server needs to answer it.
Model training data predates this revision. An MCP server written from memory speaks the 2025-11-25 protocol and will be rejected by a modern client. The failure is not a compile error and often not a runtime error either: a handshake-era server and a modern client fail in ways that read like a config problem. Work from the reference files here, and check anything version-sensitive against the live spec.
Hard Rules
- Never write MCP wire code from memory. Read
references/wire.md first. If a claim here is older than the revision you are targeting, the live spec wins.
- State is a lie the connection tells you. A server MUST NOT infer anything from a previous request on the same connection: not the protocol version, not capabilities, not client identity, not conversation continuity. One stdio process is not one session. A client may interleave unrelated requests on it.
- State that must span requests gets an explicit server-minted handle passed back as an ordinary parameter. There is no session to hang it on.
- A server MUST answer
server/discover. It is the only way left to advertise what the server supports, and it is how a dual-era client tells a modern server from a legacy one.
- Every result carries
resultType. Omitting it marks the server as legacy. A result with no resultType MUST be read as "complete".
- Servers never send JSON-RPC requests. Sampling, elicitation, and roots all run through multi round-trip requests: the server answers with
input_required, the client retries. Read references/mrtr.md before wiring any of the three.
requestState comes back from the client unverified. Sign or encrypt it. A stateless server that trusts an echoed blob has handed the client its internal state machine.
- Verify with a live handshake against the real binary. A green build proves nothing about protocol conformance, and the old smoke-test recipe (pipe an
initialize frame) now tests a method that no longer exists. Recipe in references/sdks.md.
What Changed
2025-11-25 and earlier |
2026-07-28 |
Silent-failure risk if you keep the old shape |
initialize + notifications/initialized |
Removed. Per-request _meta carries version, capabilities, identity |
High. A modern client gets an implementation-defined error, or worse, silence |
Mcp-Session-Id header, DELETE to end a session |
Removed. Servers ignore the header and never mint one |
High. Session-keyed caches and per-session state silently serve the wrong tenant |
| Capabilities exchanged once at connect |
server/discover (servers MUST implement), plus per-request io.modelcontextprotocol/clientCapabilities |
High. A server reading capabilities from a remembered handshake reads nothing |
| Results had no discriminator |
Every result MUST carry resultType: "complete" or "input_required" |
Medium. Clients treat a missing field as "complete", so a real MRTR reply gets mis-parsed |
Server sends sampling/createMessage, elicitation/create, roots/list as requests |
MRTR: server returns InputRequiredResult, client retries the original request with inputResponses |
High. The old pattern is explicitly no longer supported |
Standalone HTTP GET SSE stream; resources/subscribe / unsubscribe |
subscriptions/listen: one long-lived POST-response stream, client opts into notification types |
Medium. A modern-only server SHOULD answer GET with 405 |
Last-Event-ID resumption, SSE event ids |
Removed. A broken stream loses the in-flight request; the client re-issues with a new request id |
Medium. Resumption code silently never fires |
ping, logging/setLevel, notifications/roots/list_changed |
Removed from the core protocol |
Medium. Kept in SDKs only for legacy sessions |
| Log verbosity set once per session |
Per-request io.modelcontextprotocol/logLevel in _meta. A server MUST NOT emit notifications/message for a request that omitted it |
Medium. Log notifications stop arriving and nothing reports an error |
| List results were plain |
tools/list, prompts/list, resources/list, resources/templates/list, resources/read, server/discover MUST carry ttlMs + cacheScope |
Low. Clients default to "immediately stale" and re-fetch every time |
| Streamable HTTP POST carried no routing metadata |
Mcp-Method + Mcp-Name headers REQUIRED, validated against the body; mismatch is 400 + -32020 |
High on HTTP. A gateway routing on headers and a server executing the body is the vulnerability this closes |
| Tasks were experimental core |
The io.modelcontextprotocol/tasks extension. tasks/list and tasks/result are gone |
Medium. No compatibility shim |
Resource-not-found was -32002 |
-32602 (Invalid Params). Accept -32002 inbound from older servers |
Low |
Deprecated, Not Yet Removed
Still in the spec, still functional. New code SHOULD NOT adopt them. The removal clocks differ per feature, so read the column rather than assuming one date.
| Feature |
Migrate to |
Earliest removal |
| Roots |
Directories or files as tool parameters, resource URIs, or server configuration |
First revision on or after 2027-07-28 |
| Sampling |
The LLM provider's API directly |
First revision on or after 2027-07-28 |
| Logging |
stderr on stdio; OpenTelemetry for structured observability |
First revision on or after 2027-07-28 |
| OAuth Dynamic Client Registration |
Client ID Metadata Documents |
First revision on or after 2027-07-28 |
includeContext: "thisServer" / "allServers" |
Omit the field, or "none" |
Follows Sampling |
HTTP+SSE transport (the 2024-11-05 one) |
Streamable HTTP |
Three months after SEP-2596 reaches Final, which may already have elapsed |
The specification now runs a formal feature lifecycle (Active, Deprecated, Removed) with a minimum twelve-month deprecation window and a published registry, so a feature disappearing without notice is no longer the failure mode. Checking the registry before adopting anything is.
Files
Load the one matching the task. Do not load them all.
| Task touches |
File |
Wire shapes: _meta keys, server/discover, resultType, error codes, HTTP headers, caching, subscriptions, transports |
references/wire.md |
Sampling, elicitation, roots, InputRequiredResult, requestState and its security |
references/mrtr.md |
| Porting an existing server or client, supporting both eras, what breaks silently |
references/migrate.md |
| Which SDK version speaks which revision, per-SDK API shape, how to smoke-test a built server |
references/sdks.md |
What This Skill Does Not Cover
- Server design: which tools to expose, how to carve a large API surface, deployment model, bundling for local install. That is a product question, and the official
mcp-server-dev plugin (anthropics/claude-plugins-official) runs a discovery interview for it. Check its pinned versions before trusting its protocol claims; as of 2026-08 its version-pin file records the MCP spec claims as last verified 2026-03, which is the 2025-11-25 era.
- MCP Apps / UI widgets beyond how the extension is negotiated.
- Authorization in depth. The revision tightened OAuth (issuer validation, credentials keyed by issuer,
application_type on registration, Client ID Metadata Documents over DCR). references/migrate.md lists what changed; the auth spec pages are the authority.
Live Docs on Demand
The spec ships agent-readable markdown: append .md to any spec URL.
| Need |
URL |
| Page index |
https://modelcontextprotocol.io/llms.txt |
| Revision changelog |
https://modelcontextprotocol.io/specification/2026-07-28/changelog.md |
Statelessness, _meta, error codes |
.../specification/2026-07-28/basic/index.md |
| Version negotiation, extensions, dual-era matrix |
.../specification/2026-07-28/basic/versioning.md |
| Deprecated-features registry |
.../specification/2026-07-28/deprecated.md |
| Full schema reference |
.../specification/2026-07-28/schema.md |
Fetch the schema page before asserting any field name. Field names in this skill were read from the spec; field names in a model's memory were not.
1---2name: mcp-stateless3description: MCP knowledge pack for the stateless 2026-07-28 revision.4---56# MCP Stateless78The Model Context Protocol went stateless in revision **`2026-07-28`**. It is the largest break since launch: the `initialize` handshake, the session id, the standalone SSE stream, and server-initiated requests are all gone. Every request now carries everything a server needs to answer it.910**Model training data predates this revision.** An MCP server written from memory speaks the `2025-11-25` protocol and will be rejected by a modern client. The failure is not a compile error and often not a runtime error either: a handshake-era server and a modern client fail in ways that read like a config problem. Work from the reference files here, and check anything version-sensitive against the live spec.1112## Hard Rules13141. **Never write MCP wire code from memory.** Read `references/wire.md` first. If a claim here is older than the revision you are targeting, the live spec wins.152. **State is a lie the connection tells you.** A server MUST NOT infer anything from a previous request on the same connection: not the protocol version, not capabilities, not client identity, not conversation continuity. One stdio process is not one session. A client may interleave unrelated requests on it.163. **State that must span requests gets an explicit server-minted handle** passed back as an ordinary parameter. There is no session to hang it on.174. **A server MUST answer `server/discover`.** It is the only way left to advertise what the server supports, and it is how a dual-era client tells a modern server from a legacy one.185. **Every result carries `resultType`.** Omitting it marks the server as legacy. A result with no `resultType` MUST be read as `"complete"`.196. **Servers never send JSON-RPC requests.** Sampling, elicitation, and roots all run through multi round-trip requests: the server answers with `input_required`, the client retries. Read `references/mrtr.md` before wiring any of the three.207. **`requestState` comes back from the client unverified.** Sign or encrypt it. A stateless server that trusts an echoed blob has handed the client its internal state machine.218. **Verify with a live handshake against the real binary.** A green build proves nothing about protocol conformance, and the old smoke-test recipe (pipe an `initialize` frame) now tests a method that no longer exists. Recipe in `references/sdks.md`.2223## What Changed2425| `2025-11-25` and earlier | `2026-07-28` | Silent-failure risk if you keep the old shape |26|---|---|---|27| `initialize` + `notifications/initialized` | Removed. Per-request `_meta` carries version, capabilities, identity | High. A modern client gets an implementation-defined error, or worse, silence |28| `Mcp-Session-Id` header, DELETE to end a session | Removed. Servers ignore the header and never mint one | High. Session-keyed caches and per-session state silently serve the wrong tenant |29| Capabilities exchanged once at connect | `server/discover` (servers MUST implement), plus per-request `io.modelcontextprotocol/clientCapabilities` | High. A server reading capabilities from a remembered handshake reads nothing |30| Results had no discriminator | Every result MUST carry `resultType`: `"complete"` or `"input_required"` | Medium. Clients treat a missing field as `"complete"`, so a real MRTR reply gets mis-parsed |31| Server sends `sampling/createMessage`, `elicitation/create`, `roots/list` as requests | MRTR: server returns `InputRequiredResult`, client retries the original request with `inputResponses` | High. The old pattern is explicitly no longer supported |32| Standalone HTTP `GET` SSE stream; `resources/subscribe` / `unsubscribe` | `subscriptions/listen`: one long-lived POST-response stream, client opts into notification types | Medium. A modern-only server SHOULD answer `GET` with `405` |33| `Last-Event-ID` resumption, SSE event ids | Removed. A broken stream loses the in-flight request; the client re-issues with a **new** request id | Medium. Resumption code silently never fires |34| `ping`, `logging/setLevel`, `notifications/roots/list_changed` | Removed from the core protocol | Medium. Kept in SDKs only for legacy sessions |35| Log verbosity set once per session | Per-request `io.modelcontextprotocol/logLevel` in `_meta`. A server MUST NOT emit `notifications/message` for a request that omitted it | Medium. Log notifications stop arriving and nothing reports an error |36| List results were plain | `tools/list`, `prompts/list`, `resources/list`, `resources/templates/list`, `resources/read`, `server/discover` MUST carry `ttlMs` + `cacheScope` | Low. Clients default to "immediately stale" and re-fetch every time |37| Streamable HTTP POST carried no routing metadata | `Mcp-Method` + `Mcp-Name` headers REQUIRED, validated against the body; mismatch is `400` + `-32020` | High on HTTP. A gateway routing on headers and a server executing the body is the vulnerability this closes |38| Tasks were experimental core | The `io.modelcontextprotocol/tasks` extension. `tasks/list` and `tasks/result` are gone | Medium. No compatibility shim |39| Resource-not-found was `-32002` | `-32602` (Invalid Params). Accept `-32002` inbound from older servers | Low |4041## Deprecated, Not Yet Removed4243Still in the spec, still functional. New code SHOULD NOT adopt them. The removal clocks differ per feature, so read the column rather than assuming one date.4445| Feature | Migrate to | Earliest removal |46|---|---|---|47| Roots | Directories or files as tool parameters, resource URIs, or server configuration | First revision on or after 2027-07-28 |48| Sampling | The LLM provider's API directly | First revision on or after 2027-07-28 |49| Logging | `stderr` on stdio; OpenTelemetry for structured observability | First revision on or after 2027-07-28 |50| OAuth Dynamic Client Registration | Client ID Metadata Documents | First revision on or after 2027-07-28 |51| `includeContext: "thisServer"` / `"allServers"` | Omit the field, or `"none"` | Follows Sampling |52| HTTP+SSE transport (the `2024-11-05` one) | Streamable HTTP | **Three months after SEP-2596 reaches Final**, which may already have elapsed |5354The specification now runs a formal feature lifecycle (Active, Deprecated, Removed) with a minimum twelve-month deprecation window and a published registry, so a feature disappearing without notice is no longer the failure mode. Checking the registry before adopting anything is.5556## Files5758Load the one matching the task. Do not load them all.5960| Task touches | File |61|---|---|62| Wire shapes: `_meta` keys, `server/discover`, `resultType`, error codes, HTTP headers, caching, subscriptions, transports | `references/wire.md` |63| Sampling, elicitation, roots, `InputRequiredResult`, `requestState` and its security | `references/mrtr.md` |64| Porting an existing server or client, supporting both eras, what breaks silently | `references/migrate.md` |65| Which SDK version speaks which revision, per-SDK API shape, how to smoke-test a built server | `references/sdks.md` |6667## What This Skill Does Not Cover6869- **Server design**: which tools to expose, how to carve a large API surface, deployment model, bundling for local install. That is a product question, and the official `mcp-server-dev` plugin (`anthropics/claude-plugins-official`) runs a discovery interview for it. Check its pinned versions before trusting its protocol claims; as of 2026-08 its version-pin file records the MCP spec claims as last verified 2026-03, which is the `2025-11-25` era.70- **MCP Apps / UI widgets** beyond how the extension is negotiated.71- **Authorization in depth.** The revision tightened OAuth (issuer validation, credentials keyed by issuer, `application_type` on registration, Client ID Metadata Documents over DCR). `references/migrate.md` lists what changed; the auth spec pages are the authority.7273## Live Docs on Demand7475The spec ships agent-readable markdown: append `.md` to any spec URL.7677| Need | URL |78|---|---|79| Page index | `https://modelcontextprotocol.io/llms.txt` |80| Revision changelog | `https://modelcontextprotocol.io/specification/2026-07-28/changelog.md` |81| Statelessness, `_meta`, error codes | `.../specification/2026-07-28/basic/index.md` |82| Version negotiation, extensions, dual-era matrix | `.../specification/2026-07-28/basic/versioning.md` |83| Deprecated-features registry | `.../specification/2026-07-28/deprecated.md` |84| Full schema reference | `.../specification/2026-07-28/schema.md` |8586Fetch the schema page before asserting any field name. Field names in this skill were read from the spec; field names in a model's memory were not.