Skip to content

fix: enable per-call tool discovery for shared oauth/headers/none MCP clients and rename UpdateClientConnection to UpdateClientCredentials - #5970

Merged
Pratham-Mishra04 merged 1 commit into
devfrom
08-07-fix_discover_tools_synchronously_for_per-call_mcp_clients_fix_shared-oauth_reconnect_verify_errors
Aug 8, 2026
Merged

fix: enable per-call tool discovery for shared oauth/headers/none MCP clients and rename UpdateClientConnection to UpdateClientCredentials#5970
Pratham-Mishra04 merged 1 commit into
devfrom
08-07-fix_discover_tools_synchronously_for_per-call_mcp_clients_fix_shared-oauth_reconnect_verify_errors

Conversation

@Pratham-Mishra04

Copy link
Copy Markdown
Collaborator

Summary

Fixes a family of related bugs affecting shared MCP clients (auth_type: oauth, headers, or none) running in per-call mode (needs_session_stickiness nil/false): these clients never had their tools discovered after registration, got stuck in pending_verification after completing OAuth, and were incorrectly described as "per-user auth clients" in error messages. Additionally renames UpdateClientConnectionUpdateClientCredentials across the stack for clarity.

Changes

  • Tool discovery for shared per-call clients: performAdminToolDiscovery previously rejected oauth, headers, and none auth types outright. It now routes them through the same bearer/headers dispatch paths as their per-user counterparts, since AdminConnectionHeaders for these resolvers now delegates to ConnectionHeaders when the client is per-call.
  • AdminConnectionHeaders on shared resolvers (sharedOAuthResolver, sharedHeadersResolver, noneResolver): changed from unconditional errors to delegating to ConnectionHeaders for per-call clients, and erroring only for sticky clients (which should never reach this method).
  • VerifyHeadersConnection guard relaxed: the "user headers are required" guard now only applies when auth_type == per_user_headers; shared headers/none clients may legitimately resolve empty headers.
  • Synchronous first-discovery pass in AddClient and UpdateClientCredentials: shared per-call clients with no DiscoveredTools to restore now get an immediate tool discovery pass rather than waiting for the periodic checker's first slow tick (which uses healthyInterval, not the fast unstable interval).
  • Connection checker started for per-call clients in AddClient: the per-call branch of AddClient now starts a ClientConnectionChecker, matching what connectToMCPClient already does for sticky clients.
  • UpdateClientCredentials handles PendingVerification per-call clients: a per-call client completing its first OAuth flow (still in pending_verification) is now correctly transitioned to Healthy with a checker started, instead of being stuck permanently.
  • ErrMCPReconnectNotApplicable returned for shared per-call clients: ReconnectClient, CloseAndMarkNeedsReauth, and UpdateClientCredentials now return the same sentinel for shared per-call clients as for genuine per-user clients, with corrected error messages that no longer say "per-user auth clients."
  • HTTP handler treats ErrMCPReconnectNotApplicable as a non-error: completeMCPClientOAuth no longer rolls back the DB update or returns a 500 when the client is per-call — the fresh credential is already live for the next dial.
  • Retry loop short-circuits on ErrMCPReconnectNotApplicable: updateMCPClientCredentialsWithRetry returns immediately on this sentinel instead of burning retry attempts (the sentinel's message contains "reconnect", which previously matched the transient-retry substring check).
  • Rename UpdateClientConnectionUpdateClientCredentials across core/bifrost.go, core/mcp/clientmanager.go, core/mcp/interface.go, transports/bifrost-http/handlers/mcp.go, transports/bifrost-http/lib/config.go, and transports/bifrost-http/server/server.go.
  • UI canReconnectMCPClient utility: replaces the inline isPerUserAuth check with a function that mirrors credstore.RequiresPerCallConnection exactly — shared HTTP clients with needs_session_stickiness not explicitly true are also excluded from the Reconnect action.
  • UI: removed stale pre-opened OAuth popup: the shared-OAuth authorize path no longer pre-opens a blank popup before the confirm dialog, eliminating a stray blank tab that appeared alongside the dialog.
  • UI table layout fixes: header row now uses sticky top-0 z-20 with correct bg-muted (not bg-muted/50), and the table container uses overflow-hidden/overflow-auto correctly to keep the header pinned.
  • maps.Copy in SetClientTools: replaces the manual loop.

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/... ./transports/...

# UI
cd ui
pnpm i
pnpm test
pnpm build

Manual verification:

  1. Create a shared oauth or headers MCP client with needs_session_stickiness unset (default). Confirm tools are populated immediately after registration without waiting for the periodic checker.
  2. Complete an OAuth flow for a config.json-bootstrapped shared-OAuth client. Confirm it transitions from pending_verification to healthy with tools discovered, and does not return a 500.
  3. Attempt Reconnect on a shared per-call client. Confirm the Reconnect action is absent from the UI actions menu and the backend returns ErrMCPReconnectNotApplicable (not an opaque error).
  4. Authorize a shared-OAuth client. Confirm no stray blank tab opens alongside the confirm dialog.

Breaking changes

  • Yes

UpdateClientConnection / UpdateMCPClientConnection / UpdateClientConnection are renamed to UpdateClientCredentials / UpdateMCPClientCredentials / UpdateClientCredentials everywhere. Any external code calling these methods by name must be updated.

Security considerations

AdminConnectionHeaders for shared resolvers now resolves the same credential a real tool call would (no separate admin credential exists for these types). This is intentional and consistent with how per-call tool discovery already works for per-user auth types. Sticky clients are explicitly guarded and will still error if they somehow reach this path.

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 Aug 8, 2026

Copy link
Copy Markdown
Collaborator Author

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

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added support for shared OAuth, header-based, and unauthenticated MCP clients in per-call connections.
    • Tools are discovered automatically when clients are added or credentials are updated.
    • Reconnect actions now reflect whether a client has a persistent connection.
    • OAuth authorization dialogs now manage their own popup windows.
  • Bug Fixes

    • Credential updates for per-call clients no longer trigger unnecessary reconnect retries or failures.
    • Improved handling of empty or shared authentication headers.
  • API Updates

    • Renamed the MCP client connection update operation to “Update MCP Client Credentials.”

Walkthrough

The PR renames MCP credential-update APIs and extends per-call client support. Shared OAuth, headers, and none-auth clients can resolve credentials, discover tools, transition from pending verification, and start monitoring. HTTP handlers and the UI now use reconnect eligibility.

Changes

MCP credential and discovery flow

Layer / File(s) Summary
Credential-update API rename
core/bifrost.go, core/mcp/interface.go, core/mcp/clientmanager.go, transports/bifrost-http/...
Renames UpdateClientConnection and UpdateMCPClientConnection to credential-specific API names across interfaces, implementations, callbacks, and configuration.
Per-call discovery and credential resolution
core/mcp/clientmanager.go, core/mcp/credstore/*, core/mcp/*_test.go
Shared OAuth, headers, and none-auth per-call clients resolve admin headers, discover tools during setup, and start connection checkers.
Credential updates and lifecycle handling
core/mcp/clientmanager.go, core/mcp/connectionchecker.go, core/mcp/*_test.go
Pending-verification clients apply credentials, restore tools, perform discovery, become healthy, and start monitoring. Healthy per-call clients return ErrMCPReconnectNotApplicable.
HTTP flows and UI reconnect controls
transports/bifrost-http/handlers/*, ui/app/workspace/mcp-registry/views/*
Retry handling stops for non-applicable reconnects. OAuth flows accept successful per-call refreshes. The UI centralizes reconnect eligibility and removes pre-opened OAuth popups.

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

Sequence Diagram(s)

sequenceDiagram
  participant OAuthFlow
  participant MCPManager
  participant CredentialStore
  participant MCPServer
  participant ConnectionChecker
  OAuthFlow->>MCPManager: update MCP client credentials
  MCPManager->>CredentialStore: resolve per-call headers
  CredentialStore->>MCPServer: send resolved credentials
  MCPServer-->>MCPManager: return discovered tools
  MCPManager->>ConnectionChecker: start monitoring
  MCPManager-->>OAuthFlow: complete refresh or return not-applicable
Loading

Possibly related PRs

Suggested reviewers: akshaydeo, danpiths

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 87.50% 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 summarizes the main fixes for shared per-call MCP clients and the API rename.
Description check ✅ Passed The description covers the required sections, explains the changes, documents testing, and identifies the breaking API rename.
✨ 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 08-07-fix_discover_tools_synchronously_for_per-call_mcp_clients_fix_shared-oauth_reconnect_verify_errors

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

Pratham-Mishra04 commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator Author

Merge activity

  • Aug 8, 10:33 AM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Aug 8, 11:37 AM UTC: Graphite rebased this pull request as part of a merge.
  • Aug 8, 11:38 AM UTC: @Pratham-Mishra04 merged this pull request with Graphite.

@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

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/lib/config.go (1)

6719-6740: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Mirror PendingOAuthConfig after the runtime update.

UpdateClientCredentials treats newConfig.PendingOAuthConfig == nil as a meaningful authorization-complete state. This method updates only Headers and OauthConfigID. The old pending OAuth block can remain in MCPConfig after the runtime client has cleared it.

Set cc.PendingOAuthConfig = newConfig.PendingOAuthConfig while holding muMCP. This keeps later reads and reloads consistent with the live client.

Proposed fix
 				if newConfig.OauthConfigID != nil {
 					cc.OauthConfigID = newConfig.OauthConfigID
 				}
+				cc.PendingOAuthConfig = newConfig.PendingOAuthConfig
 				break

As per path instructions, keep runtime config updates and persistent store updates rollback-aware.

🤖 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 6719 - 6740, Update the
MCPConfig synchronization in the client-update method to assign
cc.PendingOAuthConfig from newConfig.PendingOAuthConfig while muMCP is held,
including nil to represent authorization completion. Keep the existing Headers
and OauthConfigID updates unchanged, and preserve the current rollback-aware
handling for runtime and persistent-store updates.

Source: Path instructions

🤖 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 `@core/mcp/clientmanager_test.go`:
- Around line 326-409: Register the test manager for cleanup immediately after
each NewMCPManager call in the two tests, using t.Cleanup to invoke m.Cleanup
before the test exits. Ensure the connection checkers started by
UpdateClientCredentials are stopped, including for the HTTP-backed discovery
test.

---

Outside diff comments:
In `@transports/bifrost-http/lib/config.go`:
- Around line 6719-6740: Update the MCPConfig synchronization in the
client-update method to assign cc.PendingOAuthConfig from
newConfig.PendingOAuthConfig while muMCP is held, including nil to represent
authorization completion. Keep the existing Headers and OauthConfigID updates
unchanged, and preserve the current rollback-aware handling for runtime and
persistent-store updates.
🪄 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: bd58f323-7f2b-424b-9dee-f6f005cec42e

📥 Commits

Reviewing files that changed from the base of the PR and between 5e6b9eb and bdd683f.

📒 Files selected for processing (23)
  • core/bifrost.go
  • core/mcp/addclient_discoveredtools_test.go
  • core/mcp/admin_tool_discovery_test.go
  • core/mcp/clientmanager.go
  • core/mcp/clientmanager_test.go
  • core/mcp/connectionchecker.go
  • core/mcp/connectionchecker_test.go
  • core/mcp/credstore/none.go
  • core/mcp/credstore/none_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/interface.go
  • core/mcp/reauth_state_test.go
  • core/schemas/mcp.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/mcp_updateclientcredentials_retry_test.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/server/server.go
  • ui/app/workspace/mcp-registry/views/mcpClientsTable.tsx
  • ui/app/workspace/mcp-registry/views/mcpClientsTable.utils.test.ts
  • ui/app/workspace/mcp-registry/views/mcpClientsTable.utils.ts

Comment on lines +326 to +409
func TestUpdateClientCredentials_PerCallSharedOAuth_PendingVerification_TransitionsToHealthy(t *testing.T) {
m := NewMCPManager(context.Background(), schemas.MCPConfig{}, nil, nil, nil)
config := &schemas.MCPClientConfig{
ID: "client-pending-percall",
Name: "pending-percall-client",
AuthType: schemas.MCPAuthTypeOauth,
ConnectionType: schemas.MCPConnectionTypeHTTP,
}

m.mu.Lock()
m.clientMap[config.ID] = &schemas.MCPClientState{
Name: config.Name,
ExecutionConfig: config,
State: schemas.MCPConnectionStatePendingVerification,
ToolMap: make(map[string]schemas.ChatTool),
ToolNameMapping: make(map[string]string),
}
m.mu.Unlock()

err := m.UpdateClientCredentials(config.ID, config)
require.NoError(t, err, "the first connection out of pending_verification must succeed, not report not-applicable")

m.mu.RLock()
state := *m.clientMap[config.ID]
m.mu.RUnlock()
assert.Equal(t, schemas.MCPConnectionStateHealthy, state.State, "must transition out of pending_verification, mirroring AddClient's own per-call setup")

// Without a connection checker, nothing would ever discover this
// client's tools afterward (no persistent Conn, no DiscoveredTools to
// restore for a client completing its first connection here) — see
// TestAddClient_PerCallConnection_StartsConnectionChecker for the
// AddClient-side counterpart of this same fix.
m.checkerManager.mu.RLock()
_, hasChecker := m.checkerManager.checkers[config.ID]
m.checkerManager.mu.RUnlock()
assert.True(t, hasChecker, "must start a connection checker so tools actually get discovered")

// A second call, now that the client is already Healthy, is the plain
// reauthorize case: genuinely nothing left to do.
err = m.UpdateClientCredentials(config.ID, config)
require.Error(t, err)
assert.True(t, errors.Is(err, schemas.ErrMCPReconnectNotApplicable))
}

// TestUpdateClientCredentials_PerCallSharedType_PendingVerification_DiscoversToolsSynchronously
// pins the second half of the same bug report: a connection checker alone
// is not enough, because ClientConnectionChecker.Start uses the SLOW
// healthyInterval (not the fast Unstable one) for a client that already
// starts Healthy — so without a synchronous first discovery pass, a shared
// oauth/headers/none per-call client completing OAuth/verification here
// would sit at Healthy/0-tools for up to healthyInterval before its first
// real check ever ran.
func TestUpdateClientCredentials_PerCallSharedType_PendingVerification_DiscoversToolsSynchronously(t *testing.T) {
ts, _ := buildAdminDiscoveryHTTPServer(t)

m := NewMCPManager(context.Background(), schemas.MCPConfig{}, nil, nil, nil)
config := &schemas.MCPClientConfig{
ID: "client-pending-percall-sync",
Name: "pending-percall-client-sync",
AuthType: schemas.MCPAuthTypeHeaders,
ConnectionType: schemas.MCPConnectionTypeHTTP,
ConnectionString: schemas.NewSecretVar(ts.URL),
Headers: map[string]schemas.SecretVar{"Authorization": *schemas.NewSecretVar("Bearer shared-update-token")},
}

m.mu.Lock()
m.clientMap[config.ID] = &schemas.MCPClientState{
Name: config.Name,
ExecutionConfig: config,
State: schemas.MCPConnectionStatePendingVerification,
ToolMap: make(map[string]schemas.ChatTool),
ToolNameMapping: make(map[string]string),
}
m.mu.Unlock()

err := m.UpdateClientCredentials(config.ID, config)
require.NoError(t, err)

m.mu.RLock()
state := *m.clientMap[config.ID]
m.mu.RUnlock()
assert.Equal(t, schemas.MCPConnectionStateHealthy, state.State)
assert.Contains(t, state.ToolMap, "pending-percall-client-sync-echo", "tools must be discovered synchronously, not deferred entirely to the periodic checker")
}

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.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Stop the connection checkers after each test.

These tests call UpdateClientCredentials, which starts a connection checker. The test-local MCPManager remains active after the test returns. The HTTP-backed test can continue to run after its test server closes.

Register m.Cleanup() with t.Cleanup immediately after NewMCPManager.

Proposed fix
 	m := NewMCPManager(context.Background(), schemas.MCPConfig{}, nil, nil, nil)
+	t.Cleanup(func() {
+		require.NoError(t, m.Cleanup())
+	})
🤖 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 `@core/mcp/clientmanager_test.go` around lines 326 - 409, Register the test
manager for cleanup immediately after each NewMCPManager call in the two tests,
using t.Cleanup to invoke m.Cleanup before the test exits. Ensure the connection
checkers started by UpdateClientCredentials are stopped, including for the
HTTP-backed discovery test.

@Pratham-Mishra04
Pratham-Mishra04 changed the base branch from 08-06-feat_add_vk_and_users_filters_to_oauth_grants_sidebar to graphite-base/5970 August 8, 2026 11:33
@Pratham-Mishra04
Pratham-Mishra04 changed the base branch from graphite-base/5970 to dev August 8, 2026 11:36
@Pratham-Mishra04
Pratham-Mishra04 requested a review from a team as a code owner August 8, 2026 11:36
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 08-07-fix_discover_tools_synchronously_for_per-call_mcp_clients_fix_shared-oauth_reconnect_verify_errors branch from bdd683f to 22f7baa Compare August 8, 2026 11:36
@Pratham-Mishra04
Pratham-Mishra04 merged commit 95e79e4 into dev Aug 8, 2026
14 of 15 checks passed
@Pratham-Mishra04
Pratham-Mishra04 deleted the 08-07-fix_discover_tools_synchronously_for_per-call_mcp_clients_fix_shared-oauth_reconnect_verify_errors branch August 8, 2026 11:38
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