Skip to content

mcp: Implement stateless server#965

Open
guglielmo-san wants to merge 13 commits into
mainfrom
guglielmoc/SEP-2567_2575_Stateless_MCP
Open

mcp: Implement stateless server#965
guglielmo-san wants to merge 13 commits into
mainfrom
guglielmoc/SEP-2567_2575_Stateless_MCP

Conversation

@guglielmo-san
Copy link
Copy Markdown
Contributor

@guglielmo-san guglielmo-san commented May 19, 2026

Description

This PR lays the foundational server-side groundwork for the >= 2026-06-30 sessionless 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 _meta from the raw JSON-RPC params determines whether each request follows the new protocol.

  • Per-request typed accessors on ServerRequest[P]

    func (r *ServerRequest[P]) ProtocolVersion() string
    func (r *ServerRequest[P]) ClientInfo() *Implementation
    func (r *ServerRequest[P]) ClientCapabilities() *ClientCapabilities
    
  • Reject client->server initialize, initialized and ping for new-protocol requests

  • Per-request _meta field name constants
    Three constants for the wire-protocol field names (MetaKeyProtocolVersion, MetaKeyClientInfo, MetaKeyClientCapabilities)

  • Stop synthesizing fake InitializeParams for new-protocol requests

Fixes: #966

@guglielmo-san guglielmo-san marked this pull request as ready for review May 19, 2026 15:25
@guglielmo-san guglielmo-san changed the title mcp: Implement stateless and sessionless server mcp: Implement stateless server May 20, 2026
Comment thread mcp/server.go Outdated
// InitializeParams returns the InitializeParams provided during the client's
// initial connection.
//
// Deprecated: with the >= 2026-06-30 protocol, sessions are sessionless and
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.

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.

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 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?

Comment thread mcp/server_test.go Outdated
Comment thread mcp/shared.go Outdated
Comment thread mcp/streamable.go Outdated
case notificationInitialized:
hasInitialized = true
}
if meta := extractRequestMeta(r.Params); meta != nil {
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.

why not validateRequestMeta?

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 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

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 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

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.

It makes sense, but I still need to call extractRequestMeta after to compare the _meta and header protocol value corresponds

Comment thread mcp/shared.go Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement SEP-2575: Make MCP Stateless

2 participants