Skip to content

fix: propagate auth errors (401/403) immediately instead of falling b…#1444

Open
xue-cai wants to merge 1 commit intomodelcontextprotocol:mainfrom
xue-cai:fix/auto-detect-propagate-auth-errors
Open

fix: propagate auth errors (401/403) immediately instead of falling b…#1444
xue-cai wants to merge 1 commit intomodelcontextprotocol:mainfrom
xue-cai:fix/auto-detect-propagate-auth-errors

Conversation

@xue-cai
Copy link

@xue-cai xue-cai commented Mar 16, 2026

…ack to SSE

When AutoDetectingClientSessionTransport receives a 401 or 403 from the initial Streamable HTTP POST, it should NOT fall back to SSE transport. Auth errors are not transport-related — the server understood the request but rejected the credentials. Falling back to SSE would:

  1. Fail with the same credentials (or a different transport error)
  2. Mask the real authentication error from the caller

This was discovered investigating Azure AI Search MCP endpoints returning 403 on Streamable HTTP POST, which caused the SDK to fall back to SSE GET, resulting in a confusing 405 error that hid the real auth failure.

The fix adds an explicit check for 401/403 before the SSE fallback path, and throws HttpRequestException with the auth status code immediately.

Code reference:
AutoDetectingClientSessionTransport.InitializeAsync
https://github.com/modelcontextprotocol/csharp-sdk/blob/v0.9.0-preview.2/src/ModelContextProtocol.Core/Client/AutoDetectingClientSessionTransport.cs#L61

Motivation and Context

How Has This Been Tested?

Breaking Changes

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

@xue-cai xue-cai force-pushed the fix/auto-detect-propagate-auth-errors branch from 158fc82 to 7405d8b Compare March 16, 2026 21:16
…ack to SSE

When AutoDetectingClientSessionTransport receives a 401 or 403 from the
initial Streamable HTTP POST, it should NOT fall back to SSE transport.
Auth errors are not transport-related — the server understood the request
but rejected the credentials. Falling back to SSE would:

1. Fail with the same credentials (or a different transport error)
2. Mask the real authentication error from the caller

This was discovered investigating Azure AI Search MCP endpoints returning
403 on Streamable HTTP POST, which caused the SDK to fall back to SSE GET,
resulting in a confusing 405 error that hid the real auth failure.

The fix adds an explicit check for 401/403 before the SSE fallback path,
and throws HttpRequestException with the auth status code immediately.

Code reference:
  AutoDetectingClientSessionTransport.InitializeAsync
  https://github.com/modelcontextprotocol/csharp-sdk/blob/v0.9.0-preview.2/src/ModelContextProtocol.Core/Client/AutoDetectingClientSessionTransport.cs#L61

Testing:
  # Build all frameworks (only netstandard2.0 fails — pre-existing on main):
  dotnet build tests/ModelContextProtocol.Tests/ '/p:NoWarn=NU1903%3BMCPEXP001'

  # Run auto-detect tests (4 tests × 3 frameworks = 12 total, 0 failures):
  dotnet test tests/ModelContextProtocol.Tests/ '/p:NoWarn=NU1903%3BMCPEXP001' --no-build --filter "FullyQualifiedName~HttpClientTransportAutoDetectTests" --framework net10.0
  dotnet test tests/ModelContextProtocol.Tests/ '/p:NoWarn=NU1903%3BMCPEXP001' --no-build --filter "FullyQualifiedName~HttpClientTransportAutoDetectTests" --framework net9.0
  dotnet test tests/ModelContextProtocol.Tests/ '/p:NoWarn=NU1903%3BMCPEXP001' --no-build --filter "FullyQualifiedName~HttpClientTransportAutoDetectTests" --framework net8.0

  # Run full transport suite (75 tests × 3 frameworks = 225 total, 0 failures):
  dotnet test tests/ModelContextProtocol.Tests/ '/p:NoWarn=NU1903%3BMCPEXP001' --no-build --filter "FullyQualifiedName~Transport" --framework net10.0
  dotnet test tests/ModelContextProtocol.Tests/ '/p:NoWarn=NU1903%3BMCPEXP001' --no-build --filter "FullyQualifiedName~Transport" --framework net9.0
  dotnet test tests/ModelContextProtocol.Tests/ '/p:NoWarn=NU1903%3BMCPEXP001' --no-build --filter "FullyQualifiedName~Transport" --framework net8.0

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@xue-cai xue-cai force-pushed the fix/auto-detect-propagate-auth-errors branch from 7405d8b to 13a634c Compare March 17, 2026 00:14
@halter73
Copy link
Contributor

Our auth retry logic ought to be at a lower layer than the AutoDetectingClientSessionTransport.cs‎, so if you can do the OAuth flow defined in https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization, that should work and we have a lot of tests for that.

At a higher layer, falling back for any error seems right if you do choose the default auto transport. I don't think we should special-case status codes when they're not doing anything that's part of the spec.

It is an annoying default behavior to get a 405 when the real issue is a 403, but if you're doing custom auth, you should know what transport you're using, so opting into TransportMode = HttpTransportMode.StreamableHttp should give you the desired behavior. Long term, we might just make that the default avoiding this entirely.

Another thing to consider is capturing and rethrowing the original Streamable HTTP exception using ExceptoinDispatchInfo if the SSE fallback fails.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants