feat: add MCP auth required error handling with inline banner and retry support - #3856
Conversation
📝 WalkthroughWalkthroughAdds MCP authentication error handling end-to-end: a new ChangesMCP Authentication Error Handling
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
This stack of pull requests is managed by Graphite. Learn more about stacking. |
Confidence Score: 4/5The core single-tool auth-error flow works correctly; the Execute All path is broken for auth errors and could leave users with no feedback or a degraded experience. The new auth-error handling works well for the primary single-tool execution path, but the Execute All code path in context.tsx does not propagate MCPAuthRequiredError back to the component: a single-tool Execute All produces an unhandled rejection with no visible feedback, and the multi-tool branch converts the error to a generic toast string, discarding the authorize URL and preventing the inline banner from ever appearing. ui/components/prompts/context.tsx — the handleExecuteAllToolCalls function needs to surface MCPAuthRequiredError instances per-tool rather than flatten them. Important Files Changed
|
8f9b29f to
c1d74b2
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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.
Inline comments:
In `@ui/components/prompts/components/messagesView/toolCallView.tsx`:
- Around line 466-485: Add stable data-testid attributes to the new interactive
buttons so E2E tests can target them: add a data-testid to the Authenticate
button (the Button that opens authErrors[tc.id].authorizeUrl) and to the Retry
button (the Button that calls handleRetry(tc) and uses RefreshCw), using clear,
unique values that include the tool call id (e.g.,
data-testid={`auth-btn-${tc.id}`} and data-testid={`retry-btn-${tc.id}`}).
- Line 469: The onClick handler opening external auth uses
window.open(authErrors[tc.id].authorizeUrl, "_blank") which is vulnerable to
tabnabbing and lacks test hooks; update the onClick in toolCallView.tsx to
validate/parse the authorize URL with the URL constructor inside a try/catch and
bail if invalid, then open it with window.open(parsedUrl.toString(), "_blank",
"noopener,noreferrer") (or render a safe <a> with target="_blank" rel="noopener
noreferrer") to mitigate opener risks, and add stable data-testid attributes
(e.g., data-testid={`authenticate-${tc.id}`} and data-testid={`retry-${tc.id}`})
to the inline “Authenticate” and “Retry” buttons so E2E tests can select them
reliably.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 7418c358-fa50-497a-aaed-7cee58a40a4c
📒 Files selected for processing (3)
ui/components/prompts/components/messagesView/toolCallView.tsxui/components/prompts/context.tsxui/components/prompts/utils/executor.ts
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
ui/components/prompts/components/messagesView/toolCallView.tsx (1)
177-181:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftBatch execution currently drops per-tool auth-required state.
setAuthErrors({})clears existing auth banners, but this flow never repopulatesauthErrorsfor auth failures returned fromonExecuteAllToolCalls, so users lose inline Authenticate/Retry for failed tools after Execute all. Please return structured failures (includingMCPAuthRequiredErrorper toolCallId) from the batch path and map them intoauthErrors.🤖 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 `@ui/components/prompts/components/messagesView/toolCallView.tsx` around lines 177 - 181, The batch Execute All flow currently wipes auth banners with setAuthErrors({}) and never repopulates them; change the flow around setAuthErrors / onExecuteAllToolCalls so that onExecuteAllToolCalls returns structured per-tool failures (including MCPAuthRequiredError keyed by toolCallId) and map those failures into authErrors after the call instead of clearing blindly—specifically, keep setIsExecutingAll(true), call const partialResults = await onExecuteAllToolCalls(latestCalls), then build a newAuthErrors object by iterating partialResults for each toolCallId that failed with MCPAuthRequiredError (or other auth failures) and call setAuthErrors(newAuthErrors) (merging with existing authErrors if needed) so each failed tool re-displays its Authenticate/Retry inline.
🤖 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.
Outside diff comments:
In `@ui/components/prompts/components/messagesView/toolCallView.tsx`:
- Around line 177-181: The batch Execute All flow currently wipes auth banners
with setAuthErrors({}) and never repopulates them; change the flow around
setAuthErrors / onExecuteAllToolCalls so that onExecuteAllToolCalls returns
structured per-tool failures (including MCPAuthRequiredError keyed by
toolCallId) and map those failures into authErrors after the call instead of
clearing blindly—specifically, keep setIsExecutingAll(true), call const
partialResults = await onExecuteAllToolCalls(latestCalls), then build a
newAuthErrors object by iterating partialResults for each toolCallId that failed
with MCPAuthRequiredError (or other auth failures) and call
setAuthErrors(newAuthErrors) (merging with existing authErrors if needed) so
each failed tool re-displays its Authenticate/Retry inline.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: d3295aad-e272-457f-a14c-e086baa3c159
📒 Files selected for processing (3)
ui/components/prompts/components/messagesView/toolCallView.tsxui/components/prompts/context.tsxui/components/prompts/utils/executor.ts
Merge activity
|
…ry support (#3856) ## Summary When a tool call fails because an MCP server requires OAuth authentication, the error was previously swallowed and shown only as a generic toast. This PR surfaces MCP authentication errors directly in the tool call UI, allowing users to authenticate and retry without losing context. ## Changes - Introduced a `MCPAuthRequiredError` class in `executor.ts` that captures the auth kind (`oauth` or `headers`), the MCP client name, and the authorization URL parsed from the `extra_fields.mcp_auth_required` field in the error response. - Updated `executeToolCall` to detect and throw `MCPAuthRequiredError` when the API response indicates authentication is required. - Updated the prompt context's tool call handler to re-throw `MCPAuthRequiredError` instead of catching it as a generic error toast. - Added `authErrors` state to `ToolCallMessageView` to track per-tool-call authentication errors. - Rendered an inline amber banner within the tool call card when an auth error is present, showing the MCP client name, a prompt to authenticate, an "Authenticate" button that opens the authorization URL in a new tab, and a "Retry" button to re-execute the tool call after authenticating. - Hid the normal action bar when an auth error is active to avoid conflicting UI states. ## Type of change - [ ] Bug fix - [x] Feature - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [ ] Core (Go) - [ ] Transports (HTTP) - [ ] Providers/Integrations - [ ] Plugins - [x] UI (React) - [ ] Docs ## How to test 1. Configure a prompt with an MCP tool that requires OAuth authentication. 2. Execute the tool call without being authenticated. 3. Verify that an inline amber banner appears on the tool call card with the MCP client name and an "Authenticate" button. 4. Click "Authenticate" and confirm it opens the authorization URL in a new tab. 5. After authenticating, click "Retry" and confirm the tool call executes successfully. ```sh cd ui pnpm i || npm i pnpm build || npm run build ``` #### Screenshots/Recordings Before  #### After  ## Breaking changes - [ ] Yes - [x] No ## Related issues ## Security considerations The authorization URL is opened via `window.open(..., "_blank")`, which is consistent with standard OAuth redirect flows. No tokens or credentials are stored in component state; only the error metadata (client name and URL) is retained until the error is cleared on retry. ## Checklist - [ ] I read `docs/contributing/README.md` and followed the guidelines - [ ] I added/updated tests where appropriate - [ ] I updated documentation where needed - [ ] I verified builds succeed (Go and UI) - [ ] I verified the CI pipeline passes locally if applicable <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Tool calls now show inline "authentication required" banners with optional authorize links and a Retry button. - Users can follow authorize URLs and retry individual tool executions. - Improved execution flow: bulk execution clears prior auth states before running. - **Bug Fixes** - Per-tool action controls are suppressed while a tool awaits authentication to prevent conflicting actions. - Authentication failures are surfaced distinctly instead of a generic error. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/maximhq/bifrost/pull/3856?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary This PR releases **core v1.5.14**, **framework v1.3.14**, **transports v1.5.6**, and bumps all dependent plugins to their respective `.14` patch versions. It delivers a broad set of new capabilities across MCP authentication, key rotation, OTel metrics, Bedrock/Anthropic compatibility, and UI improvements, alongside a number of targeted bug fixes and refactors. ## Changes - **Direct API Key Header** — Providers can now receive an API key passed directly via a request header (#3817) - **MCP Per-User Auth** — Introduced `MCPCredentialStore` abstraction, per-user MCP credential reconciliation, and a new per-user header auth type with lazy-auth submission flow (#3656, #3702, #3703, #3704, #3705) - **MCP TLS Configuration** — Added configurable TLS (`insecureSkipVerify`, `caCertPem`) for HTTP/SSE MCP client connections (#3779, #3783) - **MCP Sessions Management** — Filter, search, and pagination on the MCP sessions list API and table, plus a `can_reauth` identity gate (#3823, #3824, #3825) - **Key Rotation** — Keys now rotate on 401/402/403 responses; returns `502 upstream_credentials_exhausted` when all keys are permanently exhausted. Added `triggered_rotation` to `KeyAttemptRecord` and tightened `bifrost_key_rotation_events_total` semantics (#3430, #3491) - **OTel Metrics** — Added OTel spec-compatible metrics (backward compatible) with provider cache and semantic cache attributes in metrics export (#3865, #3816) - **Opus 4.8 Support** — System message handling and general compatibility for Opus 4.8 (#3868, #3878) - **Dimension Rankings** — New `GetDimensionRankings` API and dashboard tabs for team, customer, BU, and user rankings (#3766) - **Model Pricing Attributes** — `additional_attributes` field on model pricing rows with management API and UI editor (#3829) - **Prompt Cache Retention** — Added prompt cache retention parameter on responses requests (#3810) - **Tool Call Execution UI** — Inline tool-call execution, stop streaming, bulk execute/submit, and a redesigned tool-call UI (#3837, #3843) - **Sheet Navigation** — Prev/next keyboard navigation and URL state across virtual key, MCP client, and routing rule sheets (#3739, #3740, #3744, #3745) - **Bedrock Tool Name Truncation** — Truncate Bedrock function/tool names to the provider length limit - **Bedrock Guardrails** — Set guardrail config in Bedrock requests built from responses (#3862) - **Anthropic Tool Use** — Default `tool_use` input to `{}` when arguments are absent (#3880) - **Responses Streaming** — Fixed responses stream events (#3838) - **Compat Flow** — Fixed missing parameter parsing on the compat flow (#3881) - **Passthrough API Version** — Set a default API version in passthrough requests as a fallback (#3853) - **Virtual Key Updates** — Avoid overriding optional fields during virtual key update (#3855) - **User-Mode Flows** — Gate user-mode flows on caller `user_id`, skip temp token mint, and unify flow/credential kind filtering for pending flows (#3841, #3859) - **Partial Tool Calls** — Handle partial tool call execution failures and return successful results (#3849) - **URL Query Escaping** — Support escaped characters in URL query parameters (#3826) - **MCP Auth Errors** — Inline banner and retry support for MCP auth-required errors (#3856) - **Renamed Resolvers** — `staticHeadersResolver`/`serverOAuthResolver` renamed to `sharedHeadersResolver`/`sharedOAuthResolver` (#3840) - **Starlark Nested Tool Calls** — Exposed `RunWithPluginPipeline` on `ClientManager` and routed Starlark nested tool calls through the canonical plugin gate (#3794) - **Deferred-Fill OAuth Removed** — Removed deferred-fill user-mode OAuth flow support (#3839) - **Go 1.26.3** — Upgraded toolchain to Go 1.26.3 (#3782) ## Type of change - [x] Bug fix - [x] Feature - [x] Refactor - [ ] Documentation - [x] Chore/CI ## Affected areas - [x] Core (Go) - [x] Transports (HTTP) - [x] Providers/Integrations - [x] Plugins - [x] UI (React) - [ ] Docs ## How to test ```sh # Core/Transports go version # should report go1.26.3 go test ./... # UI cd ui pnpm i || npm i pnpm test || npm test pnpm build || npm run build ``` - Validate MCP per-user auth by configuring a per-user header auth type and confirming credentials are stored and reconciled on virtual key and MCP client changes. - Validate key rotation by triggering a 401/402/403 from an upstream provider and confirming rotation occurs; exhaust all keys and confirm a `502 upstream_credentials_exhausted` is returned. - Validate OTel metrics output includes `provider_cache` and `semantic_cache` attributes. - Validate Bedrock requests with tool names exceeding the provider limit are truncated correctly. - Validate Opus 4.8 system message handling by sending a request with a system message to an Opus 4.8 endpoint. ## Breaking changes - [x] Yes - [ ] No The deferred-fill user-mode OAuth flow has been removed (#3839). Any integrations relying on that flow must migrate to the new per-user credential store approach. The `staticHeadersResolver` and `serverOAuthResolver` identifiers have been renamed to `sharedHeadersResolver` and `sharedOAuthResolver` respectively (#3840); any direct references must be updated. ## Related issues #3817, #3656, #3702, #3703, #3704, #3705, #3779, #3783, #3823, #3824, #3825, #3430, #3491, #3865, #3816, #3868, #3878, #3766, #3829, #3810, #3837, #3843, #3739, #3740, #3744, #3745, #3862, #3880, #3838, #3881, #3853, #3855, #3841, #3859, #3849, #3826, #3856, #3840, #3794, #3839, #3782, #3724, #3814, #3836, #3869, #3886 ## Security considerations - MCP per-user credentials are stored via the new `MCPCredentialStore` abstraction; ensure the backing store is appropriately access-controlled and that credential values are encrypted at rest. - The direct API key header feature passes provider secrets via HTTP headers; ensure TLS is enforced on all ingress paths and that headers are not logged in plaintext. - User-mode flows are now gated on `caller user_id` and temp token minting is skipped where appropriate, reducing the surface for privilege escalation. - TLS configuration for MCP HTTP/SSE connections supports `insecureSkipVerify`; this should only be enabled in controlled environments. ## Checklist - [x] I read `docs/contributing/README.md` and followed the guidelines - [x] I added/updated tests where appropriate - [x] I updated documentation where needed - [x] I verified builds succeed (Go and UI) - [x] I verified the CI pipeline passes locally if applicable
## ✨ Features - **Direct API Key Header** - Pass a provider API key directly via request header (#3817) - **MCP Per-User Authentication** - New per-user header auth type with credential storage and lazy-auth submission flow (#3703, #3704, #3705) - **MCP TLS Configuration** - Configurable TLS (insecureSkipVerify, caCertPem) for HTTP/SSE MCP client connections (#3779, #3783) - **MCP Sessions Management** - Filter, search, and pagination on the MCP sessions list API and table, plus a can_reauth identity gate (#3823, #3824, #3825) - **Tool Call Execution UI** - Inline tool-call execution, stop streaming, bulk execute/submit, and a redesigned tool-call UI (#3837, #3843) - **Dimension Rankings Dashboard** - New dashboard tabs for team, customer, BU, and user rankings, backed by a GetDimensionRankings API (#3766) - **Model Pricing Attributes** - additional_attributes on model pricing rows with management API and UI editor (#3829) - **Prompt Cache Retention** - Prompt cache retention parameter on responses requests (#3810) - **Opus 4.8 Support** - System message handling and compatibility for Opus 4.8 (#3878, #3868) - **Key Rotation** - Rotate keys on 401/402/403 and return 502 upstream_credentials_exhausted when all keys are permanently dead (#3491) - **OTel Metrics** - OTel spec compatible metrics plus provider and semantic cache attributes in metrics export (#3865, #3816) - **Sheet Navigation** - Prev/next keyboard navigation and URL state across virtual key, MCP client, and routing rule sheets (#3739, #3740, #3744, #3745) - **Go 1.26.3** - Upgraded toolchain to Go 1.26.3 (#3782) ## 🐞 Fixed - **Bedrock Tool Names** - Truncate Bedrock function/tool names to the provider length limit - **Bedrock Guardrails** - Set guardrail config in Bedrock request built from responses (#3862) - **Anthropic Tool Use** - Default Anthropic tool_use input to {} when arguments are absent (#3880) - **Responses Streaming** - Fixed responses stream events (#3838) - **Compat Flow** - Fixed missing parameter parsing on the compat flow (#3881) - **Passthrough API Version** - Set a default API version in passthrough requests as a fallback (#3853) - **Virtual Key Updates** - Avoid overriding optional fields during virtual key update (#3855) - **User-Mode Flows** - Gate user-mode flows on caller user_id, skip temp token mint, and unify flow/credential kind filtering for pending flows (#3841, #3859) - **Partial Tool Calls** - Handle partial tool call execution failures and return successful results (#3849) - **URL Query Escaping** - Support escaped characters in URL query parameters (#3826) - **MCP Auth Errors** - Inline banner and retry support for MCP auth-required errors (#3856) - **JSON Editor Height** - Cap JSON editor max height at 400px in message views (#3842)

Summary
When a tool call fails because an MCP server requires OAuth authentication, the error was previously swallowed and shown only as a generic toast. This PR surfaces MCP authentication errors directly in the tool call UI, allowing users to authenticate and retry without losing context.
Changes
MCPAuthRequiredErrorclass inexecutor.tsthat captures the auth kind (oauthorheaders), the MCP client name, and the authorization URL parsed from theextra_fields.mcp_auth_requiredfield in the error response.executeToolCallto detect and throwMCPAuthRequiredErrorwhen the API response indicates authentication is required.MCPAuthRequiredErrorinstead of catching it as a generic error toast.authErrorsstate toToolCallMessageViewto track per-tool-call authentication errors.Type of change
Affected areas
How to test
Screenshots/Recordings
Before
After
Breaking changes
Related issues
Security considerations
The authorization URL is opened via
window.open(..., "_blank"), which is consistent with standard OAuth redirect flows. No tokens or credentials are stored in component state; only the error metadata (client name and URL) is retained until the error is cleared on retry.Checklist
docs/contributing/README.mdand followed the guidelinesSummary by CodeRabbit
New Features
Bug Fixes