mcp-stateless · v1.0 · 2026-08-09 · sha256 3bd4ee6edaf71bd7
mcp-stateless v1.0A
Immutable. This exact content is served forever at /api/v1/blob/3bd4ee6edaf71bd7.
--- name: mcp-stateless description: "MCP knowledge pack for the stateless 2026-07-28 revision: per-request `_meta`, `server/discover`, `resultType`, multi round-trip requests, stdio and Streamable HTTP transports. Use when building, reviewing, or migrating an MCP server or client, or when code still calls `initialize`, `Mcp-Session-Id`, or `resources/subscribe`." metadata: author: uwuclxdy version: "1.0" --- # 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 1. **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. 2. **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. 3. **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. 4. **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. 5. **Every result carries `resultType`.** Omitting it marks the server as legacy. A result with no `resultType` MUST be read as `"complete"`. 6. **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. 7. **`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. 8. **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.