Skip to content

fix(mcp): adopt ModelContextProtocol 2.1.0 without losing invalid_client discard - #1865

Merged
Aaronontheweb merged 4 commits into
netclaw-dev:devfrom
Aaronontheweb:deps/modelcontextprotocol-2.1
Aug 11, 2026
Merged

fix(mcp): adopt ModelContextProtocol 2.1.0 without losing invalid_client discard#1865
Aaronontheweb merged 4 commits into
netclaw-dev:devfrom
Aaronontheweb:deps/modelcontextprotocol-2.1

Conversation

@Aaronontheweb

Copy link
Copy Markdown
Collaborator

Summary

Adopt the ModelContextProtocol 2.1.0 SDK (ModelContextProtocol.Core and
ModelContextProtocol.AspNetCore, both driven by the shared
ModelContextProtocolVersion property). This supersedes the two failing
Dependabot PRs #1777 (AspNetCore) and #1778 (Core), which can be closed.

The straight version bump fails one test:
McpSdkOAuthFlowIntegrationTests.RejectedClientIdentityIsDiscardedSoTheNextAuthorizationRegistersAfresh.

Root cause

SDK 2.1 sends a server/discover probe before the initialize handshake. Its
new catch reads any 400/404 response as an unsupported-protocol signal and
falls back to the initialize handshake. During interactive OAuth, the probe's
authorization-code exchange returns 400 invalid_client when the provider has
dropped the dynamic client registration. The SDK swallows that 400 as a
protocol-fallback signal and re-invokes the one-shot authorization callback a
second time. The second call trips the flow's single-owner guard and throws
OAuth authorization is already in progress, which hides the real
invalid_client. McpClientManager.IsInvalidClientFailure no longer matches,
so the dead dynamic client identity is never discarded and every retry repeats
the failure.

ClientOAuthProvider itself is byte-identical between 2.0.0 and 2.1.0
(decompiled and diffed). The change is entirely in McpClientImpl's new
discover/fallback path.

Fix

Add OAuthClientRejectionHandler to the shared MCP HTTP pipeline
(McpHttpClientFactory). The handler reads the token endpoint's invalid_client
error for the authorization_code grant and throws
McpOAuthClientRejectedException before the SDK sees the 400. The SDK fallback
does not catch that type, so the true cause reaches the manager and the dead
identity is discarded — restoring the exact 2.0.0 behavior without disabling
protocol negotiation. The handler is scoped to the authorization-code grant, so
a refresh failure keeps the SDK's graceful null path.

Testing

  • McpSdkOAuthFlowIntegrationTests — 23/23 pass (was 22/23)
  • Netclaw.Daemon.Tests full — 1001 pass
  • Netclaw.Cli.Tests — 1318 pass
  • Netclaw.Actors.Tests — 3042 pass (1 Windows-only skip)
  • Netclaw.Configuration.Tests Http/Mcp — 16 pass, plus 5 new
    OAuthClientRejectionHandlerTests locking the handler's discriminators
    (authorization-code invalid_client throws; refresh invalid_client, other
    errors, success, and non-form requests pass through)
  • Full solution build clean; dotnet slopwatch analyze reports 0 issues;
    copyright headers verified

…ent discard

Bump the MCP SDK from 2.0.0 to 2.1.0.

SDK 2.1 sends a server/discover probe before the initialize handshake. The probe
reads a 400 response as an unsupported-protocol signal and falls back to the
initialize handshake. During interactive OAuth, that fallback calls the one-shot
authorization callback a second time. The second call fails as "authorization
already in progress" and hides the token endpoint invalid_client rejection. The
manager then keeps a dead dynamic client registration instead of discarding it.

Add OAuthClientRejectionHandler to the shared MCP HTTP pipeline. The handler
reads the invalid_client error for the authorization_code grant and throws
McpOAuthClientRejectedException before the SDK sees the 400. The SDK fallback
does not catch that type, so the true cause reaches the manager and the dead
identity is discarded. A refresh failure keeps the SDK graceful null path.

This supersedes Dependabot PRs netclaw-dev#1777 and netclaw-dev#1778.
@Aaronontheweb Aaronontheweb added the mcp Model context protocol server / client issues. label Aug 11, 2026
@Aaronontheweb
Aaronontheweb enabled auto-merge (squash) August 11, 2026 01:23

@Aaronontheweb Aaronontheweb left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Aaronontheweb
Aaronontheweb merged commit 5ee936e into netclaw-dev:dev Aug 11, 2026
15 checks passed
@Aaronontheweb Aaronontheweb mentioned this pull request Aug 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

mcp Model context protocol server / client issues.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant