diff --git a/mcp/streamable.go b/mcp/streamable.go index 1f07ea61..ca718a80 100644 --- a/mcp/streamable.go +++ b/mcp/streamable.go @@ -1514,10 +1514,25 @@ func (c *streamableServerConn) servePOST(w http.ResponseWriter, req *http.Reques // rejection as it should learn about the supported protocols from the // DiscoverResult response. if !c.stateless && jreq.Method != methodDiscover { - http.Error(w, fmt.Sprintf( - "Bad Request: protocol version %q is only supported on stateless HTTP servers (set StreamableHTTPOptions.Stateless = true)", - protocolVersion), - http.StatusBadRequest) + // This server has chosen not to serve >= 2026-07-28 requests + // unless configured as stateless (see StreamableHTTPOptions.Stateless). + c.logger.Warn("rejecting request for unsupported protocol version; set StreamableHTTPOptions.Stateless = true to serve it", + "protocolVersion", protocolVersion) + legacyVersions := make([]string, 0, len(supportedProtocolVersions)) + for _, v := range supportedProtocolVersions { + if v < protocolVersion20260728 { + legacyVersions = append(legacyVersions, v) + } + } + data, _ := json.Marshal(UnsupportedProtocolVersionData{ + Supported: legacyVersions, + Requested: protocolVersion, + }) + writeJSONRPCError(w, http.StatusBadRequest, jreq.ID, &jsonrpc.Error{ + Code: CodeUnsupportedProtocolVersion, + Message: fmt.Sprintf("protocol version %q is not supported by this server", protocolVersion), + Data: data, + }) return } if headerVersion == "" { diff --git a/mcp/streamable_test.go b/mcp/streamable_test.go index 85ee331f..fee1b220 100644 --- a/mcp/streamable_test.go +++ b/mcp/streamable_test.go @@ -3504,8 +3504,30 @@ func TestStreamableStateful_RejectsNewProtocol(t *testing.T) { if resp.StatusCode != http.StatusBadRequest { t.Fatalf("status = %d, want 400; body = %s", resp.StatusCode, respBody) } - if !strings.Contains(string(respBody), "stateless") { - t.Errorf("body = %q, want a message mentioning 'stateless'", respBody) + msg, err := jsonrpc2.DecodeMessage(respBody) + if err != nil { + t.Fatalf("failed to decode message: %v; body = %s", err, respBody) + } + wireResp, ok := msg.(*jsonrpc2.Response) + if !ok { + t.Fatalf("expected *jsonrpc2.Response, got %T", msg) + } + var wireErr *jsonrpc2.WireError + if !errors.As(wireResp.Error, &wireErr) { + t.Fatalf("expected *jsonrpc2.WireError, got %T", wireResp.Error) + } + if wireErr.Code != CodeUnsupportedProtocolVersion { + t.Errorf("error code = %d, want %d", wireErr.Code, CodeUnsupportedProtocolVersion) + } + var data UnsupportedProtocolVersionData + if err := json.Unmarshal(wireErr.Data, &data); err != nil { + t.Fatalf("error.Data is not UnsupportedProtocolVersionData: %v", err) + } + if data.Requested != protocolVersion20260728 { + t.Errorf("data.Requested = %q, want %q", data.Requested, protocolVersion20260728) + } + if slices.Contains(data.Supported, protocolVersion20260728) { + t.Errorf("data.Supported = %v, must not contain %q for a stateful server", data.Supported, protocolVersion20260728) } }