fix(settings-sync-daemon): force-overwrite Helix-owned context_servers - #2425
Merged
Conversation
When Helix changes the hardcoded definition of a Helix-owned context_server (e.g. PR #2418 switched chrome-devtools from `npx chrome-devtools-mcp@latest` to `/usr/bin/chrome-devtools-mcp`), the daemon's deep-merge in mergeSettings was treating the on-disk OLD entry as a "user override" and letting it win. This pinned the broken `npx` config in long-running containers' persisted settings.json forever and re-produced the 180s `chrome-devtools context server failed to start: Context server request timeout` errors that PR #2418 was meant to fix — even on containers running the new image and new API binary. Bug observed in https://meta.helix.ml/orgs/helix/projects/prj_01kg02vqqyg178c1n2ydscn5fb/tasks/spt_01kqc4ev5rt9rknk6g8dbkzj9a shortly after PR #2418 merged: chrome-devtools / drone-ci / github all showed "Context server request timeout" in Zed, despite the container running helix-ubuntu:6de75e (built post-merge with the new global MCP binaries) and helix-api running the new zed_config.go. Fix: introduce HELIX_OWNED_CONTEXT_SERVERS = {chrome-devtools, helix-session, helix-desktop} — the set of context_server names hardcoded in api/pkg/external-agent/zed_config.go. Two corresponding behavior changes in api/cmd/settings-sync-daemon/main.go: 1. mergeSettings: skip user-side context_server entries whose name is in HELIX_OWNED_CONTEXT_SERVERS so Helix's hardcoded definition unconditionally wins. Also strip helix-owned names from the "user-only" branch so a stale on-disk entry can't survive even when the API temporarily emits no context_servers. 2. extractUserOverrides: never capture helix-owned names as user overrides (otherwise the stale entry would round-trip back to the API and force the next sync to re-write the OLD value to disk, permanently nullifying the force-overwrite from #1). User-configured MCPs (e.g. drone-ci, github, custom servers from project skills or app config) are NOT in the helix-owned set — those legitimately can be edited by the user in their on-disk settings.json and must round-trip. Tests: - TestMergeSettings_HelixOwnedContextServersWin (4 sub-tests): force-overwrite chrome-devtools and helix-session when user has stale entries, allow user-configured drone-ci to win, strip helix-owned names even when helix has no servers. - TestExtractUserOverrides_SkipsHelixOwnedContextServers (2 sub-tests): stale on-disk helix-owned entries are not captured as user overrides; non-helix user overrides still round-trip. All sub-tests verified to FAIL when both guards are commented out (by replacing `if HELIX_OWNED_CONTEXT_SERVERS[name] {` with `if false && HELIX_OWNED_CONTEXT_SERVERS[name] {` and re-running). Full diagnosis: design/2026-05-13-mcp-cache-contention-and-duplicate-claude-spawn.md Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
lukemarsden
force-pushed
the
fix/settings-sync-helix-owned-mcps
branch
2 times, most recently
from
May 13, 2026 14:13
1b5a87b to
c6a8ae7
Compare
lukemarsden
enabled auto-merge
May 13, 2026 14:14
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.
Summary
Tail-end fix for the MCP-cache-contention investigation that PR #2418 addressed. After PR #2418 merged, the user reported
chrome-devtools / drone-ci / githubstill showing "Context server request timeout" in Zed on a long-running spec-task container — even though the container was running the newhelix-ubuntu:6de75eimage (with all the global MCP binaries) andhelix-apiwas running the newzed_config.go(withchrome-devtoolspointing at/usr/bin/chrome-devtools-mcpinstead ofnpx).Root cause:
~/.config/zed/settings.jsonis a symlink to/home/retro/work/.zed-state/config/settings.json, which lives on the persistent ZFS-backed/home/retro/workvolume. The settings-sync-daemon'smergeSettingsdeep-merge ofcontext_serverslets USER entries win for every name, so the OLDcommand: "npx", args: ["chrome-devtools-mcp@latest"]written by the pre-PR-#2418 API last week was treated as a user customization and pinned forever, no matter how many times the new API generated the correct config.extractUserOverrideshad the symmetric bug: it captured the stale on-disk entry as a user override and round-tripped it back to the API.Fix
New constant
HELIX_OWNED_CONTEXT_SERVERS = {chrome-devtools, helix-session, helix-desktop}— the names hardcoded inapi/pkg/external-agent/zed_config.go. Two corresponding behavior changes inapi/cmd/settings-sync-daemon/main.go:mergeSettings— skip user-sidecontext_serversentries whose name is inHELIX_OWNED_CONTEXT_SERVERSso Helix's hardcoded definition unconditionally wins. Also strip helix-owned names from the "user-only" branch so a stale on-disk entry can't survive even when the API temporarily emits nocontext_servers.extractUserOverrides— never capture helix-owned names as user overrides (otherwise the stale entry would round-trip back to the API and force the next sync to re-write the OLD value to disk, permanently nullifying the force-overwrite from Website #1).User-configured MCPs (e.g. drone-ci, github, custom servers from project skills or app config) are NOT in the helix-owned set — those legitimately can be edited by the user in their on-disk settings.json and must round-trip.
Tests
TestMergeSettings_HelixOwnedContextServersWin(4 sub-tests):chrome-devtoolswhen user has stalenpxversionhelix-sessionwhen user has stalesession_idURL/tokendrone-cistill wins (positive control — non-helix-owned MCP)context_serversTestExtractUserOverrides_SkipsHelixOwnedContextServers(2 sub-tests):Verified regression-test power: with both guards commented out (
if false && HELIX_OWNED_CONTEXT_SERVERS[name] {), all 5 of the 6 force-overwrite/skip sub-tests fail with the diagnostic messages in the assertions — exactly the regression we're guarding against.Test plan
CGO_ENABLED=0 go test -v -run "TestMergeSettings_HelixOwned|TestExtractUserOverrides_SkipsHelix" ./api/cmd/settings-sync-daemon/ -count=1— all 6 sub-tests pass.go build ./api/cmd/settings-sync-daemon/clean.Follow-up after merge
The user's affected long-running container (
spt_01kqc4ev5rt9rknk6g8dbkzj9a) needs a./stack build-ubuntu+ new session for the daemon binary to be replaced (perCLAUDE.md: settings-sync-daemon does not hot-reload). Once the new daemon runs, it'll force-overwrite the stale chrome-devtools/helix-session/helix-desktop entries on the next sync, and Zed will pick up the new/usr/bin/chrome-devtools-mcpconfig.Related
design/2026-05-13-mcp-cache-contention-and-duplicate-claude-spawn.md🤖 Generated with Claude Code