Skip to content

fix(mcp): disabled MCPs reactivate after provider config change #28428

@danielxxomg

Description

@danielxxomg

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

  1. Configure multiple MCP servers in opencode.json
  2. Disable some via /mcps toggle (press Space)
  3. Edit opencode.json to change a provider (e.g., switch from opencode-go to openai)
  4. Restart OpenCode (or trigger config reload)
  5. 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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions