Skip to content

fix(opencode): auto-reconnect MCP clients on unexpected transport close#31312

Open
sulthonzh wants to merge 2 commits into
anomalyco:devfrom
sulthonzh:fix/mcp-onclose-reconnect
Open

fix(opencode): auto-reconnect MCP clients on unexpected transport close#31312
sulthonzh wants to merge 2 commits into
anomalyco:devfrom
sulthonzh:fix/mcp-onclose-reconnect

Conversation

@sulthonzh
Copy link
Copy Markdown

@sulthonzh sulthonzh commented Jun 8, 2026

Issue for this PR

Closes #17099

Type of change

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

What does this PR do?

When an MCP server process dies or the HTTP transport drops, the client stays in connected state with stale tool definitions. Tools vanish permanently for the session because nothing listens for transport closure.

The watch() function only subscribes to ToolListChangedNotification. It never registers an onclose handler on the client, so when the SDK fires client.onclose after transport death, nothing handles it.

This PR registers client.onclose in watch() that:

  1. Checks s.clients[name] !== client to skip intentional disconnects
  2. Marks status as failed and clears cached defs
  3. Calls createAndStore() with the original config to reconnect
  4. Publishes ToolsChanged so the UI updates

To prevent intentional disconnects from triggering reconnect, closeClient() now deletes s.clients[name] before calling client.close(). The finalizer also snapshots and clears s.clients before closing clients during instance disposal.

How did you verify your code works?

  • 2 new tests in packages/opencode/test/mcp/lifecycle.test.ts:
    • onclose triggers automatic reconnect when transport closes unexpectedly — simulates transport close by calling client.onclose(), verifies the server reconnects and tools update from the new client
    • onclose does not trigger reconnect on intentional disconnect() — calls mcp.disconnect(), verifies status stays disabled with no reconnect
  • All 26 tests pass (24 existing + 2 new)
  • Full typecheck across 23 packages passes (pre-push hook runs bun turbo typecheck)
  • Manual verification steps:
    1. Configure a local MCP server
    2. Start OpenCode, confirm tools available
    3. Kill the MCP server process
    4. Observe log: MCP transport closed unexpectedly
    5. Restart the MCP server
    6. Confirm tools reappear without restarting OpenCode

Screenshots / recordings

N/A — no UI changes.

Checklist

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

When an MCP server process dies or the HTTP transport drops, the client
was left in connected state with stale tool definitions. No reconnect
was attempted, so tools permanently vanished for the session.

Register an onclose handler on each MCP client that:
- Detects unexpected transport closure (not intentional disconnect)
- Marks the server as failed and clears cached defs
- Re-creates the client via createAndStore() using the original config
- Publishes ToolsChanged event so the UI updates

Guard all intentional close paths (disconnect, finalizer, closeClient)
by deleting s.clients[name] BEFORE calling client.close(), so the
onclose handler sees s.clients[name] !== client and returns early.

Fixes anomalyco#17099
@github-actions github-actions Bot added needs:compliance This means the issue will auto-close after 2 hours. and removed needs:compliance This means the issue will auto-close after 2 hours. labels Jun 8, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 8, 2026

Thanks for updating your PR! It now meets our contributing guidelines. 👍

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: MCP tools permanently lost mid-session after single transient listTools() failure — no retry, no reconnect

1 participant