Skip to content

fix(core): don't duplicate MCP servers started during initial reconciliation - #58349

Merged
rekram1-node merged 1 commit into
devfrom
mcp-startup-race
Oct 23, 2026
Merged

rekram1-node merged 1 commit into
devfrom
mcp-startup-race

Conversation

@rekram1-node

Copy link
Copy Markdown
Collaborator

Fixes #52741

On startup, MCP queues a launch for each enabled server. If connect() or disconnect() for that server finishes before the queued launch runs, the launch still goes ahead. That either starts a second copy (the first one's scope gets dropped while still open, so its process leaks) or brings back a server that was just disconnected.

 queued startup, under the server lock
+  if connect/disconnect already handled this server: skip
   startServer()

A skipped launch still opens the startup latch, so calls waiting on the server don't hang. I added a test that runs both the connect and disconnect orderings; both fail without the fix.

…liation

Initial reconciliation can yield before scheduling a server's startup, letting connect() or disconnect() for that server finish first. The queued startup then spawned a second connection, leaking the first, or started a server the user had just disconnected. It now skips once the entry has left its pending state.

Fixes #52741

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

The fix looks correct. I ran the new tests against the base: both fail there (the server initializes twice after connect(), and starts once after disconnect()). Both pass with this change, and the rest of mcp.test.ts and typecheck:tsc show no regressions. Once connect() or disconnect() has finished, the server's status is never pending again. So the guard reliably skips a queued startup, and opening the latch keeps anything waiting on the server from hanging.

Public API: unchanged.

The fix is 8 lines and the test is 47. The test also depends on a bus listener pausing reconciliation at an internal point, so it can be dropped to keep the change minimal.

@rekram1-node
rekram1-node merged commit 8ecd6ad into dev Oct 23, 2026
13 checks passed
@rekram1-node
rekram1-node deleted the mcp-startup-race branch October 23, 2026 05:52
AbizardShafwan pushed a commit to AbizardShafwan/opencode that referenced this pull request Nov 9, 2026
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.

mcp: initial reconciliation can duplicate stdio servers

1 participant