Skip to content

feat(mcp): persist MCP enabled state to config file#6208

Open
mikij wants to merge 52 commits into
Kilo-Org:mainfrom
mikij:fix/#565-mcp-toggle
Open

feat(mcp): persist MCP enabled state to config file#6208
mikij wants to merge 52 commits into
Kilo-Org:mainfrom
mikij:fix/#565-mcp-toggle

Conversation

@mikij
Copy link
Copy Markdown
Contributor

@mikij mikij commented Feb 23, 2026

Add persistMcpToggle function 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 enabled config field (introduced June 2025).
The enabled field 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

  1. Added Config.persistMcpToggle(name, enabled) function in packages/opencode/src/config/config.ts:
    • Resolves the correct config path (supports project-local .opencode/opencode.json and global ~/.config/kilo/opencode.json)
    • Uses jsonc-parser to modify the config while preserving comments and formatting
    • Writes the updated config back to disk
  2. Updated MCP.connect() in packages/opencode/src/mcp/index.ts:
    • Calls Config.persistMcpToggle(name, true) after successfully connecting
    • Ensures enabled state persists to config
  3. Updated MCP.disconnect() in packages/opencode/src/mcp/index.ts:
    • Calls Config.persistMcpToggle(name, false) after disconnecting
    • Ensures disabled state persists to config

The fix bridges two separate features that were never properly integrated:

How to Test

  1. Start the CLI or TUI
  2. Open MCP dialog (TUI: press Ctrl+X, select "Toggle MCPs")
  3. Toggle an MCP server (press Space on a server to enable/disable)
  4. Restart the CLI/TUI application
  5. Verify: The MCP server retains its enabled/disabled state from step 3
    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

mikij added 12 commits February 21, 2026 14:07
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
@github-actions
Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

@mikij mikij force-pushed the fix/#565-mcp-toggle branch from 8e4a3d9 to 2d6cfc5 Compare February 23, 2026 21:14
Comment thread packages/opencode/src/config/config.ts Outdated
Comment thread packages/opencode/src/config/config.ts Outdated
Comment thread packages/opencode/src/config/config.ts
@kilo-code-bot
Copy link
Copy Markdown
Contributor

kilo-code-bot Bot commented Feb 23, 2026

Code Review Summary

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 2
SUGGESTION 0

Fix these issues in Kilo Cloud

Issue Details (click to expand)

WARNING

File Line Issue
packages/opencode/src/config/config.ts 1820 Config.update() still writes project settings to config.json, but initConfigState() only reloads that file when the same directory already contains another project config file, so /config updates can disappear after the next reload.
packages/opencode/src/mcp/index.ts 610 disconnect() sets the in-memory status to disabled before persistence succeeds, so MCPs defined only in read-only sources (managed config / KILO_CONFIG_CONTENT) still come back on the next rebuild even though the UI temporarily shows them as disabled.
Other Observations (not in diff)

No additional out-of-diff issues verified in this pass. Previously reported config.changed refresh gaps in packages/kilo-vscode/src/KiloProvider.ts and packages/app/src/context/global-sync/event-reducer.ts appear resolved.

Files Reviewed (8 files)
  • packages/app/src/context/global-sync/event-reducer.ts - 0 issues
  • packages/kilo-vscode/src/KiloProvider.ts - 0 issues
  • packages/opencode/src/config/config.ts - 1 issue
  • packages/opencode/src/mcp/index.ts - 1 issue
  • packages/opencode/src/project/state.ts - 0 issues
  • packages/opencode/src/server/event.ts - 0 issues
  • packages/sdk/js/src/v2/gen/types.gen.ts - 0 issues
  • packages/sdk/openapi.json - 0 issues

Reviewed by gpt-5.4-20260305 · 1,581,976 tokens

Comment thread packages/opencode/src/config/config.ts
@mikij mikij force-pushed the fix/#565-mcp-toggle branch from f03cf71 to 71c50d4 Compare February 26, 2026 14:11
Comment thread packages/opencode/src/config/config.ts Outdated
Comment thread packages/opencode/src/config/config.ts Outdated
Comment thread packages/opencode/src/config/config.ts Outdated
Comment thread packages/opencode/src/config/config.ts Outdated
Comment thread packages/opencode/src/config/config.ts Outdated
Comment thread packages/opencode/src/config/config.ts Outdated
Comment thread packages/opencode/src/mcp/index.ts Outdated
Comment thread packages/kilo-vscode/src/KiloProvider.ts Outdated
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
Comment thread packages/opencode/src/mcp/index.ts Outdated
…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.
Comment thread packages/opencode/src/config/config.ts
Comment thread packages/kilo-vscode/src/KiloProvider.ts Outdated
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
Comment thread packages/opencode/src/config/config.ts Outdated
Comment thread packages/opencode/src/config/config.ts
Comment thread packages/kilo-vscode/src/KiloProvider.ts Outdated
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.
Comment thread packages/opencode/src/config/config.ts Outdated
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.
Comment thread packages/opencode/src/config/config.ts Outdated
@mikij mikij force-pushed the fix/#565-mcp-toggle branch from c6abb6e to 2e43b29 Compare March 14, 2026 22:29
mikij added 2 commits March 15, 2026 00:00
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
Comment thread packages/opencode/src/config/config.ts Outdated
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.
Comment thread packages/opencode/src/config/config.ts Outdated
Comment thread packages/kilo-vscode/src/KiloProvider.ts Outdated
…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.
Comment thread packages/kilo-vscode/src/KiloProvider.ts Outdated
Comment thread packages/opencode/src/config/config.ts Outdated
…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.
Comment thread packages/opencode/src/config/config.ts
…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.
@mikij mikij force-pushed the fix/#565-mcp-toggle branch from 261c4c5 to e74b27a Compare March 15, 2026 12:57
Comment thread packages/opencode/src/config/config.ts
mikij added 2 commits March 15, 2026 20:42
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.
@markijbema markijbema self-assigned this Apr 14, 2026
@lambertjosh
Copy link
Copy Markdown
Contributor

@markijbema markijbema removed their assignment May 4, 2026
@markijbema markijbema self-requested a review May 4, 2026 07:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[CLI] Toggling MCP servers in CLI/TUI does not persist to config file

3 participants