Skip to content

feat: retain admin bootstrap credential for periodic per-user MCP tool discovery refresh - #5717

Merged
Pratham-Mishra04 merged 1 commit into
devfrom
07-30-feat_retain_admin_bootstrap_credential_for_per-user_mcp_tool_discovery
Aug 8, 2026
Merged

Pratham-Mishra04 merged 1 commit into
devfrom
07-30-feat_retain_admin_bootstrap_credential_for_per-user_mcp_tool_discovery

Conversation

@Pratham-Mishra04

Copy link
Copy Markdown
Collaborator

Summary

Per-user MCP clients (per_user_oauth and per_user_headers) never hold a persistent connection, so the periodic tool syncer was silently skipping them entirely — their tool lists could never be refreshed after initial bootstrap. This PR introduces an "admin credential" retention mechanism: the bootstrap-verification credential (OAuth token or header values) submitted by the admin during one-time setup is now persisted under auth_mode='admin' and reused by the tool syncer to run ephemeral connect-discover-close cycles, keeping per-user clients' tool lists up to date without requiring a real end-user request.

Changes

  • Admin credential retention at bootstrap time: completeMCPClientOAuth now re-tags the admin's bootstrap OAuth token from auth_mode='shared' to auth_mode='admin' (instead of revoking it) after a successful verification. verifyMCPClientHeaders now persists the admin's sample header values as an auth_mode='admin' credential row. Both are best-effort — a retention failure does not fail the request.

  • performAdminToolDiscovery on MCPManager: New method that resolves the retained admin credential via credStore.AdminConnectionHeaders and runs a one-shot ephemeral connect-discover-close cycle using the same VerifyPerUserOAuthConnection / VerifyHeadersConnection functions the bootstrap flow uses.

  • ClientToolSyncer.performSync widened: The conn == nil branch now distinguishes per-user auth types (per_user_oauth, per_user_headers) from shared-connection types. Per-user clients with no live conn are routed into performAdminToolDiscovery instead of being silently skipped; shared-connection clients mid-reconnect continue to be skipped as before.

  • AdminConnectionHeaders added to MCPCredentialStore interface and all resolvers: per_user_oauth and per_user_headers resolvers implement it meaningfully (OAuth token lookup via GetAdminAccessToken; header credential lookup via GetCredentialByMode with MCPAuthModeAdmin). All shared-credential resolvers (none, headers, oauth) return an explicit unsupported error.

  • GetAdminAccessToken added to OAuth2Provider: Resolves the auth_mode='admin' token row via GetAdminOauthTokenByConfigID and funnels it through the extracted resolveAccessToken helper (shared with GetAccessToken to avoid duplicating status/expiry/refresh/sanitize logic).

  • TokenRefreshWorker default AuthModes widened to ["shared", "admin"]: Admin-mode tokens back only the tool syncer and have no live caller to trigger lazy refresh, so they need the same proactive background sweep as shared tokens.

  • MCPAuthModeAdmin support in GetMCPPerUserHeaderCredentialByMode and UpsertMCPPerUserHeaderCredential: Admin-mode rows are scoped by mcp_client_id alone (no per-caller identity). The identity guard in mcp_headers.Provider.GetCredentialByMode is widened to allow an empty identity for MCPAuthModeAdmin only.

  • Database migration add_mcp_admin_auth_mode_indexes: Adds partial unique indexes on mcp_oauth_tokens and mcp_per_user_header_credentials for auth_mode='admin', enforcing exactly one admin credential per MCP client.

  • UI copy updates: Admin bootstrap flows for both OAuth and headers now accurately describe that the credential is retained for periodic tool-list refresh rather than discarded after a one-time test.

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

# Core/Transports
go test ./core/mcp/... ./core/schemas/... ./framework/configstore/... ./framework/mcp_headers/... ./framework/oauth2/... ./transports/bifrost-http/...

# UI
cd ui
pnpm i
pnpm build
  • Bootstrap a per_user_oauth MCP client through the admin OAuth flow and confirm the token row in mcp_oauth_tokens has auth_mode='admin' after completion.
  • Bootstrap a per_user_headers MCP client and confirm a row with auth_mode='admin' appears in mcp_per_user_header_credentials.
  • Wait for or manually trigger a tool-sync cycle and confirm the per-user client's tool list is updated (previously it would be silently skipped).
  • Confirm shared-connection clients (headers, none, oauth) with conn == nil (mid-reconnect) are still silently skipped and not routed into admin discovery.

Breaking changes

  • Yes
  • No

MCPCredentialStore and OAuth2Provider are interfaces with new required methods (AdminConnectionHeaders and GetAdminAccessToken respectively). Any external implementations of these interfaces must add stub or real implementations of these methods. The ConfigStore interface also gains GetAdminOauthTokenByConfigID. The TokenRefreshWorker default AuthModes changes from ["shared"] to ["shared", "admin"], which may cause additional token refresh queries if admin-mode rows exist.

Security considerations

The retained admin credential (auth_mode='admin') is used exclusively by the internal tool syncer for tool-list discovery — it is never injected into real end-user requests. The per_user_oauth resolver's AdminConnectionHeaders and the per_user_headers resolver's AdminConnectionHeaders both enforce this separation at the resolver level. The partial unique indexes ensure at most one admin credential exists per MCP client, preventing accumulation of stale bootstrap credentials.

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 commented Jul 30, 2026 •

Copy link
Copy Markdown
Collaborator Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

This was referenced Jul 30, 2026
@coderabbitai

coderabbitai Bot commented Jul 30, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added automatic tool discovery and periodic tool-list refresh for per-user OAuth and header-authenticated MCP connections, including connections without persistent sessions.
    • Retained successfully verified administrator credentials to support ongoing updates.
  • Bug Fixes

    • Improved handling of expired, missing, invalid, and unsupported credentials.
    • Preserved existing tools when refresh or discovery fails.
  • Documentation

    • Clarified sign-in, credential retention, and tool discovery behavior in setup messages.

Walkthrough

The change retains admin credentials for per-user MCP clients. Credential stores resolve OAuth bearer headers or filtered headers. Disconnected per-user clients use these credentials for tool discovery. Storage, token refresh, synchronization, tests, and UI copy support the flow.

Changes

Admin MCP discovery flow

Layer / File(s) Summary
Admin credential contracts and storage
core/schemas/*, framework/configstore/..., framework/mcp_headers/*
Adds admin-mode contracts, client-scoped credential lookup, uniqueness indexes, and upsert behavior.
Admin OAuth resolution and refresh
framework/oauth2/*, framework/configstore/*
Adds admin token resolution and includes admin tokens in proactive refresh.
Admin connection-header resolvers
core/mcp/credstore/*, core/mcp/auth_retry_test.go, core/mcp/reauth_state_test.go
Resolves retained OAuth bearer headers and filtered per-user headers. Unsupported modes return errors.
Verification retention
transports/bifrost-http/handlers/mcp.go, transports/bifrost-http/lib/config_test.go, ui/app/workspace/mcp-registry/views/*
Stores successful admin OAuth credentials or canonicalized headers. Supporting mocks and UI copy describe periodic tool refreshes.
Admin discovery and synchronization
core/mcp/clientmanager.go, core/mcp/toolsync.go, core/mcp/*_test.go
Disconnected per-user clients perform credential-backed discovery. Tests cover success, fail-fast errors, unsupported modes, credential failures, and tool preservation.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant performSync
  participant performAdminToolDiscovery
  participant MCPCredentialStore
  participant MCPServer
  performSync->>performAdminToolDiscovery: discover tools for disconnected per-user client
  performAdminToolDiscovery->>MCPCredentialStore: resolve retained admin headers
  performAdminToolDiscovery->>MCPServer: verify connection and request tools
  MCPServer-->>performSync: return discovered tool mappings
Loading

Possibly related issues

Possibly related PRs

Suggested reviewers: akshaydeo, danpiths, bearts

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 84.38% which is sufficient. The required threshold is 80.00%.
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.
Title check ✅ Passed The title clearly and concisely summarizes retaining admin bootstrap credentials for periodic per-user MCP tool discovery.
Description check ✅ Passed The description follows the template and covers purpose, changes, testing, affected areas, breaking changes, security, and checklist items.
✨ 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 07-30-feat_retain_admin_bootstrap_credential_for_per-user_mcp_tool_discovery

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 `@framework/oauth2/access_token_test.go`:
- Around line 165-176: Update TestAccessToken_MissingToken_ReturnsError to seed
an active token linked to the same OAuth configuration but using the opposite
auth_mode for each getter case, while leaving the requested mode without a
token. Keep the existing missing-token assertions so the test verifies
credentials are isolated between shared and admin modes.
🪄 Autofix

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: CHILL

Plan: Pro Plus

Run ID: a8939b97-5c43-4461-9205-fbbc3938a474

📥 Commits

Reviewing files that changed from the base of the PR and between 2fc7ca2 and ef71e78.

📒 Files selected for processing (37)
  • core/mcp/admin_tool_discovery_test.go
  • core/mcp/auth_retry_test.go
  • core/mcp/clientmanager.go
  • core/mcp/credstore/credstore.go
  • core/mcp/credstore/none.go
  • core/mcp/credstore/none_test.go
  • core/mcp/credstore/per_user_headers.go
  • core/mcp/credstore/per_user_headers_test.go
  • core/mcp/credstore/per_user_oauth.go
  • core/mcp/credstore/per_user_oauth_test.go
  • core/mcp/credstore/shared_headers.go
  • core/mcp/credstore/shared_headers_test.go
  • core/mcp/credstore/shared_oauth.go
  • core/mcp/credstore/shared_oauth_test.go
  • core/mcp/reauth_state_test.go
  • core/mcp/toolsync.go
  • core/mcp/toolsync_test.go
  • core/schemas/mcp.go
  • core/schemas/oauth.go
  • framework/configstore/migrations.go
  • framework/configstore/rdb.go
  • framework/configstore/rdb_mcp_admin_auth_mode_test.go
  • framework/configstore/store.go
  • framework/configstore/tables/mcpheaders.go
  • framework/configstore/tables/mcpoauth2.go
  • framework/mcp_headers/credential_test.go
  • framework/mcp_headers/main.go
  • framework/mcp_headers/main_test.go
  • framework/oauth2/access_token_test.go
  • framework/oauth2/main.go
  • framework/oauth2/sync.go
  • framework/oauth2/sync_test.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/lib/config_test.go
  • ui/app/workspace/mcp-registry/views/mcpClientSheet.tsx
  • ui/app/workspace/mcp-registry/views/mcpHeadersAuthorizer.tsx
  • ui/app/workspace/mcp-registry/views/oauth2Authorizer.tsx
🚧 Files skipped from review as they are similar to previous changes (32)
  • ui/app/workspace/mcp-registry/views/mcpClientSheet.tsx
  • framework/oauth2/sync_test.go
  • core/mcp/clientmanager.go
  • core/mcp/reauth_state_test.go
  • core/mcp/toolsync.go
  • core/schemas/mcp.go
  • framework/configstore/rdb.go
  • core/mcp/credstore/none_test.go
  • transports/bifrost-http/lib/config_test.go
  • ui/app/workspace/mcp-registry/views/oauth2Authorizer.tsx
  • core/schemas/oauth.go
  • core/mcp/credstore/shared_oauth_test.go
  • transports/bifrost-http/handlers/mcp.go
  • core/mcp/credstore/per_user_oauth_test.go
  • core/mcp/credstore/shared_headers.go
  • framework/configstore/tables/mcpoauth2.go
  • framework/mcp_headers/main.go
  • core/mcp/credstore/credstore.go
  • core/mcp/credstore/per_user_headers_test.go
  • core/mcp/admin_tool_discovery_test.go
  • core/mcp/toolsync_test.go
  • core/mcp/credstore/shared_headers_test.go
  • framework/configstore/tables/mcpheaders.go
  • framework/configstore/store.go
  • core/mcp/credstore/none.go
  • framework/configstore/migrations.go
  • core/mcp/auth_retry_test.go
  • ui/app/workspace/mcp-registry/views/mcpHeadersAuthorizer.tsx
  • framework/oauth2/main.go
  • framework/mcp_headers/main_test.go
  • core/mcp/credstore/shared_oauth.go
  • core/mcp/credstore/per_user_oauth.go

Comment thread framework/oauth2/access_token_test.go
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 07-29-feat_add_reauthorize_endpoint_for_shared_mcp_oauth_clients branch from 2fc7ca2 to bd1f787 Compare August 6, 2026 21:53
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 07-30-feat_retain_admin_bootstrap_credential_for_per-user_mcp_tool_discovery branch from ef71e78 to 97de528 Compare August 6, 2026 21:53
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
framework/configstore/rdb_mcp_admin_auth_mode_test.go (1)

22-31: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove manual GORM timestamp initialization.

GORM populates CreatedAt and UpdatedAt during Create. These tests do not assert the supplied values. Remove now, the timestamp fields, and the unused time import.

Based on learnings: models with GORM automatic timestamp fields must rely on GORM rather than time.Now().

Also applies to: 56-61

🤖 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 `@framework/configstore/rdb_mcp_admin_auth_mode_test.go` around lines 22 - 31,
Remove the manual now := time.Now() setup and the CreatedAt and UpdatedAt
assignments from both TableMCPOauthToken fixtures in the test setup. Delete the
now-unused time import and rely on GORM's automatic timestamps during Create.

Source: Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@framework/configstore/rdb_mcp_admin_auth_mode_test.go`:
- Around line 22-31: Remove the manual now := time.Now() setup and the CreatedAt
and UpdatedAt assignments from both TableMCPOauthToken fixtures in the test
setup. Delete the now-unused time import and rely on GORM's automatic timestamps
during Create.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 264b663f-be07-45a0-8e18-8396c29cf28f

📥 Commits

Reviewing files that changed from the base of the PR and between bd1f787 and 97de528.

📒 Files selected for processing (37)
  • core/mcp/admin_tool_discovery_test.go
  • core/mcp/auth_retry_test.go
  • core/mcp/clientmanager.go
  • core/mcp/credstore/credstore.go
  • core/mcp/credstore/none.go
  • core/mcp/credstore/none_test.go
  • core/mcp/credstore/per_user_headers.go
  • core/mcp/credstore/per_user_headers_test.go
  • core/mcp/credstore/per_user_oauth.go
  • core/mcp/credstore/per_user_oauth_test.go
  • core/mcp/credstore/shared_headers.go
  • core/mcp/credstore/shared_headers_test.go
  • core/mcp/credstore/shared_oauth.go
  • core/mcp/credstore/shared_oauth_test.go
  • core/mcp/reauth_state_test.go
  • core/mcp/toolsync.go
  • core/mcp/toolsync_test.go
  • core/schemas/mcp.go
  • core/schemas/oauth.go
  • framework/configstore/migrations.go
  • framework/configstore/rdb.go
  • framework/configstore/rdb_mcp_admin_auth_mode_test.go
  • framework/configstore/store.go
  • framework/configstore/tables/mcpheaders.go
  • framework/configstore/tables/mcpoauth2.go
  • framework/mcp_headers/credential_test.go
  • framework/mcp_headers/main.go
  • framework/mcp_headers/main_test.go
  • framework/oauth2/access_token_test.go
  • framework/oauth2/main.go
  • framework/oauth2/sync.go
  • framework/oauth2/sync_test.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/lib/config_test.go
  • ui/app/workspace/mcp-registry/views/mcpClientSheet.tsx
  • ui/app/workspace/mcp-registry/views/mcpHeadersAuthorizer.tsx
  • ui/app/workspace/mcp-registry/views/oauth2Authorizer.tsx
🚧 Files skipped from review as they are similar to previous changes (33)
  • core/mcp/auth_retry_test.go
  • core/mcp/clientmanager.go
  • core/mcp/credstore/per_user_headers_test.go
  • framework/configstore/tables/mcpheaders.go
  • framework/configstore/tables/mcpoauth2.go
  • ui/app/workspace/mcp-registry/views/mcpHeadersAuthorizer.tsx
  • framework/configstore/rdb.go
  • core/mcp/credstore/shared_oauth.go
  • core/mcp/credstore/per_user_oauth.go
  • transports/bifrost-http/lib/config_test.go
  • framework/oauth2/sync_test.go
  • core/schemas/oauth.go
  • core/mcp/reauth_state_test.go
  • framework/configstore/store.go
  • framework/mcp_headers/main.go
  • core/mcp/credstore/none.go
  • core/mcp/credstore/shared_headers_test.go
  • core/mcp/credstore/shared_headers.go
  • core/mcp/credstore/none_test.go
  • framework/oauth2/main.go
  • core/mcp/credstore/per_user_headers.go
  • core/mcp/credstore/shared_oauth_test.go
  • core/mcp/toolsync_test.go
  • core/schemas/mcp.go
  • core/mcp/admin_tool_discovery_test.go
  • framework/mcp_headers/main_test.go
  • transports/bifrost-http/handlers/mcp.go
  • core/mcp/credstore/credstore.go
  • core/mcp/credstore/per_user_oauth_test.go
  • framework/configstore/migrations.go
  • core/mcp/toolsync.go
  • ui/app/workspace/mcp-registry/views/mcpClientSheet.tsx
  • ui/app/workspace/mcp-registry/views/oauth2Authorizer.tsx

coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 6, 2026
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 07-29-feat_add_reauthorize_endpoint_for_shared_mcp_oauth_clients branch from bd1f787 to ef8c8b2 Compare August 8, 2026 08:43
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 07-30-feat_retain_admin_bootstrap_credential_for_per-user_mcp_tool_discovery branch from 97de528 to ad0fdc1 Compare August 8, 2026 08:43

Pratham-Mishra04 commented Aug 8, 2026 •

Copy link
Copy Markdown
Collaborator Author

Merge activity

  • Aug 8, 8:47 AM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Aug 8, 9:27 AM UTC: Graphite rebased this pull request as part of a merge.
  • Aug 8, 9:28 AM UTC: @Pratham-Mishra04 merged this pull request with Graphite.

@Pratham-Mishra04
Pratham-Mishra04 changed the base branch from 07-29-feat_add_reauthorize_endpoint_for_shared_mcp_oauth_clients to graphite-base/5717 August 8, 2026 09:23
@Pratham-Mishra04
Pratham-Mishra04 changed the base branch from graphite-base/5717 to dev August 8, 2026 09:25
@Pratham-Mishra04
Pratham-Mishra04 dismissed coderabbitai[bot]’s stale review August 8, 2026 09:25

The base branch was changed.

@Pratham-Mishra04
Pratham-Mishra04 requested a review from a team as a code owner August 8, 2026 09:25
…eturnsError to pin shared/admin credential isolation
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 07-30-feat_retain_admin_bootstrap_credential_for_per-user_mcp_tool_discovery branch from ad0fdc1 to db39b12 Compare August 8, 2026 09:26
@Pratham-Mishra04
Pratham-Mishra04 merged commit db4dded into dev Aug 8, 2026
14 of 15 checks passed
@Pratham-Mishra04
Pratham-Mishra04 deleted the 07-30-feat_retain_admin_bootstrap_credential_for_per-user_mcp_tool_discovery branch August 8, 2026 09:28
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