Skip to content

feat(webmcp): agent tool group for the OAuth debugger#3337

Open
chelojimenez wants to merge 1 commit into
mainfrom
feat/oauth-debugger-agent-tools
Open

feat(webmcp): agent tool group for the OAuth debugger#3337
chelojimenez wants to merge 1 commit into
mainfrom
feat/oauth-debugger-agent-tools

Conversation

@chelojimenez

@chelojimenez chelojimenez commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Why

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 (OAuthAuthorizationModalwindow.open), which an agent cannot complete. The opt-out was actually blocking setup and observability, not consent.

What

New oauth-flow tool group (mount-scoped via useSurfaceAgentBridge):

Tool Annotations Behavior
ui_open_oauth_server_config idempotent, non-destructive Prefills the Configure-Server modal (name/URL/registration mode) via a new agentSeed prop. 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_flow destructive ⚠, open-world, not idempotent Exactly ONE protocol step per call, replicating the Continue button's gates (no 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 returns authorization_modal_opened honestly, with a popup-blocker caveat.
ui_reset_oauth_flow idempotent, non-destructive Clears local, re-runnable debug state (the UI Reset button has no confirm either).

Snapshot = 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 to id/label (their description/details carry URLs and challenge material). The denylist test feeds a fully poisoned state and asserts no sentinel or forbidden key survives serialization.

Supporting fixes

  • oauthFlowStateRef synced synchronously by all three writers (mirroring XAAFlowTab's deliberate pattern) — previously effect-synced, so post-step reads were one commit stale.
  • existingServerNames memoized: the modal's reseed-on-open effect depends on it via generateDefaultName, so a fresh Object.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).
  • The servers-hydrated force-close effect no longer closes an agent-opened modal with a pending seed.
  • OAUTH_ERROR_CODES/extractOauthErrorCode extracted from XAAFlowTab into lib/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:client clean (manifest flip type-forces the reason-field deletion).
  • 106 files / 1061 tests green across client/src/lib/webmcp, client/src/lib/__tests__ (coverage suites), the group unit + bridge tests, the snapshot denylist test, planAuthConfigModal truth table, the modal agentSeed tests, and a new OAuthFlowTab.agent.test.tsx driving all handler branches through the real command bus against the mounted tab.
  • Budget: global catalog (16) + largest group (5) = 21 ≤ 56 — unchanged by this 3-tool group.

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-flow from 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 and useSurfaceAgentBridge on OAuthFlowTab.

Config is prefill-only: openOauthServerConfig opens the Configure-Server modal with an agentSeed (name/URL/registration mode—no credential fields), resolves edit vs add via planAuthConfigModal, 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. buildOAuthFlowSnapshot strips tokens, PKCE material, HTTP bodies, and diagram details from agent-visible state.

Supporting fixes: synchronous oauthFlowStateRef updates for post-await reads; memoized existingServerNames so the modal doesn’t re-seed on parent re-renders; hydration no longer closes an agent-opened modal with a pending seed; shared extractOauthErrorCode extracted 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-flow tool 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 returns authorization_modal_opened. Marked destructive and open-world.
      • ui_reset_oauth_flow: clears local debug state to restart the flow.
    • Safe snapshot builder (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 to id/label.
    • Group wiring: registered via useSurfaceAgentBridge; new inspector commands openOauthServerConfig, advanceOauthFlow, resetOauthFlow.
  • Bug Fixes

    • Synchronous oauthFlowStateRef updates to avoid stale post-step reads.
    • Memoized existingServerNames to prevent modal reseed wiping typed values.
    • Skip force-closing an agent-opened modal during server hydration.
    • Shared oauth-error-code helper for allowlisted code extraction.

Written for commit b5d639c. Summary will update on new commits.

Review in cubic

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>
@dosubot dosubot Bot added size:XL This PR changes 500-999 lines, ignoring generated files. enhancement New feature or request labels Jul 20, 2026
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@cursor

cursor Bot commented Jul 20, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

@chelojimenez

Copy link
Copy Markdown
Contributor Author

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@github-actions

Copy link
Copy Markdown
Contributor

Internal preview

Preview URL: https://mcp-inspector-pr-3337.up.railway.app
Deployed commit: dd83562
PR head commit: b5d639c
Backend target: staging fallback.
Health: ✅ Convex reachable
Access is employee-only in non-production environments.

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The 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

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
mcpjam-inspector/client/src/components/OAuthFlowTab.tsx (1)

468-500: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider renaming the handler-local registrationStrategy to 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 like requestedRegistrationStrategy would 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

📥 Commits

Reviewing files that changed from the base of the PR and between 659a60c and b5d639c.

📒 Files selected for processing (17)
  • mcpjam-inspector/client/src/components/OAuthFlowTab.tsx
  • mcpjam-inspector/client/src/components/__tests__/OAuthFlowTab.agent.test.tsx
  • mcpjam-inspector/client/src/components/oauth/OAuthProfileModal.tsx
  • mcpjam-inspector/client/src/components/oauth/__tests__/OAuthProfileModal.test.tsx
  • mcpjam-inspector/client/src/components/xaa/XAAFlowTab.tsx
  • mcpjam-inspector/client/src/lib/__tests__/agent-bridge-modules.ts
  • mcpjam-inspector/client/src/lib/webmcp/__tests__/auth-config-command.test.ts
  • mcpjam-inspector/client/src/lib/webmcp/__tests__/oauth-flow-snapshot.test.ts
  • mcpjam-inspector/client/src/lib/webmcp/auth-config-command.ts
  • mcpjam-inspector/client/src/lib/webmcp/groups/__tests__/oauth-flow.bridge.test.tsx
  • mcpjam-inspector/client/src/lib/webmcp/groups/__tests__/oauth-flow.test.ts
  • mcpjam-inspector/client/src/lib/webmcp/groups/index.ts
  • mcpjam-inspector/client/src/lib/webmcp/groups/oauth-flow.ts
  • mcpjam-inspector/client/src/lib/webmcp/oauth-error-code.ts
  • mcpjam-inspector/client/src/lib/webmcp/oauth-flow-snapshot.ts
  • mcpjam-inspector/shared/app-surfaces.ts
  • mcpjam-inspector/shared/inspector-command.ts

@cubic-dev-ai cubic-dev-ai Bot 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.

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({

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.) .

View Feedback

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",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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>
Suggested change
"invalid_target",
"invalid_target",
"unsupported_response_type",
"server_error",
"temporarily_unavailable",

}
}
if (typeof error === "string") {
return OAUTH_ERROR_CODES.find((code) => error.includes(code));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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>

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

Labels

enhancement New feature or request size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant