feat(desktop): reseed the MCP relay entry for claude and codex too (F4) - #566
Merged
Conversation
UI-context sharing seeded its `termipod-desktop` relay entry into kimi's
user config only, so an ad-hoc claude or codex session — one the user
started themselves rather than the hub spawning it — could never pull
desktop UI context. The toggle now reseeds all three, additively, and
removes exactly its own entry on toggle-off.
Renamed kimimcp.ts → usermcp.ts: "kimimcp" names one of the three
engines it now serves.
Both new formats were established by running the vendors' own CLIs
against throwaway config homes, not from documentation:
- Neither path is fixed. CLAUDE_CONFIG_DIR and CODEX_HOME relocate
both files, and claude's config home is PER-ACCOUNT (localagent/
store.ts already persists it for that reason). Resolving
~/.claude.json blindly would reseed the wrong account's file.
- claude's entry shape is not kimi's: `claude mcp add -s user` writes
a `type:"stdio"` discriminator alongside command/args. We match it,
and omit `env` — the constraint is no env, and an empty map states
nothing.
- .claude.json is claude's live STATE file, not a config file: caches,
ids, project history, and no `mcpServers` key at all until something
adds one. An external read-modify-write races claude's own writer.
Accepted exposure (`claude mcp add` has the same), documented at the
top of the module, and the reason it touches nothing but its own key.
- codex is TOML. A parse-and-reserialize would eat the user's comments,
so the entry is line-spliced and every other byte survives. A config
expressing mcp_servers as an inline table or a dotted key is REFUSED
('unsupported') rather than guessed at — destroying a model config is
worse than not reseeding.
Verified differentially against the vendors: our merged TOML is
byte-identical to `codex mcp add`'s output and our removal byte-identical
to `codex mcp remove`'s, the real codex lists both servers `enabled` from
a file we wrote, and the real claude discovers our entry from a
.claude.json we wrote (it then fails to connect only because the relay
copy does not exist on this machine).
One engine's failure no longer stops the others — the toggle reports a
per-engine result instead of one verdict.
20 tests (8 pre-existing kimi cases now covering the shared contract, 12
new); 8 mutations checked, all killed. Electron suite 627 pass / 0 fail,
tsc clean, doc lints green.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…reseeds `mergeSharingEntries`/`removeSharingEntries` promised "one engine's failure never stops the others", but only READ failures came back as per-engine 'corrupt'. A throw out of the WRITE itself — EACCES on the config dir, disk full — escaped the result-object literal mid-evaluation: kimi's already-done write had its result discarded and codex was never attempted, with the toggle reporting a blanket 'failed'. Each engine's op now runs behind a catch that logs and returns a new 'failed' member of McpWrite (the tmp+rename write means the target file is still untouched). Pinned by a test that makes claude's config dir read-only and asserts kimi and codex still reseed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Both PRs edited the vision-parity status block and the desktop changelog's Unreleased section. The status line now records both wedges: R4 shipped (W3 complete) and F4 shipped (first W4 wedge), leaving L3c as the only remaining item. Both changelog entries kept, F4 above R4 in land order. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This was referenced Aug 16, 2026
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.
First W4 wedge. UI-context sharing seeded its
termipod-desktoprelay entry into kimi's user config only, so an ad-hoc claude or codex session — one you started yourself rather than the hub spawning it — could never pull desktop UI context. The toggle now reseeds all three, additively, and removes exactly its own entry on toggle-off.kimimcp.ts→usermcp.ts: "kimimcp" names one of the three engines it now serves.Four corrections to the plan line, all from running the vendors' CLIs
The formats were established by running
claude mcp add -s userandcodex mcp addagainst throwaway config homes, not from documentation.CLAUDE_CONFIG_DIRandCODEX_HOMErelocate both files — and claude's config home is per-account, whichlocalagent/store.tsalready persists for exactly that reason. Resolving~/.claude.jsonblindly would reseed the wrong account's file, or one the running claude never reads.type:"stdio"discriminator alongside command/args. We match it, and omitenv— the constraint is no env, and an empty map states nothing..claude.jsonis claude's live STATE file, not a config file — caches, ids, project history; 85 KiB on a working machine, with nomcpServerskey at all until something adds one. An external read-modify-write races claude's own writer and last writer wins. That is accepted exposure (claude mcp addcarries the same) but it is documented at the top of the module and is why it touches nothing but its own key.mcp_serversas an inline table or a dotted key is refused ('unsupported') rather than guessed at — destroying someone's model config is worse than not reseeding.Verified differentially against the vendors
Not just unit tests — the real binaries:
codex mcp add's output, and our removal byte-identical tocodex mcp remove's;codex mcp listreads a file we wrote and reports both our server and the pre-existing one asenabled;claude mcp listdiscovers our entry from a.claude.jsonwe wrote (it then fails to connect only because the relay copy doesn't exist on this machine — the entry itself is well-formed and found).Other
One engine's failure no longer stops the others: the toggle returns a per-engine result instead of a single verdict, so an unreadable kimi file doesn't cost you claude and codex.
Verification: 20 tests (the 8 pre-existing kimi cases now pin the contract all three share, plus 12 new); 8 mutations checked, all killed. Electron suite 627 pass / 0 fail / 2 pre-existing skips,
tscclean, doc lints green.Branched from
main, independent of #565 (R4) — but both edit this plan's status block and the changelog's Unreleased section, so whichever merges second will need a doc rebase. I'll handle it.