Skip to content

[Server] SEP-2567: Remove session dependency for list endpoints; deprecate Mcp-Session-Id #341

Closed

Description

@chr-hertel

Implements SEP-2567 for the MCP Spec 2026-07-28 release.

Tracked by umbrella #334.

Spec summary

Removes protocol-level sessions: no session/create, no session/destroy, no Mcp-Session-Id header. tools/list / resources/list / prompts/list MUST NOT depend on per-connection or prior-call state — list endpoints become cacheable at (deployment, auth) granularity. Stateful workflows use server-minted opaque handles passed as parameters.

PHP SDK changes

  • src/Server/Session/* — entire directory becomes optional/internal-only. SessionManager, InMemorySessionStore, FileSessionStore, Psr16SessionStore, SessionInterface — keep for back-compat with old clients but cannot drive list responses.
  • src/Server/Transport/StreamableHttpTransport.php — stop minting/requiring Mcp-Session-Id; downgrade to optional legacy header.
  • src/Capability/Registry/Registry.php and all List*Handler.php — must produce identical results regardless of session. Audit handlers that read from $session->get(...).
  • RequestContext — sessions become optional, not guaranteed.
  • Documentation (docs/server-builder.md, docs/transports.md) — guide users toward stateless tool design with explicit handles.

Breaking change. Coordinate with deprecation policy (SEP-2596, umbrella #339).

Related

Activity

  1. added
    ServerIssues & PRs related to the Server component
    breaking changeBreaking the Backwards Compatibility Promise
    P0Broken core functionality, security issues, critical missing feature
    improves spec complianceImproves consistency with other SDKs such as TyepScript
    needs designValid issue but needs maintainer alignment on design or approach
    enhancementRequest for a new feature that's not currently supported
    on May 26, 2026
  2. added
    2026-07-28All issues and PRs related to the spec release 2026-07-28
    on May 26, 2026
  3. DevkumarPatel commented on Jul 7, 2026

    @DevkumarPatel
    Contributor

    Re: honoring session optionality under the current (2025-11-25) spec, ahead of the breaking 2026-07-28 work

    This SEP scopes the sessionless work to the 2026-07-28 release as a coordinated breaking change. I'd like to flag that the current 2025-11-25 spec already makes session management optional (MAY), yet the transport hard-requires it today — so there's a smaller, non-breaking slice worth considering independently.

    Current behaviour (mcp/sdk 0.6.0, Streamable HTTP): Protocol::resolveSession() always mints a session on initialize (calling $transport->setSessionId(...), so Mcp-Session-Id is always emitted), and hard-rejects every non-initialize request that lacks a stored session:

    • if (!$sessionId) → 400 "A valid session id is REQUIRED for non-initialize requests."
    • if (!$this->sessionManager->exists($id)) → 404 "Session not found or has expired."

    Per the 2025-11-25 spec, session management is entirely MAY — a server is free not to issue a session id and to treat each POST independently. There's currently no supported way to run a spec-compliant stateless server with the SDK.

    Why it matters: many deployments are stateless by nature — serverless/FaaS, autoscaled multi-instance apps, and MCP servers that just wrap an existing REST API (auth + params on every request, no subscriptions, no server-initiated messages). Because each HTTP request is a separate PHP process, the current design forces a persistent cross-request (and cross-node) session store purely to satisfy a handshake the spec says is optional.

    Proposed non-breaking slice: an opt-in Server::builder()->stateless() (or ->withoutSessions()) that, when enabled:

    1. skips setSessionId() on initialize, so no Mcp-Session-Id header is emitted, and
    2. in resolveSession(), skips the !$sessionId / !exists() rejections and uses an ephemeral per-request session.

    Stateful stays the default; statelessness becomes a one-liner. This is aligned with #341's direction (making Mcp-Session-Id optional and decoupling handlers from session state) but could land ahead of, and independently from, the breaking 2026-07-28 overhaul in #334.

    Current workaround (for reference): supplying a non-persistent SessionStoreInterface (exists() → true, read() → fresh state, write()/save() → no-op) makes each request self-contained without a real store — but the server still emits an unused Mcp-Session-Id and it relies on resolveSession()'s exact internals rather than a supported mode.

    Would a non-breaking opt-in flag under the current spec be in scope here, or would you prefer it tracked as its own issue?

  4. removed
    P0Broken core functionality, security issues, critical missing feature
    on Aug 19, 2026
  5. chr-hertel commented on Aug 19, 2026

    @chr-hertel
    MemberAuthor

    Implemented via #444, #455. StatelessProtocol::handle() builds a fresh, throwaway session per request, so list-endpoint output can't depend on prior calls.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    2026-07-28All issues and PRs related to the spec release 2026-07-28ServerIssues & PRs related to the Server componentbreaking changeBreaking the Backwards Compatibility PromiseenhancementRequest for a new feature that's not currently supportedimproves spec complianceImproves consistency with other SDKs such as TyepScriptneeds designValid issue but needs maintainer alignment on design or approach

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions