Skip to content

feat: reconcile per-user MCP credentials on VK and MCP client changes#3705

Merged
Pratham-Mishra04 merged 1 commit into
devfrom
05-23-fix_mcp_auth_session_reconciliation_fixes
May 27, 2026
Merged

feat: reconcile per-user MCP credentials on VK and MCP client changes#3705
Pratham-Mishra04 merged 1 commit into
devfrom
05-23-fix_mcp_auth_session_reconciliation_fixes

Conversation

@Pratham-Mishra04

Copy link
Copy Markdown
Collaborator

Summary

When a Virtual Key's MCP allowlist changes (via dashboard edit, AP propagation, or SCIM auto-assign) or when an MCP client's access configuration changes (VK configs diff or AllowOnAllVirtualKeys toggle), per-user credentials that are no longer valid were previously left stale. This PR introduces a reconciliation layer that orphans vk-keyed OAuth tokens and header credentials whose MCP grant was revoked, reactivates them if the grant returns, and hard-deletes any in-flight pending flow rows that can no longer complete.

Changes

  • Added DeleteVirtualKey now also hard-deletes TableMCPPerUserHeaderFlow rows tied to the deleted VK, consistent with how other credential rows are cleaned up on VK deletion.
  • Added vkEffectiveMCPClientIDs helper that computes the union of a VK's explicit per-VK MCP allowlist and MCPs with AllowOnAllVirtualKeys=true, mirroring the runtime grant check.
  • Added reconcileVKDirectTokensDB and reconcileVKDirectHeaderRowsDB — transactional DB helpers that orphan active credentials outside the effective allowlist, reactivate orphaned credentials that regained access, and hard-delete pending flow rows for lost grants.
  • Added readVKsHoldingOauthCredsForMCP and readVKsHoldingHeaderCredsForMCP to identify which VKs need re-evaluation when a change originates on the MCP side.
  • Exposed four new ConfigStore interface methods: ReconcileOauthAfterVKChange, ReconcileMCPHeadersAfterVKChange, ReconcileOauthAfterMCPChange, and ReconcileMCPHeadersAfterMCPChange.
  • Called the VK-side reconcile methods in updateVirtualKey when MCPConfigs is present in the request.
  • Called the MCP-side reconcile methods in updateMCPClient when VKConfigs changed or AllowOnAllVirtualKeys was toggled.
  • Added no-op stubs for all four new interface methods to MockConfigStore in tests.
  • Reconciliation errors are logged but non-fatal so the primary update response is not blocked.

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

  1. Create a VK with an explicit MCP allowlist and establish an active OAuth token and a header credential for one of the allowed MCPs.
  2. Edit the VK to remove that MCP from its allowlist — verify the token and credential rows transition to status='orphaned' and any pending flow rows for that MCP are deleted.
  3. Re-add the MCP to the VK's allowlist — verify the orphaned rows return to status='active'.
  4. Toggle AllowOnAllVirtualKeys on an MCP client — verify all VKs holding credentials for that MCP are re-evaluated accordingly.
  5. Delete a VK — verify mcp_per_user_header_flows rows for that VK are removed alongside other credential rows.
go test ./framework/configstore/...
go test ./transports/bifrost-http/...

Breaking changes

  • Yes
  • No

Related issues

Security considerations

Orphaned credentials are invisible to runtime grant checks (status='active' filter), so a user whose VK loses MCP access can no longer use stale credentials to reach that MCP. Pending in-flight flows for revoked grants are hard-deleted immediately, preventing completion of an auth flow that would produce an unusable credential.

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

@coderabbitai

coderabbitai Bot commented May 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@Pratham-Mishra04, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 15 minutes. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b895685c-f3aa-4f15-b55b-3c17f4c4f5ed

📥 Commits

Reviewing files that changed from the base of the PR and between 78a9988 and dd1652a.

📒 Files selected for processing (10)
  • core/mcp/credstore/per_user_headers.go
  • core/mcp/utils/utils.go
  • framework/configstore/rdb.go
  • framework/configstore/store.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/mcp_per_user_headers.go
  • transports/bifrost-http/handlers/mcp_sessions.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/lib/config_test.go
📝 Walkthrough

Walkthrough

This PR adds per-user credential reconciliation and header-key canonicalization. It introduces helper utilities to normalize header keys (lowercase+trim), canonicalizes keys at request and storage boundaries, defines a four-method ConfigStore reconciliation interface, implements RDB transactional reconciliation that orphans and reactivates per-user OAuth tokens and MCP header credentials based on VK-MCP grant changes, wires reconciliation into MCP and governance handlers with error logging, and improves error handling in session operations.

Changes

Per-user Credential Reconciliation and Header Normalization

Layer / File(s) Summary
Header canonicalization contract and utilities
core/mcp/credstore/per_user_headers.go, core/mcp/utils/utils.go
Header-key canonicalization documented as lowercase+trim per CanonicalizeHeaderKey; new exported helpers normalize keys, key slices, and maps while preserving nil inputs; callback URL base trimmed of trailing /.
Canonicalization at request and persistence boundaries
transports/bifrost-http/handlers/mcp.go, transports/bifrost-http/handlers/mcp_per_user_headers.go, transports/bifrost-http/lib/config.go
MCP handler canonicalizes request and admin-provided header keys during creation and updates; canonicalized keys persisted to storage; per-user-headers handler canonicalizes submitted keys; mcputils imported in both handlers.
Reconciliation interface contract and mocks
framework/configstore/store.go, transports/bifrost-http/lib/config_test.go
ConfigStore interface adds four reconciliation methods for OAuth/header credentials after VK or MCP changes; MockConfigStore stubs added for test isolation.
RDB reconciliation implementation
framework/configstore/rdb.go
Computes VK effective MCP allowlist from per-VK entries and allow_on_all_virtual_keys clients; orphans/reactivates VK-keyed OAuth tokens and header credentials; hard-deletes pending flows outside allowlist; four exported transactional reconciliation methods reconcile single VKs or all VKs affected by MCP changes in deterministic order; also updates config persistence and query documentation.
MCP handler snapshot and post-update flows
transports/bifrost-http/handlers/mcp.go
Snapshots AllowOnAllVirtualKeys and PerUserHeaderKeys before in-memory update; moves credential mark-needs-update after runtime update, triggering only when schema adds keys; calls OAuth and header reconciliation when grants or allowlist change.
Governance handler post-VK-update reconciliation
transports/bifrost-http/handlers/governance.go
Invokes OAuth and header reconciliation for updated VK when MCPConfigs change and configStore available; logs failures without failing update response.
Session and revoke safety improvements
transports/bifrost-http/handlers/mcp_sessions.go, transports/bifrost-http/handlers/mcp_per_user_headers.go
Session handlers treat store lookup errors as 500 instead of misses; revoke deletes pending per-user header flows matching credential's auth-mode binding before credential deletion to prevent post-revoke recreation.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • maximhq/bifrost#3565: Both PRs touch the per-user OAuth persistence layer; this PR introduces VK/MCP grant reconciliation methods that change per-user OAuth token/session statuses and delete pending flows, while PR #3565 refactors the underlying per-user OAuth flow/token/session APIs and data model to be mode/identity scoped—the reconciliation logic directly depends on those refactored OAuth store behaviors.

Suggested reviewers

  • danpiths
  • roroghost17
  • akshaydeo

Poem

🐰 Headers now stand tall and true,
With canonicalization through and through,
VKs and MCPs sync their way,
Credentials dance—orphaned stay!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main feature: reconciling per-user MCP credentials when VK and MCP client configurations change.
Description check ✅ Passed The description follows the template structure with all major sections completed: Summary, Changes, Type of change, Affected areas, How to test, Breaking changes, Security considerations, and Checklist. Content is comprehensive and addresses the reconciliation feature thoroughly.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 05-23-fix_mcp_auth_session_reconciliation_fixes

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

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

@stepsecurity-app

Copy link
Copy Markdown
Contributor

Security Policy Alert: Secret Policy Violation

This workflow run has been blocked by StepSecurity's secrets policy because it accesses secrets and the workflow file differs from the default branch.

To approve this workflow, please add the workflows-approved label to this PR.

Note: The label must be added by someone other than the PR author (Pratham-Mishra04) or automation bots to ensure proper security review.

After the label is added, you can re-run the blocked workflow to proceed.

This workflow will be automatically approved once merged into the default branch.

For more information, see StepSecurity's Secret Exfiltration Policy documentation.

@greptile-apps

greptile-apps Bot commented May 23, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 5/5

The reconciliation logic is correct and idempotent; the two remaining findings are non-blocking quality concerns that do not affect security or credential grant enforcement.

The core correctness goals are implemented correctly. The pending-only flow delete and the pre-update snapshots address the most critical issues from the prior review. The two open findings are non-fatal and do not produce wrong grant state.

framework/configstore/rdb.go (readVKsHoldingOauthCredsForMCP status filter) and transports/bifrost-http/handlers/mcp_per_user_headers.go (mergeExistingPerUserHeaders error handling)

Important Files Changed

Filename Overview
framework/configstore/rdb.go Adds per-user credential reconciliation helpers (orphan/reactivate/delete-pending). The pending-only filter for flow deletion is correctly implemented. Minor: readVKsHoldingOauthCredsForMCP and its header counterpart include VKs with only expired/failed rows, causing wasted reconcile iterations.
transports/bifrost-http/handlers/mcp.go Pre-update snapshots of existingAllowOnAllVirtualKeys and existingPerUserHeaderKeys address prior review concerns. Reconciliation fires after UpdateMCPClient succeeds. Early return on VKConfigs failure still skips reconciliation when AllowOnAllVirtualKeys was already committed.
transports/bifrost-http/handlers/mcp_per_user_headers.go Adds mergeExistingPerUserHeaders for partial header updates and headersFlowIdentity helper. Pending-flow deletion before credential revoke is correct. DB errors in the merge path are silently swallowed, producing a confusing 400 under storage outages.
transports/bifrost-http/handlers/governance.go Calls reconcile on both OAuth and header surfaces after a VK MCPConfigs update; errors are logged but non-fatal so the primary update response is unblocked.
framework/configstore/store.go Adds four new ConfigStore interface methods for per-user credential reconciliation (VK-side and MCP-side, OAuth and headers).
transports/bifrost-http/handlers/mcp_sessions.go Properly surfaces store errors as 500s instead of silently falling through to the OAuth path on DB failure.
core/mcp/utils/utils.go Adds CanonicalizeHeaderKey, CanonicalizeHeaderKeys, and CanonicalizeHeaderMap helpers; adds trailing-slash trim to BuildMCPCallbackBaseURL.
core/mcp/credstore/per_user_headers.go Comment-only changes documenting the canonical-form invariant for header keys. No logic changes.
transports/bifrost-http/lib/config.go Applies CanonicalizeHeaderKeys in mcpClientConfigToTable to normalize keys on the config-file load path.
transports/bifrost-http/lib/config_test.go Adds no-op stubs for all four new ConfigStore reconciliation methods on MockConfigStore.

Reviews (14): Last reviewed commit: "fix: mcp auth session reconciliation fix..." | Re-trigger Greptile

Comment thread framework/configstore/rdb.go Outdated
Comment thread transports/bifrost-http/handlers/mcp.go Outdated
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 05-23-fix_mcp_auth_session_reconciliation_fixes branch from fb2ba5b to 5919a07 Compare May 24, 2026 19:55
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 05-22-feat_added_ui_for_per_user_headers_mcp_auth branch from 8737c78 to 70c0449 Compare May 24, 2026 19:55

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
transports/bifrost-http/handlers/mcp.go (1)

1115-1125: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reconcile after partial-success updates too.

If allow_on_all_virtual_keys changes, the MCP config update has already committed before the vk_configs transaction runs. When that later transaction fails and the handler returns at Line 1124, the block at Lines 1193-1203 never executes, so revoked per-user OAuth/header credentials stay in their old state until another update.

💡 Minimal fix
 		}); err != nil {
+			if req.AllowOnAllVirtualKeys != existingAllowOnAllVirtualKeys {
+				if recErr := h.store.ConfigStore.ReconcileOauthAfterMCPChange(ctx, id); recErr != nil {
+					logger.Error(fmt.Sprintf("reconcile OAuth credentials after MCP %s update failed: %v", id, recErr))
+				}
+				if recErr := h.store.ConfigStore.ReconcileMCPHeadersAfterMCPChange(ctx, id); recErr != nil {
+					logger.Error(fmt.Sprintf("reconcile per-user-headers credentials after MCP %s update failed: %v", id, recErr))
+				}
+			}
 			// NOTE: Partial success — the MCP client config was already updated in DB and memory above.

Also applies to: 1183-1203

🤖 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/rdb.go`:
- Around line 5707-5717: The vkIDs slice returned by
readVKsHoldingOauthCredsForMCP is unordered and can cause deadlocks when two
MCP-triggered reconciliations lock VKs in different orders; to fix, sort the
vkIDs slice (e.g., using Go's sort.Slice or sort.Strings/int conversion)
immediately after vkIDs, before iterating and calling
reconcileVKDirectTokensDB(tx, vkID) inside the transaction, and apply the same
change to the other similar transaction block (the one mirrored at lines
~5727-5737) so both reconciliation loops iterate VKs in a deterministic,
consistent order.
- Around line 5570-5599: The orphaning/reactivation and flows delete currently
only filter on virtual_key_id (orphanQ, flowsQ), so they can touch rows from
other modes; update the queries on TableOauthUserToken (orphanQ and the
reactivation query) to also filter by auth_mode = "virtual_key" and update the
TableOauthUserSession delete (flowsQ) to also filter by flow_mode =
"virtual_key" (or the exact sentinel your codebase uses for VK-mode) and
preserve the existing allowedClientIDs conditions; this ensures only VK-mode
rows (vkID-bound tokens and sessions) are orphaned/reactivated or deleted.

In `@framework/configstore/store.go`:
- Around line 434-437: Update the comment for
ListAllPendingMCPPerUserHeaderFlows to reflect that it does not return rows
regardless of caller identity but is subject to query-scope narrowing via
ScopedDB(ctx); mention that the implementation in framework/configstore/rdb.go
uses ScopedDB(ctx) so results are limited to rows visible to the caller
(row-visibility scoping) rather than global pending rows.
🪄 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: 4997c318-9da5-41cd-a2a2-20056cd15f2b

📥 Commits

Reviewing files that changed from the base of the PR and between 70c0449 and 5919a07.

📒 Files selected for processing (5)
  • framework/configstore/rdb.go
  • framework/configstore/store.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/lib/config_test.go

Comment thread framework/configstore/rdb.go
Comment thread framework/configstore/rdb.go
Comment thread framework/configstore/store.go Outdated
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 05-22-feat_added_ui_for_per_user_headers_mcp_auth branch from 70c0449 to cf07e4e Compare May 24, 2026 21:27
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 05-23-fix_mcp_auth_session_reconciliation_fixes branch from 5919a07 to 24adf78 Compare May 24, 2026 21:27
@coderabbitai coderabbitai Bot requested a review from roroghost17 May 24, 2026 21:38
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 05-23-fix_mcp_auth_session_reconciliation_fixes branch from 24adf78 to dbf3e80 Compare May 25, 2026 06:27
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 05-22-feat_added_ui_for_per_user_headers_mcp_auth branch from cf07e4e to 522cf0a Compare May 25, 2026 06:27
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 05-23-fix_mcp_auth_session_reconciliation_fixes branch from dbf3e80 to 44bcebd Compare May 25, 2026 07:12
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 05-22-feat_added_ui_for_per_user_headers_mcp_auth branch from 522cf0a to 1c9aa14 Compare May 25, 2026 07:12
Comment thread transports/bifrost-http/handlers/mcp.go
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 05-23-fix_mcp_auth_session_reconciliation_fixes branch from 44bcebd to be4c732 Compare May 25, 2026 07:29
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 05-22-feat_added_ui_for_per_user_headers_mcp_auth branch from b36721f to c00573b Compare May 25, 2026 08:10
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 05-23-fix_mcp_auth_session_reconciliation_fixes branch 2 times, most recently from 5dde11f to ddbf8a7 Compare May 25, 2026 12:31
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 05-22-feat_added_ui_for_per_user_headers_mcp_auth branch 2 times, most recently from 2d84d56 to a396bc2 Compare May 25, 2026 13:47
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 05-23-fix_mcp_auth_session_reconciliation_fixes branch from ddbf8a7 to 3e4f2f5 Compare May 25, 2026 13:47
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 05-22-feat_added_ui_for_per_user_headers_mcp_auth branch from a396bc2 to 311e88c Compare May 25, 2026 16:03
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 05-23-fix_mcp_auth_session_reconciliation_fixes branch 2 times, most recently from e435e91 to 6a51858 Compare May 26, 2026 18:29
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 05-22-feat_added_ui_for_per_user_headers_mcp_auth branch from 311e88c to 2a4a450 Compare May 26, 2026 18:29
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 05-22-feat_added_ui_for_per_user_headers_mcp_auth branch from 2a4a450 to dfa9a71 Compare May 27, 2026 05:44
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 05-23-fix_mcp_auth_session_reconciliation_fixes branch from 6a51858 to 78a9988 Compare May 27, 2026 05:44

Pratham-Mishra04 commented May 27, 2026

Copy link
Copy Markdown
Collaborator Author

Merge activity

  • May 27, 10:30 AM UTC: A user started a stack merge that includes this pull request via Graphite.
  • May 27, 10:42 AM UTC: Graphite rebased this pull request as part of a merge.
  • May 27, 10:43 AM UTC: @Pratham-Mishra04 merged this pull request with Graphite.

@Pratham-Mishra04 Pratham-Mishra04 changed the base branch from 05-22-feat_added_ui_for_per_user_headers_mcp_auth to graphite-base/3705 May 27, 2026 10:39
@Pratham-Mishra04 Pratham-Mishra04 changed the base branch from graphite-base/3705 to dev May 27, 2026 10:40
@Pratham-Mishra04 Pratham-Mishra04 dismissed coderabbitai[bot]’s stale review May 27, 2026 10:40

The base branch was changed.

@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 05-23-fix_mcp_auth_session_reconciliation_fixes branch from 78a9988 to dd1652a Compare May 27, 2026 10:41
@Pratham-Mishra04 Pratham-Mishra04 merged commit 03bd52e into dev May 27, 2026
12 of 14 checks passed
@Pratham-Mishra04 Pratham-Mishra04 deleted the 05-23-fix_mcp_auth_session_reconciliation_fixes branch May 27, 2026 10:43
akshaydeo pushed a commit that referenced this pull request May 29, 2026
…#3705)

## Summary

When a Virtual Key's MCP allowlist changes (via dashboard edit, AP propagation, or SCIM auto-assign) or when an MCP client's access configuration changes (VK configs diff or `AllowOnAllVirtualKeys` toggle), per-user credentials that are no longer valid were previously left stale. This PR introduces a reconciliation layer that orphans vk-keyed OAuth tokens and header credentials whose MCP grant was revoked, reactivates them if the grant returns, and hard-deletes any in-flight pending flow rows that can no longer complete.

## Changes

- Added `DeleteVirtualKey` now also hard-deletes `TableMCPPerUserHeaderFlow` rows tied to the deleted VK, consistent with how other credential rows are cleaned up on VK deletion.
- Added `vkEffectiveMCPClientIDs` helper that computes the union of a VK's explicit per-VK MCP allowlist and MCPs with `AllowOnAllVirtualKeys=true`, mirroring the runtime grant check.
- Added `reconcileVKDirectTokensDB` and `reconcileVKDirectHeaderRowsDB` — transactional DB helpers that orphan active credentials outside the effective allowlist, reactivate orphaned credentials that regained access, and hard-delete pending flow rows for lost grants.
- Added `readVKsHoldingOauthCredsForMCP` and `readVKsHoldingHeaderCredsForMCP` to identify which VKs need re-evaluation when a change originates on the MCP side.
- Exposed four new `ConfigStore` interface methods: `ReconcileOauthAfterVKChange`, `ReconcileMCPHeadersAfterVKChange`, `ReconcileOauthAfterMCPChange`, and `ReconcileMCPHeadersAfterMCPChange`.
- Called the VK-side reconcile methods in `updateVirtualKey` when `MCPConfigs` is present in the request.
- Called the MCP-side reconcile methods in `updateMCPClient` when `VKConfigs` changed or `AllowOnAllVirtualKeys` was toggled.
- Added no-op stubs for all four new interface methods to `MockConfigStore` in tests.
- Reconciliation errors are logged but non-fatal so the primary update response is not blocked.

## Type of change

- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [x] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

1. Create a VK with an explicit MCP allowlist and establish an active OAuth token and a header credential for one of the allowed MCPs.
2. Edit the VK to remove that MCP from its allowlist — verify the token and credential rows transition to `status='orphaned'` and any pending flow rows for that MCP are deleted.
3. Re-add the MCP to the VK's allowlist — verify the orphaned rows return to `status='active'`.
4. Toggle `AllowOnAllVirtualKeys` on an MCP client — verify all VKs holding credentials for that MCP are re-evaluated accordingly.
5. Delete a VK — verify `mcp_per_user_header_flows` rows for that VK are removed alongside other credential rows.

```sh
go test ./framework/configstore/...
go test ./transports/bifrost-http/...
```

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

Orphaned credentials are invisible to runtime grant checks (`status='active'` filter), so a user whose VK loses MCP access can no longer use stale credentials to reach that MCP. Pending in-flight flows for revoked grants are hard-deleted immediately, preventing completion of an auth flow that would produce an unusable credential.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
@akshaydeo akshaydeo mentioned this pull request May 29, 2026
18 tasks
akshaydeo added a commit that referenced this pull request May 29, 2026
## 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
@akshaydeo akshaydeo mentioned this pull request May 29, 2026
akshaydeo added a commit that referenced this pull request May 29, 2026
## ✨ 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)
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.

2 participants