fix(mcp): persist MCP toggle state to config#608
fix(mcp): persist MCP toggle state to config#608Olusammytee wants to merge 1 commit intoKilo-Org:devfrom
Conversation
|
Thanks for your contribution! This PR doesn't have a linked issue. All PRs must reference an existing issue. Please:
See CONTRIBUTING.md for details. |
| const entry = isMcpConfigured(current) ? current : fallback | ||
| if (!entry) return | ||
|
|
||
| await Config.updateGlobal({ |
There was a problem hiding this comment.
WARNING: Config.updateGlobal triggers void Instance.disposeAll() as a side effect (see config.ts:1556). This means every call to persistEnabled will asynchronously tear down all instances — including the MCP state — closing every connected MCP client.
In connect(), this causes the just-connected client (and all other MCP clients) to be disposed in the background. The state will lazily re-initialize on next access (re-reading config and reconnecting), but this creates:
- An unnecessary disconnect/reconnect cycle for all MCP servers, not just the toggled one
- A window where MCP tools are unavailable
- Potential race conditions if other operations access MCP state during disposal
Consider writing the config file directly without going through updateGlobal, or adding a mechanism to persist config without triggering full instance disposal.
| status: "failed", | ||
| error: "Unknown error during connection", | ||
| } | ||
| await persistEnabled(name, true, mcp) |
There was a problem hiding this comment.
WARNING: Persisting enabled: true even when create() returned null (connection failed) will cause Config.updateGlobal → Instance.disposeAll() to fire, tearing down all other healthy MCP connections. On a failed connect, the side-effect cost of disposal is especially undesirable since there's no new client to show for it.
Code Review SummaryStatus: 2 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
DetailsThe core concern is that
Consider either:
Files Reviewed (1 files)
|
There was a problem hiding this comment.
Pull request overview
This PR aims to make MCP enable/disable toggles durable by persisting the enabled state into configuration when MCP servers are connected or disconnected, addressing loss of toggle state after restart (Fixes #565).
Changes:
- Add
persistEnabled()helper to update config with the MCP server’senabledstate. - Persist
enabled: trueonconnect()andenabled: falseondisconnect(). - Add fallback handling to persist even when the current config entry isn’t directly usable.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| await Config.updateGlobal({ | ||
| mcp: { | ||
| [name]: { | ||
| ...entry, |
There was a problem hiding this comment.
persistEnabled writes the entire resolved MCP config back into the user config (via ...entry). Because config loading expands {env:VAR} / {file:...} placeholders before parsing, this risks persisting secrets (e.g., auth headers) and/or large inlined file contents into the config file, and can also overwrite JSONC formatting/comments. Safer approach: only persist the enabled field (the schema already supports an mcp[name] = { enabled: boolean } override) so the existing entry stays untouched.
| ...entry, |
| await Config.updateGlobal({ | ||
| mcp: { | ||
| [name]: { | ||
| ...entry, | ||
| enabled, | ||
| }, | ||
| }, | ||
| }).catch((error) => { | ||
| log.warn("failed to persist mcp enabled state", { name, enabled, error }) | ||
| }) | ||
| } |
There was a problem hiding this comment.
Calling Config.updateGlobal() on every connect/disconnect has a large side effect: it triggers Instance.disposeAll() (via Config.updateGlobal), which will dispose state for all instances and close existing MCP clients. This can cause unrelated runtime state to reset and other MCP connections to drop when toggling a single server. Consider persisting the flag without disposing instances (or provide a lighter-weight config write path) since runtime status is already updated in-memory.
Summary
Fixes #565