Skip to content

fix(core): start newly added MCP servers in the background - #53813

Open
opencode-agent[bot] wants to merge 2 commits into
v2from
fix/mcp-background-startup
Open

opencode-agent[bot] wants to merge 2 commits into
v2from
fix/mcp-background-startup

Conversation

@opencode-agent

@opencode-agent opencode-agent Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Issue for this PR

Regression from #53775.

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Since #53775, a slow MCP server delays startup: models and /connect stay empty until every configured MCP server has connected, and the servers connect one after another instead of in parallel.

Cause: the supervisor now activates the guarded plugins (OpencodePlugin, ConfigPolicyPlugin) first. Both register ctx.mcp.transform, so MCP's first reconcile runs before the config MCP plugin has added any servers. Mcp only started servers in the background on that first reconcile (!applied && entries.size === 0). The config servers arrive on a later reconcile and go through replaceServer, which awaits startServer for each server in turn. That happens inside the plugin activation batch's notify, so the rest of plugin activation waits for it.

Fix: any server that is new to the reconcile now starts in the background. The special first-reconcile branch is removed, so new servers take the same path whether they arrive first or later. Readers still wait on each entry's startup latch. Config changes to existing servers still replace them synchronously, as before. MCP.add (HTTP API, ACP session MCPs) now waits for its own server's startup latch, so it still returns once that server has settled.

A background start now checks that its entry is still current once it holds the per-server lock. Without that check, a server removed or replaced before its start ran was still spawned from the stale entry and kept running, untracked, until the Location closed. The first-reconcile path on v2 used the same fork pattern without this check.

Remote endpoint startup is still serialized per URL (endpointLoads), because that lock lives inside startServer.

How did you verify your code works?

  • Ran serve in a clean HOME with two stdio MCP servers that each take 5s to start, polling model.list, integration.list and /api/mcp:
  • New regression test does not wait for MCP servers added after the first reconcile to start: fails on current v2 (the transform hangs) and passes here
  • New test does not start an MCP server removed before its background start: fails without the stale-entry check, passes with it
  • Updated manages live MCP servers entirely through scoped transforms, which asserted that a newly added server was already connected when its transform returned (the blocking behavior); it now waits for the server to settle
  • bun test test/mcp.test.ts (80 pass); MCP, plugin and test/plugin/ suites (435 pass); bun run typecheck in packages/core

Screenshots / recordings

N/A

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Requested by: @neriousy (Filip via Slack)

The guarded early plugin activation from #53775 runs the first MCP
reconcile before config MCP servers are registered. Servers added on a
later reconcile then went through the replace path, which connects them
one at a time while the plugin activation batch waits, so providers and
integrations did not appear until the slowest server finished starting.

Start every server that is new to the reconcile in the background, and
keep an explicit MCP.add waiting for its own server to settle.
A server removed or replaced before its background start acquired the per-server lock was still started from its stale entry, leaving an untracked process running until the Location closed.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant