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

56 lines
2.7 KiB
Markdown

# 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.