Source Intelligence
Sweep 28 Aug 2026 · 00:00Z Build v2.1.250 478 read Stable v2.1.236 Latest v2.1.250 Next v2.1.250 Feeds RSS JSON llms.txt

DisclaimerUnofficial, and not affiliated with Anthropic. Nearly all of this is read straight out of what ships: npm bundles, captured prompts, published docs. Anthropic's own notes go in verbatim, marked as theirs. The rest is my reading, and every entry carries the strings behind it. If one looks wrong, vote it down and say why.

Page history

SEP-2575: Make MCP Stateless

seps/2575-stateless-mcp

1 recorded change 841 lines First seen Last changed Upstream

History

seps/2575-stateless-mcp First recorded · 841 lines, first recorded

# SEP-2575: Make MCP Stateless ## Abstract ## Motivation ### The Problem with Statefulness ## Design Principles ### Transport Consistency ## Specification ### Overview ### Protocol Version #### HTTP #### Per-request Version #### Unsupported Protocol Versions #### Version Negotiation Flow ### Discovery for Server Capabilities #### `server/discover` RPC ### Per-Request Client Capabilities #### Per-Request Metadata Schema #### Response Streaming #### Request Cancellation ##### Resumable Streams Are Removed #### Missing Required Capabilities ### `subscriptions/listen` RPC #### Request Schema #### Acknowledgment Notification #### Multiple Concurrent Subscriptions #### Stopping a Subscription #### Transport Behavior ### Deprecated and Removed RPCs ## Rationale ### Stateless-First by Default #### Alternative Considered: Optional Handshake #### Why it was rejected: ### Explicit Session Management ### Separation of Concerns #### Alternative Considered: A Monolithic Handshake #### Why it was rejected: ## Backward Compatibility ### Supporting Multiple Versions #### Client (supporting vPrev) → Server (vPrev, vPost) #### Client (supporting vPrev, vPost) → Server (vPrev) ## Security Implications ## Reference Implementation ## FAQ ### What is protocol level statelessness? ### Does this make MCP a fully stateless protocol? ### Why is it important for STDIO to be stateless as well? ### How does `server/discover` relate to the MCP Server Card? ## Open Questions ### What belongs in `_meta` vs. as a top-level protocol field? ### Should `clientInfo` be part of `ClientCapabilities`? ## Changes since SEP became Final

The first capture of this source. The page was already there, and this is what it said.

# SEP-2575: Make MCP Stateless

> Make MCP Stateless

<div className="flex items-center gap-2 mb-4">
  <Badge color="green" shape="pill">
    Final
  </Badge>

  <Badge color="gray" shape="pill">
    Standards Track
  </Badge>
</div>

<Note>
  This SEP has reached Final status and is preserved as a historical record of
  the design as accepted. Changes made to the protocol after finalization are
  not reflected here. Refer to the [current
  specification](/specification/latest) and its changelog for authoritative
  requirements.
</Note>

| Field         | Value                                                                                                                                                                                                                                                                                                  |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **SEP**       | 2575                                                                                                                                                                                                                                                                                                   |
| **Title**     | Make MCP Stateless                                                                                                                                                                                                                                                                                     |
| **Status**    | Final                                                                                                                                                                                                                                                                                                  |
| **Type**      | Standards Track                                                                                                                                                                                                                                                                                        |
| **Created**   | 2025-06-18                                                                                                                                                                                                                                                                                             |
| **Author(s)** | Jonathan Hefner ([@jonathanhefner](https://github.com/jonathanhefner)), Mark Roth ([@markdroth](https://github.com/markdroth)), Shaun Smith ([@evalstate](https://github.com/evalstate)), Harvey Tuch ([@htuch](https://github.com/htuch)), Kurtis Van Gent ([@kurtisvg](https://github.com/kurtisvg)) |
| **Sponsor**   | Kurtis Van Gent ([@kurtisvg](https://github.com/kurtisvg))                                                                                                                                                                                                                                             |
| **PR**        | [#2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575)                                                                                                                                                                                                                        |

***

## Abstract

A truly stateless protocol, where every request is self-contained and can be
understood in isolation, is highly desirable for its inherent simplicity,
scalability, and reliability. The current Model Context Protocol (MCP) is not
stateless by default. The specification requires an initialization handshake
that establishes a session state between the client and server, which persists
for the duration of the connection.

This inherent statefulness makes it difficult to run MCP at scale. Placing an
MCP server behind a standard load balancer, for example, is challenging because
a client's session is coupled to the specific server instance holding its state.

This proposal outlines a series of changes to **enable stateless MCP as the
default**, embracing a "pay as you go" model for protocol complexity and state.
Under this model, we provide simple, stateless features by default and only
introduce the overhead of stateful, long-lived connections for cases where that
functionality is actually required.

Specifically, this SEP proposes removing the state-establishing initialization
handshake and replacing it with discrete, stateless alternatives. This initial
step allows each request to be processed independently, simplifying server-side
logic and paving the way for robust, scalable deployments.

## Motivation

The Model Context Protocol (MCP) specification currently mandates a stateful
initialization handshake. This design choice creates significant challenges for
scalability, reliability, and implementation simplicity. This SEP is motivated
by the need to address these shortcomings.

### The Problem with Statefulness

The core issue is that a server must retain session state from previous requests
to understand subsequent ones. This is in direct opposition to the design of
modern, cloud-native systems which favor stateless services for their resilience
and scalability.

1. **Impediment to Scalability:** The most critical issue is the difficulty of
   load balancing stateful MCP. A simple stateless load balancer (e.g., L4/L7
   round-robin) cannot be used, as it would route a client's requests to
   different backend servers, none of which would have the correct session
   state. Operators are forced to implement complex and fragile solutions like
   sticky sessions, which bind a client to a specific server. This complicates
   infrastructure, can lead to uneven load distribution, and makes horizontally
   scaling the service non-trivial.
2. **Poor Resilience and Fault Tolerance:** In a stateful model, if the specific
   server instance handling a client session fails, that session state is lost.
   The client must detect the connection failure, re-establish a connection
   (likely to a new server instance via the load balancer), and perform the
   entire initialization handshake again. This process is disruptive and
   inefficient, adding complexity around "resumability".
3. **Increased Implementation Complexity:** The current model imposes a
   significant burden on developers.
   * **Server-side:** Developers must implement logic to create, manage, and
     eventually garbage-collect per-client session state. This is a common
     source of bugs and memory leaks.
   * **Client-side:** Developers must write complex code to manage a persistent
     connection and handle the inevitable network failures and reconnections,
     including the logic to resynchronize state after a disconnect.

## Design Principles

This proposal establishes a "pay as you go" model for protocol complexity,
guided by the following principles in order of preference:

1. **Prioritize Stateless-ness:** Whenever possible, a request must be
   self-contained, providing all information the server needs to process it
   without relying on state from previous requests.
2. **Prefer State References:** If a fully stateless exchange is not practical,
   references to state should be passed in every request.
3. **Treat Statefulness as a Last Resort:** The complexity of stateful logic and
   long-lived streaming connections should only be accepted when no simpler
   alternative exists to solve a critical use case.

### Transport Consistency

It is critical that these stateless principles are applied consistently across
all transports. Keeping the `stdio` and `http` implementations in sync ensures a
**unified developer experience**, allowing the core protocol semantics to be
learned once and applied everywhere. This consistency simplifies the creation of
transport-agnostic libraries and tooling, and prevents protocol fragmentation
where different transports behave in fundamentally different ways. A single,
coherent protocol model is essential for a healthy ecosystem.

## Specification

### Overview

This specification fundamentally refactors the MCP interaction model to be
**stateless-first**. Currently, MCP requires a mandatory 3-way initialization
handshake before any resources can be exchanged. This handshake negotiates and
establishes several key pieces of information:

1. MCP Protocol Version
2. Server Capabilities and `serverInfo`
3. Client Capabilities and `clientInfo`

The requirement of this initialization handshake **enforces the establishment of
a state** that is expected to persist for subsequent communication between
client and server. Furthermore, by bundling these negotiations into a single
initialization phase, the specification creates an implied link between them,
particularly between the exchange of capabilities and a mandatory connection
lifecycle.

This proposal is to **remove the initialization handshake** and "unbundle" its
functions into discrete, stateless components. We will provide new, more clearly
defined mechanisms for clients and servers to exchange this information without
a mandatory state-creating cycle.

> **Note:** Session management (both transport-level and application-level) is
> addressed separately by [SEP-2322][SEP-2322] and [SEP-2567][SEP-2567]. This
> SEP focuses exclusively on removing the initialization handshake and providing
> stateless alternatives for version negotiation, discovery, and capabilities.

### Protocol Version

To make requests self-contained, metadata previously negotiated during the
handshake must now be included with **every request**.

#### HTTP

For the HTTP transport, protocol version MUST be passed as an **HTTP header**.
The header value MUST match the value provided in the request payload's `_meta`
field; otherwise the server MUST return a `400 Bad Request` (see
[SEP-2243][SEP-2243]).

* `MCP-Protocol-Version: 2025-06-18`
  * **Purpose**: To inform the server which version of the MCP specification the
    client is using for this specific request.
  * **Requirement**: This header is **MANDATORY**. Servers should reject
    requests with a missing or unsupported version.
  * This header MUST match the value provided in the Request as specified below.

#### Per-request Version

The `protocol-version` MUST be embedded directly within the `_meta` field of the
request payload. For HTTP, this \_meta MUST match the associated HTTP header, or
else the server should return a 400 Bad Request.

The following diff illustrates the required changes to `RequestMetaObject`:

```ts theme={null}
export interface RequestMetaObject extends MetaObject {
  progressToken?: ProgressToken;
+ /**
+  * The MCP Protocol Version being used for this request.
+  */
+ "io.modelcontextprotocol/protocolVersion": string;
  // Additional per-request fields (clientInfo, clientCapabilities, logLevel)
  // are introduced in the Per-Request Client Capabilities section below.
}
```

#### Unsupported Protocol Versions

If a server receives a request with a protocol version it does not implement
(whether the version is unknown to the server or is a known version the server
has chosen not to support, such as an experimental or draft version), it MUST
return a JSON-RPC error response. For HTTP, the response status code MUST be
`400 Bad Request`. The error MUST conform to the following structure:

```ts theme={null}
export const UNSUPPORTED_PROTOCOL_VERSION = -32022;

export interface UnsupportedProtocolVersionError extends Omit<
  JSONRPCErrorResponse,
  "error"
> {
  error: Error & {
    code: typeof UNSUPPORTED_PROTOCOL_VERSION;
    data: {
      /**
       * An array of protocol version strings that the server supports.
       */
      supported: string[];
      /**
       * The protocol version that was requested by the client.
       */
      requested: string;
    };
  };
}
```

#### Version Negotiation Flow

Without an initialization handshake, version negotiation happens inline:

1. The client sends a request with its preferred protocol version in the
   `MCP-Protocol-Version` header and `io.modelcontextprotocol/protocolVersion`
   `_meta` field.
2. If the server supports that version, it processes the request normally.
3. If the server does not support the requested version, it returns an
   `UnsupportedProtocolVersionError` containing its list of `supported`
   versions.
4. The client selects a mutually supported version from the list and retries.

Alternatively, a client **MAY** call `server/discover` first to learn the
server's supported versions before sending any other requests.

### Discovery for Server Capabilities

To allow clients to adapt to different server implementations, this
specification introduces a **discovery RPC**. This provides a standard mechanism
for a server to advertise its supported protocol versions and capabilities.

Servers **MUST** implement `server/discover`. Clients **MAY** call it but are
not required to — a client is free to invoke any RPC without first calling the
discovery endpoint. If a client calls an unsupported RPC, the server **MUST**
return a `Method not found` JSON-RPC error (`-32601`). For HTTP, the response
status code MUST be `404 Not Found`.

#### `server/discover` RPC

* **Purpose**: To allow a client to query the server for its supported protocol
  versions, capabilities, and other metadata.

**Request Schema:**

```ts theme={null}
export interface DiscoverRequest extends Request {
  method: "server/discover";
  params?: {};
}
```

**Response Schema:**

```ts theme={null}
export interface DiscoverResult extends Result {
  /**
   * A list of MCP Protocol Version strings that this server supports.
   * The client should choose a version from this list for use in
   * subsequent requests.
   */
  supportedVersions: string[];

  /**
   * An object detailing the capabilities of the server.
   */
  capabilities: ServerCapabilities;

  /**
   * Information about the server software implementation.
   */
  serverInfo: Implementation;

  /**
   * Natural language instructions describing how to use the server and
   * its features. This can be used by clients to improve an LLM's
   * understanding of available tools (e.g., by including it in a system prompt).
   */
  instructions?: string;
}
```

### Per-Request Client Capabilities

To complete the decoupling from the initial handshake, client capabilities are
no longer negotiated once at initialization. Instead, a client **MUST** specify
its capabilities on every request. This ensures the server is always fully
informed about what optional features the client can handle for that specific
transaction. An empty capabilities object means the client supports no optional
capabilities — servers **MUST NOT** infer capabilities from prior requests.

Cut at 300 lines.