Files
Bohdan Triapitsyn ce5c0c068e feat(mcp): reconnect failed MCP servers in managed OpenCode
OpenCode marks an MCP server failed when it does not come up at startup
or when a live connection drops, and never retries. A new managed plugin
reconnects those servers with a per-server backoff (1s doubling to a 30s
cap), wakes early on mcp.tools.changed, and stops on dispose. Only the
failed state is retried; disabled and auth states stay untouched.

The OPENCODE_CONFIG_CONTENT merge that agent-tool and system-prompt each
carried is now one shared helper so the three managed plugins compose.

Claude-Session: https://claude.ai/code/session_01VqV56Hez25hTxXH4ipJfzH
2026-09-04 22:56:27 +03:00

2.7 KiB

Managed MCP Reconnect

Purpose

OpenCode connects each configured MCP server once, when a project directory is first used. A server that does not come up then is marked failed and never retried; a server whose live connection later drops is marked failed too and stays that way until OpenCode restarts. This module injects a small plugin into the OpenCode process OpenChamber launches that reconnects those servers, so a server that was slow to start or crashed mid-session comes back on its own.

Runtime flow

  1. prepareManagedOpenCodeEnv(configContent) materializes the plugin under <openchamber-data-dir>/mcp-reconnect/ and appends its file:// URL to OPENCODE_CONFIG_CONTENT through the shared merge in packages/web/server/lib/opencode/managed-plugin-config.js.
  2. It is always on for managed OpenCode. There is no setting, because it only acts on servers OpenCode has already given up on.
  3. OpenCode loads the plugin once per project directory with an SDK client scoped to that directory, so each directory reconnects its own servers.
  4. The plugin reads MCP status one second after load, calls connect for every server in the failed state, then re-reads status after a per-server delay that doubles from one second to a cap of thirty. A server seen in any other state resets its counter. While nothing is failed it checks every thirty seconds.
  5. A dropped connection publishes mcp.tools.changed, which the plugin uses to check right away instead of waiting out the idle interval.
  6. OpenCode calls the plugin's dispose hook when it tears the directory down, which stops the loop.

Invariants

  • Only failed is retried. disabled is the user's choice, and needs_auth or needs_client_registration need the user to act; retrying those would either re-enable a server the user turned off or loop on a login prompt.
  • The plugin logs nothing and swallows every error. OpenCode already logs each failed attempt, and a status call failing during an OpenCode restart is not news.
  • One check runs at a time; a wake-up arriving during a check is honored once it finishes rather than starting a second loop.

What the UI sees

OpenCode publishes no event when a reconnect succeeds. The chat picks the server up on the next prompt because tools are resolved from live state, but the MCP page reads status on bootstrap and refresh, so it can show failed for a while after the server is back.

Runtime parity

  • Web and Desktop managed OpenCode: injected automatically.
  • External OpenCode (OPENCODE_HOST or skip-start) and VS Code's separate OpenCode lifecycle: not injected, because OpenChamber does not control that process environment.