Skip to content

fix: handle per-user OAuth re-auth, refresh token expiry, and reconnect UX#3211

Merged
akshaydeo merged 2 commits intomainfrom
05-04-feat_add_refresh_token_expiry_reauth_flow_for_per_user_mcp_oauth
May 6, 2026
Merged

fix: handle per-user OAuth re-auth, refresh token expiry, and reconnect UX#3211
akshaydeo merged 2 commits intomainfrom
05-04-feat_add_refresh_token_expiry_reauth_flow_for_per_user_mcp_oauth

Conversation

@Pratham-Mishra04
Copy link
Copy Markdown
Collaborator

Summary

This PR improves the robustness and observability of Bifrost's per-user OAuth flow. It fixes a re-authentication bug where a user whose refresh token was permanently rejected (or whose token row was purged) could not re-initiate the OAuth flow due to a unique constraint violation on the session token. It also ensures that ErrOAuth2TokenExpired is treated as a re-auth signal alongside ErrOAuth2TokenNotFound, and adds a self-contained OAuth demo server for end-to-end testing of the full per-user OAuth path including token refresh.

Changes

  • Re-auth session upsert: InitiateUserOAuthFlow now checks for an existing session row by session token before inserting. If one exists, it updates the row in place (rotating state, PKCE verifier, and expiry) rather than inserting a duplicate, avoiding unique constraint failures on re-authentication.
  • Permanent refresh failure handling: RefreshUserAccessToken now detects permanent upstream rejections (HTTP 401, or RFC 6749 invalid_grant/unauthorized_client 400s), purges the dead token row, marks the session as needs_reauth, and returns ErrOAuth2TokenExpired so the caller surfaces an inline auth URL.
  • ErrOAuth2TokenExpired as re-auth sentinel: ResolvePerUserOAuthToken now treats both ErrOAuth2TokenNotFound and ErrOAuth2TokenExpired as signals to fall through to the re-auth branch, rather than surfacing them as errors.
  • ErrMCPReconnectNotApplicable sentinel: A new typed error distinguishes "reconnect is not meaningful for this client type" from a generic failure. The HTTP handler returns 400 (not 500) when this error is encountered.
  • Improved reconnect error message: The error returned for per-user OAuth reconnect attempts now wraps ErrMCPReconnectNotApplicable with a descriptive message explaining why no shared connection exists.
  • OAuth demo server: A new self-contained Go server (examples/mcps/oauth-demo-server) implements RFC 9728, RFC 8414, RFC 7591, and the authorization code + PKCE + refresh token flow with a deliberately short (30s) access token TTL to make the refresh path easy to observe.
  • UI — Auth column in MCP clients table: An "Auth" column is added to the MCP clients table displaying the auth type (None, Headers, OAuth, Per-user OAuth).
  • UI — Reconnect button tooltip: The reconnect button for per-user OAuth clients now uses a Radix Tooltip instead of a native title attribute, so the explanation is visible even though the button is disabled.
  • UI — mcp_external_client_url warning: A warning is added to the config UI explaining that changing this URL after clients have completed OAuth will break them due to the locked redirect_uri.
  • Docs — redirect_uri lock warnings: Warnings are added to gateway-url.mdx and oauth.mdx explaining that the redirect_uri is locked at Dynamic Client Registration time and how to recover if the URL changes.
  • Log level: RevokeToken success log is demoted from Info to Debug to reduce noise.

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Chore/CI

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (React)
  • Docs

How to test

End-to-end per-user OAuth with refresh:

# Start the demo OAuth server
cd examples/mcps/oauth-demo-server
go run main.go
# Listening on http://localhost:3003

# Add an MCP client in Bifrost:
# {
#   "name": "oauth_demo",
#   "connection_type": "http",
#   "connection_string": "http://localhost:3003/mcp",
#   "auth_type": "oauth2",
#   "is_per_user": true,
#   "tools_to_execute": ["*"]
# }

# Call a tool — Bifrost should redirect to the consent page.
# Complete login, then call again — should succeed.
# Wait >30s, call again — watch [token] log lines on the demo server for a refresh_token grant.
# Wait >2min from initial auth — refresh should be rejected; Bifrost should surface a new auth URL.

Re-auth after token purge:

  1. Complete OAuth for a user.
  2. Manually delete the token row from the DB (or wait for the refresh token to expire and trigger a permanent rejection).
  3. Call a tool again — Bifrost should return an inline auth URL rather than an error.

Reconnect API for per-user OAuth:

curl -X POST /api/mcp/clients/{id}/reconnect
# Expect: 400 Bad Request with descriptive message, not 500

UI:

cd ui
pnpm i
pnpm build
  • Verify the MCP clients table shows an "Auth" column.
  • Verify hovering the disabled reconnect button on a per-user OAuth client shows the tooltip.
  • Verify the mcp_external_client_url field shows the redirect URI warning.

Breaking changes

  • Yes
  • No

The reconnect endpoint now returns 400 instead of 500 for per-user OAuth clients. Callers that were matching on 500 for this case should be updated to handle 400.

Security considerations

  • PKCE verifier and CSRF state are always rotated on re-authentication, even when updating an existing session row — no session fixation risk.
  • Permanent refresh token rejection purges the token row immediately, preventing stale credentials from being retried.
  • The demo server is an in-memory fixture with no persistence and is not intended for production use.

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

@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@Pratham-Mishra04 Pratham-Mishra04 changed the title Y fix: handle per-user OAuth re-auth, refresh token expiry, and reconnect UX May 4, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 4, 2026

Confidence Score: 4/5

Core OAuth fixes are correct and safe to merge; the demo server has open issues from prior review rounds.

The three primary bug fixes are well-implemented: the re-auth upsert now correctly surfaces DB errors rather than silently swallowing them, the ErrOAuth2TokenExpired sentinel propagates cleanly through the errors.Is chain in ResolvePerUserOAuthToken, and the typed ErrMCPReconnectNotApplicable error lets the HTTP handler return 400 without relying on error string matching. The demo server carries two unresolved findings from earlier review rounds: the startup-log config block prints an invalid Bifrost auth type, and handleRefreshTokenGrant has a TOCTOU window that allows double-issuance under concurrent load.

examples/mcps/oauth-demo-server/main.go — two findings flagged in prior review rounds remain unresolved (invalid startup-log config block and refresh-token TOCTOU race).

Important Files Changed

Filename Overview
framework/oauth2/main.go Core OAuth fix: InitiateUserOAuthFlow now properly handles the re-auth upsert with error surfacing; RefreshUserAccessToken purges dead tokens on permanent rejection and returns ErrOAuth2TokenExpired.
core/mcp/utils/utils.go Correct fix: ResolvePerUserOAuthToken now treats ErrOAuth2TokenExpired as a re-auth sentinel alongside ErrOAuth2TokenNotFound, allowing the full re-auth path to trigger on permanent refresh failure.
core/schemas/mcp.go Adds ErrMCPReconnectNotApplicable sentinel for distinguishing per-user OAuth reconnect from generic failures; clean addition.
transports/bifrost-http/handlers/mcp.go Reconnect handler correctly returns 400 (not 500) when ErrMCPReconnectNotApplicable is unwrapped from the error chain.
transports/bifrost-http/lib/ctx.go New ValidateBaseURL helper correctly validates scheme + host presence; empty strings pass through (nil-clears the override on failure in sanitizeMCPExternalOAuthURLs).
transports/bifrost-http/handlers/config.go URL validation moved to the top of updateConfig before any live mutations, preventing partial-update state on invalid URLs.
examples/mcps/oauth-demo-server/main.go Functional demo server implementing RFC 9728 / RFC 8414 / RFC 7591 / PKCE. Has a known TOCTOU race in handleRefreshTokenGrant and the startup-log config block prints auth_type: "oauth2" which is not a valid Bifrost auth type.
ui/app/workspace/mcp-registry/views/mcpClientsTable.tsx Auth column and Radix Tooltip for disabled reconnect button correctly handle all four auth types and disabled states.
ui/app/workspace/config/views/mcpView.tsx Adds clear redirect_uri lock warning to the mcp_external_client_url field in the config UI.
transports/bifrost-http/lib/config.go New sanitizeMCPExternalOAuthURLs helper applied to both file-config paths, preventing invalid URLs from reaching OAuth URL generation.

Reviews (7): Last reviewed commit: "feat: add refresh token expiry reauth fl..." | Re-trigger Greptile

Comment thread examples/mcps/oauth-demo-server/main.go
Comment thread framework/oauth2/main.go Outdated
Comment thread examples/mcps/oauth-demo-server/main.go
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 4, 2026

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • MCP clients table shows each client's authentication type
    • Added an example OAuth 2.1 authorization + MCP demo server
  • Bug Fixes

    • Per-user OAuth token expiry now triggers re-authentication flow
    • Reconnect attempts for per-user-OAuth clients return a clear 400 error
    • MCP public/client URL fields validated when saving/loading config
    • Token refresh/rotation and per-user session handling improved to avoid duplicate sessions
  • Documentation

    • Warnings added about changing client URL breaking OAuth redirect_uri and recovery steps
  • UI Improvements

    • Improved reconnect tooltips and disabled-button accessibility

Walkthrough

Adds an exported sentinel ErrMCPReconnectNotApplicable, treats expired per‑user OAuth tokens like missing tokens to trigger re‑auth, wraps the per‑user OAuth reconnect early return with that sentinel, upserts per‑user OAuth sessions, extends token/session DB fields, validates/sanitizes MCP external URLs, surfaces redirect_uri warnings in docs/UI, and adds an OAuth+MCP demo server.

Changes

Per-user OAuth / Reconnect

Layer / File(s) Summary
Sentinel Error
core/schemas/mcp.go
Add exported ErrMCPReconnectNotApplicable sentinel.
Token Resolution
core/mcp/utils/utils.go
Treat ErrOAuth2TokenExpired like ErrOAuth2TokenNotFound so both initiate per‑user re‑auth and return *schemas.MCPUserOAuthRequiredError.
Client Reconnect
core/mcp/clientmanager.go
ReconnectClient per‑user OAuth early return now wraps ErrMCPReconnectNotApplicable with fmt.Errorf(...%w...) instead of returning a raw string.
Handler Response
transports/bifrost-http/handlers/mcp.go
reconnectMCPClient checks errors.Is(..., ErrMCPReconnectNotApplicable) and responds HTTP 400 with that error message (other errors remain 500).
Session & Token Management
framework/oauth2/main.go, framework/configstore/tables/oauth.go
InitiateUserOAuthFlow upserts per‑user sessions by sessionToken (rotate PKCE/state while reusing session ID). RefreshUserAccessToken treats permanent upstream rejections as terminal (purges per‑user token, marks session needs_reauth, returns ErrOAuth2TokenExpired). DB structs extended with discovery/PKCE/session fields and ExpiresAt on token records; TablePerUserOAuthSession adds VirtualKey relation. RevokeToken log level lowered.

Config Validation, Docs & UI

Layer / File(s) Summary
URL Validation API
transports/bifrost-http/lib/ctx.go
Add exported ValidateBaseURL(val string) error (empty allowed; otherwise require scheme and host).
Config Loading / Syncing
transports/bifrost-http/lib/config.go
Add sanitizeMCPExternalOAuthURLs to validate MCP external OAuth override URLs; invalid overrides log a warning and are cleared. Invoked from loadClientConfig in both code paths.
Config Update Handler
transports/bifrost-http/handlers/config.go
updateConfig validates submitted mcp_external_server_url and mcp_external_client_url with ValidateBaseURL, returning HTTP 400 field errors on failure and preventing partial updates.
Docs & UI Warnings
docs/mcp/oauth.mdx, docs/mcp/gateway-url.mdx, ui/app/workspace/config/views/mcpView.tsx
Add warnings that upstream OAuth providers lock the registered redirect_uri to the current external client URL; changing it can break existing clients and requires clearing stored OAuth client credentials and re‑authorizing.
UI Table & Tooltip
ui/app/workspace/mcp-registry/views/mcpClientsTable.tsx
Add Auth column via getAuthTypeDisplay, adjust empty‑state colSpan, and replace native title tooltip with Radix TooltipProvider/Tooltip to preserve tooltips on disabled reconnect buttons.

OAuth Demo Server (example)

Layer / File(s) Summary
Module File
examples/mcps/oauth-demo-server/go.mod
Add oauth-demo-server module and pinned dependency on github.com/mark3labs/mcp-go v0.43.2.
Demo Implementation
examples/mcps/oauth-demo-server/main.go
New self‑contained mock OAuth 2.1 + MCP demo implementing discovery, dynamic client registration, PKCE authorization code flow with rotation, token issuance/rotation, Bearer‑protected MCP endpoints and example tools, and request logging.

Sequence Diagram

sequenceDiagram
    participant Client as MCP Client
    participant Bifrost as Bifrost
    participant OAuth as OAuth Server
    participant User as User

    Client->>Bifrost: Request needing per-user auth
    Bifrost->>Bifrost: ResolvePerUserOAuthToken
    Bifrost->>OAuth: Attempt token refresh/validation
    OAuth-->>Bifrost: ErrOAuth2TokenExpired or token not found
    Bifrost->>Bifrost: InitiateUserOAuthFlow (upsert session, rotate PKCE/state)
    Bifrost->>User: Redirect to OAuth /authorize (PKCE)
    User->>OAuth: Authorize (login/consent)
    OAuth->>User: Redirect back with code
    User->>Bifrost: Callback with code
    Bifrost->>OAuth: Exchange code -> /token
    OAuth-->>Bifrost: access_token + refresh_token
    Bifrost->>Client: Proceed with MCP request handling
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰
Expired tokens sleep — I nudge the flow awake,
I upsert sessions, spin PKCE, and bake,
When redirect locks, clear creds and re‑authorize,
A rabbit hops: reconnect in fresh sunrise.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.17% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main changes: fixing per-user OAuth re-authentication, refresh token expiry handling, and reconnect user experience improvements.
Description check ✅ Passed The description is comprehensive and well-structured, covering all required template sections including summary, detailed changes, type of change, affected areas, testing instructions, and security considerations.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 05-04-feat_add_refresh_token_expiry_reauth_flow_for_per_user_mcp_oauth

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.

🔧 golangci-lint (2.12.1)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@examples/mcps/oauth-demo-server/go.mod`:
- Around line 5-10: The go.mod currently requires github.com/mark3labs/mcp-go
v0.43.2 which pulls in the vulnerable github.com/buger/jsonparser v1.1.1; fix by
either updating the mcp-go requirement to v0.49.0 (or later) in go.mod (replace
the existing github.com/mark3labs/mcp-go version) or add a replace directive
that pins github.com/buger/jsonparser to v1.1.2 or newer; ensure you run go mod
tidy and verify the transitive dependency no longer resolves to v1.1.1.

In `@examples/mcps/oauth-demo-server/main.go`:
- Around line 568-605: The refresh rotation is not atomic: the code loads old
under st.mu, then releases the lock and later deletes/rotates, allowing two
concurrent refreshes to both succeed; fix by doing the lookup, expiry check and
the invalidation/rotation atomically under the same mutex. Specifically, in the
refresh handler keep st.mu locked across reading st.refreshTokens[rt], checking
time.Now().After(old.RefreshExpiresAt), deleting st.refreshTokens[rt] and
st.accessTokens[old.AccessToken], then call issueTokens (or modify issueTokens
so it can be safely invoked while holding the lock) and insert the new rec into
st.refreshTokens/st.accessTokens before unlocking, using the same rt/old
identifiers to ensure single-use semantics for rt.

In `@framework/oauth2/main.go`:
- Around line 859-871: The lookup error from
p.configStore.GetOauthUserSessionBySessionToken must not be ignored: check the
returned err immediately after calling GetOauthUserSessionBySessionToken (not
just existing), and if err != nil return/wrap that error instead of proceeding
to the insert path; only proceed to update existing (via
p.configStore.UpdateOauthUserSession) when existing != nil and err == nil,
otherwise propagate the retrieval error so transient read failures aren't masked
and you avoid creating misleading create/unique-index errors.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 82d6d6e1-5c51-474b-986c-084aae702dd9

📥 Commits

Reviewing files that changed from the base of the PR and between 76e9d94 and 09c4f02.

⛔ Files ignored due to path filters (1)
  • examples/mcps/oauth-demo-server/go.sum is excluded by !**/*.sum
📒 Files selected for processing (11)
  • core/mcp/clientmanager.go
  • core/mcp/utils/utils.go
  • core/schemas/mcp.go
  • docs/mcp/gateway-url.mdx
  • docs/mcp/oauth.mdx
  • examples/mcps/oauth-demo-server/go.mod
  • examples/mcps/oauth-demo-server/main.go
  • framework/oauth2/main.go
  • transports/bifrost-http/handlers/mcp.go
  • ui/app/workspace/config/views/mcpView.tsx
  • ui/app/workspace/mcp-registry/views/mcpClientsTable.tsx

Comment thread examples/mcps/oauth-demo-server/go.mod
Comment thread examples/mcps/oauth-demo-server/main.go
Comment thread framework/oauth2/main.go Outdated
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 04-30-fix_semantic_cache_fixes branch from 76e9d94 to 2ee69fd Compare May 5, 2026 12:20
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 05-04-feat_add_refresh_token_expiry_reauth_flow_for_per_user_mcp_oauth branch from 09c4f02 to bc19ce7 Compare May 5, 2026 12:20
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@transports/bifrost-http/lib/ctx.go`:
- Around line 670-683: ValidateBaseURL currently parses the raw input but
BuildBaseURL first normalizes by trimming whitespace and trailing slashes,
causing mismatched validation; update ValidateBaseURL to mirror BuildBaseURL
normalization: trim surrounding whitespace and any trailing slash(es) before
checking for empty and before calling url.Parse, then validate parsed.Scheme and
parsed.Host and return the same error message if invalid (reference:
ValidateBaseURL and BuildBaseURL).
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bae884fc-20be-4bc7-a0fd-babb2eea7d96

📥 Commits

Reviewing files that changed from the base of the PR and between 09c4f02 and bc19ce7.

⛔ Files ignored due to path filters (1)
  • examples/mcps/oauth-demo-server/go.sum is excluded by !**/*.sum
📒 Files selected for processing (14)
  • core/mcp/clientmanager.go
  • core/mcp/utils/utils.go
  • core/schemas/mcp.go
  • docs/mcp/gateway-url.mdx
  • docs/mcp/oauth.mdx
  • examples/mcps/oauth-demo-server/go.mod
  • examples/mcps/oauth-demo-server/main.go
  • framework/oauth2/main.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/lib/ctx.go
  • ui/app/workspace/config/views/mcpView.tsx
  • ui/app/workspace/mcp-registry/views/mcpClientsTable.tsx
✅ Files skipped from review due to trivial changes (5)
  • docs/mcp/gateway-url.mdx
  • core/schemas/mcp.go
  • docs/mcp/oauth.mdx
  • ui/app/workspace/mcp-registry/views/mcpClientsTable.tsx
  • ui/app/workspace/config/views/mcpView.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • examples/mcps/oauth-demo-server/go.mod
  • framework/oauth2/main.go

Comment thread transports/bifrost-http/lib/ctx.go
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 05-04-feat_add_refresh_token_expiry_reauth_flow_for_per_user_mcp_oauth branch from bc19ce7 to c092d13 Compare May 5, 2026 15:29
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 04-30-fix_semantic_cache_fixes branch from 2ee69fd to 59459da Compare May 5, 2026 15:29
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

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 `@framework/oauth2/main.go`:
- Around line 853-894: This code performs a read-then-write TOCTOU around the
session_token unique index: concurrent requests can both see no existing row and
one will fail CreateOauthUserSession; fix by making the write atomic in
p.configStore — add and call a store-level upsert/claim method (e.g.,
UpsertOauthUserSession or ClaimOauthUserSession) that performs an INSERT ... ON
CONFLICT (session_token) DO UPDATE (or equivalent transactional lock) to
create-or-update the row; if you cannot change the store API, implement a retry
path in this function: call CreateOauthUserSession, and on unique-constraint
error reload with GetOauthUserSessionBySessionToken and then
UpdateOauthUserSession (or loop few times) to avoid the race. Ensure you
reference p.configStore, GetOauthUserSessionBySessionToken,
CreateOauthUserSession, UpdateOauthUserSession and the session_token unique
constraint when implementing.
- Around line 1128-1147: The delete of the stale token
(p.configStore.DeleteOauthUserToken) is load-bearing and must gate the "re-auth
required" sentinel; change the flow so that if DeleteOauthUserToken returns an
error you log and return that delete error (wrapped) immediately and do NOT
proceed to return schemas.ErrOAuth2TokenExpired or update the session; only when
the delete succeeds should you continue to perform the best-effort session
lookup (p.configStore.GetOauthUserSessionBySessionToken) and
UpdateOauthUserSession and finally return the wrapped ErrOAuth2TokenExpired
sentinel.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: feb58271-bec3-4ec5-adc5-d5208c994b7f

📥 Commits

Reviewing files that changed from the base of the PR and between bc19ce7 and c092d13.

⛔ Files ignored due to path filters (1)
  • examples/mcps/oauth-demo-server/go.sum is excluded by !**/*.sum
📒 Files selected for processing (14)
  • core/mcp/clientmanager.go
  • core/mcp/utils/utils.go
  • core/schemas/mcp.go
  • docs/mcp/gateway-url.mdx
  • docs/mcp/oauth.mdx
  • examples/mcps/oauth-demo-server/go.mod
  • examples/mcps/oauth-demo-server/main.go
  • framework/oauth2/main.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/lib/ctx.go
  • ui/app/workspace/config/views/mcpView.tsx
  • ui/app/workspace/mcp-registry/views/mcpClientsTable.tsx
✅ Files skipped from review due to trivial changes (6)
  • examples/mcps/oauth-demo-server/go.mod
  • transports/bifrost-http/lib/ctx.go
  • core/schemas/mcp.go
  • core/mcp/utils/utils.go
  • docs/mcp/oauth.mdx
  • ui/app/workspace/config/views/mcpView.tsx
🚧 Files skipped from review as they are similar to previous changes (6)
  • docs/mcp/gateway-url.mdx
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/config.go
  • ui/app/workspace/mcp-registry/views/mcpClientsTable.tsx
  • examples/mcps/oauth-demo-server/main.go

Comment thread framework/oauth2/main.go
Comment thread framework/oauth2/main.go
@Pratham-Mishra04 Pratham-Mishra04 changed the base branch from 04-30-fix_semantic_cache_fixes to graphite-base/3211 May 5, 2026 20:34
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 05-04-feat_add_refresh_token_expiry_reauth_flow_for_per_user_mcp_oauth branch from c092d13 to e34323a Compare May 6, 2026 07:32
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
transports/bifrost-http/lib/ctx.go (1)

624-637: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Normalize ValidateBaseURL input to match BuildBaseURL.

ValidateBaseURL validates the raw string, but BuildBaseURL trims whitespace/trailing slashes first. This can reject values that runtime later accepts.

Suggested fix
 func ValidateBaseURL(val string) error {
-	if val == "" {
+	normalized := strings.TrimRight(strings.TrimSpace(val), "/")
+	if normalized == "" {
 		return nil
 	}
-	parsed, err := url.Parse(val)
+	parsed, err := url.Parse(normalized)
 	if err != nil || parsed.Scheme == "" || parsed.Host == "" {
 		return fmt.Errorf("must be a fully-qualified URL with scheme and host (e.g. https://proxy.example.com), got %q", val)
 	}
 	return nil
 }
🤖 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 `@transports/bifrost-http/lib/ctx.go` around lines 624 - 637, ValidateBaseURL
currently parses the raw val directly, which can reject strings that
BuildBaseURL would accept; normalize the input first by trimming surrounding
whitespace and any trailing slashes (same normalization BuildBaseURL uses),
treat the trimmed-empty string as allowed, then parse the normalized value and
validate parsed.Scheme and parsed.Host; update ValidateBaseURL to operate on the
normalized value and keep the same error message and behavior otherwise
(referencing the ValidateBaseURL and BuildBaseURL functions).
🤖 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 `@transports/bifrost-http/handlers/config.go`:
- Around line 431-438: Run lib.ValidateBaseURL on
payload.ClientConfig.MCPExternalServerURL.GetValue() and
payload.ClientConfig.MCPExternalClientURL.GetValue() before any live mutations
(e.g., before calling UpdateDropExcessRequests, performing the MCP tool-manager
reload, compat plugin reload, or updating the in-memory MCP config). If either
validation fails, immediately SendError with StatusBadRequest and return so no
side effects are applied; move the existing validation block to the very start
of the handler (prior to the code that triggers those reloads/updates).

In `@transports/bifrost-http/lib/config.go`:
- Around line 707-712: The warning currently logged by ValidateBaseURL for
configData.Client.MCPExternalServerURL and MCPExternalClientURL does not prevent
those invalid values from being persisted; change the code so that when
ValidateBaseURL(...) returns an error you both log the warning and clear the
override (e.g., set client.MCPExternalServerURL = nil and
client.MCPExternalClientURL = nil) so the invalid values are not kept when
assigning config.ClientConfig = configData.Client; extract this logic into a
small helper (e.g., sanitizeMCPExternalOAuthURLs(client
*configstore.ClientConfig)) and call it for the client before assigning to
config.ClientConfig to ensure invalid overrides are discarded.

---

Duplicate comments:
In `@transports/bifrost-http/lib/ctx.go`:
- Around line 624-637: ValidateBaseURL currently parses the raw val directly,
which can reject strings that BuildBaseURL would accept; normalize the input
first by trimming surrounding whitespace and any trailing slashes (same
normalization BuildBaseURL uses), treat the trimmed-empty string as allowed,
then parse the normalized value and validate parsed.Scheme and parsed.Host;
update ValidateBaseURL to operate on the normalized value and keep the same
error message and behavior otherwise (referencing the ValidateBaseURL and
BuildBaseURL functions).
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c785bae6-bf1c-4be3-8aa7-a091e9ab2d72

📥 Commits

Reviewing files that changed from the base of the PR and between c092d13 and e34323a.

⛔ Files ignored due to path filters (1)
  • examples/mcps/oauth-demo-server/go.sum is excluded by !**/*.sum
📒 Files selected for processing (15)
  • core/mcp/clientmanager.go
  • core/mcp/utils/utils.go
  • core/schemas/mcp.go
  • docs/mcp/gateway-url.mdx
  • docs/mcp/oauth.mdx
  • examples/mcps/oauth-demo-server/go.mod
  • examples/mcps/oauth-demo-server/main.go
  • framework/configstore/tables/oauth.go
  • framework/oauth2/main.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/lib/ctx.go
  • ui/app/workspace/config/views/mcpView.tsx
  • ui/app/workspace/mcp-registry/views/mcpClientsTable.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • ui/app/workspace/mcp-registry/views/mcpClientsTable.tsx
  • core/mcp/clientmanager.go
  • framework/oauth2/main.go

Comment thread transports/bifrost-http/handlers/config.go Outdated
Comment thread transports/bifrost-http/lib/config.go Outdated
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 05-04-feat_add_refresh_token_expiry_reauth_flow_for_per_user_mcp_oauth branch from e34323a to 553a797 Compare May 6, 2026 07:53
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (3)
transports/bifrost-http/lib/ctx.go (1)

632-637: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Normalize ValidateBaseURL input to match BuildBaseURL behavior.

ValidateBaseURL validates raw input, while BuildBaseURL normalizes with TrimSpace + trailing-slash trim first. This can reject values that runtime would accept.

Suggested fix
 func ValidateBaseURL(val string) error {
-	if val == "" {
+	normalized := strings.TrimRight(strings.TrimSpace(val), "/")
+	if normalized == "" {
 		return nil
 	}
-	parsed, err := url.Parse(val)
+	parsed, err := url.Parse(normalized)
 	if err != nil || parsed.Scheme == "" || parsed.Host == "" {
 		return fmt.Errorf("must be a fully-qualified URL with scheme and host (e.g. https://proxy.example.com)")
 	}
 	return nil
 }
#!/bin/bash
# Verify normalization mismatch between ValidateBaseURL and BuildBaseURL
rg -n -A6 -B2 'func ValidateBaseURL|func BuildBaseURL|TrimRight\(strings.TrimSpace|url.Parse\(' transports/bifrost-http/lib/ctx.go
🤖 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 `@transports/bifrost-http/lib/ctx.go` around lines 632 - 637, ValidateBaseURL
currently parses raw input directly; make it normalize the input exactly like
BuildBaseURL by applying strings.TrimSpace and trimming trailing slashes (e.g.,
strings.TrimRight(..., "/")) before calling url.Parse. Update the
ValidateBaseURL function to use the normalized value for parsing and for the
subsequent checks (parsed.Scheme and parsed.Host) so inputs accepted at runtime
by BuildBaseURL are also validated successfully.
examples/mcps/oauth-demo-server/main.go (1)

568-605: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Make refresh-token rotation atomic.

old is loaded under one lock and revoked under another. Two concurrent grant_type=refresh_token requests can both pass the existence/expiry checks and each mint a fresh token pair, so the demo stops enforcing single-use refresh tokens and won't reliably reproduce invalid_grant on replay. Keep lookup, expiry check, deletion, and new-token insertion in one critical section, or split issueTokens into a locked helper for the refresh path.

🤖 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 `@examples/mcps/oauth-demo-server/main.go` around lines 568 - 605, The
refresh-token rotation is non-atomic: `old` is read under st.mu then revoked
under a separate lock, allowing concurrent `grant_type=refresh_token` requests
to both succeed; fix by performing lookup, expiry check
(time.Now().After(old.RefreshExpiresAt)), deletion from `st.refreshTokens` and
`st.accessTokens`, and insertion of the new token record returned by
`issueTokens` while holding the same `st.mu` (or refactor `issueTokens` into a
locked helper used only for the refresh path), then release the lock and emit
logs; ensure `rt`, `old`, `rec`, `st.refreshTokens`, `st.accessTokens`, and
`issueTokens` are the referenced symbols when implementing the atomic critical
section.
transports/bifrost-http/lib/config.go (1)

699-709: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't nil out file-backed MCP external URL overrides here.

These fields are mutated in-place on the same configData.Client object that is later assigned into config.ClientConfig and persisted on the no-DB/hash-mismatch paths. If the value was an env.VAR reference that is temporarily unset or invalid at startup, Lines 705 and 709 permanently erase that reference, so a later restart can no longer recover when the env var is fixed.

💡 Minimal fix
 func sanitizeMCPExternalOAuthURLs(client *configstore.ClientConfig) {
 	if client == nil {
 		return
 	}
 	if err := ValidateBaseURL(client.MCPExternalServerURL.GetValue()); err != nil {
 		logger.Warn("mcp_external_server_url %v; override will be ignored and OAuth URLs will fall back to the request Host header", err)
-		client.MCPExternalServerURL = nil
 	}
 	if err := ValidateBaseURL(client.MCPExternalClientURL.GetValue()); err != nil {
 		logger.Warn("mcp_external_client_url %v; override will be ignored and OAuth URLs will fall back to the request Host header", err)
-		client.MCPExternalClientURL = nil
 	}
 }
🤖 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 `@transports/bifrost-http/lib/config.go` around lines 699 - 709,
sanitizeMCPExternalOAuthURLs currently mutates the passed
configstore.ClientConfig by setting client.MCPExternalServerURL and
client.MCPExternalClientURL to nil on validation failure, permanently erasing
file/env-backed references; instead, stop mutating those fields in-place — leave
client.MCPExternalServerURL and client.MCPExternalClientURL untouched when
ValidateBaseURL returns an error and only treat the value as "ignored" at
runtime (e.g., by returning an error/boolean, using a local copy, or setting an
ephemeral in-memory flag) so that configData.Client later assigned into
config.ClientConfig retains the original file/env reference and can recover on
restart; update sanitizeMCPExternalOAuthURLs to implement this non-destructive
behavior.
🤖 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 `@framework/configstore/tables/oauth.go`:
- Around line 18-34: The BeforeSave hook in TableOauthConfig should set
EncryptionStatus = "encrypted" whenever encrypt.IsEnabled() is true (even if
ClientSecret/CodeVerifier are empty or unchanged) so startup batch encryption
won't repeatedly revisit discovery/public-client rows; update the
TableOauthConfig.BeforeSave method to check encrypt.IsEnabled() and
unconditionally set EncryptionStatus = "encrypted" in that branch (and set to
"plain_text" when encryption is disabled) while preserving existing encryption
logic for ClientSecret and CodeVerifier.

In `@ui/app/workspace/mcp-registry/views/mcpClientsTable.tsx`:
- Around line 291-331: The tooltip wrapper span should get a single computed
"disabled" flag so pointer-events-none is applied whenever the Button is
disabled for any reason (isPerUserOAuth, c.config.disabled,
reconnectingClients.includes(c.config.client_id), or !hasUpdateMCPClientAccess);
compute e.g. const reconnectDisabled = isPerUserOAuth || c.config.disabled ||
reconnectingClients.includes(c.config.client_id) || !hasUpdateMCPClientAccess
and use that for the span's className (pointer-events-none when true) while
keeping the Button's disabled prop set to the same reconnectDisabled and keeping
existing aria-label, onClick, and tooltip content logic (symbols to update: the
wrapping <span>, the className, and the Button disabled prop; refer to
reconnectingClients, isPerUserOAuth, c.config.disabled,
hasUpdateMCPClientAccess, handleReconnect).

---

Duplicate comments:
In `@examples/mcps/oauth-demo-server/main.go`:
- Around line 568-605: The refresh-token rotation is non-atomic: `old` is read
under st.mu then revoked under a separate lock, allowing concurrent
`grant_type=refresh_token` requests to both succeed; fix by performing lookup,
expiry check (time.Now().After(old.RefreshExpiresAt)), deletion from
`st.refreshTokens` and `st.accessTokens`, and insertion of the new token record
returned by `issueTokens` while holding the same `st.mu` (or refactor
`issueTokens` into a locked helper used only for the refresh path), then release
the lock and emit logs; ensure `rt`, `old`, `rec`, `st.refreshTokens`,
`st.accessTokens`, and `issueTokens` are the referenced symbols when
implementing the atomic critical section.

In `@transports/bifrost-http/lib/config.go`:
- Around line 699-709: sanitizeMCPExternalOAuthURLs currently mutates the passed
configstore.ClientConfig by setting client.MCPExternalServerURL and
client.MCPExternalClientURL to nil on validation failure, permanently erasing
file/env-backed references; instead, stop mutating those fields in-place — leave
client.MCPExternalServerURL and client.MCPExternalClientURL untouched when
ValidateBaseURL returns an error and only treat the value as "ignored" at
runtime (e.g., by returning an error/boolean, using a local copy, or setting an
ephemeral in-memory flag) so that configData.Client later assigned into
config.ClientConfig retains the original file/env reference and can recover on
restart; update sanitizeMCPExternalOAuthURLs to implement this non-destructive
behavior.

In `@transports/bifrost-http/lib/ctx.go`:
- Around line 632-637: ValidateBaseURL currently parses raw input directly; make
it normalize the input exactly like BuildBaseURL by applying strings.TrimSpace
and trimming trailing slashes (e.g., strings.TrimRight(..., "/")) before calling
url.Parse. Update the ValidateBaseURL function to use the normalized value for
parsing and for the subsequent checks (parsed.Scheme and parsed.Host) so inputs
accepted at runtime by BuildBaseURL are also validated successfully.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 42757af4-8f8f-48bc-b456-9a0172b0ba21

📥 Commits

Reviewing files that changed from the base of the PR and between e34323a and 553a797.

⛔ Files ignored due to path filters (1)
  • examples/mcps/oauth-demo-server/go.sum is excluded by !**/*.sum
📒 Files selected for processing (15)
  • core/mcp/clientmanager.go
  • core/mcp/utils/utils.go
  • core/schemas/mcp.go
  • docs/mcp/gateway-url.mdx
  • docs/mcp/oauth.mdx
  • examples/mcps/oauth-demo-server/go.mod
  • examples/mcps/oauth-demo-server/main.go
  • framework/configstore/tables/oauth.go
  • framework/oauth2/main.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/lib/ctx.go
  • ui/app/workspace/config/views/mcpView.tsx
  • ui/app/workspace/mcp-registry/views/mcpClientsTable.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • examples/mcps/oauth-demo-server/go.mod

Comment thread framework/configstore/tables/oauth.go
Comment thread ui/app/workspace/mcp-registry/views/mcpClientsTable.tsx
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 05-04-feat_add_refresh_token_expiry_reauth_flow_for_per_user_mcp_oauth branch from 553a797 to 825ea3b Compare May 6, 2026 08:11
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
ui/app/workspace/mcp-registry/views/mcpClientsTable.tsx (1)

307-314: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use one isReconnectDisabled flag for both disabled and pointer-event passthrough.

The button is disabled for four conditions, but pointer-events-none only covers two. Tooltip hover can still fail for reconnecting / RBAC-disabled states.

💡 Suggested fix
+const isReconnectDisabled =
+	isPerUserOAuth ||
+	c.config.disabled ||
+	reconnectingClients.includes(c.config.client_id) ||
+	!hasUpdateMCPClientAccess;
+
 <Button
@@
-	disabled={
-		isPerUserOAuth ||
-		c.config.disabled ||
-		reconnectingClients.includes(c.config.client_id) ||
-		!hasUpdateMCPClientAccess
-	}
-	className={isPerUserOAuth || c.config.disabled ? "pointer-events-none" : undefined}
+	disabled={isReconnectDisabled}
+	className={isReconnectDisabled ? "pointer-events-none" : undefined}
 >
🤖 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/app/workspace/mcp-registry/views/mcpClientsTable.tsx` around lines 307 -
314, Create a single boolean flag (e.g., isReconnectDisabled) that combines the
four conditions currently used to set disabled (isPerUserOAuth ||
c.config.disabled || reconnectingClients.includes(c.config.client_id) ||
!hasUpdateMCPClientAccess) and use that one flag for both the button's disabled
prop and the className ternary that applies "pointer-events-none"; locate where
the button is rendered (references: isPerUserOAuth, c.config.disabled,
reconnectingClients.includes(c.config.client_id), hasUpdateMCPClientAccess) and
replace the repeated condition with the new isReconnectDisabled variable so
tooltip/pointer behavior is consistent across all disabling reasons.
transports/bifrost-http/lib/config.go (1)

703-709: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Do not clear mcp_external_*_url on validation failure; preserve EnvVar references.

Setting client.MCPExternalServerURL = nil / client.MCPExternalClientURL = nil drops env.VAR references when startup validation fails transiently (e.g., env not yet available), so later restarts cannot recover from the original reference.

💡 Suggested fix
 func sanitizeMCPExternalOAuthURLs(client *configstore.ClientConfig) {
 	if client == nil {
 		return
 	}
 	if err := ValidateBaseURL(client.MCPExternalServerURL.GetValue()); err != nil {
 		logger.Warn("mcp_external_server_url %v; override will be ignored and OAuth URLs will fall back to the request Host header", err)
-		client.MCPExternalServerURL = nil
 	}
 	if err := ValidateBaseURL(client.MCPExternalClientURL.GetValue()); err != nil {
 		logger.Warn("mcp_external_client_url %v; override will be ignored and OAuth URLs will fall back to the request Host header", err)
-		client.MCPExternalClientURL = nil
 	}
 }

Based on learnings: “mcp_external_server_url and mcp_external_client_url … intentionally warn but keep the original EnvVar value rather than clearing it … Do not flag or suggest clearing these fields on validation failure.”

🤖 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 `@transports/bifrost-http/lib/config.go` around lines 703 - 709, The validation
code currently clears client.MCPExternalServerURL and
client.MCPExternalClientURL on ValidateBaseURL failure which drops EnvVar
references; instead remove the assignments that set those fields to nil and only
emit the warning via logger.Warn so the original EnvVar-backed values remain
intact for later retries; locate the ValidateBaseURL checks around
client.MCPExternalServerURL.GetValue() and
client.MCPExternalClientURL.GetValue() and delete the lines that assign nil
while keeping the warning behavior.
🤖 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 `@framework/oauth2/main.go`:
- Around line 863-878: The current flow fetches a session via
GetOauthUserSessionBySessionToken and then blindly mutates fields like
existing.MCPClientID, OauthConfigID, State, RedirectURI, etc., which can
reassign a session to a different MCP client; change the logic in the block
handling existing (the code that sets existing.MCPClientID,
existing.OauthConfigID, existing.State, existing.RedirectURI,
existing.CodeVerifier, existing.VirtualKeyID, existing.UserID, existing.Status,
existing.ExpiresAt and calls UpdateOauthUserSession) to first verify the session
belongs to the same MCP client (compare existing.MCPClientID to mcpClientID and
existing.OauthConfigID to oauthConfigID), and if they differ, do not update the
row—instead return an error (or create/return a new isolated session token) so
you never repoint an existing session to a different MCP client; ensure you use
GetOauthUserSessionBySessionToken and UpdateOauthUserSession as the modification
points.

---

Duplicate comments:
In `@transports/bifrost-http/lib/config.go`:
- Around line 703-709: The validation code currently clears
client.MCPExternalServerURL and client.MCPExternalClientURL on ValidateBaseURL
failure which drops EnvVar references; instead remove the assignments that set
those fields to nil and only emit the warning via logger.Warn so the original
EnvVar-backed values remain intact for later retries; locate the ValidateBaseURL
checks around client.MCPExternalServerURL.GetValue() and
client.MCPExternalClientURL.GetValue() and delete the lines that assign nil
while keeping the warning behavior.

In `@ui/app/workspace/mcp-registry/views/mcpClientsTable.tsx`:
- Around line 307-314: Create a single boolean flag (e.g., isReconnectDisabled)
that combines the four conditions currently used to set disabled (isPerUserOAuth
|| c.config.disabled || reconnectingClients.includes(c.config.client_id) ||
!hasUpdateMCPClientAccess) and use that one flag for both the button's disabled
prop and the className ternary that applies "pointer-events-none"; locate where
the button is rendered (references: isPerUserOAuth, c.config.disabled,
reconnectingClients.includes(c.config.client_id), hasUpdateMCPClientAccess) and
replace the repeated condition with the new isReconnectDisabled variable so
tooltip/pointer behavior is consistent across all disabling reasons.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a047924b-5562-4a18-805b-16b128dbbbe4

📥 Commits

Reviewing files that changed from the base of the PR and between 553a797 and 825ea3b.

⛔ Files ignored due to path filters (1)
  • examples/mcps/oauth-demo-server/go.sum is excluded by !**/*.sum
📒 Files selected for processing (15)
  • core/mcp/clientmanager.go
  • core/mcp/utils/utils.go
  • core/schemas/mcp.go
  • docs/mcp/gateway-url.mdx
  • docs/mcp/oauth.mdx
  • examples/mcps/oauth-demo-server/go.mod
  • examples/mcps/oauth-demo-server/main.go
  • framework/configstore/tables/oauth.go
  • framework/oauth2/main.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/lib/ctx.go
  • ui/app/workspace/config/views/mcpView.tsx
  • ui/app/workspace/mcp-registry/views/mcpClientsTable.tsx

Comment thread framework/oauth2/main.go
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 6, 2026
Copy link
Copy Markdown
Contributor

akshaydeo commented May 6, 2026

Merge activity

  • May 6, 8:41 AM UTC: A user started a stack merge that includes this pull request via Graphite.
  • May 6, 8:42 AM UTC: @akshaydeo merged this pull request with Graphite.

@akshaydeo akshaydeo changed the base branch from graphite-base/3211 to main May 6, 2026 08:42
@akshaydeo akshaydeo dismissed coderabbitai[bot]’s stale review May 6, 2026 08:42

The base branch was changed.

@akshaydeo akshaydeo requested a review from a team as a code owner May 6, 2026 08:42
@akshaydeo akshaydeo merged commit 8107511 into main May 6, 2026
11 of 17 checks passed
@akshaydeo akshaydeo deleted the 05-04-feat_add_refresh_token_expiry_reauth_flow_for_per_user_mcp_oauth branch May 6, 2026 08:42
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.

3 participants