feat(webmcp): agent tool group for the OAuth debugger#3337
Conversation
The oauth-flow surface was opted out of agent tools with "the agent must not drive authorization steps" — but consent there is structurally human-gated: the authorization step opens a sign-in popup on the third party's page, which an agent cannot complete. What the opt-out actually blocked was setup and observability. New oauth-flow group (mount-scoped via useSurfaceAgentBridge): - ui_open_oauth_server_config — prefills the Configure-Server modal (name/URL/registration mode) via a new agentSeed prop; credentials are typed by the human and cannot be passed (no fields in the schema). - ui_advance_oauth_flow — exactly one protocol step per call, mirroring the Continue button's gates; destructiveHint (a step can POST a persistent DCR registration to a third-party AS) so every advance shows the confirmation pill. At the authorization step it advances to generate the authorization URL, opens the human popup, and reports authorization_modal_opened honestly (with a popup-blocker caveat). - ui_reset_oauth_flow — clears local, re-runnable debug state. Snapshot is a pure denylist-tested builder (oauth-flow-snapshot.ts): state only — step progress, badges, status codes, token PRESENCE, and an allowlisted OAuth error code. Never tokens, PKCE material, auth codes, raw errors, or HTTP bodies; sequence steps are stripped to id/label. Supporting changes: - oauthFlowStateRef is now synced synchronously by all three writers (XAAFlowTab's proven pattern) so post-step reads aren't a commit stale. - existingServerNames memoized: the modal's reseed-on-open effect depends on it, so a fresh array per parent render wiped typed values. - The servers-hydrated force-close effect skips agent-opened modals. - OAUTH_ERROR_CODES/extractOauthErrorCode extracted from XAAFlowTab into lib/webmcp/oauth-error-code.ts (shared with the snapshot builder). - planAuthConfigModal (auth-config-command.ts): edit-vs-add resolution, rejecting a name that matches a different existing server. XAA stays opted out for now — flow-driving there mints real signed org-issuer assertions with no human step after the click; its prefill+snapshot-only integration lands as its own PR. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_d3708f51-e14d-4df1-8868-c5abf066addf) |
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
Internal previewPreview URL: https://mcp-inspector-pr-3337.up.railway.app |
WalkthroughThe OAuth debugger now supports grouped agent tools for opening server configuration, advancing one OAuth step, and resetting flow state. Inspector command types, validation, allowlisted error extraction, and redacted snapshots were added. OAuth flow state is synchronized between React state and a ref for bridge commands, while profile modals accept agent-provided seed values. The OAuth surface manifest and bridge registry now expose the new tools, with tests covering command behavior, modal seeding, bridge lifecycle, step execution, and snapshot redaction. Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install timed out. The project may have too many dependencies for the sandbox. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
mcpjam-inspector/client/src/components/OAuthFlowTab.tsx (1)
468-500: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider renaming the handler-local
registrationStrategyto avoid shadowing.The outer component-scoped
registrationStrategy(the selected profile's strategy) is eclipsed here by a same-named, same-typed local. It reads correctly today, but the collision is a quiet trap for a future edit that reaches for the profile value inside this handler. A name likerequestedRegistrationStrategywould make intent unmistakable.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@mcpjam-inspector/client/src/components/OAuthFlowTab.tsx` around lines 468 - 500, Rename the handler-local registrationStrategy variable in the payload registrationMode parsing and validation flow to requestedRegistrationStrategy, updating all references in that flow while leaving the outer component-scoped registrationStrategy unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@mcpjam-inspector/client/src/components/OAuthFlowTab.tsx`:
- Around line 468-500: Rename the handler-local registrationStrategy variable in
the payload registrationMode parsing and validation flow to
requestedRegistrationStrategy, updating all references in that flow while
leaving the outer component-scoped registrationStrategy unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: caf09d2d-0c6f-4187-b2bc-9d7deb5ead4b
📒 Files selected for processing (17)
mcpjam-inspector/client/src/components/OAuthFlowTab.tsxmcpjam-inspector/client/src/components/__tests__/OAuthFlowTab.agent.test.tsxmcpjam-inspector/client/src/components/oauth/OAuthProfileModal.tsxmcpjam-inspector/client/src/components/oauth/__tests__/OAuthProfileModal.test.tsxmcpjam-inspector/client/src/components/xaa/XAAFlowTab.tsxmcpjam-inspector/client/src/lib/__tests__/agent-bridge-modules.tsmcpjam-inspector/client/src/lib/webmcp/__tests__/auth-config-command.test.tsmcpjam-inspector/client/src/lib/webmcp/__tests__/oauth-flow-snapshot.test.tsmcpjam-inspector/client/src/lib/webmcp/auth-config-command.tsmcpjam-inspector/client/src/lib/webmcp/groups/__tests__/oauth-flow.bridge.test.tsxmcpjam-inspector/client/src/lib/webmcp/groups/__tests__/oauth-flow.test.tsmcpjam-inspector/client/src/lib/webmcp/groups/index.tsmcpjam-inspector/client/src/lib/webmcp/groups/oauth-flow.tsmcpjam-inspector/client/src/lib/webmcp/oauth-error-code.tsmcpjam-inspector/client/src/lib/webmcp/oauth-flow-snapshot.tsmcpjam-inspector/shared/app-surfaces.tsmcpjam-inspector/shared/inspector-command.ts
There was a problem hiding this comment.
3 issues found across 17 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="mcpjam-inspector/client/src/lib/webmcp/groups/oauth-flow.ts">
<violation number="1" location="mcpjam-inspector/client/src/lib/webmcp/groups/oauth-flow.ts:99">
P2: Retrying `ui_open_oauth_server_config` with the same seed while the modal is open resets the user's in-progress edits, so this tool is not safe to retry as advertised. The seed should only apply on a closed-to-open transition, or an equivalent request should be ignored while the form is already open.
(Based on your team's feedback about preserving dialog prefill while open.) [FEEDBACK_USED].</violation>
</file>
<file name="mcpjam-inspector/client/src/lib/webmcp/oauth-error-code.ts">
<violation number="1" location="mcpjam-inspector/client/src/lib/webmcp/oauth-error-code.ts:18">
P2: Authorization-server failures using standard RFC 6749 codes such as `unsupported_response_type`, `server_error`, or `temporarily_unavailable` are reduced to error presence only, so the OAuth debugger loses the actual failure code in snapshots and XAA outcomes. Including the authorization-endpoint codes in this shared allowlist preserves observability without exposing raw provider text.</violation>
<violation number="2" location="mcpjam-inspector/client/src/lib/webmcp/oauth-error-code.ts:41">
P2: Raw error text can be misclassified as an OAuth code because substring matching accepts embedded or negated text, which can make the debugger report the wrong failure and can affect XAA's `ras_rejected` decision. Matching a standalone code token in the fallback (while retaining exact structured-field matching) would avoid these false positives.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| ); | ||
| } | ||
| } | ||
| const response = await dispatchInspectorCommand({ |
There was a problem hiding this comment.
P2: Retrying ui_open_oauth_server_config with the same seed while the modal is open resets the user's in-progress edits, so this tool is not safe to retry as advertised. The seed should only apply on a closed-to-open transition, or an equivalent request should be ignored while the form is already open.
(Based on your team's feedback about preserving dialog prefill while open.) .
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At mcpjam-inspector/client/src/lib/webmcp/groups/oauth-flow.ts, line 99:
<comment>Retrying `ui_open_oauth_server_config` with the same seed while the modal is open resets the user's in-progress edits, so this tool is not safe to retry as advertised. The seed should only apply on a closed-to-open transition, or an equivalent request should be ignored while the form is already open.
(Based on your team's feedback about preserving dialog prefill while open.) .</comment>
<file context>
@@ -0,0 +1,170 @@
+ );
+ }
+ }
+ const response = await dispatchInspectorCommand({
+ type: "openOauthServerConfig",
+ payload: {
</file context>
| "unauthorized_client", | ||
| "unsupported_grant_type", | ||
| "invalid_scope", | ||
| "invalid_target", |
There was a problem hiding this comment.
P2: Authorization-server failures using standard RFC 6749 codes such as unsupported_response_type, server_error, or temporarily_unavailable are reduced to error presence only, so the OAuth debugger loses the actual failure code in snapshots and XAA outcomes. Including the authorization-endpoint codes in this shared allowlist preserves observability without exposing raw provider text.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At mcpjam-inspector/client/src/lib/webmcp/oauth-error-code.ts, line 18:
<comment>Authorization-server failures using standard RFC 6749 codes such as `unsupported_response_type`, `server_error`, or `temporarily_unavailable` are reduced to error presence only, so the OAuth debugger loses the actual failure code in snapshots and XAA outcomes. Including the authorization-endpoint codes in this shared allowlist preserves observability without exposing raw provider text.</comment>
<file context>
@@ -0,0 +1,44 @@
+ "unauthorized_client",
+ "unsupported_grant_type",
+ "invalid_scope",
+ "invalid_target",
+] as const;
+
</file context>
| "invalid_target", | |
| "invalid_target", | |
| "unsupported_response_type", | |
| "server_error", | |
| "temporarily_unavailable", |
| } | ||
| } | ||
| if (typeof error === "string") { | ||
| return OAUTH_ERROR_CODES.find((code) => error.includes(code)); |
There was a problem hiding this comment.
P2: Raw error text can be misclassified as an OAuth code because substring matching accepts embedded or negated text, which can make the debugger report the wrong failure and can affect XAA's ras_rejected decision. Matching a standalone code token in the fallback (while retaining exact structured-field matching) would avoid these false positives.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At mcpjam-inspector/client/src/lib/webmcp/oauth-error-code.ts, line 41:
<comment>Raw error text can be misclassified as an OAuth code because substring matching accepts embedded or negated text, which can make the debugger report the wrong failure and can affect XAA's `ras_rejected` decision. Matching a standalone code token in the fallback (while retaining exact structured-field matching) would avoid these false positives.</comment>
<file context>
@@ -0,0 +1,44 @@
+ }
+ }
+ if (typeof error === "string") {
+ return OAUTH_ERROR_CODES.find((code) => error.includes(code));
+ }
+ return undefined;
</file context>
Why
The
oauth-flowsurface was opted out of agent tools with "the agent must not drive authorization steps" — but consent there is structurally human-gated: the authorization step opens a sign-in popup on the third party's page (OAuthAuthorizationModal→window.open), which an agent cannot complete. The opt-out was actually blocking setup and observability, not consent.What
New
oauth-flowtool group (mount-scoped viauseSurfaceAgentBridge):ui_open_oauth_server_configagentSeedprop. No credential fields in the schema — the human types those. Rejects a name matching a different existing server (planAuthConfigModal, no fuzzy guessing).ui_advance_oauth_flowno profile→ invalid_request,in flight→ retryable execution_failed,complete→ invalid_request). Destructive because a step can POST a persistent DCR registration to a third-party AS — the pill gates every advance. At the authorization step it advances first (generating the authorization URL), opens the human popup, and returnsauthorization_modal_openedhonestly, with a popup-blocker caveat.ui_reset_oauth_flowSnapshot = pure, denylist-tested builder (
oauth-flow-snapshot.ts): step progress, badges, HTTP status codes, token presence booleans, and an allowlisted OAuth error code. The view type structurally omits tokens/PKCE/auth-code/raw-body fields; sequence-diagram steps are stripped toid/label(theirdescription/detailscarry URLs and challenge material). The denylist test feeds a fully poisoned state and asserts no sentinel or forbidden key survives serialization.Supporting fixes
oauthFlowStateRefsynced synchronously by all three writers (mirroring XAAFlowTab's deliberate pattern) — previously effect-synced, so post-step reads were one commit stale.existingServerNamesmemoized: the modal's reseed-on-open effect depends on it viagenerateDefaultName, so a freshObject.keys()per parent render re-seeded the open modal and wiped typed values (pre-existing bug, hotter now that agents send humans into this modal).OAUTH_ERROR_CODES/extractOauthErrorCodeextracted from XAAFlowTab intolib/webmcp/oauth-error-code.ts(one allowlist, shared with the snapshot builder).Deliberately NOT included
XAA stays opted out: once started, its flow mints real signed org-issuer identity assertions with no human step after the click (the mock-IdP SSO step is a server-side mint, not a login). A prefill+snapshot-only XAA integration is planned as its own follow-up PR, which will also rewrite that surface's reason string.
Testing
npm run typecheck:clientclean (manifest flip type-forces the reason-field deletion).client/src/lib/webmcp,client/src/lib/__tests__(coverage suites), the group unit + bridge tests, the snapshot denylist test,planAuthConfigModaltruth table, the modalagentSeedtests, and a newOAuthFlowTab.agent.test.tsxdriving all handler branches through the real command bus against the mounted tab.Known accepted race (documented in-code): the 500 ms post-callback exchange timer can auto-advance concurrently with an agent advance — identical exposure to the human Continue button.
🤖 Generated with Claude Code
Note
Medium Risk
Agents can drive real third-party OAuth HTTP and persistent DCR via approval-gated advance, but consent stays human-only and snapshots aggressively redact secrets; misconfiguration or concurrent advance races match existing UI exposure.
Overview
Enables agent assistance on the OAuth debugger by flipping
oauth-flowfrom opted-out to a mount-scoped tool group (ui_open_oauth_server_config,ui_advance_oauth_flow,ui_reset_oauth_flow) wired through new inspector commands anduseSurfaceAgentBridgeonOAuthFlowTab.Config is prefill-only:
openOauthServerConfigopens the Configure-Server modal with anagentSeed(name/URL/registration mode—no credential fields), resolves edit vs add viaplanAuthConfigModal, and rejects naming a different existing server or an unsupported registration mode for the protocol version.Advance mirrors Continue: one protocol step per call, approval-gated as destructive/open-world; at PKCE/authorization it generates the URL, opens the human popup, and returns
authorization_modal_opened. Results and snapshots expose only allowlisted OAuth error codes, not raw errors.buildOAuthFlowSnapshotstrips tokens, PKCE material, HTTP bodies, and diagram details from agent-visible state.Supporting fixes: synchronous
oauthFlowStateRefupdates for post-await reads; memoizedexistingServerNamesso the modal doesn’t re-seed on parent re-renders; hydration no longer closes an agent-opened modal with a pending seed; sharedextractOauthErrorCodeextracted from XAA.Reviewed by Cursor Bugbot for commit b5d639c. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by cubic
Adds an agent tool group for the OAuth debugger to enable setup and one-step advancement while keeping consent human. Also adds a safe, denylist-tested snapshot that avoids leaking secrets.
New Features
oauth-flowtool group (mount-scoped):ui_open_oauth_server_config: opens the Configure-Server modal with optional prefill (name/URL/registration mode). No credential fields; the user types those.ui_advance_oauth_flow: advances exactly one protocol step per call. At authorization, opens the human sign-in popup and returnsauthorization_modal_opened. Marked destructive and open-world.ui_reset_oauth_flow: clears local debug state to restart the flow.oauth-flow-snapshot.ts): exposes step progress, HTTP status, token presence, and an allowlisted OAuth error code. Never emits tokens, PKCE, auth codes, raw errors, or HTTP bodies. Sequence steps stripped toid/label.useSurfaceAgentBridge; new inspector commandsopenOauthServerConfig,advanceOauthFlow,resetOauthFlow.Bug Fixes
oauthFlowStateRefupdates to avoid stale post-step reads.existingServerNamesto prevent modal reseed wiping typed values.oauth-error-codehelper for allowlisted code extraction.Written for commit b5d639c. Summary will update on new commits.