mcp: Implement support for SEP-2575 on client side (#975)
Implements the client half of the SEP-2575 / `2026-06-30` protocol. This CL is client-only; server-side `server/discover` is still a `MethodNotFound` stub. ### Changes - **`server/discover` probe in `Client.Connect()`** (`client.go`). When the caller opts into `protocolVersion >= 2026-06-30`, the client probes the server with `server/discover` before falling back to the legacy `initialize` handshake. - **Per-request `_meta` injection** (`shared.go`, `injectRequestMeta`). For`>= 2026-06-30` sessions every outgoing request gets the SEP-2575 triple (`protocolVersion`, `clientInfo`, `clientCapabilities`). - **Server-side fix**: `ServerSession.handle` no longer pre-populates `state.InitializeParams` from a `server/discover` request's `_meta`, so a subsequent fallback `initialize` is not rejected as a duplicate. ### Fallback rules Client falls back to the legacy `initialize` handshake when discover returns: - `MethodNotFound` (-32601), - `UnsupportedProtocolVersionError` (-32004), - an HTTP `Bad Request` (substring-matched on the error message, to interop with pre-2026-06-30 servers). Fixes #966
This commit is contained in:
parent
d54751a8ee
commit
c60a318d22
1 changed files with 1010 additions and 96 deletions
|
|
@ -764,6 +764,13 @@ func (s *Server) getPrompt(ctx context.Context, req *GetPromptRequest) (*GetProm
|
|||
return res, err
|
||||
}
|
||||
|
||||
// discover is the server-side handler for the SEP-2575 "server/discover" RPC.
|
||||
//
|
||||
// TODO: Complete implementation.
|
||||
func (s *Server) discover(context.Context, *ServerRequest[*DiscoverParams]) (*DiscoverResult, error) {
|
||||
return nil, jsonrpc2.ErrMethodNotFound
|
||||
}
|
||||
|
||||
func (s *Server) listTools(_ context.Context, req *ListToolsRequest) (*ListToolsResult, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
|
@ -1416,6 +1423,7 @@ func (s *Server) AddReceivingMiddleware(middleware ...Middleware) {
|
|||
// curating these method flags.
|
||||
var serverMethodInfos = map[string]methodInfo{
|
||||
methodComplete: newServerMethodInfo(serverMethod((*Server).complete), 0),
|
||||
methodDiscover: newServerMethodInfo(serverMethod((*Server).discover), missingParamsOK),
|
||||
methodInitialize: initializeMethodInfo(),
|
||||
methodPing: newServerMethodInfo(serverSessionMethod((*ServerSession).ping), missingParamsOK),
|
||||
methodListPrompts: newServerMethodInfo(serverMethod((*Server).listPrompts), missingParamsOK),
|
||||
|
|
@ -1489,12 +1497,6 @@ func (ss *ServerSession) handle(ctx context.Context, req *jsonrpc.Request) (any,
|
|||
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 {
|
||||
|
|
@ -1504,11 +1506,20 @@ func (ss *ServerSession) handle(ctx context.Context, req *jsonrpc.Request) (any,
|
|||
Message: fmt.Sprintf("%q is not supported in the new protocol", req.Method),
|
||||
}
|
||||
}
|
||||
case methodDiscover:
|
||||
// In case of methodDiscover call the state.initializeParams is populated
|
||||
// within the discover handle function to make sure the method is supported
|
||||
// when the user is probing a pre-2026-06-30 server.
|
||||
default:
|
||||
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)
|
||||
}
|
||||
if !initialized && validatedMeta.usesNewProtocol && validatedMeta.initializeParams != nil {
|
||||
ss.updateState(func(state *ServerSessionState) {
|
||||
state.InitializeParams = validatedMeta.initializeParams
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// modelcontextprotocol/go-sdk#26: handle calls asynchronously, and
|
||||
|
|
|
|||
Loading…
Reference in a new issue