Skip to content

fixes race condition while loading mcp servers from config file#1470

Merged
akshaydeo merged 1 commit intomainfrom
01-29-fixes_race_condition_while_loading_mcp_servers_from_config_file
Jan 29, 2026
Merged

fixes race condition while loading mcp servers from config file#1470
akshaydeo merged 1 commit intomainfrom
01-29-fixes_race_condition_while_loading_mcp_servers_from_config_file

Conversation

@akshaydeo
Copy link
Copy Markdown
Contributor

@akshaydeo akshaydeo commented Jan 29, 2026

Support for MCP Client Names in Virtual Key Configurations

This PR adds support for referencing MCP clients by name in virtual key configurations, making it easier to define MCP access permissions in config files.

Issue

Closes #1462

Changes

  • Added support for mcp_client_name in virtual key MCP configurations, allowing config files to reference MCP clients by their human-readable names instead of database IDs
  • Implemented a resolution mechanism that converts MCP client names to their corresponding database IDs during config loading
  • Added graceful handling for unresolvable MCP client names, with appropriate warning logs
  • Created a new example configuration demonstrating the feature with PostgreSQL and MCP clients
  • Added comprehensive tests to verify the name resolution works correctly

Type of change

  • Feature
  • Bug fix

Affected areas

  • Core (Go)
  • Transports (HTTP)

How to test

# Run the tests for the new functionality
go test ./transports/bifrost-http/lib -run TestSQLite_VKMCPConfig_MCPClientNameResolution
go test ./transports/bifrost-http/lib -run TestSQLite_VKMCPConfig_MCPClientNameNotFound

# Try the example config
cp examples/configs/withpostgresmcpclientsinconfig/config.json ./config.json
# Set required environment variables
# Start Bifrost

Breaking changes

  • No

Related issues

Fixes an issue where virtual keys with MCP configurations in config files would fail with foreign key constraint violations when using client names instead of IDs.

Security considerations

No security implications - this is a usability improvement that maintains the same access control model.

Checklist

  • I added/updated tests where appropriate
  • I added example configuration

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 29, 2026

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added support for referencing MCP clients by name in configuration files instead of IDs.
    • Introduced new configuration example demonstrating PostgreSQL-backed deployment with governance and MCP client integrations.
  • Tests

    • Added tests for MCP client name resolution and graceful handling of missing clients.

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

Adds support for resolving MCP client names from config files to MCP client IDs before persisting virtual-key MCP configs, plus example JSON configs, tests, and a script config entry to exercise the new workflow.

Changes

Cohort / File(s) Summary
Config examples
\.github/workflows/configs/withpostgresmcpclientsinconfig/config.json, examples/configs/withpostgresmcpclientsinconfig/config.json
New JSON configs demonstrating PostgreSQL-backed stores, MCP clients referenced by name, virtual keys with mcp_configs, governance plugin, and env-backed secrets.
Config parsing model
framework/configstore/tables/virtualkey.go
Added MCPClientName string to TableVirtualKeyMCPConfig and UnmarshalJSON to accept mcp_client_name (config format) alongside mcp_client_id (DB format).
Name resolution logic
transports/bifrost-http/lib/config.go
Added non-exported resolveMCPConfigClientIDs(...) to look up MCP client IDs by name, filter out unresolved entries, log warnings, and invoked it at governance merge/create paths to avoid FK failures.
Tests
transports/bifrost-http/lib/config_test.go
Added tests verifying successful resolution of mcp_client_name to IDs and graceful skipping when names are not found.
Release/test script
.github/workflows/scripts/release-bifrost-http.sh
Added withpostgresmcpclientsinconfig to the CONFIGS_TO_TEST array to include the new example in CI runs.

Sequence Diagram

sequenceDiagram
    participant CF as Config File
    participant Parser as Config Parser
    participant Resolver as Name Resolver
    participant Store as ConfigStore
    participant DB as PostgreSQL

    CF->>Parser: load config.json (virtual_keys + mcp_configs with mcp_client_name)
    Parser->>Resolver: resolveMCPConfigClientIDs(mcp_configs, vk_id)
    Resolver->>Store: query MCP clients by name
    Store->>DB: SELECT id FROM config_mcp_clients WHERE name IN (...)
    DB-->>Store: return matching ids
    Store-->>Resolver: matched ids
    Resolver->>Resolver: map names -> ids, drop unresolved (warn)
    Resolver-->>Parser: return filtered mcp_configs with mcp_client_id
    Parser->>Store: persist virtual key and governance_virtual_key_mcp_configs
    Store->>DB: INSERT governance_virtual_key_mcp_configs (...) (FK satisfied)
    DB-->>Store: success
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Poem

🐇 I hopped through configs, sniffed each name,
Found MCP friends and matched their fame.
Skipped the strangers with a gentle thump,
Now VKs link cleanly—no startup bump.
Hooray for names that finally find home!

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The PR title mentions fixing a race condition but the description focuses on adding MCP client name support, making the title vague about the actual primary change. Clarify the title to better reflect the main change: either 'Add MCP client name support in virtual key configurations' or 'Resolve MCP client names to IDs during config loading' would be more descriptive.
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The PR description covers the main changes, affected areas, testing instructions, and security considerations as required by the template, though Type of change and Checklist sections could be more complete.
Linked Issues check ✅ Passed The PR successfully addresses all objectives from issue #1462: prevents foreign key violations by resolving MCP client names to IDs during config loading and gracefully skips unresolved clients with warnings.
Out of Scope Changes check ✅ Passed All changes are directly related to the linked issue #1462: adding MCP client name resolution, implementing resolution logic, adding tests, and providing example configurations.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 01-29-fixes_race_condition_while_loading_mcp_servers_from_config_file

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

Copy link
Copy Markdown
Contributor Author

akshaydeo commented Jan 29, 2026

@akshaydeo akshaydeo mentioned this pull request Jan 29, 2026
16 tasks
@akshaydeo akshaydeo marked this pull request as ready for review January 29, 2026 11:57
@akshaydeo akshaydeo force-pushed the 01-29-fixes_race_condition_while_loading_mcp_servers_from_config_file branch 2 times, most recently from 9f543d0 to d46d034 Compare January 29, 2026 12:03
@akshaydeo akshaydeo force-pushed the 01-19-feat_plugin_schemas_refactor_and_added_mcp_plugins branch from 10fddc2 to 3be2a0e Compare January 29, 2026 12:03
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

🤖 Fix all issues with AI agents
In @.github/workflows/scripts/release-bifrost-http.sh:
- Around line 154-155: The test config named withpostgresmcpclientsinconfig
references MCP HTTP endpoints (WeatherService at localhost:8080/mcp and
CalendarService at localhost:8081/mcp) that are not provided by the compose
setup and cause Bifrost to fail on init; fix this by either adding lightweight
MCP mock servers to your docker-compose so those endpoints exist, or edit the
withpostgresmcpclientsinconfig test config to set those MCP clients' is_enabled
to false (or mark them optional/fault-tolerant) so Bifrost won’t attempt
mandatory connections to localhost:8080/mcp and localhost:8081/mcp during
integration tests.

In `@transports/bifrost-http/lib/config_test.go`:
- Around line 9927-9997: The inline JSON uses tempDir unescaped and a hardcoded
"/" path separator which breaks on Windows; change to build paths with
filepath.Join (e.g., dbPath := filepath.Join(tempDir, "config.db") and cfgPath
:= filepath.Join(tempDir, "config.json")) and inject the DB path into configJSON
using a JSON-quoted %q (e.g., fmt.Sprintf(..., dbPath, keyID)) so backslashes
are escaped; finally use os.WriteFile(cfgPath, []byte(configJSON), 0644) to
write the file. Ensure you update the other similar test block the same way.
🧹 Nitpick comments (2)
transports/bifrost-http/lib/config.go (1)

1983-2033: Cache MCP client lookups to avoid repeated DB hits.

If multiple MCP configs reference the same client name, a small cache prevents duplicate GetMCPClientByName calls.

♻️ Suggested cache to avoid duplicate lookups
 func resolveMCPConfigClientIDs(
 	ctx context.Context,
 	store configstore.ConfigStore,
 	mcpConfigs []configstoreTables.TableVirtualKeyMCPConfig,
 	virtualKeyID string,
 ) []configstoreTables.TableVirtualKeyMCPConfig {
 	if store == nil || len(mcpConfigs) == 0 {
 		return mcpConfigs
 	}
 
 	resolvedConfigs := make([]configstoreTables.TableVirtualKeyMCPConfig, 0, len(mcpConfigs))
+	resolvedByName := make(map[string]*configstoreTables.TableMCPClient)
 
 	for i := range mcpConfigs {
 		mc := &mcpConfigs[i]
 
 		// If MCPClientID is already set (e.g., from database or direct construction), keep it
 		if mc.MCPClientID != 0 {
 			resolvedConfigs = append(resolvedConfigs, *mc)
 			continue
 		}
 
 		// If MCPClientName is set (from config.json parsing), resolve it to MCPClientID
 		if mc.MCPClientName != "" {
+			if cached, ok := resolvedByName[mc.MCPClientName]; ok {
+				if cached != nil {
+					mc.MCPClientID = cached.ID
+					resolvedConfigs = append(resolvedConfigs, *mc)
+				}
+				continue
+			}
 			mcpClient, err := store.GetMCPClientByName(ctx, mc.MCPClientName)
 			if err != nil {
 				logger.Warn("virtual key %s: failed to resolve MCP client '%s': %v (skipping this MCP config)",
 					virtualKeyID, mc.MCPClientName, err)
+				resolvedByName[mc.MCPClientName] = nil
 				continue
 			}
 			if mcpClient == nil {
 				logger.Warn("virtual key %s: MCP client '%s' not found (skipping this MCP config)",
 					virtualKeyID, mc.MCPClientName)
+				resolvedByName[mc.MCPClientName] = nil
 				continue
 			}
+			resolvedByName[mc.MCPClientName] = mcpClient
 			mc.MCPClientID = mcpClient.ID
 			resolvedConfigs = append(resolvedConfigs, *mc)
 			continue
 		}
examples/configs/withpostgresmcpclientsinconfig/config.json (1)

1-139: Consider env placeholders for Postgres passwords in the example.

This encourages safer copy‑paste defaults for users consuming the example config.

💡 Example tweak
-      "password": "bifrost_password",
+      "password": "env.BIFROST_DB_PASSWORD",
-      "password": "bifrost_password",
+      "password": "env.BIFROST_DB_PASSWORD",

Comment thread .github/workflows/scripts/release-bifrost-http.sh
Comment thread transports/bifrost-http/lib/config_test.go
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

🤖 Fix all issues with AI agents
In `@transports/bifrost-http/lib/config_test.go`:
- Around line 9925-9997: The JSON string interpolates raw tempDir which on
Windows can include backslashes that form invalid JSON escape sequences; fix by
building filesystem paths with filepath.Join (e.g., path :=
filepath.Join(tempDir, "config.db") and cfgPath := filepath.Join(tempDir,
"config.json")) and inject them into configJSON using a quoted format (use %q or
strconv.Quote) so the path is properly escaped in the JSON string, and use
cfgPath in the os.WriteFile call instead of tempDir+"/config.json"; update the
code sections that set configJSON and write the file (variables: configJSON,
tempDir, and the os.WriteFile call).

In `@transports/bifrost-http/lib/config.go`:
- Around line 1352-1354: The comment incorrectly claims the
resolveMCPConfigClientIDs call is "done outside the transaction setup" even
though it is invoked inside the ExecuteTransaction callback; update the comment
near the resolveMCPConfigClientIDs(ctx, config.ConfigStore, mcpConfigs,
virtualKey.ID) call to accurately state that the resolution runs within the
transaction context (or simply remove the "outside the transaction" phrase), and
keep mention that it only performs reads against config.ConfigStore and operates
on mcpConfigs and virtualKey.ID.
🧹 Nitpick comments (1)
.github/workflows/configs/withpostgresmcpclientsinconfig/config.json (1)

21-28: Hardcoded database credentials in workflow config.

The password bifrost_password is hardcoded. While this is acceptable for CI test configurations, consider using environment variable references (e.g., "password": "env.POSTGRES_PASSWORD") for consistency with other sensitive values in this file that already use the env. pattern (like env.BIFROST_ADMIN_PASSWORD).

♻️ Optional: Use environment variables for database credentials
   "config_store": {
     "enabled": true,
     "type": "postgres",
     "config": {
       "host": "localhost",
       "port": "5432",
       "user": "bifrost",
-      "password": "bifrost_password",
+      "password": "env.POSTGRES_PASSWORD",
       "db_name": "bifrost",
       "ssl_mode": "disable"
     }
   },
   "logs_store": {
     "enabled": true,
     "type": "postgres",
     "config": {
       "host": "localhost",
       "port": "5432",
       "user": "bifrost",
-      "password": "bifrost_password",
+      "password": "env.POSTGRES_PASSWORD",
       "db_name": "bifrost",
       "ssl_mode": "disable"
     }
   },

Also applies to: 33-40

Comment thread transports/bifrost-http/lib/config_test.go
Comment thread transports/bifrost-http/lib/config.go
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: 0

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)

1048-1116: Config hash computed before MCPClientName resolution; mcp_client_name-only changes undetected.

GenerateVirtualKeyHash is called at line 1050 with MCPClientID = 0 (from JSON unmarshaling), but resolveMCPConfigClientIDs is invoked later at lines 1084-1086 and 1114-1116. Since the hash includes only MCPClientID and ToolsToExecute in VirtualKeyMCPConfigHashInput, a change to mcp_client_name alone will produce the same hash (both resolve from MCPClientID = 0). This prevents detection and synchronization of name-only changes from config.json.

Resolve MCP client names before computing the hash, or update GenerateVirtualKeyHash to include MCPClientName when MCPClientID is zero.

🧹 Nitpick comments (1)
examples/configs/withpostgresmcpclientsinconfig/config.json (1)

18-39: Consider env-backed Postgres passwords in examples.

Even for sample configs, using env.* for the password reduces the chance of users copying plaintext secrets into production configs.

Suggested change
-      "password": "bifrost_password",
+      "password": "env.BIFROST_POSTGRES_PASSWORD",
...
-      "password": "bifrost_password",
+      "password": "env.BIFROST_POSTGRES_PASSWORD",

@akshaydeo akshaydeo force-pushed the 01-19-feat_plugin_schemas_refactor_and_added_mcp_plugins branch from 3be2a0e to 95e2529 Compare January 29, 2026 13:21
@akshaydeo akshaydeo force-pushed the 01-29-fixes_race_condition_while_loading_mcp_servers_from_config_file branch from d46d034 to 8f6f420 Compare January 29, 2026 13:21
Base automatically changed from 01-19-feat_plugin_schemas_refactor_and_added_mcp_plugins to main January 29, 2026 13:32
Copy link
Copy Markdown
Contributor Author

akshaydeo commented Jan 29, 2026

Merge activity

  • Jan 29, 1:33 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Jan 29, 1:34 PM UTC: @akshaydeo merged this pull request with Graphite.

@akshaydeo akshaydeo merged commit ab685cf into main Jan 29, 2026
1 of 3 checks passed
@akshaydeo akshaydeo deleted the 01-29-fixes_race_condition_while_loading_mcp_servers_from_config_file branch January 29, 2026 13:34
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.

[Bug]: Virtual key mcp_configs fail on startup due to race condition with MCP client registration

1 participant