feat(cli): install command auto-writes ~/.claude.json MCP entry
Some checks failed
CI / Tests / 🧪 Test (push) Has been cancelled

The previous flow printed a \`claude mcp add ...\` command and asked
users to paste it. That's 2 steps, a typo surface, and a point of
user dropoff. Replace with direct read-modify-write of ~/.claude.json.

install:
- preflights bun on PATH (clear error + Bun.com link if missing)
- verifies the MCP entry file exists on disk
- reads ~/.claude.json (empty object if absent)
- adds/updates mcpServers.claudemesh with resolved absolute path
- writes back with 0600 perms, creates parent dir if needed
- read-back verification (bails loudly if post-write state is wrong)
- idempotent: re-running returns "unchanged" if entry already matches
- preserves existing mcpServers entries + other top-level config keys

uninstall:
- removes the claudemesh entry if present
- no-ops cleanly when entry or config file doesn't exist
- doesn't touch anything else

Both print a clear post-action hint: "Restart Claude Code to load
the MCP server. Then join a mesh with claudemesh join <invite-link>".

verified locally with HOME=/tmp/fake-home:
- fresh install → ✓ added, config emitted correctly
- re-install → ✓ unchanged (idempotent)
- install alongside existing "other-mcp" entry → both preserved,
  plus unrelated top-level keys kept verbatim
- uninstall → ✓ removed, claudemesh gone, other entries intact
- uninstall again → · not present (no error)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-04-05 14:19:58 +01:00
parent d1cab7b807
commit 47304d2a52
2 changed files with 167 additions and 26 deletions

View File

@@ -10,7 +10,7 @@
*/
import { startMcpServer } from "./mcp/server";
import { runInstall } from "./commands/install";
import { runInstall, runUninstall } from "./commands/install";
import { runJoin } from "./commands/join";
import { runList } from "./commands/list";
import { runLeave } from "./commands/leave";
@@ -22,7 +22,8 @@ Usage:
claudemesh <command> [args]
Commands:
install Print Claude Code MCP registration instructions
install Register claudemesh as a Claude Code MCP server
uninstall Remove claudemesh MCP server registration
join <link> Join a mesh via invite link (ic://join/...)
list Show all joined meshes
leave <slug> Leave a joined mesh
@@ -47,6 +48,9 @@ async function main(): Promise<void> {
case "install":
runInstall();
return;
case "uninstall":
runUninstall();
return;
case "join":
await runJoin(args);
return;