Skip to content

Commit dd97816

Browse files
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.
1 parent 00d3642 commit dd97816

12 files changed

Lines changed: 702 additions & 187 deletions

File tree

docs/mcpgodebug.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,17 @@ Options listed below were added and will be removed in the 1.9.0 version of the
3939
restoring the previous behavior. The default behavior was changed so that
4040
stateless servers ignore session IDs entirely and reject `DELETE` with 405.
4141

42+
- `nomethodnotfoundcodeinerror` added. If set to `1`, the jsonrpc2 layer will not
43+
include the MethodNotFound Error (`-32601`) in the error response when the
44+
requested method in STDIO transport is not found. The default behavior was
45+
changed to include the MethodNotFound Error in the error response when the
46+
requested method in STDIO transport is not found.
47+
48+
- `noprotocolerrorbody` added. If set to `1`, the streamable HTTP client will
49+
not attempt to decode the JSON-RPC error body of a non-2xx HTTP response,
50+
restoring the previous behavior. The default behavior was changed so that
51+
the client always attempts to surface the underlying JSON-RPC error.
52+
4253
### 1.6.0
4354

4455
Options listed below were added and will be removed in the 1.8.0 version of the SDK.

internal/docs/mcpgodebug.src.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,17 @@ Options listed below were added and will be removed in the 1.9.0 version of the
3838
restoring the previous behavior. The default behavior was changed so that
3939
stateless servers ignore session IDs entirely and reject `DELETE` with 405.
4040

41+
- `nomethodnotfoundcodeinerror` added. If set to `1`, the jsonrpc2 layer will not
42+
include the MethodNotFound Error (`-32601`) in the error response when the
43+
requested method in STDIO transport is not found. The default behavior was
44+
changed to include the MethodNotFound Error in the error response when the
45+
requested method in STDIO transport is not found.
46+
47+
- `noprotocolerrorbody` added. If set to `1`, the streamable HTTP client will
48+
not attempt to decode the JSON-RPC error body of a non-2xx HTTP response,
49+
restoring the previous behavior. The default behavior was changed so that
50+
the client always attempts to surface the underlying JSON-RPC error.
51+
4152
### 1.6.0
4253

4354
Options listed below were added and will be removed in the 1.8.0 version of the SDK.

internal/jsonrpc2/conn.go

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,25 @@ import (
1414
"time"
1515

1616
"github.com/modelcontextprotocol/go-sdk/internal/json"
17+
"github.com/modelcontextprotocol/go-sdk/internal/mcpgodebug"
1718
)
1819

20+
// nomethodnotfoundcodeinerror is a compatibility parameter that restores the
21+
// pre-fix behavior of [processResult], where wrapped [ErrNotHandled] or
22+
// [ErrMethodNotFound] errors returned by request handlers were not
23+
// recognized as "method not found" signals. The original switch statement
24+
// compared sentinel errors with ==, which never matched errors returned via
25+
// fmt.Errorf("%w: ...", ErrNotHandled, ...) — including the ones produced
26+
// by checkRequest. As a result the wire error response carried code 0
27+
// instead of code -32601. The fix uses errors.Is to recognize wrapped
28+
// sentinels and append the method name to the message.
29+
//
30+
// To restore the previous behavior, set MCPGODEBUG=nomethodnotfoundcodeinerror=1.
31+
// This option will be removed in a future SDK version.
32+
// See the documentation for the mcpgodebug package for instructions on how
33+
// to use it.
34+
var nomethodnotfoundcodeinerror = mcpgodebug.Value("nomethodnotfoundcodeinerror")
35+
1936
// Connection manages the jsonrpc2 protocol, connecting responses back to their
2037
// calls. Connection is bidirectional; it does not have a designated server or
2138
// client end.
@@ -648,9 +665,7 @@ func (c *Connection) handleAsync() {
648665

649666
// processResult processes the result of a request and, if appropriate, sends a response.
650667
func (c *Connection) processResult(from any, req *incomingRequest, result any, err error) error {
651-
switch err {
652-
case ErrNotHandled, ErrMethodNotFound:
653-
// Add detail describing the unhandled method.
668+
if nomethodnotfoundcodeinerror != "1" && (errors.Is(err, ErrNotHandled) || errors.Is(err, ErrMethodNotFound)) {
654669
err = fmt.Errorf("%w: %q", ErrMethodNotFound, req.Method)
655670
}
656671

internal/jsonrpc2/wire.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ var (
3333
// ErrServerClosing is returned for calls that arrive while the server is closing.
3434
ErrServerClosing = NewError(-32006, "server is closing")
3535
// ErrClientClosing is a dummy error returned for calls initiated while the client is closing.
36-
ErrClientClosing = NewError(-32003, "client is closing")
36+
ErrClientClosing = NewError(-32007, "client is closing")
3737

3838
// The following errors have special semantics for MCP transports
3939

0 commit comments

Comments
 (0)