diff --git a/internal/jsonrpc2/wire.go b/internal/jsonrpc2/wire.go index 1215f14..4d123f2 100644 --- a/internal/jsonrpc2/wire.go +++ b/internal/jsonrpc2/wire.go @@ -31,9 +31,9 @@ var ( // ErrUnknown should be used for all non coded errors. ErrUnknown = NewError(-32001, "unknown error") // ErrServerClosing is returned for calls that arrive while the server is closing. - ErrServerClosing = NewError(-32006, "server is closing") + ErrServerClosing = NewError(-32004, "server is closing") // ErrClientClosing is a dummy error returned for calls initiated while the client is closing. - ErrClientClosing = NewError(-32007, "client is closing") + ErrClientClosing = NewError(-32003, "client is closing") // The following errors have special semantics for MCP transports @@ -45,8 +45,6 @@ var ( // should be returned to the caller to indicate that the specific request is // invalid in the current context. ErrRejected = NewError(-32005, "rejected by transport") - // ErrUnsupportedProtocolVersion is returned when a server does not support the protocol version. - ErrUnsupportedProtocolVersion = NewError(-32004, "unsupported protocol version") ) const wireVersion = "2.0" diff --git a/mcp/client.go b/mcp/client.go index 785b186..e64b8ff 100644 --- a/mcp/client.go +++ b/mcp/client.go @@ -306,7 +306,7 @@ func (c *Client) Connect(ctx context.Context, t Transport, opts *ClientSessionOp // Try to negotiate a mutually supported version if the server // reports an UnsupportedProtocolVersionError with a supported version. var werr *jsonrpc.Error - if errors.As(err, &werr) && werr.Code == CodeUnsupportedProtocolVersion && werr.Data != nil { + if errors.As(err, &werr) && werr.Code == CodeUnsupportedProtocolVersion && len(werr.Data) > 0 { var data UnsupportedProtocolVersionData if err := json.Unmarshal(werr.Data, &data); err == nil { if negotiatedVersion := negotiateMutuallySupportedVersion(data.Supported); negotiatedVersion != "" && negotiatedVersion >= protocolVersion20260728 { @@ -384,7 +384,10 @@ func (c *Client) discover(ctx context.Context, cs *ClientSession) (*InitializeRe if negotiated == "" || negotiated < protocolVersion20260728 { // If there is no overlap, fall back to initialize so version // negotiation can happen via the legacy path. - return nil, jsonrpc2.ErrUnsupportedProtocolVersion + return nil, &jsonrpc2.WireError{ + Code: CodeUnsupportedProtocolVersion, + Message: "unsupported protocol version", + } } return &InitializeResult{ diff --git a/mcp/shared.go b/mcp/shared.go index 128cbc2..a15ec20 100644 --- a/mcp/shared.go +++ b/mcp/shared.go @@ -355,15 +355,15 @@ func clientSessionMethod[P Params, R Result](f func(*ClientSession, context.Cont // MCP-specific error codes. const ( - // CodeMissingRequiredClientCapabilities is the JSON-RPC error code defined by - // SEP-2575 for MissingRequiredClientCapabilitiesError. - CodeMissingRequiredClientCapabilities = -32003 - // CodeUnsupportedProtocolVersion is the JSON-RPC error code defined by - // SEP-2575 for UnsupportedProtocolVersionError. - CodeUnsupportedProtocolVersion = -32004 // CodeHeaderMismatch indicates that HTTP headers do not match the corresponding values // in the request body, or that required headers are missing or malformed. - CodeHeaderMismatch = -32001 + CodeHeaderMismatch = -32020 + // CodeMissingRequiredClientCapabilities is the JSON-RPC error code defined by + // SEP-2575 for MissingRequiredClientCapabilitiesError. + CodeMissingRequiredClientCapabilities = -32021 + // CodeUnsupportedProtocolVersion is the JSON-RPC error code defined by + // SEP-2575 for UnsupportedProtocolVersionError. + CodeUnsupportedProtocolVersion = -32022 // CodeURLElicitationRequired indicates that the server requires URL elicitation // before processing the request. The client should execute the elicitation handler // with the elicitations provided in the error data. diff --git a/mcp/streamable.go b/mcp/streamable.go index 2d020c6..f01266a 100644 --- a/mcp/streamable.go +++ b/mcp/streamable.go @@ -955,7 +955,8 @@ func (s *stream) release() { // // Per SEP-2575: // - MethodNotFound (-32601) MUST return HTTP 404. -// - InvalidParams (-32602) and UnsupportedProtocolVersion (-32004) MUST +// - InvalidParams (-32602), UnsupportedProtocolVersion (-32022) and +// CodeMissingRequiredClientCapabilities (-32021) MUST // return HTTP 400. func extractErrorStatus(ctx context.Context, msg jsonrpc.Message) int { if protocolVersionFromContext(ctx) < protocolVersion20260728 { diff --git a/mcp/streamable_test.go b/mcp/streamable_test.go index a9855cb..539789b 100644 --- a/mcp/streamable_test.go +++ b/mcp/streamable_test.go @@ -2050,7 +2050,7 @@ func TestStreamableMcpHeaderValidation(t *testing.T) { } // TestStreamableMcpHeaderValidationErrorFormat verifies that header -// validation errors return a JSON-RPC error with code -32001 and +// validation errors return a JSON-RPC error with code -32020 and // Content-Type application/json, per SEP-2243. func TestStreamableMcpHeaderValidationErrorFormat(t *testing.T) { orig := supportedProtocolVersions @@ -2125,7 +2125,7 @@ func TestStreamableMcpHeaderValidationErrorFormat(t *testing.T) { t.Errorf("Content-Type = %q, want %q", baseMediaType(toolCallResp.Header.Get("Content-Type")), "application/json") } - // Verify JSON-RPC error body contains error code -32001. + // Verify JSON-RPC error body contains error code -32020. msg, err := jsonrpc2.DecodeMessage(toolCallRespBody) if err != nil { t.Fatalf("failed to decode message: %v", err) @@ -2138,8 +2138,8 @@ func TestStreamableMcpHeaderValidationErrorFormat(t *testing.T) { if !errors.As(resp.Error, &wireErr) { t.Fatalf("expected *jsonrpc2.WireError, got %T", resp.Error) } - if wireErr.Code != -32001 { - t.Errorf("wireErr.Code = %d, want -32001", wireErr.Code) + if wireErr.Code != CodeHeaderMismatch { + t.Errorf("wireErr.Code = %d, want %d", wireErr.Code, CodeHeaderMismatch) } if !strings.Contains(wireErr.Message, "Mcp-Method header value") { t.Errorf("wireErr.Message = %q, want it to contain %q", wireErr.Message, "Mcp-Method header value")