You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
StreamableHttpClientSessionTransport correctly converts the structured HTTP 400 response to McpProtocolException. However, McpClientImpl.ConnectAsync has a special catch for InvalidRequest errors whose message contains Mcp-Session-Id. That catch describes the error as locally generated and rethrows it, even though client-side protocol exceptions currently originate from remote JSON-RPC responses. The remote error therefore never reaches the generic McpProtocolException branch that falls back to initialize.
The 2026-07-28 Streamable HTTP compatibility rules identify only -32020 (HeaderMismatch), -32021 (MissingRequiredClientCapability), and -32022 (UnsupportedProtocolVersion) as recognized modern MCP errors. A structured -32600 response is not a recognized modern error and should fall back to initialize, regardless of message text.
The special message-based catch appears stale: there are no local client-side McpProtocolException throw sites on current main, and none were found in the commit that introduced the guard. It should be removed unless a concrete local producer still requires distinct handling.
2. AutoDetect loses HTTP status semantics for structured non-400 responses
AutoDetectingClientSessionTransport converts any unsuccessful response containing a JSON-RPC error envelope into McpProtocolException, regardless of HTTP status. A structured 401, 403, or 500 carrying a non-modern error can consequently reach the generic protocol-error branch and incorrectly trigger initialize fallback.
The body-based backward-compatibility decision applies to HTTP 400. Other HTTP failures should retain their HTTP semantics unless the body contains one of the recognized modern MCP errors.
Expected behavior
Use a shared classification rule in the Streamable HTTP and AutoDetect transports:
HTTP 400 with any JSON-RPC error: surface it as McpProtocolException; connection logic handles recognized modern errors or falls back for other errors.
Any HTTP status with -32020, -32021, or -32022: surface the corresponding protocol exception.
Any other HTTP status with any other JSON-RPC error: surface HttpRequestException while preserving the status and response body.
A structured JSON-RPC response proves the endpoint speaks Streamable HTTP, so AutoDetect should not attempt deprecated SSE solely because the response is an HTTP failure.
Suggested tests
Cover both HttpTransportMode.StreamableHttp and HttpTransportMode.AutoDetect where applicable:
Structured HTTP 400 + -32600 mentioning Mcp-Session-Id falls back to initialize.
Structured HTTP 400 + other non-modern JSON-RPC errors falls back.
Structured HTTP 400 + -32020, -32021, or -32022 retains modern handling.
Structured HTTP 401, 403, and 500 under AutoDetect surface HttpRequestException with the original status and do not send initialize or attempt SSE.
Description
Two related gaps remain in the HTTP backward-compatibility handling for the
server/discoverprobe.1. Structured HTTP 400 with
-32600can skipinitializefallbackA session-enforcing pre-SEP-2575 server can respond to the initial
server/discoverrequest with:StreamableHttpClientSessionTransportcorrectly converts the structured HTTP 400 response toMcpProtocolException. However,McpClientImpl.ConnectAsynchas a special catch forInvalidRequesterrors whose message containsMcp-Session-Id. That catch describes the error as locally generated and rethrows it, even though client-side protocol exceptions currently originate from remote JSON-RPC responses. The remote error therefore never reaches the genericMcpProtocolExceptionbranch that falls back toinitialize.The 2026-07-28 Streamable HTTP compatibility rules identify only
-32020(HeaderMismatch),-32021(MissingRequiredClientCapability), and-32022(UnsupportedProtocolVersion) as recognized modern MCP errors. A structured-32600response is not a recognized modern error and should fall back toinitialize, regardless of message text.The special message-based catch appears stale: there are no local client-side
McpProtocolExceptionthrow sites on currentmain, and none were found in the commit that introduced the guard. It should be removed unless a concrete local producer still requires distinct handling.2.
AutoDetectloses HTTP status semantics for structured non-400 responsesAutoDetectingClientSessionTransportconverts any unsuccessful response containing a JSON-RPC error envelope intoMcpProtocolException, regardless of HTTP status. A structured 401, 403, or 500 carrying a non-modern error can consequently reach the generic protocol-error branch and incorrectly triggerinitializefallback.The body-based backward-compatibility decision applies to HTTP 400. Other HTTP failures should retain their HTTP semantics unless the body contains one of the recognized modern MCP errors.
Expected behavior
Use a shared classification rule in the Streamable HTTP and AutoDetect transports:
McpProtocolException; connection logic handles recognized modern errors or falls back for other errors.-32020,-32021, or-32022: surface the corresponding protocol exception.HttpRequestExceptionwhile preserving the status and response body.Suggested tests
Cover both
HttpTransportMode.StreamableHttpandHttpTransportMode.AutoDetectwhere applicable:-32600mentioningMcp-Session-Idfalls back toinitialize.-32020,-32021, or-32022retains modern handling.HttpRequestExceptionwith the original status and do not sendinitializeor attempt SSE.Related: #1765, #1766, #1767, #1768.
Note
This issue was drafted with GitHub Copilot.