mcp: Implement stateless server (SEP-2275) (#965)
## Description
This PR lays the foundational server-side groundwork for the `>=
2026-06-30` sessionless and stateless feature introduced by
[SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575)
and SEP-2567, tracked in
[design/stateless.md](614460a225/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]`**
```go
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
This commit is contained in:
parent
c044cdb734
commit
4cbdd6a514
1 changed files with 1113 additions and 62 deletions
|
|
@ -1450,13 +1450,32 @@ func (ss *ServerSession) handle(ctx context.Context, req *jsonrpc.Request) (any,
|
|||
initialized := ss.state.InitializeParams != nil
|
||||
ss.mu.Unlock()
|
||||
|
||||
// From the spec:
|
||||
// "The client SHOULD NOT send requests other than pings before the server
|
||||
// has responded to the initialize request."
|
||||
// Per-request protocol detection (SEP-2575): if the request carries
|
||||
// `io.modelcontextprotocol/protocolVersion` in its `_meta` field, it
|
||||
// follows the new sessionless protocol. The initialization gate is
|
||||
// skipped for such requests.
|
||||
validatedMeta, perRequestErr := validateRequestMeta(req)
|
||||
if perRequestErr != nil {
|
||||
return nil, perRequestErr
|
||||
}
|
||||
|
||||
if !initialized && validatedMeta.usesNewProtocol && validatedMeta.initializeParams != nil {
|
||||
ss.updateState(func(state *ServerSessionState) {
|
||||
state.InitializeParams = validatedMeta.initializeParams
|
||||
})
|
||||
}
|
||||
|
||||
switch req.Method {
|
||||
case methodInitialize, methodPing, notificationInitialized:
|
||||
if validatedMeta.usesNewProtocol {
|
||||
ss.server.opts.Logger.Error("method removed in the new protocol", "method", req.Method)
|
||||
return nil, &jsonrpc.Error{
|
||||
Code: jsonrpc.CodeMethodNotFound,
|
||||
Message: fmt.Sprintf("%q is not supported in the new protocol", req.Method),
|
||||
}
|
||||
}
|
||||
default:
|
||||
if !initialized {
|
||||
if !initialized && !validatedMeta.usesNewProtocol {
|
||||
ss.server.opts.Logger.Error("method invalid during initialization", "method", req.Method)
|
||||
return nil, fmt.Errorf("method %q is invalid during session initialization", req.Method)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue