feat(mcp): persist MCP enabled state to config file#6208
Open
mikij wants to merge 52 commits into
Open
Conversation
Add persistMcpToggle function to persist MCP client enabled/disabled state to the configuration file. This ensures MCP client states are preserved across application restarts. Closes Kilo-Org#565
- Use same config file resolution logic for persisting MCP toggle state as used during config loading - Only persist enabled state when MCP connection actually succeeds (status === "connected") - Add error handling with catch blocks to prevent crashes during config writes - Check for custom config flags (KILO_CONFIG, KILO_DISABLE_PROJECT_CONFIG) when determining config path
Update config file discovery logic to check `.opencode` directories and KILO_CONFIG_DIR before falling back to project-wide search. This ensures local configuration files take precedence, providing more predictable config resolution behavior.
Extract config state initialization into a named function and add targeted disposal to preserve MCP connections when config files are modified. Previously, the entire instance was disposed, causing MCP connections to be lost. Add a new `disposeEntry` function in State namespace to allow disposing specific state entries without affecting other state instances. Also extend config file resolution to check `~/.opencode/` directory before other config locations, providing a user-level configuration option.
The persistMcpToggle function now searches through all config file locations to find where the MCP server is actually defined before writing its enabled state. This ensures that toggle state is persisted to the same file that contains the MCP server definition, preventing state from being written to a different config file. The search follows the same priority order as config loading, checking managed config, custom config paths, home directory, and project directories.
Skip managed config directories when writing MCP toggle state and find the next writable location. Short-circuit when no changes needed to avoid unnecessary file writes and config reloads.
…notifications - Reorder resolveConfigPath() to check KILO_CONFIG_DIR before .opencode directories, matching the loading precedence where KILO_CONFIG_DIR has higher priority than .opencode/. This prevents toggles from being written to lower-precedence config files - Add Config.Event.Changed BusEvent and emit it via GlobalBus after State.disposeEntry() in setMcpEnabled(), ensuring UI/watchers are notified when config changes instead of silent invalidation
Contributor
|
The following comment was made by an LLM, it may be inaccurate: |
…er to its current state
8e4a3d9 to
2d6cfc5
Compare
Contributor
Code Review SummaryStatus: 2 Issues Found | Recommendation: Address before merge Overview
Fix these issues in Kilo Cloud Issue Details (click to expand)WARNING
Other Observations (not in diff)No additional out-of-diff issues verified in this pass. Previously reported Files Reviewed (8 files)
Reviewed by gpt-5.4-20260305 · 1,581,976 tokens |
… of config changes, matching the pattern in persistMcpToggle()
f03cf71 to
71c50d4
Compare
The config change event handler was incorrectly comparing event.properties.directory against this.projectID, which would not match the actual workspace directory. This caused config change events to be missed or incorrectly filtered. The fix uses this.getWorkspaceDirectory() to properly compare against the current workspace directory, ensuring config updates are correctly handled for the active project. Fixes Kilo-Org#565
…cation The persistMcpToggle function now returns a boolean indicating whether the operation succeeded. This allows callers to conditionally update state based on persistence success, preventing status inconsistencies when config writes fail. The MCP disable logic now only sets status to 'disabled' if the persistence operation succeeds. This change improves error handling and state consistency in the MCP module.
The config change event handler was calling fetchAndSendConfig() which only refreshes configuration data. It now calls reloadAfterAuthChange() to ensure both config and dependent state (including MCP toggle status) are properly updated when configuration changes occur. The config module was also refactored to use a unified CONFIG_FILES_KILO array for both project and global contexts, simplifying config file discovery logic. Fixes Kilo-Org#565
The config change event handler in KiloProvider now checks all affected worktree directories (root workspace + session directories) instead of only the root workspace. This ensures MCP toggle state updates correctly across multiple worktrees. Supporting changes in the config module: - Split CONFIG_FILES_KILO into CONFIG_FILES_PROJECT and CONFIG_FILES_KILO to separate project-level findUp() scanning from explicit local config resolution - Config writing now properly disposes all project config states when modifying global configuration files - Config changed events are now emitted to notify UI/watchers of updates - Added State.disposeAllEntries() to support bulk state disposal These changes ensure that configuration changes propagate correctly throughout the system, especially for MCP toggle functionality across multiple worktrees.
The config write logic now identifies shared configuration files by checking if the file path is outside the current project directory, rather than only checking against the home directory. This ensures that changes to shared configs (e.g., in monorepos or shared workspaces) properly dispose all project config states, preventing stale configuration reads across multiple projects.
c6abb6e to
2e43b29
Compare
Modified `persistMcpToggle` to emit change events for all registered project directories when a shared configuration change occurs. This guarantees that all active worktrees and workspaces updated their internal state and UI following a global toggle. - Added `State.keys()` to expose all registered project directories - Updated `persistMcpToggle` to track affected directories before disposal - Added `.cocoindex_code` database files to `.gitignore` Fixes Kilo-Org#565
When writing to a shared config file (e.g., global MCP toggle), dispose ALL state for ALL projects including MCP clients, not just config cache. This ensures proper cleanup when shared configuration changes affect all instances. Previously, only config cache was cleared via disposeAllEntries(initConfigState). Now, State.disposeAll() clears all state (config + MCP clients + everything else) for all registered directories.
…eter Replace string prefix check with path.relative for accurate shared config detection. Add optional directory parameter to KiloProvider fetch methods to support worktree-specific MCP toggle operations.
…ctive session - KiloProvider: refresh only when config change affects the current session directory, avoiding overwriting other worktrees - opencode config: dispose state for other instances when a shared config changes, preserving the current instance (MCP client remains alive) - opencode state: introduce disposeAllExcept(excludeKey) to dispose all but the specified instance This prevents unintended state loss in background worktrees and ensures the active session remains intact during shared config changes.
…ess of shared config setting The State.disposeEntry call was only executing in the non-shared config branch, causing stale state to persist when toggling shared config. Moved the disposal call outside the conditional to ensure the current instance's state is properly cleaned up in all scenarios while MCP.connect handles connection lifecycle.
261c4c5 to
e74b27a
Compare
Added support for `.kilocode` directories alongside existing `.kilo` directories in both MCP config detection and general config path resolution. This provides backward compatibility for projects using the legacy `.kilocode` directory name while maintaining the existing precedence order.
Contributor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add
persistMcpTogglefunction to persist MCP server enabled/disabled state to the configuration file. This ensures MCP server states are preserved across application restarts.Closes #6157
Context
When toggling MCP servers via the CLI/TUI (through the connect/disconnect endpoints introduced in PR #4509), the enabled/disabled state was only stored in memory and lost after restarting the application. This was an oversight where the runtime toggle feature (added Dec 2025) was never integrated with the persistent
enabledconfig field (introduced June 2025).The
enabledfield was designed to control startup behavior, but could only be modified by manually editing the config file. Users expected toggles to persist but they didn't.Implementation
Config.persistMcpToggle(name, enabled)function inpackages/opencode/src/config/config.ts:.opencode/opencode.jsonand global~/.config/kilo/opencode.json)jsonc-parserto modify the config while preserving comments and formattingMCP.connect()inpackages/opencode/src/mcp/index.ts:Config.persistMcpToggle(name, true)after successfully connectingMCP.disconnect()inpackages/opencode/src/mcp/index.ts:Config.persistMcpToggle(name, false)after disconnectingThe fix bridges two separate features that were never properly integrated:
enabledfield for manual config editingHow to Test
Ctrl+X, select "Toggle MCPs")Alternative test via CLI:
Connect an MCP server
curl -X POST http://localhost:4096/mcp/my-server/connect
Check it's enabled in config
cat ~/.config/kilo/opencode.json | grep -A 3 "my-server"
Restart application
Verify server is still connected on startup
Get in Touch
861519093852012544