mcp: Implement stateless server#965
Conversation
…lidation and accessors
…est rejections and update associated tests
…ed regression tests
…t stateless protocol to stateless HTTP servers
…n the new protocol session
| // InitializeParams returns the InitializeParams provided during the client's | ||
| // initial connection. | ||
| // | ||
| // Deprecated: with the >= 2026-06-30 protocol, sessions are sessionless and |
There was a problem hiding this comment.
Also I'm not sure it is not too early to deprecate it.
There's a comment from Jonathan suggesting to not deprecate any APIs prematurely. people
there was this part inthe design is also kind of self-contradicting in this regard:
Session objects (ServerSession/ClientSession) are the core part of the Go MCP SDK API. They are returned from Connect functions and they are used to interact with the peer. Even though SEP-2567 removes the concept of sessions from the protocol, we cannot simply remove these objects. Instead, we will adjust their lifetime of the server side to be per-request and attempt to fill the session state on both sides with the most appropriate data available.
and there's the part you implemented:
ServerSession.InitializeParams() returns nil for new-protocol sessions — there is no pre-population of session state. Returning nil is a clear signal to callers that they should use the ServerRequest accessors instead.
I think we should keep session for now before v2 and populate it with minimal information extracted from meta for newer protocol versions. returning what we have from InitializeParams(), wdyt?
and also a nit: sessions are sessionless -(maybe)-> connections are sessionless.
There was a problem hiding this comment.
I think we should keep session for now before v2 and populate it with minimal information extracted from meta for newer protocol versions. returning what we have from InitializeParams(), wdyt?
It makes sense, but would you change it on every request with the _meta params or just on the client discover?
| case notificationInitialized: | ||
| hasInitialized = true | ||
| } | ||
| if meta := extractRequestMeta(r.Params); meta != nil { |
There was a problem hiding this comment.
why not validateRequestMeta?
There was a problem hiding this comment.
I implemented like this to do the actual validation only on the handle() function, as suggested by the stateless doc
On the server side, ServerSession.handle() validates that the required _meta fields (protocolVersion, clientInfo, clientCapabilities) are present for >= 2026-06-30 requests
There was a problem hiding this comment.
I see. I think we can use protocolVersion which is already in the scope from Mcp-Protocol-Version header, the spec says new clients must send it, so let's just check the version is newer
https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#protocol-version-header
There was a problem hiding this comment.
It makes sense, but I still need to call extractRequestMeta after to compare the _meta and header protocol value corresponds
…protocol version checking in server and tests
… protocol version validation logic
…ata into requests
Description
This PR lays the foundational server-side groundwork for the
>= 2026-06-30sessionless and stateless feature introduced by SEP-2575 and SEP-2567, tracked in design/stateless.md.SEP-2575: Stateless MCP
Per-request protocol detection in
ServerSession.handle()Unmarshal
_metafrom the raw JSON-RPC params determines whether each request follows the new protocol.Per-request typed accessors on
ServerRequest[P]Reject client->server
initialize,initializedandpingfor new-protocol requestsPer-request
_metafield name constantsThree constants for the wire-protocol field names (
MetaKeyProtocolVersion,MetaKeyClientInfo,MetaKeyClientCapabilities)Stop synthesizing fake
InitializeParamsfor new-protocol requestsFixes: #966