mcp: Implement server-side support for discover method (SEP-2575) (#987)

## Summary
Part 3 of the SEP-2575 ("Make MCP Stateless") implementation. Builds on
PR #975 which landed the client-side discover/fallback flow and the
per-request `_meta` plumbing. This PR adds the **server-side**
counterparts:

- `server/discover` handler (replaces the
stub),ProtocolVersionSupporter`
- transport hook for per-transport version filtering,
- transport-agnostic version validation and method-removal enforcement
in `ServerSession.handle`,
- HTTP-level wire compliance for SEP-2575 error codes/status codes,
- bug fix in `internal/jsonrpc2`,`processResult` with an `MCPGODEBUG`
opt-out for backward compatibility.
This commit is contained in:
Guglielmo Colombo 2026-06-08 17:52:10 +02:00 • committed by GitHub
commit dd978160ab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 702 additions and 187 deletions

View file

@ -38,6 +38,17 @@ Options listed below were added and will be removed in the 1.9.0 version of the
restoring the previous behavior. The default behavior was changed so that
stateless servers ignore session IDs entirely and reject `DELETE` with 405.
- `nomethodnotfoundcodeinerror` added. If set to `1`, the jsonrpc2 layer will not
include the MethodNotFound Error (`-32601`) in the error response when the
requested method in STDIO transport is not found. The default behavior was
changed to include the MethodNotFound Error in the error response when the
requested method in STDIO transport is not found.
- `noprotocolerrorbody` added. If set to `1`, the streamable HTTP client will
not attempt to decode the JSON-RPC error body of a non-2xx HTTP response,
restoring the previous behavior. The default behavior was changed so that
the client always attempts to surface the underlying JSON-RPC error.
### 1.6.0
Options listed below were added and will be removed in the 1.8.0 version of the SDK.

View file

@ -14,8 +14,25 @@ import (
"time"
"github.com/modelcontextprotocol/go-sdk/internal/json"
"github.com/modelcontextprotocol/go-sdk/internal/mcpgodebug"
)
// nomethodnotfoundcodeinerror is a compatibility parameter that restores the
// pre-fix behavior of [processResult], where wrapped [ErrNotHandled] or
// [ErrMethodNotFound] errors returned by request handlers were not
// recognized as "method not found" signals. The original switch statement
// compared sentinel errors with ==, which never matched errors returned via
// fmt.Errorf("%w: ...", ErrNotHandled, ...) — including the ones produced
// by checkRequest. As a result the wire error response carried code 0
// instead of code -32601. The fix uses errors.Is to recognize wrapped
// sentinels and append the method name to the message.
//
// To restore the previous behavior, set MCPGODEBUG=nomethodnotfoundcodeinerror=1.
// This option will be removed in a future SDK version.
// See the documentation for the mcpgodebug package for instructions on how
// to use it.
var nomethodnotfoundcodeinerror = mcpgodebug.Value("nomethodnotfoundcodeinerror")
// Connection manages the jsonrpc2 protocol, connecting responses back to their
// calls. Connection is bidirectional; it does not have a designated server or
// client end.
@ -648,9 +665,7 @@ func (c *Connection) handleAsync() {
// processResult processes the result of a request and, if appropriate, sends a response.
func (c *Connection) processResult(from any, req *incomingRequest, result any, err error) error {
switch err {
case ErrNotHandled, ErrMethodNotFound:
// Add detail describing the unhandled method.
if nomethodnotfoundcodeinerror != "1" && (errors.Is(err, ErrNotHandled) || errors.Is(err, ErrMethodNotFound)) {
err = fmt.Errorf("%w: %q", ErrMethodNotFound, req.Method)
}

View file

@ -33,7 +33,7 @@ var (
// ErrServerClosing is returned for calls that arrive while the server is closing.
ErrServerClosing = NewError(-32006, "server is closing")
// ErrClientClosing is a dummy error returned for calls initiated while the client is closing.
ErrClientClosing = NewError(-32003, "client is closing")
ErrClientClosing = NewError(-32007, "client is closing")
// The following errors have special semantics for MCP transports