Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/mcpgodebug.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ Options listed below were added and will be removed in the 1.11.0 version of the
behavior was changed to reject such messages as invalid requests, as the
specification lists `params` as required for all three.

- `nopreinitinvalidrequest` added. If set to `1`, a legacy server session that
has not been initialized refuses a request other than `initialize` or `ping`
with JSON-RPC error code `0`, restoring the previous behavior. The default
behavior was changed to refuse it with `-32600` (Invalid Request): `0`
tells a client nothing and is indistinguishable from an unset code. See
issue #1367.

### 1.8.0

Options listed below were added and will be removed in the 1.9.0 version of the SDK.
Expand Down
7 changes: 7 additions & 0 deletions internal/docs/mcpgodebug.src.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ Options listed below were added and will be removed in the 1.11.0 version of the
behavior was changed to reject such messages as invalid requests, as the
specification lists `params` as required for all three.

- `nopreinitinvalidrequest` added. If set to `1`, a legacy server session that
has not been initialized refuses a request other than `initialize` or `ping`
with JSON-RPC error code `0`, restoring the previous behavior. The default
behavior was changed to refuse it with `-32600` (Invalid Request): `0`
tells a client nothing and is indistinguishable from an unset code. See
issue #1367.

### 1.8.0

Options listed below were added and will be removed in the 1.9.0 version of the SDK.
Expand Down
14 changes: 13 additions & 1 deletion mcp/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2010,6 +2010,14 @@ func (ss *ServerSession) receivingMethodHandler() MethodHandler {
// getConn implements [session.getConn].
func (ss *ServerSession) getConn() *jsonrpc2.Connection { return ss.conn }

// nopreinitinvalidrequest is a compatibility parameter that restores the
// previous behavior of a legacy server session that has not been initialized,
// where a request other than initialize or ping was refused with JSON-RPC
// error code 0 instead of -32600 (Invalid Request). See the documentation for
// the mcpgodebug package for instructions how to enable it. The option will be
// removed in a future version of the SDK.
var nopreinitinvalidrequest = mcpgodebug.Value("nopreinitinvalidrequest")

// handle invokes the method described by the given JSON RPC request.
func (ss *ServerSession) handle(ctx context.Context, req *jsonrpc.Request) (any, error) {
ss.mu.Lock()
Expand Down Expand Up @@ -2076,7 +2084,11 @@ func (ss *ServerSession) handle(ctx context.Context, req *jsonrpc.Request) (any,
if !initialized && !validatedMeta.usesNewProtocol && req.IsCall() &&
req.Method != methodInitialize && req.Method != methodPing {
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)
err := fmt.Errorf("method %q is invalid during session initialization", req.Method)
if nopreinitinvalidrequest == "1" {
return nil, err
}
return nil, &jsonrpc.Error{Code: jsonrpc.CodeInvalidRequest, Message: err.Error()}
}

// modelcontextprotocol/go-sdk#26: handle calls asynchronously, and
Expand Down
20 changes: 20 additions & 0 deletions mcp/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2445,6 +2445,26 @@ func TestServerHandle_LegacyCallBeforeInitialize(t *testing.T) {
if want := fmt.Sprintf("method %q is invalid during session initialization", tc.method); err.Error() != want {
t.Errorf("handle(%q) error = %q, want %q", tc.method, err.Error(), want)
}
var rpcErr *jsonrpc.Error
if !errors.As(err, &rpcErr) || rpcErr.Code != jsonrpc.CodeInvalidRequest {
t.Errorf("handle(%q) error = %#v, want a JSON-RPC error with code %d", tc.method, err, jsonrpc.CodeInvalidRequest)
}
// MCPGODEBUG=nopreinitinvalidrequest=1 restores the old code, 0.
func() {
defer func(old string) { nopreinitinvalidrequest = old }(nopreinitinvalidrequest)
nopreinitinvalidrequest = "1"
_, err := ss.handle(ctx, &jsonrpc.Request{
ID: jsonrpc2.Int64ID(2),
Method: tc.method,
Params: json.RawMessage(tc.params),
})
if err == nil {
t.Fatalf("handle(%q), old error code restored, served the call before initialize", tc.method)
}
if errors.As(err, &rpcErr) {
t.Errorf("handle(%q), old error code restored, error = %#v, want a plain error (code 0 on the wire)", tc.method, err)
}
}()
if handlerRan {
t.Errorf("handle(%q) ran the server's handler for a call it refused", tc.method)
}
Expand Down
1 change: 1 addition & 0 deletions mcp/streamable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1291,6 +1291,7 @@ func TestStreamableServerTransport(t *testing.T) {
messages: []jsonrpc.Message{req(2, "tools/call", &CallToolParams{Name: "tool"})},
wantStatusCode: http.StatusOK,
wantMessages: []jsonrpc.Message{resp(2, nil, &jsonrpc.Error{
Code: jsonrpc.CodeInvalidRequest,
Message: `method "tools/call" is invalid during session initialization`,
})},
},
Expand Down
2 changes: 1 addition & 1 deletion mcp/testdata/conformance/server/lifecycle.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ See also modelcontextprotocol/go-sdk#225.
"jsonrpc": "2.0",
"id": 2,
"error": {
"code": 0,
"code": -32600,
"message": "method \"tools/list\" is invalid during session initialization"
}
}
Expand Down
Loading