Description
When a user adds or modifies a provider in their opencode.json, ALL MCP servers restart and re-activate — including ones explicitly disabled via the /mcps toggle. This forces unwanted tools into the context window, increases token consumption, and confuses the agent.
The root issue is that the MCP disabled state is only in-memory (s.status[name]) and is never persisted to disk. Any config change that triggers an Instance reload wipes this state, causing all MCPs to come back enabled.
Steps to reproduce
- Configure multiple MCP servers in
opencode.json
- Disable some via
/mcps toggle (press Space)
- Edit
opencode.json to change a provider (e.g., switch from opencode-go to openai)
- Restart OpenCode (or trigger config reload)
- Run
/mcps — all previously disabled MCPs are now enabled again
Technical Root Cause
The disabled state is only stored in-memory in packages/opencode/src/mcp/index.ts:
- Toggle path:
MCP.disconnect() sets s.status[name] = { status: "disabled" } (in-memory only)
- Init path:
MCP.state() only reads from cfg.mcp (config file) — no persisted disabled state consulted
- Reload: Config change →
markInstanceForDisposal() → full Instance.disposeAll() → fresh MCP.state() → disabled state lost
Compare with model selection, which already persists to disk via Global.Path.state/model.json — the pattern already exists in the codebase.
Expected Behavior
MCP disabled state should survive:
Impact
- Context window: Users with many MCPs keep most disabled to save tokens. Reactivation wastes ~20-65K tokens per session.
- UX friction: Users must manually re-disable MCPs after every provider config change.
- Security surface: MCPs like
chrome-devtools (browser launch) and morphllm-fast-apply (remote npm code) remain active against user intent.
Proposed Fix Direction
Persist disabled MCP IDs to a state file (e.g., mcp-state.json in Global.Path.state), same pattern as model.json. Read it during MCP.state() init. No changes to Instance lifecycle or reload mechanism needed.
Related Issues / PRs
Operating System
Windows 11
Terminal
Windows Terminal
Description
When a user adds or modifies a provider in their
opencode.json, ALL MCP servers restart and re-activate — including ones explicitly disabled via the/mcpstoggle. This forces unwanted tools into the context window, increases token consumption, and confuses the agent.The root issue is that the MCP disabled state is only in-memory (
s.status[name]) and is never persisted to disk. Any config change that triggers an Instance reload wipes this state, causing all MCPs to come back enabled.Steps to reproduce
opencode.json/mcpstoggle (press Space)opencode.jsonto change a provider (e.g., switch fromopencode-gotoopenai)/mcps— all previously disabled MCPs are now enabled againTechnical Root Cause
The disabled state is only stored in-memory in
packages/opencode/src/mcp/index.ts:MCP.disconnect()setss.status[name] = { status: "disabled" }(in-memory only)MCP.state()only reads fromcfg.mcp(config file) — no persisted disabled state consultedmarkInstanceForDisposal()→ fullInstance.disposeAll()→ freshMCP.state()→ disabled state lostCompare with model selection, which already persists to disk via
Global.Path.state/model.json— the pattern already exists in the codebase.Expected Behavior
MCP disabled state should survive:
/reloadcommand (once feat: add /reload slash command #9871 is merged)Impact
chrome-devtools(browser launch) andmorphllm-fast-apply(remote npm code) remain active against user intent.Proposed Fix Direction
Persist disabled MCP IDs to a state file (e.g.,
mcp-state.jsoninGlobal.Path.state), same pattern asmodel.json. Read it duringMCP.state()init. No changes to Instance lifecycle or reload mechanism needed.Related Issues / PRs
/reloadcommand (hot reload, not yet merged)Operating System
Windows 11
Terminal
Windows Terminal