Skip to content

SEP-2575: Make MCP Stateless#2575

Open
kurtisvg wants to merge 69 commits intomodelcontextprotocol:mainfrom
kurtisvg:sep-1442-reborn
Open

SEP-2575: Make MCP Stateless#2575
kurtisvg wants to merge 69 commits intomodelcontextprotocol:mainfrom
kurtisvg:sep-1442-reborn

Conversation

@kurtisvg
Copy link
Copy Markdown
Contributor

Summary

This PR introduces a SEP for making MCP stateless-by-default, migrated from the original issue discussion at #1442 into the PR-based SEP format per SEP-1850.

Motivation

The current MCP specification requires a mandatory initialization handshake that establishes persistent session state. This creates significant challenges for scalability (load balancing requires sticky sessions), resilience (server failure loses session state), and implementation complexity (both client and server must manage session lifecycles).

What This SEP Proposes

This SEP removes the initialization handshake and replaces it with discrete, stateless alternatives following a "pay as you go" model:

  • Per-request protocol version — via MCP-Protocol-Version HTTP header and _meta field, with version negotiation through UnsupportedVersionError responses
  • server/discover RPC — optional discovery endpoint for server capabilities, supported versions, and metadata, replacing the capability exchange from initialization
  • Per-request client capabilities — clients specify capabilities in _meta per-request instead of negotiating once at connection time
  • messages/listen RPC — dedicated endpoint for client-initiated streaming, replacing the existing GET endpoint behavior for Streamable HTTP

Relationship to Other SEPs

This SEP focuses on removing the initialization handshake and providing stateless alternatives. Session-related concerns are handled separately:

The content of this SEP will be updated in subsequent commits to reflect these decisions and remove session-related material that is now covered by those SEPs.

Fixes #1442

@kurtisvg kurtisvg self-assigned this Apr 14, 2026
@kurtisvg kurtisvg changed the title SEP-XXXX: Make MCP Stateless SEP-2575: Make MCP Stateless Apr 14, 2026
@kurtisvg kurtisvg requested review from a team as code owners April 14, 2026 21:48
@kurtisvg kurtisvg added SEP transport Related to MCP transports labels Apr 14, 2026
Comment thread docs/seps/2575-stateless-mcp.mdx Outdated
Comment thread docs/seps/2575-stateless-mcp.mdx Outdated
Comment thread docs/seps/2575-stateless-mcp.mdx Outdated
Comment thread docs/seps/2575-stateless-mcp.mdx Outdated
Comment thread docs/seps/2575-stateless-mcp.mdx Outdated
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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add the ttl to this that @CaitieM20 is proposing in the other SEP?

One thought on my mind is that for older server/newer client compatibility, realistically the client will actually need to remember the server's older protocol version if it wants to avoid an error on every request trying to figure out the compatible version, so we need a way to cache that.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree it should be added somewhere. I wasn't sure which would get merged first.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, I was going to suggest the exact same thing. I'd prefer to err on the side of putting it in both places, to make sure it doesn't get missed. :)

Comment thread docs/seps/2575-stateless-mcp.mdx Outdated
Comment thread docs/seps/2575-stateless-mcp.mdx Outdated
@dsp-ant dsp-ant added the roadmap/transport Roadmap: Transport Evolution & Scalability (incl. Server Cards) label Apr 15, 2026
Comment thread docs/seps/2575-stateless-mcp.mdx Outdated
@localden localden added the draft SEP proposal with a sponsor. label Apr 15, 2026
Comment thread seps/2575-stateless-mcp.md Outdated
Comment thread seps/2575-stateless-mcp.md Outdated
Comment thread seps/2575-stateless-mcp.md
Comment thread seps/2575-stateless-mcp.md Outdated
Comment thread seps/2575-stateless-mcp.md Outdated
Comment on lines +158 to +164
export interface RequestMetaObject extends MetaObject {
progressToken?: ProgressToken;
+ /**
+ * The MCP Protocol Version being used for this request.
+ */
+ "io.modelcontextprotocol/protocolVersion": string;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we are overloading _meta here over time. Would we ever add any top level field anymore or shove everything into meta? For me the distinction might be more along the lines of what is a required field by the protocol vs what is an optional field or something provided by an extension. I can't fully point my finger at it, but it feels more right to me, to have a top protocolVersion field instead.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we started with a top-level field, and there was some feedback that we should use _meta instead because of this line in the spec:

Additionally, definitions in the schema may reserve particular names for purpose-specific metadata, as declared in those definitions.

Is protocolVersion the only field you think makes sense at the top level? Or should the others be as well?

Comment thread seps/2575-stateless-mcp.md
Comment thread seps/2575-stateless-mcp.md
Comment thread seps/2575-stateless-mcp.md Outdated
Comment thread seps/2575-stateless-mcp.md Outdated
Comment thread seps/2575-stateless-mcp.md Outdated
Comment thread seps/2575-stateless-mcp.md Outdated
Comment thread seps/2575-stateless-mcp.md Outdated
Comment thread seps/2575-stateless-mcp.md Outdated
kurtisvg added 9 commits May 8, 2026 08:33
- Update Lifecycle Management description to reflect per-request model
- Add per-request protocol fields table to _meta section
- Remove initialize cancellation restriction (initialize no longer exists)
- Add Transport-Specific Cancellation section: HTTP uses stream close, stdio uses notifications/cancelled
…-2575)

- New basic/utilities/subscriptions.mdx: full subscriptions/listen mechanics
- resources.mdx: trim subscriptions section to cross-link subscriptions page
- logging.mdx: remove logging/setLevel; document per-request logLevel and subscriptions/listen
- docs.json: add subscriptions to draft nav
- versioning.mdx: per-request version negotiation via _meta, not initialization
- server-concepts.mdx: resources/subscribe → subscriptions/listen in protocol table
- authorization.mdx: remove Mcp-Session-Id hardening bullet; reframe as per-request token-based auth
- client/roots, sampling, elicitation: capability declared in clientCapabilities per-request, not during initialization
- server/prompts: capability declared in DiscoverResult, not during initialization
- server/tools: remove broken #initialization link from server name note
- architecture/index: rewrite Capability Negotiation section for per-request model
- basic/utilities/tasks: update capability declaration and negotiation prose
@kurtisvg kurtisvg force-pushed the sep-1442-reborn branch from 3a8dd6e to b460206 Compare May 8, 2026 14:38
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label May 8, 2026
Comment thread docs/docs/learn/architecture.mdx
Comment thread docs/docs/learn/architecture.mdx Outdated
Comment thread docs/docs/learn/architecture.mdx Outdated
Comment thread docs/docs/learn/versioning.mdx Outdated
| `promptsListChanged` | `boolean` | Receive `notifications/prompts/list_changed` when prompts change |
| `resourcesListChanged` | `boolean` | Receive `notifications/resources/list_changed` when list changes |
| `resourceSubscriptions` | `string[]` | Receive `notifications/resources/updated` for these resource URIs |
| `logLevel` | `LoggingLevel` | Receive `notifications/message` at or above this severity level |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this meant to be here? Don't we have that in metadata?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the SEP, I have it listed as both. I think I was thinking of _meta as the "per-request" stream while this is more of the "notifications events" stream and might have notifications more general for the server. But agree it's confusing.

I'll reduce to _meta -- this is going to be marked as deprecated soon anyway, so probably not worth overthinking.

Comment thread docs/specification/draft/basic/index.mdx Outdated
Comment thread docs/specification/draft/basic/lifecycle.mdx
Comment thread docs/specification/draft/basic/index.mdx
Comment thread docs/specification/draft/client/roots.mdx Outdated
Comment thread docs/specification/draft/basic/lifecycle.mdx
@kurtisvg kurtisvg requested a review from pja-ant May 9, 2026 01:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

accepted SEP accepted by core maintainers, but still requires final wording and reference implementation. documentation Improvements or additions to documentation roadmap/transport Roadmap: Transport Evolution & Scalability (incl. Server Cards) SEP transport Related to MCP transports

Projects

Status: Review Batch

Development

Successfully merging this pull request may close these issues.

SEP-1442: Make MCP Stateless (by default)