Fix HTTP fallback error handling (#1791)
This commit is contained in:
parent
609499b298
commit
df11d7da3b
3 changed files with 146 additions and 24 deletions
|
|
@ -77,16 +77,19 @@ internal sealed partial class AutoDetectingClientSessionTransport : ITransport
|
|||
else if (await StreamableHttpClientSessionTransport.TryReadJsonRpcErrorAsync(response, cancellationToken).ConfigureAwait(false) is { } parsedError)
|
||||
{
|
||||
// A JSON-RPC error envelope in the body means the peer IS a Streamable HTTP server.
|
||||
// It just rejected our specific request (e.g., -32022 UnsupportedProtocolVersion,
|
||||
// -32021 MissingRequiredClientCapability, -32020 HeaderMismatch, or any other
|
||||
// application-level error). Don't fall back to SSE — that would mask the real signal
|
||||
// and surface a misleading "session id required" error from the SSE GET path.
|
||||
// Adopt the Streamable HTTP transport and throw the structured exception so the
|
||||
// connect-time fallback logic can react per spec PR #2844. Setting ActiveTransport
|
||||
// first makes the catch filter below leave the now-owned transport alone.
|
||||
// Adopt it before surfacing the failure so the catch filter leaves the now-owned
|
||||
// transport alone, and never mask the response by attempting deprecated SSE.
|
||||
LogUsingStreamableHttp(_name);
|
||||
ActiveTransport = streamableHttpTransport;
|
||||
throw McpSessionHandler.CreateRemoteProtocolExceptionFromError(parsedError);
|
||||
|
||||
if (StreamableHttpClientSessionTransport.ShouldSurfaceJsonRpcErrorAsProtocolException(response.StatusCode, parsedError))
|
||||
{
|
||||
throw McpSessionHandler.CreateRemoteProtocolExceptionFromError(parsedError);
|
||||
}
|
||||
|
||||
// TryReadJsonRpcErrorAsync buffered the content, so this preserves the same response
|
||||
// body and status without consuming the network stream a second time.
|
||||
throw await HttpResponseMessageExtensions.CreateHttpRequestExceptionWithBodyAsync(response, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -367,14 +367,6 @@ internal sealed partial class McpClientImpl : McpClient
|
|||
// fallback): falling back to initialize wouldn't fix a malformed envelope.
|
||||
throw;
|
||||
}
|
||||
catch (McpProtocolException ex) when (
|
||||
ex.ErrorCode == McpErrorCode.InvalidRequest &&
|
||||
ex.Message.Contains(McpHttpHeaders.SessionId, StringComparison.Ordinal))
|
||||
{
|
||||
// Local transport validation: a 2026-07-28+ response must not carry HTTP session state.
|
||||
// This is not evidence of an initialize-handshake server, so do not fall back.
|
||||
throw;
|
||||
}
|
||||
catch (McpProtocolException)
|
||||
{
|
||||
// Per spec PR #2844, the fallback MUST NOT be keyed to a single error code.
|
||||
|
|
|
|||
|
|
@ -80,8 +80,7 @@ internal sealed partial class StreamableHttpClientSessionTransport : TransportBa
|
|||
// for robustness. Servers occasionally emit them with 4xx codes other than 400.
|
||||
if (!response.IsSuccessStatusCode &&
|
||||
await TryReadJsonRpcErrorAsync(response, cancellationToken).ConfigureAwait(false) is { } parsedError &&
|
||||
(response.StatusCode == HttpStatusCode.BadRequest ||
|
||||
IsPerRequestMetadataProtocolErrorCode((McpErrorCode)parsedError.Error.Code)))
|
||||
ShouldSurfaceJsonRpcErrorAsProtocolException(response.StatusCode, parsedError))
|
||||
{
|
||||
throw McpSessionHandler.CreateRemoteProtocolExceptionFromError(parsedError);
|
||||
}
|
||||
|
|
@ -89,10 +88,11 @@ internal sealed partial class StreamableHttpClientSessionTransport : TransportBa
|
|||
await response.EnsureSuccessStatusCodeWithResponseBodyAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static bool IsPerRequestMetadataProtocolErrorCode(McpErrorCode code) =>
|
||||
code is McpErrorCode.UnsupportedProtocolVersion
|
||||
or McpErrorCode.MissingRequiredClientCapability
|
||||
or McpErrorCode.HeaderMismatch;
|
||||
internal static bool ShouldSurfaceJsonRpcErrorAsProtocolException(HttpStatusCode statusCode, JsonRpcError error) =>
|
||||
statusCode == HttpStatusCode.BadRequest ||
|
||||
(McpErrorCode)error.Error.Code is McpErrorCode.UnsupportedProtocolVersion
|
||||
or McpErrorCode.MissingRequiredClientCapability
|
||||
or McpErrorCode.HeaderMismatch;
|
||||
|
||||
/// <summary>
|
||||
/// Reads a JSON-RPC error envelope from an <c>application/json</c> response body, returning
|
||||
|
|
|
|||
Loading…
Reference in a new issue