Skip to content

feat(onboarding): auto-register MCP entry into Claude Desktop / Claude Code / Cursor configs (re-PR) - #147

Merged
szhygulin merged 1 commit into
mainfrom
feat/client-auto-register
Apr 25, 2026
Merged

feat(onboarding): auto-register MCP entry into Claude Desktop / Claude Code / Cursor configs (re-PR)#147
szhygulin merged 1 commit into
mainfrom
feat/client-auto-register

Conversation

@szhygulin

Copy link
Copy Markdown
Collaborator

Summary

Re-opening this against main directly. Original PR #144 was stacked on feat/broad-audience-onboarding-v1 and merged into that feature branch instead of main; PR #142 was then squash-merged to main directly, leaving #144's content unable to flow up. PR #146 reverted #144 on the feature branch. Net: main has #142, but the auto-register feature never landed there.

This PR fixes that. The branch is rebased onto current main (which already has #142 + #143 — Solana staking-portfolio integration); the diff is exactly the four files from the original #144.

Item 1.2 from claude-work/HIGH-plan-broad-audience-onboarding.md. Eliminates the single biggest non-dev friction point — finding + editing claude_desktop_config.json (or per-OS equivalents) by hand.

What ships

src/setup/register-clients.ts — pure module, no console output, no prompts. Three exports the wizard composes:

  • getClientConfigPaths() — per-OS list of (Claude Desktop, Claude Code, Cursor) user-level config paths.
  • detectClient(configPath)configured / installed / absent.
  • patchClientConfig(client, configPath, opts?) — additive + idempotent + atomic + reversible. Reads existing JSON, merges in mcpServers.vaultpilot-mcp, atomic-renames, leaves a <file>.vaultpilot.bak next to it.

The MCP entry is { command: "node", args: ["<abs path>/dist/index.js"] }, resolved via import.meta.url. Works whether the user npm i -g'd or cloned from source — absolute path doesn't depend on PATH and won't break under a reinstall under a different prefix.

Wizard wiring

src/setup.ts gains an offerClientAutoRegister(p) helper called at the end of runFullWizard(). Detects clients, prints which were found, asks [Y/n], runs the patch sweep, prints a per-client summary (✓ Added / ✓ Already configured / · Not detected / ✗ Error).

Auto-register is opt-in (default Y, but the user must press enter): even with backups, touching another app's config is the kind of thing to ask before doing.

Per-project / per-workspace configs are NOT touched

<project>/.claude/settings.json and <workspace>/.cursor/mcp.json are deliberately skipped. The wizard runs from an arbitrary CWD; the user might be in ~, in a personal-finance repo, or anywhere. Patching the wrong project's config is worse than skipping. Documented in the module doc-comment + the README.

Hardening

  • Atomic write via tmp + rename. A crashed wizard never leaves a half-written config.
  • 0o600 file mode preserved. MCP configs may carry API keys; tightening perms is cheap insurance.
  • Malformed-JSON-existing-config returns a structured error result rather than crashing or overwriting.
  • Empty-file existing config is treated as {} and patched normally.
  • vaultpilot-mcp already present → already-present result with no write. Re-running the wizard is safe.

README

"Use with Claude Desktop" section rewritten to mention auto-register first and surface the manual JSON snippet as a fallback for users who prefer to edit by hand. Lists every detected path so users know what the wizard is reaching for.

Test plan

🤖 Generated with Claude Code

…e Code / Cursor configs

Item 1.2 from HIGH-plan-broad-audience-onboarding.md. Stacks on top of
PR #142 (zero-config RPC + udev + deep-link URLs). Eliminates the
single biggest non-dev friction point — finding + editing
`claude_desktop_config.json` (or per-OS equivalents) by hand.

## What ships

`src/setup/register-clients.ts` — pure module, no console output, no
prompts. Three exports the wizard composes:

- `getClientConfigPaths()` — per-OS list of (Claude Desktop, Claude
  Code, Cursor) user-level config paths.
- `detectClient(configPath)` — `configured` / `installed` / `absent`.
  `installed` = parent dir exists but config doesn't (Claude Desktop
  creates `~/Library/Application Support/Claude` on install whether
  or not any MCP servers are configured).
- `patchClientConfig(client, configPath, opts?)` — additive +
  idempotent + atomic + reversible. Reads existing JSON, merges in
  `mcpServers.vaultpilot-mcp`, atomic-renames, leaves a
  `<file>.vaultpilot.bak` next to it.

The MCP entry is `{ command: "node", args: ["<abs path>/dist/index.js"] }`,
resolved via `import.meta.url`. Works whether the user `npm i -g`'d
or cloned from source — absolute path doesn't depend on PATH and
won't break under a reinstall under a different prefix.

## Wizard wiring

`src/setup.ts` gains an `offerClientAutoRegister(p)` helper called at
the end of `runFullWizard()`. Detects clients, prints which were
found, asks `[Y/n]`, runs the patch sweep, prints a per-client
summary (`✓ Added` / `✓ Already configured` / `· Not detected` /
`✗ Error`).

Auto-register is opt-in (default Y, but the user must press enter):
even with backups, touching another app's config is the kind of thing
to ask before doing.

## Per-project / per-workspace configs are NOT touched

`<project>/.claude/settings.json` and `<workspace>/.cursor/mcp.json`
are deliberately skipped. The wizard runs from an arbitrary CWD; the
user might be in `~`, in a personal-finance repo, or anywhere. Patching
the wrong project's config is worse than skipping. Documented in the
module doc-comment + the README.

## Hardening

- Atomic write via tmp + rename. A crashed wizard never leaves a half-
  written config.
- 0o600 file mode preserved. MCP configs may carry API keys; tightening
  perms is cheap insurance.
- Malformed-JSON-existing-config returns a structured `error` result
  rather than crashing or overwriting.
- Empty-file existing config is treated as `{}` and patched normally.
- `vaultpilot-mcp` already present → `already-present` result with no
  write. Re-running the wizard is safe.

## README

"Use with Claude Desktop" section rewritten to mention auto-register
first and surface the manual JSON snippet as a fallback for users who
prefer to edit by hand. Lists every detected path so users know what
the wizard is reaching for.

## Verification

- `npm test` — **810/810** pass (+15 new register-clients tests
  covering: per-OS path detection, three detect states, six patch
  paths including idempotent + malformed-JSON + perms, integration
  test for partial-detection, and the summary formatter).
- `npm run build` — clean TS.
- Manual: ran `vaultpilot-mcp-setup` end-to-end on this Linux box,
  confirmed it added the vaultpilot-mcp entry to `~/.claude.json`
  alongside an existing entry, backed up the original, and reported
  Cursor as not-detected (no `~/.cursor/`).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@szhygulin
szhygulin merged commit 997cdb3 into main Apr 25, 2026
3 checks passed
@szhygulin
szhygulin deleted the feat/client-auto-register branch April 25, 2026 02:14
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.

1 participant