Skip to content

feat(desktop): reseed the MCP relay entry for claude and codex too (F4) - #566

Merged
physercoe merged 3 commits into
mainfrom
feat-vision-f4-user-mcp-reseed
Aug 16, 2026
Merged

feat(desktop): reseed the MCP relay entry for claude and codex too (F4)#566
physercoe merged 3 commits into
mainfrom
feat-vision-f4-user-mcp-reseed

Conversation

@physercoe

Copy link
Copy Markdown
Owner

First W4 wedge. UI-context sharing seeded its termipod-desktop relay 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.tsusermcp.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 user and codex mcp add against throwaway config homes, not from documentation.

  1. Neither path is fixed. CLAUDE_CONFIG_DIR and CODEX_HOME relocate both files — and claude's config home is per-account, which localagent/store.ts already persists for exactly that reason. Resolving ~/.claude.json blindly would reseed the wrong account's file, or one the running claude never reads.
  2. claude's entry shape is not kimi's. Its CLI 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.
  3. ⚠️ .claude.json is claude's live STATE file, not a config file — caches, ids, project history; 85 KiB on a working machine, with no mcpServers key 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 add carries the same) but it is documented at the top of the module and is why it touches nothing but its own key.
  4. codex is TOML, and a parse-and-reserialize would eat the user's comments. The entry is line-spliced so 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 someone's model config is worse than not reseeding.

Verified differentially against the vendors

Not just unit tests — the real binaries:

  • 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 mcp list reads a file we wrote and reports both our server and the pre-existing one as enabled;
  • the real claude mcp list discovers our entry from a .claude.json we 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, tsc clean, 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.

Ubuntu and others added 3 commits August 16, 2026 10:33
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>
@physercoe
physercoe merged commit 8bb0d5e into main Aug 16, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants