Make MCP client setup just work: c8ctl mcp install <client>
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 7
- Forks
- 0
- Avg merge
- 15h 36m
- Merged PRs (30d)
- 28
Description
User report
Hi team, I had a bit of trouble configuring the c8ctl mcp-proxy in Claude. Our docs say the key name in the JSON config is
server, but Claude Desktop expectsmcpServersin itsclaude_desktop_config.json; everything else is being ignored. When restarting Claude, it shows two error messages, but these can safely be ignored.
The docs have a key-name defect (server → mcpServers). The fix "update the docs" is unsatisfying — reading docs to hand-edit a JSON file is a 2006 onboarding experience. We should make this just work without docs.
Proposed solution: c8ctl mcp install <client>
Add install / uninstall subcommands that edit the MCP client's config file for the user:
c8ctl mcp install claude-desktop [--profile <name>] [--alias <name>]
c8ctl mcp install cursor [--profile <name>] [--alias <name>]
c8ctl mcp install vscode [--profile <name>] [--alias <name>]
c8ctl mcp uninstall claude-desktop [--alias <name>]
c8ctl mcp list # show installed entries across known clients
Behaviour
- Resolve client config path per OS:
- Claude Desktop (macOS):
~/Library/Application Support/Claude/claude_desktop_config.json - Claude Desktop (Windows/WSL):
%APPDATA%/Claude/claude_desktop_config.json - Cursor:
~/.cursor/mcp.json - VS Code (user MCP):
~/Library/Application Support/Code/User/mcp.jsonor theworkbench.mcp.serverssetting
- Claude Desktop (macOS):
- Read, merge, write:
- Preserve existing unrelated keys (
preferences, othermcpServersentries). - Use the client's correct top-level key name (
mcpServersfor Claude,serversfor VS Code's MCP schema, etc.). - Key the entry by
--alias(default: active profile name, fallback"camunda").
- Preserve existing unrelated keys (
- Embed the active profile's credentials:
- Read from the c8ctl profile store (same config
c8ctlalready knows). - Emit the
envblock withCAMUNDA_BASE_URL,CAMUNDA_CLIENT_ID,CAMUNDA_CLIENT_SECRET,CAMUNDA_OAUTH_URL,CAMUNDA_TOKEN_AUDIENCE. - Use the correct
command+argsshape:"command": "npx", "args": ["-y", "@camunda8/cli", "mcp-proxy"].
- Read from the c8ctl profile store (same config
- Atomic write: write to
tmpfile+ rename, so a partial failure doesn't corrupt the user's Claude config. --dry-run: print the merged JSON without writing (we already have thedryRun()helper — uses the existing framework).- Friendly confirmation:
✔ Installed camunda → claude-desktop Config: ~/Library/Application Support/Claude/claude_desktop_config.json Restart Claude Desktop to activate.
Why this kills the defect class, not just the instance
- No docs hand-off: the JSON key name (
mcpServersvsservervsservers) is owned by code with a test per client. A key-name typo becomes a test failure, not a user-hours-lost incident. - Secret-handling normalisation: today users copy-paste secrets from c8ctl profile output into Claude's config. The
installcommand keeps secrets inside a single trusted tool and writes them withchmod 600(where applicable). - Multi-profile pattern: users juggling saas-stable + local + customer-dev get
c8ctl mcp install claude-desktop --profile customer-dev --alias acmeand end up with named, clearly-scoped entries. - Symmetric uninstall: removes the defect-class of "leftover stale credentials in Claude config" that happens with hand-edited JSON.
- Per-client adapters are isolated: one small adapter per client (Claude, Cursor, VS Code). Each adapter tests the key name, path resolution, and merge semantics. Adding a new client (Zed, Windsurf, …) is a single file.
Docs impact
After this ships, the docs collapse to:
Run
c8ctl mcp install claude-desktopand restart Claude. Done.
The key-name defect in current docs becomes unreachable — no user is hand-editing that file any more.
Open design choices (flag if anything is contentious)
- Command shape:
c8ctl mcp install <client>vs. reusingmcp-proxy install <client>. Prefer a newmcpverb —mcp-proxyis the runtime,mcpis the configurator. - Client enum: start with
claude-desktop. Addcursorandvscodein the same PR if cheap; otherwise stub them with a "not yet supported" error that links to the issue. - Alias defaulting: use the active profile name. If that collides with an existing entry, error with a hint to pass
--alias. - Secrets handling: for OAuth client secrets, keep as plain env vars (matches Claude's current config shape) — OS keyring integration is a separate, larger issue.
Related
- #288 — normalise command architecture. This new
mcpcommand goes in as a canonical-shape handler from day one. - Existing
mcp-proxyruntime command stays unchanged.
Acceptance
c8ctl mcp install claude-desktopwrites a valid merged config that Claude Desktop accepts without errors on restart.- Tests per client adapter verify key names + merge semantics against realistic pre-existing config fixtures (including the
preferences+ unrelatedmcpServersblocks). --dry-runrenders through the framework's JSON/text modes.- Uninstall is symmetric and safe on already-absent entries.
- Docs section reduced to a single-line install invocation.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by locating the existing mcp-proxy command, the profile store, the dryRun() helper, and the canonical command architecture described in #288. Define client adapters for the requested config paths, key names, merge and atomic-write behavior, then add fixtures covering install, uninstall, and dry-run. Done means the acceptance cases pass and the documentation becomes a single install invocation.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- cli
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100