API-285: One-step MCP install (nansen mcp install <client>) - #487
API-285: One-step MCP install (nansen mcp install <client>)#487gulshngill wants to merge 4 commits into
Conversation
pr-reviewer Summary for #12d05f5📝 1 finding Review completed. Please address the findings below. Findings by Severity
Review effort: 3/5 (Moderate) SummaryThis is a well-engineered feature addition. The security threat-modelling is thorough — merge-only atomic writes, backup-before-write, key never printed, no shell injection, hardcoded HTTPS URL, pinned Findings
|
Add `nansen mcp install/uninstall <client>` to write the hosted Nansen MCP server (https://mcp.nansen.ai/ra/mcp) into Claude Code, Claude Desktop, or Cursor configs. Merge-only atomic writes with backup, key never printed, --dry-run supported. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
a49f962 to
e2bc3b3
Compare
|
🤖 Automated Branch Sync (claude-code) — 2026-08-13T17:02:07Z Action: Rebased Conflict resolved (1 file): Auto-merged cleanly, both intents verified intact:
Verified: full suite 1985 passed / 2 skipped / 0 failed,
Left for a human (not changed during sync): |
|
🤖 Automated Branch Sync (claude-code) — 2026-08-13T18:56:53Z Action: No rebase needed — Merge state is |
|
🤖 Automated Branch Sync (claude-code) — 2026-08-14T00:58:01Z Action: No rebase needed —
|
|
🤖 Automated Branch Sync (claude-code) — 2026-08-14T02:56:49Z Action: No rebase needed — Merge is gated only by |
|
🤖 Automated Branch Sync (claude-code) — 2026-08-14T04:57:08Z Action: No rebase needed — |
|
🤖 Automated Branch Sync (claude-code) — 2026-08-14T14:57:24Z Action: No rebase needed — |
|
🤖 Automated Branch Sync (claude-code) — 2026-08-17T05:50:35Z Action: No rebase needed — Merge is still gated on |
|
🤖 Automated PR Review (codex) — 2026-08-17T06:04:44Z Reviewed: MCP install/uninstall behavior, client config formats, merge/backup/atomic-write paths, error handling, key exposure, CLI/schema/docs integration, and test coverage |
|
🤖 Automated Branch Sync (claude-code) — 2026-08-17T08:57:58Z Action: No rebase needed — ℹ️ Merge state is |
|
🤖 Automated PR Review (codex) — 2026-08-17T09:02:56Z Reviewed: MCP client config formats, install/uninstall behavior, atomic writes, backups, error handling, API-key exposure, CLI/schema/docs integration, and test coverage |
|
🤖 Automated Branch Sync (claude-code) — 2026-08-17T12:59:06Z Action: Checked Merge is currently gated on |
Addresses both pr-reviewer findings on #487: - `mcp uninstall` now copies the config to `.bak` (best-effort chmod 0600) before writing, mirroring `install`. Only runs when there is an entry to remove and not under `--dry-run`, so a missing config or a no-op uninstall still writes nothing. copyFileSync failures surface as-is, like install. Adds a notice that the backup retains the API key just removed. - `runCLI` passes `log: deps.log ?? output` to `buildMcpCommands`, so mcp output honours the caller's stdout sink instead of falling back to bare console.log. An explicit `log` dep still wins, preserving test injection. Regression tests: uninstall backup content/mode/log, no backup when there is nothing to remove (incl. dry-run), failed backup copy leaves the config untouched, and runCLI routing mcp output through `output` with console.log unused. README + changeset wording updated to cover uninstall. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Summary
Implements API-285 — one-step install of the hosted Nansen MCP server into local MCP clients:
The command writes a
nansenentry into the client's own config file using the API key fromnansen login/NANSEN_API_KEY. No network calls, no shelling out — pure fs operations.Provider research (public sources)
Surveyed one-step-install mechanisms from Nansen's own MCP docs, Claude Code (
claude mcp add,.mcp.json), Claude Desktop, Cursor (~/.cursor/mcp.json+ deeplinks), VS Code (serverskey,code --add-mcp), Codex, Gemini CLI, and vendor installers (Sentry wizard, Smithery CLI, Stripe, GitHub MCP badges). Key facts driving the design:https://mcp.nansen.ai/ra/mcp, auth viaNANSEN-API-KEYheader (docs) — so entries are remote-URL, no local server process.~/.claude.jsonentries require"type": "http"next tourl; Cursor infers fromurl; Claude Desktop's config is stdio-only, so it bridges vianpx mcp-remote(pinnedmcp-remote@0.1.38, header arg written without a space after the colon to dodge Claude Desktop's arg-splitting bug).--dry-run, uninstall support.Security decisions (threat-modeled independently)
mcpServers.nansenis ever assigned; all sibling servers and unrelated keys pass through. Unparseable JSON → refuse with the file path, never repair/overwrite..bakcopy before every install write. Atomic temp-file + rename so a crash can't truncate the config. Type guard onmcpServers.--dry-run(redacted), or errors. New dirs 0700, files 0600, backup 0600, existing target chmod'd 0600 post-write (best-effort). Explicit plaintext + settings-sync warnings on install. Telemetry already sends flag names only, so no key material can leak there.claude mcp addetc. deliberately not exec'd); client name validated against a closed set before any path math; no user-supplied paths.--url/env override — a redirectable URL would exfiltrate the key);mcp-remotepinned exact; the docs'--allow-httpflag deliberately dropped (URL is HTTPS).realpathSyncso the rename edits the real file instead of replacing the link. TOCTOU judged not realistic (same-user home dir).Tests
New
src/__tests__/mcp.test.js(27 tests): per-platform path resolution incl. claude-desktop-on-Linux error, per-client entry shapes (pinned version, no--allow-http, no-space header), merge/remove purity + non-objectmcpServersguards, and handler tests against real temp dirs — file/dir modes, backup content + mode, idempotent re-install, corrupt-JSON refusal (file untouched), not-logged-in (no writes),--dry-run(no writes, key never printed), key-never-in-output, uninstall (incl. no-key and no-entry paths), symlink follow, schema.json registration, andrunCLIrouting /--dry-runboolean-flag parsing.npm test: 52 files, 1913 passed / 2 skipped ✅npm run lint: clean ✅$HOME, help paths,nansen schema mcp.Limitations / follow-up
~/.claude.jsonis also rewritten by live Claude Code sessions — a session saving state after our write can drop the entry (last-writer-wins, not corruption). Output tells the user to restart; if it bites, fallback isexecFile('claude', ['mcp','add',...]).mcp-remotepin (0.1.38) trades missed upstream security fixes for protection against compromised future releases; bumping is a one-constant change.scripts/postinstall.jsalready distributes agent skills — MCP install is a second, parallel distribution channel; worth a docs pass later on when to use which.🤖 Generated with Claude Code