Skip to content

Surface MCP OAuth reauthorization during catalog discovery - #1818

Merged
RhysSullivan merged 8 commits into
UsefulSoftwareCo:mainfrom
ramarivera:fix/mcp-oauth-refresh-reauth
Aug 29, 2026
Merged

Surface MCP OAuth reauthorization during catalog discovery#1818
RhysSullivan merged 8 commits into
UsefulSoftwareCo:mainfrom
ramarivera:fix/mcp-oauth-refresh-reauth

Conversation

@ramarivera

Copy link
Copy Markdown
Contributor

Reported issue

An OAuth-backed remote MCP can reject Executor's stored bearer before the locally stored expiry. During catalog discovery, Executor handed that 401 to the MCP SDK's interactive OAuth fallback. A background refresh couldn't finish authorization, but the SDK first performed OAuth metadata discovery and Dynamic Client Registration. Executor then preserved the previous catalog under a generic degraded health result, leaving clients with zero or stale tools and no reliable reconnect signal.

Fix

OAuth-backed MCP transports now stop at the authenticated HTTP boundary when the resolved bearer gets a 401. The connector carries a structured reauthorization error through discovery instead of allowing the SDK to start an interactive fallback or register a disposable client.

Incomplete catalog results can now carry an actionable health result. Core preserves the last authoritative catalog while persisting the MCP connection as expired with a reconnect-required detail. Generic incomplete discovery remains degraded, and API-key or unauthenticated 401 behavior is unchanged.

The regression coverage exercises the real rejected-bearer path, asserts that no registration request occurs, and proves the expired verdict survives connections.refresh. The MCP plugin suite, core SDK suite, lint, formatting, and affected-package type checks pass locally.

Fixes #1816

🤖 This content was generated with AI assistance using GPT-5.6 Sol via Hermes Agent.

Copilot AI lite review requested due to automatic review settings August 28, 2026 08:51

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR improves the MCP plugin + core tool-sync path so that when an OAuth-backed MCP server rejects a previously stored bearer with 401, catalog discovery stops at the authenticated HTTP boundary and surfaces a structured “reauthorization required” outcome. Core can now preserve the last authoritative tool catalog while persisting an actionable expired health verdict to drive reconnect UX, and it avoids triggering the MCP SDK’s OAuth metadata discovery / Dynamic Client Registration during background refresh.

Changes:

  • Add a staticOAuthBearer transport flag to treat 401 as McpOAuthReauthorizationRequired (preventing SDK OAuth fallback + DCR during discovery).
  • Thread a structured reauthorizationRequired signal through discovery errors and map it to health: { status: "expired" } in resolveTools.
  • Extend core’s ResolveToolsResult to optionally carry health, and persist that health during tool sync; add regression tests and a changeset.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/plugins/mcp/src/sdk/plugin.ts Marks OAuth-backed remote transports as staticOAuthBearer and surfaces reauth-required discovery as expired health in resolveTools.
packages/plugins/mcp/src/sdk/plugin.test.ts Adds regression coverage ensuring rejected OAuth bearer surfaces expired health and avoids DCR.
packages/plugins/mcp/src/sdk/errors.ts Extends McpToolDiscoveryError with an optional reauthorizationRequired structural signal.
packages/plugins/mcp/src/sdk/discover.ts Maps McpOAuthReauthorizationRequired into McpToolDiscoveryError.reauthorizationRequired.
packages/plugins/mcp/src/sdk/connection.ts Implements staticOAuthBearer handling: throw typed reauth error on 401, and improve nested-cause detection.
packages/core/sdk/src/plugin.ts Adds optional health?: HealthCheckResult to ResolveToolsResult so plugins can surface actionable outcomes during discovery.
packages/core/sdk/src/executor.ts Persists result.health (when provided) while preserving prior catalogs on incomplete discovery.
packages/core/sdk/src/executor.test.ts Adds a regression test verifying actionable health survives connections.refresh.
.changeset/mcp-oauth-refresh-reauthorization.md Documents the patch-level behavior change for SDK + MCP plugin packages.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

detail: expect.stringContaining("reauthorization"),
},
});
expect(ledger.requests.filter((url) => new URL(url).pathname === "/register")).toEqual([]);
Comment on lines +45 to +48
/** The MCP OAuth provider reached the interactive authorization boundary.
* Catalog callers use this structural signal to request reconnect without
* parsing or exposing an upstream error message. */
reauthorizationRequired: Schema.optional(Schema.Boolean),
@RhysSullivan
RhysSullivan merged commit 06bf742 into UsefulSoftwareCo:main Aug 29, 2026
40 checks passed
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.

MCP OAuth tool refresh hides reauthorization and re-registers DCR client

3 participants