feat(mcp): sync MCP servers across enabled CLIs - #521
opticon454 wants to merge 5 commits into
Conversation
Adds capabilities.mcpConfig to the CLI registry (Claude, Gemini, Codex, OpenCode), an additive src/mcp-sync.ts, GET/POST /api/mcp-sync and a Settings > Agents & CLIs control. Never edits or removes an existing server; backs up each file it changes; reports conflicts. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…rted CLIs Formats verified against real agy/gemini/codex mcp add output. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…try tests Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…xpress Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
Thanks @opticon454, this is a well-built feature. It adds a registry capability ( I tried it against real configs in throwaway HOMEs and found a few things that need fixing before merge. Must fix
Should fix
Smaller items, fine in the same round
Optional: the UI says conflicting names are "left unchanged", but the first CLI's definition is still copied to the CLIs that lack the name ( Separately, I still need to decide on the product side whether Codeman writes other CLIs' own user config at all (it has avoided that so far) and whether this sits behind an opt-in setting. I will post that decision here before you start on the fixes, so nothing is wasted. After that, a push covering items 1 to 4 with tests gets another review. |
Opt-in (mcpSyncEnabled, default OFF; routes 403 until on). Review fixes: - codex TOML read/validated with smol-toml: CRLF, inline tables and command-less tables no longer yield a duplicate [mcp_servers.x]; the new text is re-parsed before writing - null-prototype tables and own-key checks; unsafe names ignored at every level - servers switched off in their own CLI (codex/opencode/antigravity) are not copied - only CLIs that are installed or already have a config file take part - files receiving env/headers are left 0600; symlinked configs are written through - one apply at a time (409), unique tmp files cleaned on failure, failed status - routes set real HTTP status codes; api-reference section; format type single-sourced Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
Thanks for the thorough review. Pushed Product decision: it is now opt-in. Must fix
Should fix Smaller items Optional items: the conflict wording in Settings now says the first CLI's definition is copied where the name is missing; the format list is a single While here: the routes previously returned error bodies with HTTP 200; they now set real status codes (403/409/500). CI gate on the branch: typecheck, lint, format, public assets, catalogue and 8561 tests pass. |
|
Heads-up on merge order, so nothing surprises you. I checked my open PRs against each other with trial merges. Every overlap is a textual conflict in a shared registry or list (adjacent insertions), none is a behavioural interaction, but whichever lands first, the others will need a rebase:
I'm not touching this PR for it. As each of #520, #522 and #523 merges I will rebase the others onto master, resolve the conflicts, re-run the full gate and push, so you should not have to deal with any of it. If you would rather take them in a particular order, say so and I will keep to it. #521 is the largest, so landing it first means the smaller PRs rebase once onto it. |
What
Sync MCP servers between the agent CLIs. Each CLI keeps its own user-level MCP list in its own file and dialect; this reads every enabled, installed CLI's list and adds any server a CLI is missing from the others.
Opt-in. Settings → Agents & CLIs → MCP servers → "Enable MCP server sync" (
mcpSyncEnabled, synced, default OFF). While it is off,GET/POST /api/mcp-syncanswer403and the Settings controls are hidden. Nothing about it runs or writes anything until a user turns it on.capabilities.mcpConfig({ path, format }, home-relative, schema-guarded against../absolute paths). Declared for Claude, Gemini, Codex, OpenCode and Antigravity. No code branches on a CLI id.src/mcp-sync.ts: the adapters and the sync. Codex'sconfig.tomlis read withsmol-toml(new dependency: BSD-3, zero deps, ~1.9.0).GET /api/mcp-syncpreviews,POST /api/mcp-syncapplies. Admin only in multi-user mode. Documented indocs/api-reference.md.Behaviour
absentand never created.enabled = false, opencodeenabled: false, antigravitydisabled: true), since copying would switch them on elsewhere.<file>.codeman-bak(overwritten by each sync); the new text is re-parsed and every added server must read back as intended before the write; unparseable files (OpenCode JSONC, TOML with a duplicate table) are never written; symlinked configs are written through, not replaced; a file that receivesenv/headersis left0600; one apply at a time (409); temp files unique and removed on failure.unsupported.__proto__/constructor/prototypeare ignored at every level and untrusted-name tables have no prototype.Formats
Checked against what the CLI's own
mcp addwrites (throwaway HOME): Claude, Gemini (url+type), Codex, Antigravity (~/.gemini/config/mcp_config.json,serverUrl). OpenCode follows its documentedmcpshape; it was not installed to verify. After a sync,codex mcp list,gemini mcp listandagy mcp listall load the result.Tests
test/mcp-sync.test.ts,test/mcp-sync-registry.test.ts,test/routes/mcp-sync-routes.test.ts: real CLI output as fixtures; CRLF / inline-table / command-less codex tables; sub-table__proto__andtoString-named servers; disabled servers per dialect; installed gating; permissions; symlinks; concurrency; opt-in 403s; enabled-only; no secrets in responses; multi-user gating; path-traversal rejection. Full CI gate on this branch: typecheck, lint, format, public assets, catalogue and 8561 tests pass.The first revision was tested by hand in the running app by the author. This push (opt-in setting and review fixes) is covered by the test suite and by running the real
codex,geminiandagyCLIs against the synced files in a throwaway HOME; it has not yet been exercised by hand in the Settings UI.🤖 Generated with Claude Code