feat(mcp): clerk mcp install/list/uninstall/run + doctor MCP check - #307

Merged
rafa-thayto merged 4 commits into
mainfrom
add-mcp-server-cli
Jul 22, 2026
Merged

feat(mcp): clerk mcp install/list/uninstall/run + doctor MCP check#307
rafa-thayto merged 4 commits into
mainfrom
add-mcp-server-cli

Conversation

@rafa-thayto

@rafa-thaytorafa-thayto commented May 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a clerk mcp command group that connects the Clerk remote MCP server (https://mcp.clerk.com/mcp) to 10 AI clients, plus an MCP health check in clerk doctor.

  • clerk mcp install — register the server. Human mode: interactive multiselect over detected clients; agent mode / --all / --client <id> (repeatable): non-interactive. Install always converges: whatever entry sits under the name is replaced (remove-then-add through the client's CLI, or an overwrite for file-backed clients) — there is no --force and no conflict state.
  • clerk mcp list — show every Clerk-flavored entry (named clerk or pointing at a *.clerk.com host). JSON output is { entries, failures }, so an unreadable client config surfaces structurally instead of reading as "nothing installed".
  • clerk mcp uninstall — remove the entry. Human mode prompts with the clients that actually have it (nothing pre-checked); agent mode / --all targets every client. Nothing matched → warm hint, exit 0.
  • clerk mcp run — a built-in stdio↔Streamable-HTTP bridge that editors spawn (replaces npx mcp-remote; no npx dependency). Every client config stores the same URL-less descriptor { "command": "clerk", "args": ["mcp", "run"] }; the bridge resolves its target at spawn time (CLERK_MCP_URL > env profile mcpUrl > hosted default).
  • clerk doctor — MCP check probes each distinct configured URL via the initialize handshake when an entry is installed (skips otherwise; warns, never fails), and distinguishes "config unreadable" from "nothing installed".

Registration is delegated to each client's own CLI

Clients that ship a non-interactive registration CLI are driven through it — the client owns its config format and write safety. The rest get direct file writes. There is no file-write fallback for CLI-backed clients: a missing binary fails that client with an actionable error (mcp_client_cli_not_found + docs link), and detection is PATH-based so the picker only offers clients we can actually drive.

Client--client idRegistered viaRemoved via
Claude Codeclaudeclaude mcp add --scope userclaude mcp remove
Cursorcursorfile write (no CLI exists)file write
GitHub Copilot / VS Codevscode (copilot)code --add-mcp '<json>'file write (add-only CLI)
Windsurfwindsurffile write (no CLI exists)file write
Gemini CLIgeminigemini mcp add --scope usergemini mcp remove
Codexcodexcodex mcp add (TOML config)codex mcp remove
opencodeopencodefile write (its mcp add is an interactive-only wizard)file write
OpenClawopenclawopenclaw mcp add --no-probeopenclaw mcp unset
Warpwarpfile write to ~/.warp/.mcp.json (no registration CLI)file write
Hermes Agenthermeshermes mcp add (stdin-piped confirm + post-add verify)hermes mcp remove

All entries are user-global, so the server is available in every project.

Design notes

  • Client CLIs are spawned safely: stdin closed (a prompting CLI EOF-errors instead of hanging agent runs) + a 15s hard timeout. Windows .cmd/.bat shims are launched through cmd.exe /c.
  • Hermes hardening: its mcp add ends in a confirm prompt and exits 0 on cancel without saving — so the affirmative answer is piped to stdin and the config is re-read after add; a silent no-op becomes mcp_client_cli_failed. Its YAML config is read-only to us (both mutations go through its CLI), so hand-written YAML is never rewritten.
  • Reads stay ours:list, doctor, and the uninstall picker parse config files directly (JSON/TOML/YAML codecs, incl. OpenClaw's nested mcp.servers map). Legacy descriptor shapes ({url}, {serverUrl}, mcp-remote args, clerk mcp run --url) still round-trip.
  • Agent mode throughout: non-TTY runs auto-emit JSON, never prompt, and every registration-blocking error carries a machine-readable code plus a docsUrl to the Clerk MCP setup docs (agent mode gets the .md variant) for the manual fallback.
  • Partial failure is structural: per-client failures land in a failures: [{ client, error }] array in install/uninstall/list JSON; the command exits non-zero only when every targeted client fails.
  • Auth (current limitation): the bridge is transport-only. A 401/403 on the initial handshake exits with a clear error; on later requests it's surfaced to the editor as a per-request JSON-RPC error without killing the bridge.

Test plan

  • bun run format:check, bun run lint, bun run typecheck — green
  • bun run test — 2095 tests green, including per-client argv contracts for all six CLI dialects, file shapes for the four file-backed clients, nested-topKey + YAML codec units, corrupt-config failure threading, Hermes verify-add, and Windows .cmd-shim spawning
  • Live verification (macOS): every CLI dialect verified against the real client CLI — claude/gemini/codex/code (duplicate-add/remove semantics), hermes (prompt + lying exit code discovered this way), openclaw (sandboxed OPENCLAW_STATE_DIR)
  • Real round-trip: uninstall --allinstall --all across all detected clients with zero failures; opencode's own CLI reports ✓ clerk connected and Hermes reports clerk ✓ enabled; bridge handshake against the hosted server returns Clerk MCP Server

@changeset-bot

changeset-botBot commented May 25, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e22734c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkMinor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented May 25, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds MCP server support to the Clerk CLI: new subcommands (clerk mcp install/list/uninstall), a JSON-backed client factory and five client integrations (Claude Code, Cursor, VS Code, Windsurf, Gemini), per-client config read/write helpers, collection of installed entries, an initialize-handshake probe supporting JSON and SSE with a 10s timeout, a doctor check (checkMcp), environment/profile mcpUrl wiring, CLI wiring, comprehensive tests, and documentation including a changeset and README updates.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 24.44% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check⚠️ WarningThe title matches the MCP install/list/uninstall and doctor work, but it incorrectly includes a run command that isn't in this changeset.Remove the unsupported "run" reference and keep the title focused on install/list/uninstall plus the doctor MCP check.
✅ Passed checks (3 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
Description check✅ PassedThe description is about the same MCP feature area and doctor check, so it is related despite several inaccurate details.

Comment @coderabbitai help to get the list of available commands.

@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 2 times, most recently from b4bc06c to 4f53e9fCompareMay 30, 2026 12:14

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/cli-core/src/commands/mcp/clients/user-scope.test.ts (1)

2-2: ⚡ Quick win

Use Bun file APIs here instead of node:fs read/write helpers.

These tests currently use readFile/writeFile from node:fs/promises, which goes against the repo’s Bun-specific file I/O guideline. Bun.file(...).json()/text() and Bun.write(...) would keep this aligned with the rest of the codebase. As per coding guidelines, **/*.{ts,tsx,js,jsx}: Prefer Bun.file over node:fs's readFile/writeFile for file operations.

Also applies to: 45-46, 64-66

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/cli-core/src/commands/mcp/clients/user-scope.test.ts` at line 2,
Replace node:fs/promises read/write helpers imported as mkdir, mkdtemp,
readFile, rm, writeFile in user-scope.test.ts with Bun file APIs: use
Bun.file(path).text()/json() for reads and Bun.write/Bun.writeSync (or
Bun.file(path).write(...)) for writes, and keep using mkdtemp/mkdir/rm
equivalents via Bun or the existing helpers if available; update all call sites
that use readFile/writeFile (and any promise-based usages) to the corresponding
Bun.file(...).text()/json() and Bun.write(...) patterns so the tests follow the
repo’s Bun-specific I/O guideline.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli-core/src/commands/mcp/clients/user-scope.test.ts`:
- Around line 7-12: The top-level module mock of node:os (mock.module("node:os",
...)) is never torn down causing cross-test leakage; add a cleanup via
afterAll(() => mock.restoreModule("node:os")) (or equivalent) to restore the
original module and avoid order-dependent failures, and update file I/O in this
test to use Bun APIs instead of node:fs/promises—replace readFile/writeFile
calls with Bun.file(path).json()/text() for reads and Bun.file(path).write(...)
or Bun.write(...) for writes so the test uses Bun's file operations correctly;
reference mock.module, mock.restoreModule, mockHome and the test file's existing
fs read/write usages when making the changes.
---
Nitpick comments:
In `@packages/cli-core/src/commands/mcp/clients/user-scope.test.ts`:
- Line 2: Replace node:fs/promises read/write helpers imported as mkdir,
mkdtemp, readFile, rm, writeFile in user-scope.test.ts with Bun file APIs: use
Bun.file(path).text()/json() for reads and Bun.write/Bun.writeSync (or
Bun.file(path).write(...)) for writes, and keep using mkdtemp/mkdir/rm
equivalents via Bun or the existing helpers if available; update all call sites
that use readFile/writeFile (and any promise-based usages) to the corresponding
Bun.file(...).text()/json() and Bun.write(...) patterns so the tests follow the
repo’s Bun-specific I/O guideline.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 33d9756c-11d6-436b-8465-7e750f438029

📥 Commits

Reviewing files that changed from the base of the PR and between 292e92f and 4f53e9f.

📒 Files selected for processing (34)
  • .changeset/mcp-install.md
  • README.md
  • packages/cli-core/src/cli-program.ts
  • packages/cli-core/src/commands/doctor/README.md
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/doctor/context.test.ts
  • packages/cli-core/src/commands/doctor/index.ts
  • packages/cli-core/src/commands/mcp/README.md
  • packages/cli-core/src/commands/mcp/clients/claude-code.ts
  • packages/cli-core/src/commands/mcp/clients/clients.test.ts
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.ts
  • packages/cli-core/src/commands/mcp/clients/paths.ts
  • packages/cli-core/src/commands/mcp/clients/registry.ts
  • packages/cli-core/src/commands/mcp/clients/types.ts
  • packages/cli-core/src/commands/mcp/clients/user-scope.test.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/commands/mcp/collect.ts
  • packages/cli-core/src/commands/mcp/index.ts
  • packages/cli-core/src/commands/mcp/install.test.ts
  • packages/cli-core/src/commands/mcp/install.ts
  • packages/cli-core/src/commands/mcp/list.test.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/mcp/probe.test.ts
  • packages/cli-core/src/commands/mcp/probe.ts
  • packages/cli-core/src/commands/mcp/shared.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts
  • packages/cli-core/src/commands/mcp/uninstall.ts
  • packages/cli-core/src/lib/environment.ts
  • packages/cli-core/src/lib/errors.ts
✅ Files skipped from review due to trivial changes (4)
  • .changeset/mcp-install.md
  • packages/cli-core/src/commands/doctor/README.md
  • README.md
  • packages/cli-core/src/commands/mcp/README.md
🚧 Files skipped from review as they are similar to previous changes (26)
  • packages/cli-core/src/commands/mcp/list.test.ts
  • packages/cli-core/src/commands/mcp/clients/registry.ts
  • packages/cli-core/src/commands/mcp/index.ts
  • packages/cli-core/src/commands/mcp/clients/claude-code.ts
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/commands/mcp/clients/types.ts
  • packages/cli-core/src/commands/mcp/probe.test.ts
  • packages/cli-core/src/lib/errors.ts
  • packages/cli-core/src/commands/doctor/index.ts
  • packages/cli-core/src/commands/mcp/clients/paths.ts
  • packages/cli-core/src/commands/mcp/install.ts
  • packages/cli-core/src/lib/environment.ts
  • packages/cli-core/src/commands/mcp/collect.ts
  • packages/cli-core/src/commands/mcp/uninstall.ts
  • packages/cli-core/src/commands/mcp/clients/clients.test.ts
  • packages/cli-core/src/commands/mcp/probe.ts
  • packages/cli-core/src/commands/mcp/install.test.ts
  • packages/cli-core/src/cli-program.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts

Comment threadpackages/cli-core/src/commands/mcp/clients/user-scope.test.ts Outdated
@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@2.0.1-snapshot.bdf8faf
PackageVersion
clerk2.0.1-snapshot.bdf8faf

Published from bdf8faf

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli-core/src/commands/mcp/clients/clients.test.ts`:
- Around line 65-96: The VS Code test is leaking to the real config because
vscodeUserDir() respects XDG_CONFIG_HOME/APPDATA; update the tests to isolate
that by setting XDG_CONFIG_HOME and APPDATA to a directory under mockHome (or
delete them) before running the VS Code-specific assertions and restore them
after; ensure the expectation for the VS Code path does not call vscodeUserDir()
(i.e. compute the expected path explicitly inside the test instead of reusing
expectedPath()/vscodeUserDir()) so the test verifies the function under test
rather than mirroring the same helper.
In `@packages/cli-core/src/commands/mcp/clients/paths.ts`:
- Around line 26-35: vscodeUserDir currently uses the nullish coalescing
operator (??) with process.env.APPDATA and process.env.XDG_CONFIG_HOME which
treats empty strings as set; update vscodeUserDir to treat empty or
whitespace-only env vars as unset by checking truthiness/trim (e.g. derive local
vars like appData = process.env.APPDATA?.trim() ? process.env.APPDATA :
undefined and xdg = process.env.XDG_CONFIG_HOME?.trim() ?
process.env.XDG_CONFIG_HOME : undefined) and then fall back to join(homedir(),
...) in the switch cases; reference the vscodeUserDir function and its uses of
process.env.APPDATA and process.env.XDG_CONFIG_HOME, and ensure platform(),
homedir(), and join() behavior is preserved.
In `@packages/cli-core/src/lib/environment.ts`:
- Around line 154-165: getMcpUrl currently lets an empty CLERK_MCP_URL ("") win
due to the nullish coalescing operator; treat empty/whitespace-only env values
as unset by trimming and checking truthiness before falling back. Update
getMcpUrl (function name: getMcpUrl) to compute const env =
process.env.CLERK_MCP_URL?.trim(); then return env && env.length ? env :
getCurrentEnv().mcpUrl ?? DEFAULT_MCP_URL so blank overrides no longer produce
an unusable empty string.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2826916f-5886-46c5-96c7-da4b8e7b1a92

📥 Commits

Reviewing files that changed from the base of the PR and between bdf8faf and d80abcd.

📒 Files selected for processing (23)
  • .changeset/mcp-install.md
  • packages/cli-core/src/cli-program.ts
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/mcp/README.md
  • packages/cli-core/src/commands/mcp/clients/claude-code.ts
  • packages/cli-core/src/commands/mcp/clients/clients.test.ts
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.ts
  • packages/cli-core/src/commands/mcp/clients/paths.ts
  • packages/cli-core/src/commands/mcp/clients/user-scope.test.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/commands/mcp/install.test.ts
  • packages/cli-core/src/commands/mcp/install.ts
  • packages/cli-core/src/commands/mcp/list.test.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/mcp/shared.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts
  • packages/cli-core/src/lib/environment.ts
  • packages/cli-core/src/lib/errors.ts
💤 Files with no reviewable changes (3)
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/cli-program.ts
✅ Files skipped from review due to trivial changes (2)
  • .changeset/mcp-install.md
  • packages/cli-core/src/commands/mcp/README.md
🚧 Files skipped from review as they are similar to previous changes (12)
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/lib/errors.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/clients/user-scope.test.ts
  • packages/cli-core/src/commands/mcp/shared.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.ts
  • packages/cli-core/src/commands/mcp/install.test.ts

Comment threadpackages/cli-core/src/commands/mcp/clients/clients.test.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/paths.ts Outdated
Comment threadpackages/cli-core/src/lib/environment.ts
@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@2.0.1-snapshot.d637eb0
PackageVersion
clerk2.0.1-snapshot.d637eb0

Published from d637eb0

@wyattjoh
wyattjoh self-requested a review June 12, 2026 16:26
@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 2 times, most recently from 19b2820 to 0d5281dCompareJune 15, 2026 12:22

@wyattjohwyattjoh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid, well-tested feature surface. The error handling, URL hardening, and per-client config abstraction are all carefully done. A few non-blocking observations below, mostly around concurrency edge cases in the stdio bridge and the shared ~/.claude.json write path.

Comment threadpackages/cli-core/src/commands/mcp/clients/claude.ts
Comment threadpackages/cli-core/src/commands/mcp/run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/run.ts
Comment threadpackages/cli-core/src/lib/input-json.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/toml-config.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/shared.ts
Comment threadpackages/cli-core/src/commands/mcp/install.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.test.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.test.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/gemini.ts Outdated
@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 3 times, most recently from 161868b to c13e55aCompareJune 27, 2026 12:17

@wyattjohwyattjoh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strong, well-tested PR. The main cleanup is leftover --url surface from the "drop --url from public CLI" commit (a help example that now errors, an unactionable error message, the README, and the changeset), plus a couple of narrow correctness edge cases noted inline.

Comment threadpackages/cli-core/src/commands/mcp/index.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/README.md Outdated
Comment thread.changeset/mcp-install.md Outdated
Comment threadpackages/cli-core/src/commands/mcp/run.ts
Comment threadpackages/cli-core/src/commands/mcp/clients/json-config.ts
Comment threadpackages/cli-core/src/commands/mcp/install.ts
Comment threadpackages/cli-core/src/commands/mcp/run.ts
Comment threadpackages/cli-core/src/commands/mcp/shared.ts
@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 4 times, most recently from c70a7f1 to 89d5811CompareJuly 15, 2026 12:16
@rafa-thaytorafa-thayto changed the title feat(mcp): add clerk mcp install/list/uninstall + doctor MCP checkfeat(mcp): clerk mcp install/list/uninstall/run across 10 clients + doctor MCP checkJul 21, 2026
@rafa-thaytorafa-thayto changed the title feat(mcp): clerk mcp install/list/uninstall/run across 10 clients + doctor MCP checkfeat(mcp): clerk mcp install/list/uninstall/run + doctor MCP checkJul 21, 2026
… + doctor MCP check
Registers the Clerk remote MCP server (https://mcp.clerk.com/mcp) in AI
editors and agent CLIs, with an MCP reachability check in `clerk doctor`.
Supported clients (all user-global): Claude Code, Cursor, GitHub Copilot /
VS Code (`--client vscode` or `copilot`), Windsurf, Gemini, Codex, opencode,
OpenClaw, Warp, and Hermes Agent.
Registration is delegated to each client's own CLI where a non-interactive
one exists (`claude`/`gemini`/`codex`/`openclaw`/`hermes` `mcp add`,
`code --add-mcp`), with PATH-based detection and no file-write fallback;
Cursor, Windsurf, Warp, and opencode (interactive-only CLI) get atomic
file writes. Install always converges via remove-then-add. Client CLIs are
spawned with stdin closed and a 15s timeout; Windows `.cmd` shims run
through `cmd.exe /c`; Hermes gets its confirm prompt answered via stdin and
a post-add verification because its cancel path exits 0 without saving.
Every client stores the same URL-less bridge descriptor
`{command: "clerk", args: ["mcp", "run"]}`. `clerk mcp run` is a built-in
stdio<->Streamable-HTTP bridge (replaces npx mcp-remote) that resolves its
target at spawn time (CLERK_MCP_URL > env profile mcpUrl > hosted default),
serializes stdout frames, caps both stdin lines and upstream SSE/JSON
bodies at 16 MiB, and surfaces pre-session 401/403 as a clear error while
answering in-session auth failures per-request.
Reads stay file-based (JSON/TOML/YAML codecs; TOML and YAML are read-only
since those clients' CLIs own both mutations). `--name` is allowlisted
([A-Za-z0-9_-], no leading dash) because it is spliced into client CLI argv
and used as a config key. `list --json` reports { entries, failures } so an
unreadable config surfaces structurally; install/uninstall carry the same
failures array and exit non-zero only when every targeted client fails.
Agent mode never prompts and always emits JSON with machine-readable error
codes plus a docs URL (clerk.com/docs/guides/ai/mcp/clerk-mcp-server).
Verified live against the real client CLIs on macOS: full uninstall/install
round-trips with zero failures; opencode reports "clerk connected" and
Hermes "clerk enabled" through their own CLIs; OpenClaw's dialect verified
in a sandboxed OPENCLAW_STATE_DIR; bridge handshake against the hosted
server returns "Clerk MCP Server".
Comment threadpackages/cli-core/src/commands/doctor/check-mcp.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/make-cli-client.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/make-client.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/probe.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/make-client.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/toml-config.ts
Comment threadpackages/cli-core/src/commands/mcp/probe.ts
Comment threadpackages/cli-core/src/commands/doctor/check-mcp.ts
Comment threadpackages/cli-core/src/commands/mcp/clients/json-config.ts
@rafa-thayto
rafa-thayto merged commit 8bf74ab into mainJul 22, 2026
9 of 10 checks passed
@rafa-thayto
rafa-thayto deleted the add-mcp-server-cli branch July 22, 2026 14:37
@github-actionsgithub-actionsBot mentioned this pull request Jul 22, 2026
Sign up for freeto 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.

2 participants

@rafa-thayto@wyattjoh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat(mcp): clerk mcp install/list/uninstall/run + doctor MCP check - #307

Merged
rafa-thayto merged 4 commits into
mainfrom
add-mcp-server-cli
Jul 22, 2026
Merged

feat(mcp): clerk mcp install/list/uninstall/run + doctor MCP check#307
rafa-thayto merged 4 commits into
mainfrom
add-mcp-server-cli

Conversation

@rafa-thayto

@rafa-thaytorafa-thayto commented May 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a clerk mcp command group that connects the Clerk remote MCP server (https://mcp.clerk.com/mcp) to 10 AI clients, plus an MCP health check in clerk doctor.

  • clerk mcp install — register the server. Human mode: interactive multiselect over detected clients; agent mode / --all / --client <id> (repeatable): non-interactive. Install always converges: whatever entry sits under the name is replaced (remove-then-add through the client's CLI, or an overwrite for file-backed clients) — there is no --force and no conflict state.
  • clerk mcp list — show every Clerk-flavored entry (named clerk or pointing at a *.clerk.com host). JSON output is { entries, failures }, so an unreadable client config surfaces structurally instead of reading as "nothing installed".
  • clerk mcp uninstall — remove the entry. Human mode prompts with the clients that actually have it (nothing pre-checked); agent mode / --all targets every client. Nothing matched → warm hint, exit 0.
  • clerk mcp run — a built-in stdio↔Streamable-HTTP bridge that editors spawn (replaces npx mcp-remote; no npx dependency). Every client config stores the same URL-less descriptor { "command": "clerk", "args": ["mcp", "run"] }; the bridge resolves its target at spawn time (CLERK_MCP_URL > env profile mcpUrl > hosted default).
  • clerk doctor — MCP check probes each distinct configured URL via the initialize handshake when an entry is installed (skips otherwise; warns, never fails), and distinguishes "config unreadable" from "nothing installed".

Registration is delegated to each client's own CLI

Clients that ship a non-interactive registration CLI are driven through it — the client owns its config format and write safety. The rest get direct file writes. There is no file-write fallback for CLI-backed clients: a missing binary fails that client with an actionable error (mcp_client_cli_not_found + docs link), and detection is PATH-based so the picker only offers clients we can actually drive.

Client--client idRegistered viaRemoved via
Claude Codeclaudeclaude mcp add --scope userclaude mcp remove
Cursorcursorfile write (no CLI exists)file write
GitHub Copilot / VS Codevscode (copilot)code --add-mcp '<json>'file write (add-only CLI)
Windsurfwindsurffile write (no CLI exists)file write
Gemini CLIgeminigemini mcp add --scope usergemini mcp remove
Codexcodexcodex mcp add (TOML config)codex mcp remove
opencodeopencodefile write (its mcp add is an interactive-only wizard)file write
OpenClawopenclawopenclaw mcp add --no-probeopenclaw mcp unset
Warpwarpfile write to ~/.warp/.mcp.json (no registration CLI)file write
Hermes Agenthermeshermes mcp add (stdin-piped confirm + post-add verify)hermes mcp remove

All entries are user-global, so the server is available in every project.

Design notes

  • Client CLIs are spawned safely: stdin closed (a prompting CLI EOF-errors instead of hanging agent runs) + a 15s hard timeout. Windows .cmd/.bat shims are launched through cmd.exe /c.
  • Hermes hardening: its mcp add ends in a confirm prompt and exits 0 on cancel without saving — so the affirmative answer is piped to stdin and the config is re-read after add; a silent no-op becomes mcp_client_cli_failed. Its YAML config is read-only to us (both mutations go through its CLI), so hand-written YAML is never rewritten.
  • Reads stay ours:list, doctor, and the uninstall picker parse config files directly (JSON/TOML/YAML codecs, incl. OpenClaw's nested mcp.servers map). Legacy descriptor shapes ({url}, {serverUrl}, mcp-remote args, clerk mcp run --url) still round-trip.
  • Agent mode throughout: non-TTY runs auto-emit JSON, never prompt, and every registration-blocking error carries a machine-readable code plus a docsUrl to the Clerk MCP setup docs (agent mode gets the .md variant) for the manual fallback.
  • Partial failure is structural: per-client failures land in a failures: [{ client, error }] array in install/uninstall/list JSON; the command exits non-zero only when every targeted client fails.
  • Auth (current limitation): the bridge is transport-only. A 401/403 on the initial handshake exits with a clear error; on later requests it's surfaced to the editor as a per-request JSON-RPC error without killing the bridge.

Test plan

  • bun run format:check, bun run lint, bun run typecheck — green
  • bun run test — 2095 tests green, including per-client argv contracts for all six CLI dialects, file shapes for the four file-backed clients, nested-topKey + YAML codec units, corrupt-config failure threading, Hermes verify-add, and Windows .cmd-shim spawning
  • Live verification (macOS): every CLI dialect verified against the real client CLI — claude/gemini/codex/code (duplicate-add/remove semantics), hermes (prompt + lying exit code discovered this way), openclaw (sandboxed OPENCLAW_STATE_DIR)
  • Real round-trip: uninstall --allinstall --all across all detected clients with zero failures; opencode's own CLI reports ✓ clerk connected and Hermes reports clerk ✓ enabled; bridge handshake against the hosted server returns Clerk MCP Server

@changeset-bot

changeset-botBot commented May 25, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e22734c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkMinor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented May 25, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds MCP server support to the Clerk CLI: new subcommands (clerk mcp install/list/uninstall), a JSON-backed client factory and five client integrations (Claude Code, Cursor, VS Code, Windsurf, Gemini), per-client config read/write helpers, collection of installed entries, an initialize-handshake probe supporting JSON and SSE with a 10s timeout, a doctor check (checkMcp), environment/profile mcpUrl wiring, CLI wiring, comprehensive tests, and documentation including a changeset and README updates.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 24.44% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check⚠️ WarningThe title matches the MCP install/list/uninstall and doctor work, but it incorrectly includes a run command that isn't in this changeset.Remove the unsupported "run" reference and keep the title focused on install/list/uninstall plus the doctor MCP check.
✅ Passed checks (3 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
Description check✅ PassedThe description is about the same MCP feature area and doctor check, so it is related despite several inaccurate details.

Comment @coderabbitai help to get the list of available commands.

@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 2 times, most recently from b4bc06c to 4f53e9fCompareMay 30, 2026 12:14

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/cli-core/src/commands/mcp/clients/user-scope.test.ts (1)

2-2: ⚡ Quick win

Use Bun file APIs here instead of node:fs read/write helpers.

These tests currently use readFile/writeFile from node:fs/promises, which goes against the repo’s Bun-specific file I/O guideline. Bun.file(...).json()/text() and Bun.write(...) would keep this aligned with the rest of the codebase. As per coding guidelines, **/*.{ts,tsx,js,jsx}: Prefer Bun.file over node:fs's readFile/writeFile for file operations.

Also applies to: 45-46, 64-66

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/cli-core/src/commands/mcp/clients/user-scope.test.ts` at line 2,
Replace node:fs/promises read/write helpers imported as mkdir, mkdtemp,
readFile, rm, writeFile in user-scope.test.ts with Bun file APIs: use
Bun.file(path).text()/json() for reads and Bun.write/Bun.writeSync (or
Bun.file(path).write(...)) for writes, and keep using mkdtemp/mkdir/rm
equivalents via Bun or the existing helpers if available; update all call sites
that use readFile/writeFile (and any promise-based usages) to the corresponding
Bun.file(...).text()/json() and Bun.write(...) patterns so the tests follow the
repo’s Bun-specific I/O guideline.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli-core/src/commands/mcp/clients/user-scope.test.ts`:
- Around line 7-12: The top-level module mock of node:os (mock.module("node:os",
...)) is never torn down causing cross-test leakage; add a cleanup via
afterAll(() => mock.restoreModule("node:os")) (or equivalent) to restore the
original module and avoid order-dependent failures, and update file I/O in this
test to use Bun APIs instead of node:fs/promises—replace readFile/writeFile
calls with Bun.file(path).json()/text() for reads and Bun.file(path).write(...)
or Bun.write(...) for writes so the test uses Bun's file operations correctly;
reference mock.module, mock.restoreModule, mockHome and the test file's existing
fs read/write usages when making the changes.
---
Nitpick comments:
In `@packages/cli-core/src/commands/mcp/clients/user-scope.test.ts`:
- Line 2: Replace node:fs/promises read/write helpers imported as mkdir,
mkdtemp, readFile, rm, writeFile in user-scope.test.ts with Bun file APIs: use
Bun.file(path).text()/json() for reads and Bun.write/Bun.writeSync (or
Bun.file(path).write(...)) for writes, and keep using mkdtemp/mkdir/rm
equivalents via Bun or the existing helpers if available; update all call sites
that use readFile/writeFile (and any promise-based usages) to the corresponding
Bun.file(...).text()/json() and Bun.write(...) patterns so the tests follow the
repo’s Bun-specific I/O guideline.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 33d9756c-11d6-436b-8465-7e750f438029

📥 Commits

Reviewing files that changed from the base of the PR and between 292e92f and 4f53e9f.

📒 Files selected for processing (34)
  • .changeset/mcp-install.md
  • README.md
  • packages/cli-core/src/cli-program.ts
  • packages/cli-core/src/commands/doctor/README.md
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/doctor/context.test.ts
  • packages/cli-core/src/commands/doctor/index.ts
  • packages/cli-core/src/commands/mcp/README.md
  • packages/cli-core/src/commands/mcp/clients/claude-code.ts
  • packages/cli-core/src/commands/mcp/clients/clients.test.ts
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.ts
  • packages/cli-core/src/commands/mcp/clients/paths.ts
  • packages/cli-core/src/commands/mcp/clients/registry.ts
  • packages/cli-core/src/commands/mcp/clients/types.ts
  • packages/cli-core/src/commands/mcp/clients/user-scope.test.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/commands/mcp/collect.ts
  • packages/cli-core/src/commands/mcp/index.ts
  • packages/cli-core/src/commands/mcp/install.test.ts
  • packages/cli-core/src/commands/mcp/install.ts
  • packages/cli-core/src/commands/mcp/list.test.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/mcp/probe.test.ts
  • packages/cli-core/src/commands/mcp/probe.ts
  • packages/cli-core/src/commands/mcp/shared.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts
  • packages/cli-core/src/commands/mcp/uninstall.ts
  • packages/cli-core/src/lib/environment.ts
  • packages/cli-core/src/lib/errors.ts
✅ Files skipped from review due to trivial changes (4)
  • .changeset/mcp-install.md
  • packages/cli-core/src/commands/doctor/README.md
  • README.md
  • packages/cli-core/src/commands/mcp/README.md
🚧 Files skipped from review as they are similar to previous changes (26)
  • packages/cli-core/src/commands/mcp/list.test.ts
  • packages/cli-core/src/commands/mcp/clients/registry.ts
  • packages/cli-core/src/commands/mcp/index.ts
  • packages/cli-core/src/commands/mcp/clients/claude-code.ts
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/commands/mcp/clients/types.ts
  • packages/cli-core/src/commands/mcp/probe.test.ts
  • packages/cli-core/src/lib/errors.ts
  • packages/cli-core/src/commands/doctor/index.ts
  • packages/cli-core/src/commands/mcp/clients/paths.ts
  • packages/cli-core/src/commands/mcp/install.ts
  • packages/cli-core/src/lib/environment.ts
  • packages/cli-core/src/commands/mcp/collect.ts
  • packages/cli-core/src/commands/mcp/uninstall.ts
  • packages/cli-core/src/commands/mcp/clients/clients.test.ts
  • packages/cli-core/src/commands/mcp/probe.ts
  • packages/cli-core/src/commands/mcp/install.test.ts
  • packages/cli-core/src/cli-program.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts

Comment threadpackages/cli-core/src/commands/mcp/clients/user-scope.test.ts Outdated
@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@2.0.1-snapshot.bdf8faf
PackageVersion
clerk2.0.1-snapshot.bdf8faf

Published from bdf8faf

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli-core/src/commands/mcp/clients/clients.test.ts`:
- Around line 65-96: The VS Code test is leaking to the real config because
vscodeUserDir() respects XDG_CONFIG_HOME/APPDATA; update the tests to isolate
that by setting XDG_CONFIG_HOME and APPDATA to a directory under mockHome (or
delete them) before running the VS Code-specific assertions and restore them
after; ensure the expectation for the VS Code path does not call vscodeUserDir()
(i.e. compute the expected path explicitly inside the test instead of reusing
expectedPath()/vscodeUserDir()) so the test verifies the function under test
rather than mirroring the same helper.
In `@packages/cli-core/src/commands/mcp/clients/paths.ts`:
- Around line 26-35: vscodeUserDir currently uses the nullish coalescing
operator (??) with process.env.APPDATA and process.env.XDG_CONFIG_HOME which
treats empty strings as set; update vscodeUserDir to treat empty or
whitespace-only env vars as unset by checking truthiness/trim (e.g. derive local
vars like appData = process.env.APPDATA?.trim() ? process.env.APPDATA :
undefined and xdg = process.env.XDG_CONFIG_HOME?.trim() ?
process.env.XDG_CONFIG_HOME : undefined) and then fall back to join(homedir(),
...) in the switch cases; reference the vscodeUserDir function and its uses of
process.env.APPDATA and process.env.XDG_CONFIG_HOME, and ensure platform(),
homedir(), and join() behavior is preserved.
In `@packages/cli-core/src/lib/environment.ts`:
- Around line 154-165: getMcpUrl currently lets an empty CLERK_MCP_URL ("") win
due to the nullish coalescing operator; treat empty/whitespace-only env values
as unset by trimming and checking truthiness before falling back. Update
getMcpUrl (function name: getMcpUrl) to compute const env =
process.env.CLERK_MCP_URL?.trim(); then return env && env.length ? env :
getCurrentEnv().mcpUrl ?? DEFAULT_MCP_URL so blank overrides no longer produce
an unusable empty string.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2826916f-5886-46c5-96c7-da4b8e7b1a92

📥 Commits

Reviewing files that changed from the base of the PR and between bdf8faf and d80abcd.

📒 Files selected for processing (23)
  • .changeset/mcp-install.md
  • packages/cli-core/src/cli-program.ts
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/mcp/README.md
  • packages/cli-core/src/commands/mcp/clients/claude-code.ts
  • packages/cli-core/src/commands/mcp/clients/clients.test.ts
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.ts
  • packages/cli-core/src/commands/mcp/clients/paths.ts
  • packages/cli-core/src/commands/mcp/clients/user-scope.test.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/commands/mcp/install.test.ts
  • packages/cli-core/src/commands/mcp/install.ts
  • packages/cli-core/src/commands/mcp/list.test.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/mcp/shared.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts
  • packages/cli-core/src/lib/environment.ts
  • packages/cli-core/src/lib/errors.ts
💤 Files with no reviewable changes (3)
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/cli-program.ts
✅ Files skipped from review due to trivial changes (2)
  • .changeset/mcp-install.md
  • packages/cli-core/src/commands/mcp/README.md
🚧 Files skipped from review as they are similar to previous changes (12)
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/lib/errors.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/clients/user-scope.test.ts
  • packages/cli-core/src/commands/mcp/shared.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.ts
  • packages/cli-core/src/commands/mcp/install.test.ts

Comment threadpackages/cli-core/src/commands/mcp/clients/clients.test.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/paths.ts Outdated
Comment threadpackages/cli-core/src/lib/environment.ts
@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@2.0.1-snapshot.d637eb0
PackageVersion
clerk2.0.1-snapshot.d637eb0

Published from d637eb0

@wyattjoh
wyattjoh self-requested a review June 12, 2026 16:26
@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 2 times, most recently from 19b2820 to 0d5281dCompareJune 15, 2026 12:22

@wyattjohwyattjoh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid, well-tested feature surface. The error handling, URL hardening, and per-client config abstraction are all carefully done. A few non-blocking observations below, mostly around concurrency edge cases in the stdio bridge and the shared ~/.claude.json write path.

Comment threadpackages/cli-core/src/commands/mcp/clients/claude.ts
Comment threadpackages/cli-core/src/commands/mcp/run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/run.ts
Comment threadpackages/cli-core/src/lib/input-json.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/toml-config.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/shared.ts
Comment threadpackages/cli-core/src/commands/mcp/install.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.test.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.test.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/gemini.ts Outdated
@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 3 times, most recently from 161868b to c13e55aCompareJune 27, 2026 12:17

@wyattjohwyattjoh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strong, well-tested PR. The main cleanup is leftover --url surface from the "drop --url from public CLI" commit (a help example that now errors, an unactionable error message, the README, and the changeset), plus a couple of narrow correctness edge cases noted inline.

Comment threadpackages/cli-core/src/commands/mcp/index.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/README.md Outdated
Comment thread.changeset/mcp-install.md Outdated
Comment threadpackages/cli-core/src/commands/mcp/run.ts
Comment threadpackages/cli-core/src/commands/mcp/clients/json-config.ts
Comment threadpackages/cli-core/src/commands/mcp/install.ts
Comment threadpackages/cli-core/src/commands/mcp/run.ts
Comment threadpackages/cli-core/src/commands/mcp/shared.ts
@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 4 times, most recently from c70a7f1 to 89d5811CompareJuly 15, 2026 12:16
@rafa-thaytorafa-thayto changed the title feat(mcp): add clerk mcp install/list/uninstall + doctor MCP checkfeat(mcp): clerk mcp install/list/uninstall/run across 10 clients + doctor MCP checkJul 21, 2026
@rafa-thaytorafa-thayto changed the title feat(mcp): clerk mcp install/list/uninstall/run across 10 clients + doctor MCP checkfeat(mcp): clerk mcp install/list/uninstall/run + doctor MCP checkJul 21, 2026
… + doctor MCP check
Registers the Clerk remote MCP server (https://mcp.clerk.com/mcp) in AI
editors and agent CLIs, with an MCP reachability check in `clerk doctor`.
Supported clients (all user-global): Claude Code, Cursor, GitHub Copilot /
VS Code (`--client vscode` or `copilot`), Windsurf, Gemini, Codex, opencode,
OpenClaw, Warp, and Hermes Agent.
Registration is delegated to each client's own CLI where a non-interactive
one exists (`claude`/`gemini`/`codex`/`openclaw`/`hermes` `mcp add`,
`code --add-mcp`), with PATH-based detection and no file-write fallback;
Cursor, Windsurf, Warp, and opencode (interactive-only CLI) get atomic
file writes. Install always converges via remove-then-add. Client CLIs are
spawned with stdin closed and a 15s timeout; Windows `.cmd` shims run
through `cmd.exe /c`; Hermes gets its confirm prompt answered via stdin and
a post-add verification because its cancel path exits 0 without saving.
Every client stores the same URL-less bridge descriptor
`{command: "clerk", args: ["mcp", "run"]}`. `clerk mcp run` is a built-in
stdio<->Streamable-HTTP bridge (replaces npx mcp-remote) that resolves its
target at spawn time (CLERK_MCP_URL > env profile mcpUrl > hosted default),
serializes stdout frames, caps both stdin lines and upstream SSE/JSON
bodies at 16 MiB, and surfaces pre-session 401/403 as a clear error while
answering in-session auth failures per-request.
Reads stay file-based (JSON/TOML/YAML codecs; TOML and YAML are read-only
since those clients' CLIs own both mutations). `--name` is allowlisted
([A-Za-z0-9_-], no leading dash) because it is spliced into client CLI argv
and used as a config key. `list --json` reports { entries, failures } so an
unreadable config surfaces structurally; install/uninstall carry the same
failures array and exit non-zero only when every targeted client fails.
Agent mode never prompts and always emits JSON with machine-readable error
codes plus a docs URL (clerk.com/docs/guides/ai/mcp/clerk-mcp-server).
Verified live against the real client CLIs on macOS: full uninstall/install
round-trips with zero failures; opencode reports "clerk connected" and
Hermes "clerk enabled" through their own CLIs; OpenClaw's dialect verified
in a sandboxed OPENCLAW_STATE_DIR; bridge handshake against the hosted
server returns "Clerk MCP Server".
Comment threadpackages/cli-core/src/commands/doctor/check-mcp.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/make-cli-client.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/make-client.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/probe.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/make-client.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/toml-config.ts
Comment threadpackages/cli-core/src/commands/mcp/probe.ts
Comment threadpackages/cli-core/src/commands/doctor/check-mcp.ts
Comment threadpackages/cli-core/src/commands/mcp/clients/json-config.ts
@rafa-thayto
rafa-thayto merged commit 8bf74ab into mainJul 22, 2026
9 of 10 checks passed
@rafa-thayto
rafa-thayto deleted the add-mcp-server-cli branch July 22, 2026 14:37
@github-actionsgithub-actionsBot mentioned this pull request Jul 22, 2026
Sign up for freeto 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.

2 participants

@rafa-thayto@wyattjoh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(mcp): clerk mcp install/list/uninstall/run + doctor MCP check - #307

Merged
rafa-thayto merged 4 commits into
mainfrom
add-mcp-server-cli
Jul 22, 2026
Merged

feat(mcp): clerk mcp install/list/uninstall/run + doctor MCP check#307
rafa-thayto merged 4 commits into
mainfrom
add-mcp-server-cli

Conversation

@rafa-thayto

@rafa-thaytorafa-thayto commented May 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a clerk mcp command group that connects the Clerk remote MCP server (https://mcp.clerk.com/mcp) to 10 AI clients, plus an MCP health check in clerk doctor.

  • clerk mcp install — register the server. Human mode: interactive multiselect over detected clients; agent mode / --all / --client <id> (repeatable): non-interactive. Install always converges: whatever entry sits under the name is replaced (remove-then-add through the client's CLI, or an overwrite for file-backed clients) — there is no --force and no conflict state.
  • clerk mcp list — show every Clerk-flavored entry (named clerk or pointing at a *.clerk.com host). JSON output is { entries, failures }, so an unreadable client config surfaces structurally instead of reading as "nothing installed".
  • clerk mcp uninstall — remove the entry. Human mode prompts with the clients that actually have it (nothing pre-checked); agent mode / --all targets every client. Nothing matched → warm hint, exit 0.
  • clerk mcp run — a built-in stdio↔Streamable-HTTP bridge that editors spawn (replaces npx mcp-remote; no npx dependency). Every client config stores the same URL-less descriptor { "command": "clerk", "args": ["mcp", "run"] }; the bridge resolves its target at spawn time (CLERK_MCP_URL > env profile mcpUrl > hosted default).
  • clerk doctor — MCP check probes each distinct configured URL via the initialize handshake when an entry is installed (skips otherwise; warns, never fails), and distinguishes "config unreadable" from "nothing installed".

Registration is delegated to each client's own CLI

Clients that ship a non-interactive registration CLI are driven through it — the client owns its config format and write safety. The rest get direct file writes. There is no file-write fallback for CLI-backed clients: a missing binary fails that client with an actionable error (mcp_client_cli_not_found + docs link), and detection is PATH-based so the picker only offers clients we can actually drive.

Client--client idRegistered viaRemoved via
Claude Codeclaudeclaude mcp add --scope userclaude mcp remove
Cursorcursorfile write (no CLI exists)file write
GitHub Copilot / VS Codevscode (copilot)code --add-mcp '<json>'file write (add-only CLI)
Windsurfwindsurffile write (no CLI exists)file write
Gemini CLIgeminigemini mcp add --scope usergemini mcp remove
Codexcodexcodex mcp add (TOML config)codex mcp remove
opencodeopencodefile write (its mcp add is an interactive-only wizard)file write
OpenClawopenclawopenclaw mcp add --no-probeopenclaw mcp unset
Warpwarpfile write to ~/.warp/.mcp.json (no registration CLI)file write
Hermes Agenthermeshermes mcp add (stdin-piped confirm + post-add verify)hermes mcp remove

All entries are user-global, so the server is available in every project.

Design notes

  • Client CLIs are spawned safely: stdin closed (a prompting CLI EOF-errors instead of hanging agent runs) + a 15s hard timeout. Windows .cmd/.bat shims are launched through cmd.exe /c.
  • Hermes hardening: its mcp add ends in a confirm prompt and exits 0 on cancel without saving — so the affirmative answer is piped to stdin and the config is re-read after add; a silent no-op becomes mcp_client_cli_failed. Its YAML config is read-only to us (both mutations go through its CLI), so hand-written YAML is never rewritten.
  • Reads stay ours:list, doctor, and the uninstall picker parse config files directly (JSON/TOML/YAML codecs, incl. OpenClaw's nested mcp.servers map). Legacy descriptor shapes ({url}, {serverUrl}, mcp-remote args, clerk mcp run --url) still round-trip.
  • Agent mode throughout: non-TTY runs auto-emit JSON, never prompt, and every registration-blocking error carries a machine-readable code plus a docsUrl to the Clerk MCP setup docs (agent mode gets the .md variant) for the manual fallback.
  • Partial failure is structural: per-client failures land in a failures: [{ client, error }] array in install/uninstall/list JSON; the command exits non-zero only when every targeted client fails.
  • Auth (current limitation): the bridge is transport-only. A 401/403 on the initial handshake exits with a clear error; on later requests it's surfaced to the editor as a per-request JSON-RPC error without killing the bridge.

Test plan

  • bun run format:check, bun run lint, bun run typecheck — green
  • bun run test — 2095 tests green, including per-client argv contracts for all six CLI dialects, file shapes for the four file-backed clients, nested-topKey + YAML codec units, corrupt-config failure threading, Hermes verify-add, and Windows .cmd-shim spawning
  • Live verification (macOS): every CLI dialect verified against the real client CLI — claude/gemini/codex/code (duplicate-add/remove semantics), hermes (prompt + lying exit code discovered this way), openclaw (sandboxed OPENCLAW_STATE_DIR)
  • Real round-trip: uninstall --allinstall --all across all detected clients with zero failures; opencode's own CLI reports ✓ clerk connected and Hermes reports clerk ✓ enabled; bridge handshake against the hosted server returns Clerk MCP Server

@changeset-bot

changeset-botBot commented May 25, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e22734c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkMinor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented May 25, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds MCP server support to the Clerk CLI: new subcommands (clerk mcp install/list/uninstall), a JSON-backed client factory and five client integrations (Claude Code, Cursor, VS Code, Windsurf, Gemini), per-client config read/write helpers, collection of installed entries, an initialize-handshake probe supporting JSON and SSE with a 10s timeout, a doctor check (checkMcp), environment/profile mcpUrl wiring, CLI wiring, comprehensive tests, and documentation including a changeset and README updates.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 24.44% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check⚠️ WarningThe title matches the MCP install/list/uninstall and doctor work, but it incorrectly includes a run command that isn't in this changeset.Remove the unsupported "run" reference and keep the title focused on install/list/uninstall plus the doctor MCP check.
✅ Passed checks (3 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
Description check✅ PassedThe description is about the same MCP feature area and doctor check, so it is related despite several inaccurate details.

Comment @coderabbitai help to get the list of available commands.

@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 2 times, most recently from b4bc06c to 4f53e9fCompareMay 30, 2026 12:14

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/cli-core/src/commands/mcp/clients/user-scope.test.ts (1)

2-2: ⚡ Quick win

Use Bun file APIs here instead of node:fs read/write helpers.

These tests currently use readFile/writeFile from node:fs/promises, which goes against the repo’s Bun-specific file I/O guideline. Bun.file(...).json()/text() and Bun.write(...) would keep this aligned with the rest of the codebase. As per coding guidelines, **/*.{ts,tsx,js,jsx}: Prefer Bun.file over node:fs's readFile/writeFile for file operations.

Also applies to: 45-46, 64-66

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/cli-core/src/commands/mcp/clients/user-scope.test.ts` at line 2,
Replace node:fs/promises read/write helpers imported as mkdir, mkdtemp,
readFile, rm, writeFile in user-scope.test.ts with Bun file APIs: use
Bun.file(path).text()/json() for reads and Bun.write/Bun.writeSync (or
Bun.file(path).write(...)) for writes, and keep using mkdtemp/mkdir/rm
equivalents via Bun or the existing helpers if available; update all call sites
that use readFile/writeFile (and any promise-based usages) to the corresponding
Bun.file(...).text()/json() and Bun.write(...) patterns so the tests follow the
repo’s Bun-specific I/O guideline.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli-core/src/commands/mcp/clients/user-scope.test.ts`:
- Around line 7-12: The top-level module mock of node:os (mock.module("node:os",
...)) is never torn down causing cross-test leakage; add a cleanup via
afterAll(() => mock.restoreModule("node:os")) (or equivalent) to restore the
original module and avoid order-dependent failures, and update file I/O in this
test to use Bun APIs instead of node:fs/promises—replace readFile/writeFile
calls with Bun.file(path).json()/text() for reads and Bun.file(path).write(...)
or Bun.write(...) for writes so the test uses Bun's file operations correctly;
reference mock.module, mock.restoreModule, mockHome and the test file's existing
fs read/write usages when making the changes.
---
Nitpick comments:
In `@packages/cli-core/src/commands/mcp/clients/user-scope.test.ts`:
- Line 2: Replace node:fs/promises read/write helpers imported as mkdir,
mkdtemp, readFile, rm, writeFile in user-scope.test.ts with Bun file APIs: use
Bun.file(path).text()/json() for reads and Bun.write/Bun.writeSync (or
Bun.file(path).write(...)) for writes, and keep using mkdtemp/mkdir/rm
equivalents via Bun or the existing helpers if available; update all call sites
that use readFile/writeFile (and any promise-based usages) to the corresponding
Bun.file(...).text()/json() and Bun.write(...) patterns so the tests follow the
repo’s Bun-specific I/O guideline.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 33d9756c-11d6-436b-8465-7e750f438029

📥 Commits

Reviewing files that changed from the base of the PR and between 292e92f and 4f53e9f.

📒 Files selected for processing (34)
  • .changeset/mcp-install.md
  • README.md
  • packages/cli-core/src/cli-program.ts
  • packages/cli-core/src/commands/doctor/README.md
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/doctor/context.test.ts
  • packages/cli-core/src/commands/doctor/index.ts
  • packages/cli-core/src/commands/mcp/README.md
  • packages/cli-core/src/commands/mcp/clients/claude-code.ts
  • packages/cli-core/src/commands/mcp/clients/clients.test.ts
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.ts
  • packages/cli-core/src/commands/mcp/clients/paths.ts
  • packages/cli-core/src/commands/mcp/clients/registry.ts
  • packages/cli-core/src/commands/mcp/clients/types.ts
  • packages/cli-core/src/commands/mcp/clients/user-scope.test.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/commands/mcp/collect.ts
  • packages/cli-core/src/commands/mcp/index.ts
  • packages/cli-core/src/commands/mcp/install.test.ts
  • packages/cli-core/src/commands/mcp/install.ts
  • packages/cli-core/src/commands/mcp/list.test.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/mcp/probe.test.ts
  • packages/cli-core/src/commands/mcp/probe.ts
  • packages/cli-core/src/commands/mcp/shared.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts
  • packages/cli-core/src/commands/mcp/uninstall.ts
  • packages/cli-core/src/lib/environment.ts
  • packages/cli-core/src/lib/errors.ts
✅ Files skipped from review due to trivial changes (4)
  • .changeset/mcp-install.md
  • packages/cli-core/src/commands/doctor/README.md
  • README.md
  • packages/cli-core/src/commands/mcp/README.md
🚧 Files skipped from review as they are similar to previous changes (26)
  • packages/cli-core/src/commands/mcp/list.test.ts
  • packages/cli-core/src/commands/mcp/clients/registry.ts
  • packages/cli-core/src/commands/mcp/index.ts
  • packages/cli-core/src/commands/mcp/clients/claude-code.ts
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/commands/mcp/clients/types.ts
  • packages/cli-core/src/commands/mcp/probe.test.ts
  • packages/cli-core/src/lib/errors.ts
  • packages/cli-core/src/commands/doctor/index.ts
  • packages/cli-core/src/commands/mcp/clients/paths.ts
  • packages/cli-core/src/commands/mcp/install.ts
  • packages/cli-core/src/lib/environment.ts
  • packages/cli-core/src/commands/mcp/collect.ts
  • packages/cli-core/src/commands/mcp/uninstall.ts
  • packages/cli-core/src/commands/mcp/clients/clients.test.ts
  • packages/cli-core/src/commands/mcp/probe.ts
  • packages/cli-core/src/commands/mcp/install.test.ts
  • packages/cli-core/src/cli-program.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts

Comment threadpackages/cli-core/src/commands/mcp/clients/user-scope.test.ts Outdated
@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@2.0.1-snapshot.bdf8faf
PackageVersion
clerk2.0.1-snapshot.bdf8faf

Published from bdf8faf

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli-core/src/commands/mcp/clients/clients.test.ts`:
- Around line 65-96: The VS Code test is leaking to the real config because
vscodeUserDir() respects XDG_CONFIG_HOME/APPDATA; update the tests to isolate
that by setting XDG_CONFIG_HOME and APPDATA to a directory under mockHome (or
delete them) before running the VS Code-specific assertions and restore them
after; ensure the expectation for the VS Code path does not call vscodeUserDir()
(i.e. compute the expected path explicitly inside the test instead of reusing
expectedPath()/vscodeUserDir()) so the test verifies the function under test
rather than mirroring the same helper.
In `@packages/cli-core/src/commands/mcp/clients/paths.ts`:
- Around line 26-35: vscodeUserDir currently uses the nullish coalescing
operator (??) with process.env.APPDATA and process.env.XDG_CONFIG_HOME which
treats empty strings as set; update vscodeUserDir to treat empty or
whitespace-only env vars as unset by checking truthiness/trim (e.g. derive local
vars like appData = process.env.APPDATA?.trim() ? process.env.APPDATA :
undefined and xdg = process.env.XDG_CONFIG_HOME?.trim() ?
process.env.XDG_CONFIG_HOME : undefined) and then fall back to join(homedir(),
...) in the switch cases; reference the vscodeUserDir function and its uses of
process.env.APPDATA and process.env.XDG_CONFIG_HOME, and ensure platform(),
homedir(), and join() behavior is preserved.
In `@packages/cli-core/src/lib/environment.ts`:
- Around line 154-165: getMcpUrl currently lets an empty CLERK_MCP_URL ("") win
due to the nullish coalescing operator; treat empty/whitespace-only env values
as unset by trimming and checking truthiness before falling back. Update
getMcpUrl (function name: getMcpUrl) to compute const env =
process.env.CLERK_MCP_URL?.trim(); then return env && env.length ? env :
getCurrentEnv().mcpUrl ?? DEFAULT_MCP_URL so blank overrides no longer produce
an unusable empty string.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2826916f-5886-46c5-96c7-da4b8e7b1a92

📥 Commits

Reviewing files that changed from the base of the PR and between bdf8faf and d80abcd.

📒 Files selected for processing (23)
  • .changeset/mcp-install.md
  • packages/cli-core/src/cli-program.ts
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/mcp/README.md
  • packages/cli-core/src/commands/mcp/clients/claude-code.ts
  • packages/cli-core/src/commands/mcp/clients/clients.test.ts
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.ts
  • packages/cli-core/src/commands/mcp/clients/paths.ts
  • packages/cli-core/src/commands/mcp/clients/user-scope.test.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/commands/mcp/install.test.ts
  • packages/cli-core/src/commands/mcp/install.ts
  • packages/cli-core/src/commands/mcp/list.test.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/mcp/shared.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts
  • packages/cli-core/src/lib/environment.ts
  • packages/cli-core/src/lib/errors.ts
💤 Files with no reviewable changes (3)
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/cli-program.ts
✅ Files skipped from review due to trivial changes (2)
  • .changeset/mcp-install.md
  • packages/cli-core/src/commands/mcp/README.md
🚧 Files skipped from review as they are similar to previous changes (12)
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/lib/errors.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/clients/user-scope.test.ts
  • packages/cli-core/src/commands/mcp/shared.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.ts
  • packages/cli-core/src/commands/mcp/install.test.ts

Comment threadpackages/cli-core/src/commands/mcp/clients/clients.test.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/paths.ts Outdated
Comment threadpackages/cli-core/src/lib/environment.ts
@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@2.0.1-snapshot.d637eb0
PackageVersion
clerk2.0.1-snapshot.d637eb0

Published from d637eb0

@wyattjoh
wyattjoh self-requested a review June 12, 2026 16:26
@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 2 times, most recently from 19b2820 to 0d5281dCompareJune 15, 2026 12:22

@wyattjohwyattjoh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid, well-tested feature surface. The error handling, URL hardening, and per-client config abstraction are all carefully done. A few non-blocking observations below, mostly around concurrency edge cases in the stdio bridge and the shared ~/.claude.json write path.

Comment threadpackages/cli-core/src/commands/mcp/clients/claude.ts
Comment threadpackages/cli-core/src/commands/mcp/run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/run.ts
Comment threadpackages/cli-core/src/lib/input-json.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/toml-config.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/shared.ts
Comment threadpackages/cli-core/src/commands/mcp/install.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.test.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.test.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/gemini.ts Outdated
@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 3 times, most recently from 161868b to c13e55aCompareJune 27, 2026 12:17

@wyattjohwyattjoh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strong, well-tested PR. The main cleanup is leftover --url surface from the "drop --url from public CLI" commit (a help example that now errors, an unactionable error message, the README, and the changeset), plus a couple of narrow correctness edge cases noted inline.

Comment threadpackages/cli-core/src/commands/mcp/index.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/README.md Outdated
Comment thread.changeset/mcp-install.md Outdated
Comment threadpackages/cli-core/src/commands/mcp/run.ts
Comment threadpackages/cli-core/src/commands/mcp/clients/json-config.ts
Comment threadpackages/cli-core/src/commands/mcp/install.ts
Comment threadpackages/cli-core/src/commands/mcp/run.ts
Comment threadpackages/cli-core/src/commands/mcp/shared.ts
@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 4 times, most recently from c70a7f1 to 89d5811CompareJuly 15, 2026 12:16
@rafa-thaytorafa-thayto changed the title feat(mcp): add clerk mcp install/list/uninstall + doctor MCP checkfeat(mcp): clerk mcp install/list/uninstall/run across 10 clients + doctor MCP checkJul 21, 2026
@rafa-thaytorafa-thayto changed the title feat(mcp): clerk mcp install/list/uninstall/run across 10 clients + doctor MCP checkfeat(mcp): clerk mcp install/list/uninstall/run + doctor MCP checkJul 21, 2026
… + doctor MCP check
Registers the Clerk remote MCP server (https://mcp.clerk.com/mcp) in AI
editors and agent CLIs, with an MCP reachability check in `clerk doctor`.
Supported clients (all user-global): Claude Code, Cursor, GitHub Copilot /
VS Code (`--client vscode` or `copilot`), Windsurf, Gemini, Codex, opencode,
OpenClaw, Warp, and Hermes Agent.
Registration is delegated to each client's own CLI where a non-interactive
one exists (`claude`/`gemini`/`codex`/`openclaw`/`hermes` `mcp add`,
`code --add-mcp`), with PATH-based detection and no file-write fallback;
Cursor, Windsurf, Warp, and opencode (interactive-only CLI) get atomic
file writes. Install always converges via remove-then-add. Client CLIs are
spawned with stdin closed and a 15s timeout; Windows `.cmd` shims run
through `cmd.exe /c`; Hermes gets its confirm prompt answered via stdin and
a post-add verification because its cancel path exits 0 without saving.
Every client stores the same URL-less bridge descriptor
`{command: "clerk", args: ["mcp", "run"]}`. `clerk mcp run` is a built-in
stdio<->Streamable-HTTP bridge (replaces npx mcp-remote) that resolves its
target at spawn time (CLERK_MCP_URL > env profile mcpUrl > hosted default),
serializes stdout frames, caps both stdin lines and upstream SSE/JSON
bodies at 16 MiB, and surfaces pre-session 401/403 as a clear error while
answering in-session auth failures per-request.
Reads stay file-based (JSON/TOML/YAML codecs; TOML and YAML are read-only
since those clients' CLIs own both mutations). `--name` is allowlisted
([A-Za-z0-9_-], no leading dash) because it is spliced into client CLI argv
and used as a config key. `list --json` reports { entries, failures } so an
unreadable config surfaces structurally; install/uninstall carry the same
failures array and exit non-zero only when every targeted client fails.
Agent mode never prompts and always emits JSON with machine-readable error
codes plus a docs URL (clerk.com/docs/guides/ai/mcp/clerk-mcp-server).
Verified live against the real client CLIs on macOS: full uninstall/install
round-trips with zero failures; opencode reports "clerk connected" and
Hermes "clerk enabled" through their own CLIs; OpenClaw's dialect verified
in a sandboxed OPENCLAW_STATE_DIR; bridge handshake against the hosted
server returns "Clerk MCP Server".
Comment threadpackages/cli-core/src/commands/doctor/check-mcp.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/make-cli-client.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/make-client.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/probe.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/make-client.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/toml-config.ts
Comment threadpackages/cli-core/src/commands/mcp/probe.ts
Comment threadpackages/cli-core/src/commands/doctor/check-mcp.ts
Comment threadpackages/cli-core/src/commands/mcp/clients/json-config.ts
@rafa-thayto
rafa-thayto merged commit 8bf74ab into mainJul 22, 2026
9 of 10 checks passed
@rafa-thayto
rafa-thayto deleted the add-mcp-server-cli branch July 22, 2026 14:37
@github-actionsgithub-actionsBot mentioned this pull request Jul 22, 2026
Sign up for freeto 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.

2 participants

@rafa-thayto@wyattjoh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(mcp): clerk mcp install/list/uninstall/run + doctor MCP check - #307

Merged
rafa-thayto merged 4 commits into
mainfrom
add-mcp-server-cli
Jul 22, 2026
Merged

feat(mcp): clerk mcp install/list/uninstall/run + doctor MCP check#307
rafa-thayto merged 4 commits into
mainfrom
add-mcp-server-cli

Conversation

@rafa-thayto

@rafa-thaytorafa-thayto commented May 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a clerk mcp command group that connects the Clerk remote MCP server (https://mcp.clerk.com/mcp) to 10 AI clients, plus an MCP health check in clerk doctor.

  • clerk mcp install — register the server. Human mode: interactive multiselect over detected clients; agent mode / --all / --client <id> (repeatable): non-interactive. Install always converges: whatever entry sits under the name is replaced (remove-then-add through the client's CLI, or an overwrite for file-backed clients) — there is no --force and no conflict state.
  • clerk mcp list — show every Clerk-flavored entry (named clerk or pointing at a *.clerk.com host). JSON output is { entries, failures }, so an unreadable client config surfaces structurally instead of reading as "nothing installed".
  • clerk mcp uninstall — remove the entry. Human mode prompts with the clients that actually have it (nothing pre-checked); agent mode / --all targets every client. Nothing matched → warm hint, exit 0.
  • clerk mcp run — a built-in stdio↔Streamable-HTTP bridge that editors spawn (replaces npx mcp-remote; no npx dependency). Every client config stores the same URL-less descriptor { "command": "clerk", "args": ["mcp", "run"] }; the bridge resolves its target at spawn time (CLERK_MCP_URL > env profile mcpUrl > hosted default).
  • clerk doctor — MCP check probes each distinct configured URL via the initialize handshake when an entry is installed (skips otherwise; warns, never fails), and distinguishes "config unreadable" from "nothing installed".

Registration is delegated to each client's own CLI

Clients that ship a non-interactive registration CLI are driven through it — the client owns its config format and write safety. The rest get direct file writes. There is no file-write fallback for CLI-backed clients: a missing binary fails that client with an actionable error (mcp_client_cli_not_found + docs link), and detection is PATH-based so the picker only offers clients we can actually drive.

Client--client idRegistered viaRemoved via
Claude Codeclaudeclaude mcp add --scope userclaude mcp remove
Cursorcursorfile write (no CLI exists)file write
GitHub Copilot / VS Codevscode (copilot)code --add-mcp '<json>'file write (add-only CLI)
Windsurfwindsurffile write (no CLI exists)file write
Gemini CLIgeminigemini mcp add --scope usergemini mcp remove
Codexcodexcodex mcp add (TOML config)codex mcp remove
opencodeopencodefile write (its mcp add is an interactive-only wizard)file write
OpenClawopenclawopenclaw mcp add --no-probeopenclaw mcp unset
Warpwarpfile write to ~/.warp/.mcp.json (no registration CLI)file write
Hermes Agenthermeshermes mcp add (stdin-piped confirm + post-add verify)hermes mcp remove

All entries are user-global, so the server is available in every project.

Design notes

  • Client CLIs are spawned safely: stdin closed (a prompting CLI EOF-errors instead of hanging agent runs) + a 15s hard timeout. Windows .cmd/.bat shims are launched through cmd.exe /c.
  • Hermes hardening: its mcp add ends in a confirm prompt and exits 0 on cancel without saving — so the affirmative answer is piped to stdin and the config is re-read after add; a silent no-op becomes mcp_client_cli_failed. Its YAML config is read-only to us (both mutations go through its CLI), so hand-written YAML is never rewritten.
  • Reads stay ours:list, doctor, and the uninstall picker parse config files directly (JSON/TOML/YAML codecs, incl. OpenClaw's nested mcp.servers map). Legacy descriptor shapes ({url}, {serverUrl}, mcp-remote args, clerk mcp run --url) still round-trip.
  • Agent mode throughout: non-TTY runs auto-emit JSON, never prompt, and every registration-blocking error carries a machine-readable code plus a docsUrl to the Clerk MCP setup docs (agent mode gets the .md variant) for the manual fallback.
  • Partial failure is structural: per-client failures land in a failures: [{ client, error }] array in install/uninstall/list JSON; the command exits non-zero only when every targeted client fails.
  • Auth (current limitation): the bridge is transport-only. A 401/403 on the initial handshake exits with a clear error; on later requests it's surfaced to the editor as a per-request JSON-RPC error without killing the bridge.

Test plan

  • bun run format:check, bun run lint, bun run typecheck — green
  • bun run test — 2095 tests green, including per-client argv contracts for all six CLI dialects, file shapes for the four file-backed clients, nested-topKey + YAML codec units, corrupt-config failure threading, Hermes verify-add, and Windows .cmd-shim spawning
  • Live verification (macOS): every CLI dialect verified against the real client CLI — claude/gemini/codex/code (duplicate-add/remove semantics), hermes (prompt + lying exit code discovered this way), openclaw (sandboxed OPENCLAW_STATE_DIR)
  • Real round-trip: uninstall --allinstall --all across all detected clients with zero failures; opencode's own CLI reports ✓ clerk connected and Hermes reports clerk ✓ enabled; bridge handshake against the hosted server returns Clerk MCP Server

@changeset-bot

changeset-botBot commented May 25, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e22734c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkMinor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented May 25, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds MCP server support to the Clerk CLI: new subcommands (clerk mcp install/list/uninstall), a JSON-backed client factory and five client integrations (Claude Code, Cursor, VS Code, Windsurf, Gemini), per-client config read/write helpers, collection of installed entries, an initialize-handshake probe supporting JSON and SSE with a 10s timeout, a doctor check (checkMcp), environment/profile mcpUrl wiring, CLI wiring, comprehensive tests, and documentation including a changeset and README updates.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 24.44% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check⚠️ WarningThe title matches the MCP install/list/uninstall and doctor work, but it incorrectly includes a run command that isn't in this changeset.Remove the unsupported "run" reference and keep the title focused on install/list/uninstall plus the doctor MCP check.
✅ Passed checks (3 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
Description check✅ PassedThe description is about the same MCP feature area and doctor check, so it is related despite several inaccurate details.

Comment @coderabbitai help to get the list of available commands.

@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 2 times, most recently from b4bc06c to 4f53e9fCompareMay 30, 2026 12:14

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/cli-core/src/commands/mcp/clients/user-scope.test.ts (1)

2-2: ⚡ Quick win

Use Bun file APIs here instead of node:fs read/write helpers.

These tests currently use readFile/writeFile from node:fs/promises, which goes against the repo’s Bun-specific file I/O guideline. Bun.file(...).json()/text() and Bun.write(...) would keep this aligned with the rest of the codebase. As per coding guidelines, **/*.{ts,tsx,js,jsx}: Prefer Bun.file over node:fs's readFile/writeFile for file operations.

Also applies to: 45-46, 64-66

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/cli-core/src/commands/mcp/clients/user-scope.test.ts` at line 2,
Replace node:fs/promises read/write helpers imported as mkdir, mkdtemp,
readFile, rm, writeFile in user-scope.test.ts with Bun file APIs: use
Bun.file(path).text()/json() for reads and Bun.write/Bun.writeSync (or
Bun.file(path).write(...)) for writes, and keep using mkdtemp/mkdir/rm
equivalents via Bun or the existing helpers if available; update all call sites
that use readFile/writeFile (and any promise-based usages) to the corresponding
Bun.file(...).text()/json() and Bun.write(...) patterns so the tests follow the
repo’s Bun-specific I/O guideline.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli-core/src/commands/mcp/clients/user-scope.test.ts`:
- Around line 7-12: The top-level module mock of node:os (mock.module("node:os",
...)) is never torn down causing cross-test leakage; add a cleanup via
afterAll(() => mock.restoreModule("node:os")) (or equivalent) to restore the
original module and avoid order-dependent failures, and update file I/O in this
test to use Bun APIs instead of node:fs/promises—replace readFile/writeFile
calls with Bun.file(path).json()/text() for reads and Bun.file(path).write(...)
or Bun.write(...) for writes so the test uses Bun's file operations correctly;
reference mock.module, mock.restoreModule, mockHome and the test file's existing
fs read/write usages when making the changes.
---
Nitpick comments:
In `@packages/cli-core/src/commands/mcp/clients/user-scope.test.ts`:
- Line 2: Replace node:fs/promises read/write helpers imported as mkdir,
mkdtemp, readFile, rm, writeFile in user-scope.test.ts with Bun file APIs: use
Bun.file(path).text()/json() for reads and Bun.write/Bun.writeSync (or
Bun.file(path).write(...)) for writes, and keep using mkdtemp/mkdir/rm
equivalents via Bun or the existing helpers if available; update all call sites
that use readFile/writeFile (and any promise-based usages) to the corresponding
Bun.file(...).text()/json() and Bun.write(...) patterns so the tests follow the
repo’s Bun-specific I/O guideline.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 33d9756c-11d6-436b-8465-7e750f438029

📥 Commits

Reviewing files that changed from the base of the PR and between 292e92f and 4f53e9f.

📒 Files selected for processing (34)
  • .changeset/mcp-install.md
  • README.md
  • packages/cli-core/src/cli-program.ts
  • packages/cli-core/src/commands/doctor/README.md
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/doctor/context.test.ts
  • packages/cli-core/src/commands/doctor/index.ts
  • packages/cli-core/src/commands/mcp/README.md
  • packages/cli-core/src/commands/mcp/clients/claude-code.ts
  • packages/cli-core/src/commands/mcp/clients/clients.test.ts
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.ts
  • packages/cli-core/src/commands/mcp/clients/paths.ts
  • packages/cli-core/src/commands/mcp/clients/registry.ts
  • packages/cli-core/src/commands/mcp/clients/types.ts
  • packages/cli-core/src/commands/mcp/clients/user-scope.test.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/commands/mcp/collect.ts
  • packages/cli-core/src/commands/mcp/index.ts
  • packages/cli-core/src/commands/mcp/install.test.ts
  • packages/cli-core/src/commands/mcp/install.ts
  • packages/cli-core/src/commands/mcp/list.test.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/mcp/probe.test.ts
  • packages/cli-core/src/commands/mcp/probe.ts
  • packages/cli-core/src/commands/mcp/shared.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts
  • packages/cli-core/src/commands/mcp/uninstall.ts
  • packages/cli-core/src/lib/environment.ts
  • packages/cli-core/src/lib/errors.ts
✅ Files skipped from review due to trivial changes (4)
  • .changeset/mcp-install.md
  • packages/cli-core/src/commands/doctor/README.md
  • README.md
  • packages/cli-core/src/commands/mcp/README.md
🚧 Files skipped from review as they are similar to previous changes (26)
  • packages/cli-core/src/commands/mcp/list.test.ts
  • packages/cli-core/src/commands/mcp/clients/registry.ts
  • packages/cli-core/src/commands/mcp/index.ts
  • packages/cli-core/src/commands/mcp/clients/claude-code.ts
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/commands/mcp/clients/types.ts
  • packages/cli-core/src/commands/mcp/probe.test.ts
  • packages/cli-core/src/lib/errors.ts
  • packages/cli-core/src/commands/doctor/index.ts
  • packages/cli-core/src/commands/mcp/clients/paths.ts
  • packages/cli-core/src/commands/mcp/install.ts
  • packages/cli-core/src/lib/environment.ts
  • packages/cli-core/src/commands/mcp/collect.ts
  • packages/cli-core/src/commands/mcp/uninstall.ts
  • packages/cli-core/src/commands/mcp/clients/clients.test.ts
  • packages/cli-core/src/commands/mcp/probe.ts
  • packages/cli-core/src/commands/mcp/install.test.ts
  • packages/cli-core/src/cli-program.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts

Comment threadpackages/cli-core/src/commands/mcp/clients/user-scope.test.ts Outdated
@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@2.0.1-snapshot.bdf8faf
PackageVersion
clerk2.0.1-snapshot.bdf8faf

Published from bdf8faf

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli-core/src/commands/mcp/clients/clients.test.ts`:
- Around line 65-96: The VS Code test is leaking to the real config because
vscodeUserDir() respects XDG_CONFIG_HOME/APPDATA; update the tests to isolate
that by setting XDG_CONFIG_HOME and APPDATA to a directory under mockHome (or
delete them) before running the VS Code-specific assertions and restore them
after; ensure the expectation for the VS Code path does not call vscodeUserDir()
(i.e. compute the expected path explicitly inside the test instead of reusing
expectedPath()/vscodeUserDir()) so the test verifies the function under test
rather than mirroring the same helper.
In `@packages/cli-core/src/commands/mcp/clients/paths.ts`:
- Around line 26-35: vscodeUserDir currently uses the nullish coalescing
operator (??) with process.env.APPDATA and process.env.XDG_CONFIG_HOME which
treats empty strings as set; update vscodeUserDir to treat empty or
whitespace-only env vars as unset by checking truthiness/trim (e.g. derive local
vars like appData = process.env.APPDATA?.trim() ? process.env.APPDATA :
undefined and xdg = process.env.XDG_CONFIG_HOME?.trim() ?
process.env.XDG_CONFIG_HOME : undefined) and then fall back to join(homedir(),
...) in the switch cases; reference the vscodeUserDir function and its uses of
process.env.APPDATA and process.env.XDG_CONFIG_HOME, and ensure platform(),
homedir(), and join() behavior is preserved.
In `@packages/cli-core/src/lib/environment.ts`:
- Around line 154-165: getMcpUrl currently lets an empty CLERK_MCP_URL ("") win
due to the nullish coalescing operator; treat empty/whitespace-only env values
as unset by trimming and checking truthiness before falling back. Update
getMcpUrl (function name: getMcpUrl) to compute const env =
process.env.CLERK_MCP_URL?.trim(); then return env && env.length ? env :
getCurrentEnv().mcpUrl ?? DEFAULT_MCP_URL so blank overrides no longer produce
an unusable empty string.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2826916f-5886-46c5-96c7-da4b8e7b1a92

📥 Commits

Reviewing files that changed from the base of the PR and between bdf8faf and d80abcd.

📒 Files selected for processing (23)
  • .changeset/mcp-install.md
  • packages/cli-core/src/cli-program.ts
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/mcp/README.md
  • packages/cli-core/src/commands/mcp/clients/claude-code.ts
  • packages/cli-core/src/commands/mcp/clients/clients.test.ts
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.ts
  • packages/cli-core/src/commands/mcp/clients/paths.ts
  • packages/cli-core/src/commands/mcp/clients/user-scope.test.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/commands/mcp/install.test.ts
  • packages/cli-core/src/commands/mcp/install.ts
  • packages/cli-core/src/commands/mcp/list.test.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/mcp/shared.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts
  • packages/cli-core/src/lib/environment.ts
  • packages/cli-core/src/lib/errors.ts
💤 Files with no reviewable changes (3)
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/cli-program.ts
✅ Files skipped from review due to trivial changes (2)
  • .changeset/mcp-install.md
  • packages/cli-core/src/commands/mcp/README.md
🚧 Files skipped from review as they are similar to previous changes (12)
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/lib/errors.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/clients/user-scope.test.ts
  • packages/cli-core/src/commands/mcp/shared.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.ts
  • packages/cli-core/src/commands/mcp/install.test.ts

Comment threadpackages/cli-core/src/commands/mcp/clients/clients.test.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/paths.ts Outdated
Comment threadpackages/cli-core/src/lib/environment.ts
@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@2.0.1-snapshot.d637eb0
PackageVersion
clerk2.0.1-snapshot.d637eb0

Published from d637eb0

@wyattjoh
wyattjoh self-requested a review June 12, 2026 16:26
@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 2 times, most recently from 19b2820 to 0d5281dCompareJune 15, 2026 12:22

@wyattjohwyattjoh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid, well-tested feature surface. The error handling, URL hardening, and per-client config abstraction are all carefully done. A few non-blocking observations below, mostly around concurrency edge cases in the stdio bridge and the shared ~/.claude.json write path.

Comment threadpackages/cli-core/src/commands/mcp/clients/claude.ts
Comment threadpackages/cli-core/src/commands/mcp/run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/run.ts
Comment threadpackages/cli-core/src/lib/input-json.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/toml-config.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/shared.ts
Comment threadpackages/cli-core/src/commands/mcp/install.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.test.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.test.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/gemini.ts Outdated
@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 3 times, most recently from 161868b to c13e55aCompareJune 27, 2026 12:17

@wyattjohwyattjoh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strong, well-tested PR. The main cleanup is leftover --url surface from the "drop --url from public CLI" commit (a help example that now errors, an unactionable error message, the README, and the changeset), plus a couple of narrow correctness edge cases noted inline.

Comment threadpackages/cli-core/src/commands/mcp/index.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/README.md Outdated
Comment thread.changeset/mcp-install.md Outdated
Comment threadpackages/cli-core/src/commands/mcp/run.ts
Comment threadpackages/cli-core/src/commands/mcp/clients/json-config.ts
Comment threadpackages/cli-core/src/commands/mcp/install.ts
Comment threadpackages/cli-core/src/commands/mcp/run.ts
Comment threadpackages/cli-core/src/commands/mcp/shared.ts
@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 4 times, most recently from c70a7f1 to 89d5811CompareJuly 15, 2026 12:16
@rafa-thaytorafa-thayto changed the title feat(mcp): add clerk mcp install/list/uninstall + doctor MCP checkfeat(mcp): clerk mcp install/list/uninstall/run across 10 clients + doctor MCP checkJul 21, 2026
@rafa-thaytorafa-thayto changed the title feat(mcp): clerk mcp install/list/uninstall/run across 10 clients + doctor MCP checkfeat(mcp): clerk mcp install/list/uninstall/run + doctor MCP checkJul 21, 2026
… + doctor MCP check
Registers the Clerk remote MCP server (https://mcp.clerk.com/mcp) in AI
editors and agent CLIs, with an MCP reachability check in `clerk doctor`.
Supported clients (all user-global): Claude Code, Cursor, GitHub Copilot /
VS Code (`--client vscode` or `copilot`), Windsurf, Gemini, Codex, opencode,
OpenClaw, Warp, and Hermes Agent.
Registration is delegated to each client's own CLI where a non-interactive
one exists (`claude`/`gemini`/`codex`/`openclaw`/`hermes` `mcp add`,
`code --add-mcp`), with PATH-based detection and no file-write fallback;
Cursor, Windsurf, Warp, and opencode (interactive-only CLI) get atomic
file writes. Install always converges via remove-then-add. Client CLIs are
spawned with stdin closed and a 15s timeout; Windows `.cmd` shims run
through `cmd.exe /c`; Hermes gets its confirm prompt answered via stdin and
a post-add verification because its cancel path exits 0 without saving.
Every client stores the same URL-less bridge descriptor
`{command: "clerk", args: ["mcp", "run"]}`. `clerk mcp run` is a built-in
stdio<->Streamable-HTTP bridge (replaces npx mcp-remote) that resolves its
target at spawn time (CLERK_MCP_URL > env profile mcpUrl > hosted default),
serializes stdout frames, caps both stdin lines and upstream SSE/JSON
bodies at 16 MiB, and surfaces pre-session 401/403 as a clear error while
answering in-session auth failures per-request.
Reads stay file-based (JSON/TOML/YAML codecs; TOML and YAML are read-only
since those clients' CLIs own both mutations). `--name` is allowlisted
([A-Za-z0-9_-], no leading dash) because it is spliced into client CLI argv
and used as a config key. `list --json` reports { entries, failures } so an
unreadable config surfaces structurally; install/uninstall carry the same
failures array and exit non-zero only when every targeted client fails.
Agent mode never prompts and always emits JSON with machine-readable error
codes plus a docs URL (clerk.com/docs/guides/ai/mcp/clerk-mcp-server).
Verified live against the real client CLIs on macOS: full uninstall/install
round-trips with zero failures; opencode reports "clerk connected" and
Hermes "clerk enabled" through their own CLIs; OpenClaw's dialect verified
in a sandboxed OPENCLAW_STATE_DIR; bridge handshake against the hosted
server returns "Clerk MCP Server".
Comment threadpackages/cli-core/src/commands/doctor/check-mcp.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/make-cli-client.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/make-client.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/probe.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/make-client.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/toml-config.ts
Comment threadpackages/cli-core/src/commands/mcp/probe.ts
Comment threadpackages/cli-core/src/commands/doctor/check-mcp.ts
Comment threadpackages/cli-core/src/commands/mcp/clients/json-config.ts
@rafa-thayto
rafa-thayto merged commit 8bf74ab into mainJul 22, 2026
9 of 10 checks passed
@rafa-thayto
rafa-thayto deleted the add-mcp-server-cli branch July 22, 2026 14:37
@github-actionsgithub-actionsBot mentioned this pull request Jul 22, 2026
Sign up for freeto 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.

2 participants

@rafa-thayto@wyattjoh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat(mcp): clerk mcp install/list/uninstall/run + doctor MCP check - #307

Merged
rafa-thayto merged 4 commits into
mainfrom
add-mcp-server-cli
Jul 22, 2026
Merged

feat(mcp): clerk mcp install/list/uninstall/run + doctor MCP check#307
rafa-thayto merged 4 commits into
mainfrom
add-mcp-server-cli

Conversation

@rafa-thayto

@rafa-thaytorafa-thayto commented May 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a clerk mcp command group that connects the Clerk remote MCP server (https://mcp.clerk.com/mcp) to 10 AI clients, plus an MCP health check in clerk doctor.

  • clerk mcp install — register the server. Human mode: interactive multiselect over detected clients; agent mode / --all / --client <id> (repeatable): non-interactive. Install always converges: whatever entry sits under the name is replaced (remove-then-add through the client's CLI, or an overwrite for file-backed clients) — there is no --force and no conflict state.
  • clerk mcp list — show every Clerk-flavored entry (named clerk or pointing at a *.clerk.com host). JSON output is { entries, failures }, so an unreadable client config surfaces structurally instead of reading as "nothing installed".
  • clerk mcp uninstall — remove the entry. Human mode prompts with the clients that actually have it (nothing pre-checked); agent mode / --all targets every client. Nothing matched → warm hint, exit 0.
  • clerk mcp run — a built-in stdio↔Streamable-HTTP bridge that editors spawn (replaces npx mcp-remote; no npx dependency). Every client config stores the same URL-less descriptor { "command": "clerk", "args": ["mcp", "run"] }; the bridge resolves its target at spawn time (CLERK_MCP_URL > env profile mcpUrl > hosted default).
  • clerk doctor — MCP check probes each distinct configured URL via the initialize handshake when an entry is installed (skips otherwise; warns, never fails), and distinguishes "config unreadable" from "nothing installed".

Registration is delegated to each client's own CLI

Clients that ship a non-interactive registration CLI are driven through it — the client owns its config format and write safety. The rest get direct file writes. There is no file-write fallback for CLI-backed clients: a missing binary fails that client with an actionable error (mcp_client_cli_not_found + docs link), and detection is PATH-based so the picker only offers clients we can actually drive.

Client--client idRegistered viaRemoved via
Claude Codeclaudeclaude mcp add --scope userclaude mcp remove
Cursorcursorfile write (no CLI exists)file write
GitHub Copilot / VS Codevscode (copilot)code --add-mcp '<json>'file write (add-only CLI)
Windsurfwindsurffile write (no CLI exists)file write
Gemini CLIgeminigemini mcp add --scope usergemini mcp remove
Codexcodexcodex mcp add (TOML config)codex mcp remove
opencodeopencodefile write (its mcp add is an interactive-only wizard)file write
OpenClawopenclawopenclaw mcp add --no-probeopenclaw mcp unset
Warpwarpfile write to ~/.warp/.mcp.json (no registration CLI)file write
Hermes Agenthermeshermes mcp add (stdin-piped confirm + post-add verify)hermes mcp remove

All entries are user-global, so the server is available in every project.

Design notes

  • Client CLIs are spawned safely: stdin closed (a prompting CLI EOF-errors instead of hanging agent runs) + a 15s hard timeout. Windows .cmd/.bat shims are launched through cmd.exe /c.
  • Hermes hardening: its mcp add ends in a confirm prompt and exits 0 on cancel without saving — so the affirmative answer is piped to stdin and the config is re-read after add; a silent no-op becomes mcp_client_cli_failed. Its YAML config is read-only to us (both mutations go through its CLI), so hand-written YAML is never rewritten.
  • Reads stay ours:list, doctor, and the uninstall picker parse config files directly (JSON/TOML/YAML codecs, incl. OpenClaw's nested mcp.servers map). Legacy descriptor shapes ({url}, {serverUrl}, mcp-remote args, clerk mcp run --url) still round-trip.
  • Agent mode throughout: non-TTY runs auto-emit JSON, never prompt, and every registration-blocking error carries a machine-readable code plus a docsUrl to the Clerk MCP setup docs (agent mode gets the .md variant) for the manual fallback.
  • Partial failure is structural: per-client failures land in a failures: [{ client, error }] array in install/uninstall/list JSON; the command exits non-zero only when every targeted client fails.
  • Auth (current limitation): the bridge is transport-only. A 401/403 on the initial handshake exits with a clear error; on later requests it's surfaced to the editor as a per-request JSON-RPC error without killing the bridge.

Test plan

  • bun run format:check, bun run lint, bun run typecheck — green
  • bun run test — 2095 tests green, including per-client argv contracts for all six CLI dialects, file shapes for the four file-backed clients, nested-topKey + YAML codec units, corrupt-config failure threading, Hermes verify-add, and Windows .cmd-shim spawning
  • Live verification (macOS): every CLI dialect verified against the real client CLI — claude/gemini/codex/code (duplicate-add/remove semantics), hermes (prompt + lying exit code discovered this way), openclaw (sandboxed OPENCLAW_STATE_DIR)
  • Real round-trip: uninstall --allinstall --all across all detected clients with zero failures; opencode's own CLI reports ✓ clerk connected and Hermes reports clerk ✓ enabled; bridge handshake against the hosted server returns Clerk MCP Server

@changeset-bot

changeset-botBot commented May 25, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e22734c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkMinor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented May 25, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds MCP server support to the Clerk CLI: new subcommands (clerk mcp install/list/uninstall), a JSON-backed client factory and five client integrations (Claude Code, Cursor, VS Code, Windsurf, Gemini), per-client config read/write helpers, collection of installed entries, an initialize-handshake probe supporting JSON and SSE with a 10s timeout, a doctor check (checkMcp), environment/profile mcpUrl wiring, CLI wiring, comprehensive tests, and documentation including a changeset and README updates.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 24.44% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check⚠️ WarningThe title matches the MCP install/list/uninstall and doctor work, but it incorrectly includes a run command that isn't in this changeset.Remove the unsupported "run" reference and keep the title focused on install/list/uninstall plus the doctor MCP check.
✅ Passed checks (3 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
Description check✅ PassedThe description is about the same MCP feature area and doctor check, so it is related despite several inaccurate details.

Comment @coderabbitai help to get the list of available commands.

@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 2 times, most recently from b4bc06c to 4f53e9fCompareMay 30, 2026 12:14

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/cli-core/src/commands/mcp/clients/user-scope.test.ts (1)

2-2: ⚡ Quick win

Use Bun file APIs here instead of node:fs read/write helpers.

These tests currently use readFile/writeFile from node:fs/promises, which goes against the repo’s Bun-specific file I/O guideline. Bun.file(...).json()/text() and Bun.write(...) would keep this aligned with the rest of the codebase. As per coding guidelines, **/*.{ts,tsx,js,jsx}: Prefer Bun.file over node:fs's readFile/writeFile for file operations.

Also applies to: 45-46, 64-66

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/cli-core/src/commands/mcp/clients/user-scope.test.ts` at line 2,
Replace node:fs/promises read/write helpers imported as mkdir, mkdtemp,
readFile, rm, writeFile in user-scope.test.ts with Bun file APIs: use
Bun.file(path).text()/json() for reads and Bun.write/Bun.writeSync (or
Bun.file(path).write(...)) for writes, and keep using mkdtemp/mkdir/rm
equivalents via Bun or the existing helpers if available; update all call sites
that use readFile/writeFile (and any promise-based usages) to the corresponding
Bun.file(...).text()/json() and Bun.write(...) patterns so the tests follow the
repo’s Bun-specific I/O guideline.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli-core/src/commands/mcp/clients/user-scope.test.ts`:
- Around line 7-12: The top-level module mock of node:os (mock.module("node:os",
...)) is never torn down causing cross-test leakage; add a cleanup via
afterAll(() => mock.restoreModule("node:os")) (or equivalent) to restore the
original module and avoid order-dependent failures, and update file I/O in this
test to use Bun APIs instead of node:fs/promises—replace readFile/writeFile
calls with Bun.file(path).json()/text() for reads and Bun.file(path).write(...)
or Bun.write(...) for writes so the test uses Bun's file operations correctly;
reference mock.module, mock.restoreModule, mockHome and the test file's existing
fs read/write usages when making the changes.
---
Nitpick comments:
In `@packages/cli-core/src/commands/mcp/clients/user-scope.test.ts`:
- Line 2: Replace node:fs/promises read/write helpers imported as mkdir,
mkdtemp, readFile, rm, writeFile in user-scope.test.ts with Bun file APIs: use
Bun.file(path).text()/json() for reads and Bun.write/Bun.writeSync (or
Bun.file(path).write(...)) for writes, and keep using mkdtemp/mkdir/rm
equivalents via Bun or the existing helpers if available; update all call sites
that use readFile/writeFile (and any promise-based usages) to the corresponding
Bun.file(...).text()/json() and Bun.write(...) patterns so the tests follow the
repo’s Bun-specific I/O guideline.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 33d9756c-11d6-436b-8465-7e750f438029

📥 Commits

Reviewing files that changed from the base of the PR and between 292e92f and 4f53e9f.

📒 Files selected for processing (34)
  • .changeset/mcp-install.md
  • README.md
  • packages/cli-core/src/cli-program.ts
  • packages/cli-core/src/commands/doctor/README.md
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/doctor/context.test.ts
  • packages/cli-core/src/commands/doctor/index.ts
  • packages/cli-core/src/commands/mcp/README.md
  • packages/cli-core/src/commands/mcp/clients/claude-code.ts
  • packages/cli-core/src/commands/mcp/clients/clients.test.ts
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.ts
  • packages/cli-core/src/commands/mcp/clients/paths.ts
  • packages/cli-core/src/commands/mcp/clients/registry.ts
  • packages/cli-core/src/commands/mcp/clients/types.ts
  • packages/cli-core/src/commands/mcp/clients/user-scope.test.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/commands/mcp/collect.ts
  • packages/cli-core/src/commands/mcp/index.ts
  • packages/cli-core/src/commands/mcp/install.test.ts
  • packages/cli-core/src/commands/mcp/install.ts
  • packages/cli-core/src/commands/mcp/list.test.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/mcp/probe.test.ts
  • packages/cli-core/src/commands/mcp/probe.ts
  • packages/cli-core/src/commands/mcp/shared.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts
  • packages/cli-core/src/commands/mcp/uninstall.ts
  • packages/cli-core/src/lib/environment.ts
  • packages/cli-core/src/lib/errors.ts
✅ Files skipped from review due to trivial changes (4)
  • .changeset/mcp-install.md
  • packages/cli-core/src/commands/doctor/README.md
  • README.md
  • packages/cli-core/src/commands/mcp/README.md
🚧 Files skipped from review as they are similar to previous changes (26)
  • packages/cli-core/src/commands/mcp/list.test.ts
  • packages/cli-core/src/commands/mcp/clients/registry.ts
  • packages/cli-core/src/commands/mcp/index.ts
  • packages/cli-core/src/commands/mcp/clients/claude-code.ts
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/commands/mcp/clients/types.ts
  • packages/cli-core/src/commands/mcp/probe.test.ts
  • packages/cli-core/src/lib/errors.ts
  • packages/cli-core/src/commands/doctor/index.ts
  • packages/cli-core/src/commands/mcp/clients/paths.ts
  • packages/cli-core/src/commands/mcp/install.ts
  • packages/cli-core/src/lib/environment.ts
  • packages/cli-core/src/commands/mcp/collect.ts
  • packages/cli-core/src/commands/mcp/uninstall.ts
  • packages/cli-core/src/commands/mcp/clients/clients.test.ts
  • packages/cli-core/src/commands/mcp/probe.ts
  • packages/cli-core/src/commands/mcp/install.test.ts
  • packages/cli-core/src/cli-program.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts

Comment threadpackages/cli-core/src/commands/mcp/clients/user-scope.test.ts Outdated
@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@2.0.1-snapshot.bdf8faf
PackageVersion
clerk2.0.1-snapshot.bdf8faf

Published from bdf8faf

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli-core/src/commands/mcp/clients/clients.test.ts`:
- Around line 65-96: The VS Code test is leaking to the real config because
vscodeUserDir() respects XDG_CONFIG_HOME/APPDATA; update the tests to isolate
that by setting XDG_CONFIG_HOME and APPDATA to a directory under mockHome (or
delete them) before running the VS Code-specific assertions and restore them
after; ensure the expectation for the VS Code path does not call vscodeUserDir()
(i.e. compute the expected path explicitly inside the test instead of reusing
expectedPath()/vscodeUserDir()) so the test verifies the function under test
rather than mirroring the same helper.
In `@packages/cli-core/src/commands/mcp/clients/paths.ts`:
- Around line 26-35: vscodeUserDir currently uses the nullish coalescing
operator (??) with process.env.APPDATA and process.env.XDG_CONFIG_HOME which
treats empty strings as set; update vscodeUserDir to treat empty or
whitespace-only env vars as unset by checking truthiness/trim (e.g. derive local
vars like appData = process.env.APPDATA?.trim() ? process.env.APPDATA :
undefined and xdg = process.env.XDG_CONFIG_HOME?.trim() ?
process.env.XDG_CONFIG_HOME : undefined) and then fall back to join(homedir(),
...) in the switch cases; reference the vscodeUserDir function and its uses of
process.env.APPDATA and process.env.XDG_CONFIG_HOME, and ensure platform(),
homedir(), and join() behavior is preserved.
In `@packages/cli-core/src/lib/environment.ts`:
- Around line 154-165: getMcpUrl currently lets an empty CLERK_MCP_URL ("") win
due to the nullish coalescing operator; treat empty/whitespace-only env values
as unset by trimming and checking truthiness before falling back. Update
getMcpUrl (function name: getMcpUrl) to compute const env =
process.env.CLERK_MCP_URL?.trim(); then return env && env.length ? env :
getCurrentEnv().mcpUrl ?? DEFAULT_MCP_URL so blank overrides no longer produce
an unusable empty string.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2826916f-5886-46c5-96c7-da4b8e7b1a92

📥 Commits

Reviewing files that changed from the base of the PR and between bdf8faf and d80abcd.

📒 Files selected for processing (23)
  • .changeset/mcp-install.md
  • packages/cli-core/src/cli-program.ts
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/mcp/README.md
  • packages/cli-core/src/commands/mcp/clients/claude-code.ts
  • packages/cli-core/src/commands/mcp/clients/clients.test.ts
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.ts
  • packages/cli-core/src/commands/mcp/clients/paths.ts
  • packages/cli-core/src/commands/mcp/clients/user-scope.test.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/commands/mcp/install.test.ts
  • packages/cli-core/src/commands/mcp/install.ts
  • packages/cli-core/src/commands/mcp/list.test.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/mcp/shared.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts
  • packages/cli-core/src/lib/environment.ts
  • packages/cli-core/src/lib/errors.ts
💤 Files with no reviewable changes (3)
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/cli-program.ts
✅ Files skipped from review due to trivial changes (2)
  • .changeset/mcp-install.md
  • packages/cli-core/src/commands/mcp/README.md
🚧 Files skipped from review as they are similar to previous changes (12)
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/lib/errors.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/clients/user-scope.test.ts
  • packages/cli-core/src/commands/mcp/shared.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.ts
  • packages/cli-core/src/commands/mcp/install.test.ts

Comment threadpackages/cli-core/src/commands/mcp/clients/clients.test.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/paths.ts Outdated
Comment threadpackages/cli-core/src/lib/environment.ts
@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@2.0.1-snapshot.d637eb0
PackageVersion
clerk2.0.1-snapshot.d637eb0

Published from d637eb0

@wyattjoh
wyattjoh self-requested a review June 12, 2026 16:26
@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 2 times, most recently from 19b2820 to 0d5281dCompareJune 15, 2026 12:22

@wyattjohwyattjoh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid, well-tested feature surface. The error handling, URL hardening, and per-client config abstraction are all carefully done. A few non-blocking observations below, mostly around concurrency edge cases in the stdio bridge and the shared ~/.claude.json write path.

Comment threadpackages/cli-core/src/commands/mcp/clients/claude.ts
Comment threadpackages/cli-core/src/commands/mcp/run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/run.ts
Comment threadpackages/cli-core/src/lib/input-json.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/toml-config.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/shared.ts
Comment threadpackages/cli-core/src/commands/mcp/install.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.test.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.test.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/gemini.ts Outdated
@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 3 times, most recently from 161868b to c13e55aCompareJune 27, 2026 12:17

@wyattjohwyattjoh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strong, well-tested PR. The main cleanup is leftover --url surface from the "drop --url from public CLI" commit (a help example that now errors, an unactionable error message, the README, and the changeset), plus a couple of narrow correctness edge cases noted inline.

Comment threadpackages/cli-core/src/commands/mcp/index.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/README.md Outdated
Comment thread.changeset/mcp-install.md Outdated
Comment threadpackages/cli-core/src/commands/mcp/run.ts
Comment threadpackages/cli-core/src/commands/mcp/clients/json-config.ts
Comment threadpackages/cli-core/src/commands/mcp/install.ts
Comment threadpackages/cli-core/src/commands/mcp/run.ts
Comment threadpackages/cli-core/src/commands/mcp/shared.ts
@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 4 times, most recently from c70a7f1 to 89d5811CompareJuly 15, 2026 12:16
@rafa-thaytorafa-thayto changed the title feat(mcp): add clerk mcp install/list/uninstall + doctor MCP checkfeat(mcp): clerk mcp install/list/uninstall/run across 10 clients + doctor MCP checkJul 21, 2026
@rafa-thaytorafa-thayto changed the title feat(mcp): clerk mcp install/list/uninstall/run across 10 clients + doctor MCP checkfeat(mcp): clerk mcp install/list/uninstall/run + doctor MCP checkJul 21, 2026
… + doctor MCP check
Registers the Clerk remote MCP server (https://mcp.clerk.com/mcp) in AI
editors and agent CLIs, with an MCP reachability check in `clerk doctor`.
Supported clients (all user-global): Claude Code, Cursor, GitHub Copilot /
VS Code (`--client vscode` or `copilot`), Windsurf, Gemini, Codex, opencode,
OpenClaw, Warp, and Hermes Agent.
Registration is delegated to each client's own CLI where a non-interactive
one exists (`claude`/`gemini`/`codex`/`openclaw`/`hermes` `mcp add`,
`code --add-mcp`), with PATH-based detection and no file-write fallback;
Cursor, Windsurf, Warp, and opencode (interactive-only CLI) get atomic
file writes. Install always converges via remove-then-add. Client CLIs are
spawned with stdin closed and a 15s timeout; Windows `.cmd` shims run
through `cmd.exe /c`; Hermes gets its confirm prompt answered via stdin and
a post-add verification because its cancel path exits 0 without saving.
Every client stores the same URL-less bridge descriptor
`{command: "clerk", args: ["mcp", "run"]}`. `clerk mcp run` is a built-in
stdio<->Streamable-HTTP bridge (replaces npx mcp-remote) that resolves its
target at spawn time (CLERK_MCP_URL > env profile mcpUrl > hosted default),
serializes stdout frames, caps both stdin lines and upstream SSE/JSON
bodies at 16 MiB, and surfaces pre-session 401/403 as a clear error while
answering in-session auth failures per-request.
Reads stay file-based (JSON/TOML/YAML codecs; TOML and YAML are read-only
since those clients' CLIs own both mutations). `--name` is allowlisted
([A-Za-z0-9_-], no leading dash) because it is spliced into client CLI argv
and used as a config key. `list --json` reports { entries, failures } so an
unreadable config surfaces structurally; install/uninstall carry the same
failures array and exit non-zero only when every targeted client fails.
Agent mode never prompts and always emits JSON with machine-readable error
codes plus a docs URL (clerk.com/docs/guides/ai/mcp/clerk-mcp-server).
Verified live against the real client CLIs on macOS: full uninstall/install
round-trips with zero failures; opencode reports "clerk connected" and
Hermes "clerk enabled" through their own CLIs; OpenClaw's dialect verified
in a sandboxed OPENCLAW_STATE_DIR; bridge handshake against the hosted
server returns "Clerk MCP Server".
Comment threadpackages/cli-core/src/commands/doctor/check-mcp.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/make-cli-client.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/make-client.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/probe.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/make-client.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/toml-config.ts
Comment threadpackages/cli-core/src/commands/mcp/probe.ts
Comment threadpackages/cli-core/src/commands/doctor/check-mcp.ts
Comment threadpackages/cli-core/src/commands/mcp/clients/json-config.ts
@rafa-thayto
rafa-thayto merged commit 8bf74ab into mainJul 22, 2026
9 of 10 checks passed
@rafa-thayto
rafa-thayto deleted the add-mcp-server-cli branch July 22, 2026 14:37
@github-actionsgithub-actionsBot mentioned this pull request Jul 22, 2026
Sign up for freeto 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.

2 participants

@rafa-thayto@wyattjoh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(mcp): clerk mcp install/list/uninstall/run + doctor MCP check - #307

Merged
rafa-thayto merged 4 commits into
mainfrom
add-mcp-server-cli
Jul 22, 2026
Merged

feat(mcp): clerk mcp install/list/uninstall/run + doctor MCP check#307
rafa-thayto merged 4 commits into
mainfrom
add-mcp-server-cli

Conversation

@rafa-thayto

@rafa-thaytorafa-thayto commented May 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a clerk mcp command group that connects the Clerk remote MCP server (https://mcp.clerk.com/mcp) to 10 AI clients, plus an MCP health check in clerk doctor.

  • clerk mcp install — register the server. Human mode: interactive multiselect over detected clients; agent mode / --all / --client <id> (repeatable): non-interactive. Install always converges: whatever entry sits under the name is replaced (remove-then-add through the client's CLI, or an overwrite for file-backed clients) — there is no --force and no conflict state.
  • clerk mcp list — show every Clerk-flavored entry (named clerk or pointing at a *.clerk.com host). JSON output is { entries, failures }, so an unreadable client config surfaces structurally instead of reading as "nothing installed".
  • clerk mcp uninstall — remove the entry. Human mode prompts with the clients that actually have it (nothing pre-checked); agent mode / --all targets every client. Nothing matched → warm hint, exit 0.
  • clerk mcp run — a built-in stdio↔Streamable-HTTP bridge that editors spawn (replaces npx mcp-remote; no npx dependency). Every client config stores the same URL-less descriptor { "command": "clerk", "args": ["mcp", "run"] }; the bridge resolves its target at spawn time (CLERK_MCP_URL > env profile mcpUrl > hosted default).
  • clerk doctor — MCP check probes each distinct configured URL via the initialize handshake when an entry is installed (skips otherwise; warns, never fails), and distinguishes "config unreadable" from "nothing installed".

Registration is delegated to each client's own CLI

Clients that ship a non-interactive registration CLI are driven through it — the client owns its config format and write safety. The rest get direct file writes. There is no file-write fallback for CLI-backed clients: a missing binary fails that client with an actionable error (mcp_client_cli_not_found + docs link), and detection is PATH-based so the picker only offers clients we can actually drive.

Client--client idRegistered viaRemoved via
Claude Codeclaudeclaude mcp add --scope userclaude mcp remove
Cursorcursorfile write (no CLI exists)file write
GitHub Copilot / VS Codevscode (copilot)code --add-mcp '<json>'file write (add-only CLI)
Windsurfwindsurffile write (no CLI exists)file write
Gemini CLIgeminigemini mcp add --scope usergemini mcp remove
Codexcodexcodex mcp add (TOML config)codex mcp remove
opencodeopencodefile write (its mcp add is an interactive-only wizard)file write
OpenClawopenclawopenclaw mcp add --no-probeopenclaw mcp unset
Warpwarpfile write to ~/.warp/.mcp.json (no registration CLI)file write
Hermes Agenthermeshermes mcp add (stdin-piped confirm + post-add verify)hermes mcp remove

All entries are user-global, so the server is available in every project.

Design notes

  • Client CLIs are spawned safely: stdin closed (a prompting CLI EOF-errors instead of hanging agent runs) + a 15s hard timeout. Windows .cmd/.bat shims are launched through cmd.exe /c.
  • Hermes hardening: its mcp add ends in a confirm prompt and exits 0 on cancel without saving — so the affirmative answer is piped to stdin and the config is re-read after add; a silent no-op becomes mcp_client_cli_failed. Its YAML config is read-only to us (both mutations go through its CLI), so hand-written YAML is never rewritten.
  • Reads stay ours:list, doctor, and the uninstall picker parse config files directly (JSON/TOML/YAML codecs, incl. OpenClaw's nested mcp.servers map). Legacy descriptor shapes ({url}, {serverUrl}, mcp-remote args, clerk mcp run --url) still round-trip.
  • Agent mode throughout: non-TTY runs auto-emit JSON, never prompt, and every registration-blocking error carries a machine-readable code plus a docsUrl to the Clerk MCP setup docs (agent mode gets the .md variant) for the manual fallback.
  • Partial failure is structural: per-client failures land in a failures: [{ client, error }] array in install/uninstall/list JSON; the command exits non-zero only when every targeted client fails.
  • Auth (current limitation): the bridge is transport-only. A 401/403 on the initial handshake exits with a clear error; on later requests it's surfaced to the editor as a per-request JSON-RPC error without killing the bridge.

Test plan

  • bun run format:check, bun run lint, bun run typecheck — green
  • bun run test — 2095 tests green, including per-client argv contracts for all six CLI dialects, file shapes for the four file-backed clients, nested-topKey + YAML codec units, corrupt-config failure threading, Hermes verify-add, and Windows .cmd-shim spawning
  • Live verification (macOS): every CLI dialect verified against the real client CLI — claude/gemini/codex/code (duplicate-add/remove semantics), hermes (prompt + lying exit code discovered this way), openclaw (sandboxed OPENCLAW_STATE_DIR)
  • Real round-trip: uninstall --allinstall --all across all detected clients with zero failures; opencode's own CLI reports ✓ clerk connected and Hermes reports clerk ✓ enabled; bridge handshake against the hosted server returns Clerk MCP Server

@changeset-bot

changeset-botBot commented May 25, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e22734c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkMinor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented May 25, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds MCP server support to the Clerk CLI: new subcommands (clerk mcp install/list/uninstall), a JSON-backed client factory and five client integrations (Claude Code, Cursor, VS Code, Windsurf, Gemini), per-client config read/write helpers, collection of installed entries, an initialize-handshake probe supporting JSON and SSE with a 10s timeout, a doctor check (checkMcp), environment/profile mcpUrl wiring, CLI wiring, comprehensive tests, and documentation including a changeset and README updates.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 24.44% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check⚠️ WarningThe title matches the MCP install/list/uninstall and doctor work, but it incorrectly includes a run command that isn't in this changeset.Remove the unsupported "run" reference and keep the title focused on install/list/uninstall plus the doctor MCP check.
✅ Passed checks (3 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
Description check✅ PassedThe description is about the same MCP feature area and doctor check, so it is related despite several inaccurate details.

Comment @coderabbitai help to get the list of available commands.

@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 2 times, most recently from b4bc06c to 4f53e9fCompareMay 30, 2026 12:14

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/cli-core/src/commands/mcp/clients/user-scope.test.ts (1)

2-2: ⚡ Quick win

Use Bun file APIs here instead of node:fs read/write helpers.

These tests currently use readFile/writeFile from node:fs/promises, which goes against the repo’s Bun-specific file I/O guideline. Bun.file(...).json()/text() and Bun.write(...) would keep this aligned with the rest of the codebase. As per coding guidelines, **/*.{ts,tsx,js,jsx}: Prefer Bun.file over node:fs's readFile/writeFile for file operations.

Also applies to: 45-46, 64-66

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/cli-core/src/commands/mcp/clients/user-scope.test.ts` at line 2,
Replace node:fs/promises read/write helpers imported as mkdir, mkdtemp,
readFile, rm, writeFile in user-scope.test.ts with Bun file APIs: use
Bun.file(path).text()/json() for reads and Bun.write/Bun.writeSync (or
Bun.file(path).write(...)) for writes, and keep using mkdtemp/mkdir/rm
equivalents via Bun or the existing helpers if available; update all call sites
that use readFile/writeFile (and any promise-based usages) to the corresponding
Bun.file(...).text()/json() and Bun.write(...) patterns so the tests follow the
repo’s Bun-specific I/O guideline.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli-core/src/commands/mcp/clients/user-scope.test.ts`:
- Around line 7-12: The top-level module mock of node:os (mock.module("node:os",
...)) is never torn down causing cross-test leakage; add a cleanup via
afterAll(() => mock.restoreModule("node:os")) (or equivalent) to restore the
original module and avoid order-dependent failures, and update file I/O in this
test to use Bun APIs instead of node:fs/promises—replace readFile/writeFile
calls with Bun.file(path).json()/text() for reads and Bun.file(path).write(...)
or Bun.write(...) for writes so the test uses Bun's file operations correctly;
reference mock.module, mock.restoreModule, mockHome and the test file's existing
fs read/write usages when making the changes.
---
Nitpick comments:
In `@packages/cli-core/src/commands/mcp/clients/user-scope.test.ts`:
- Line 2: Replace node:fs/promises read/write helpers imported as mkdir,
mkdtemp, readFile, rm, writeFile in user-scope.test.ts with Bun file APIs: use
Bun.file(path).text()/json() for reads and Bun.write/Bun.writeSync (or
Bun.file(path).write(...)) for writes, and keep using mkdtemp/mkdir/rm
equivalents via Bun or the existing helpers if available; update all call sites
that use readFile/writeFile (and any promise-based usages) to the corresponding
Bun.file(...).text()/json() and Bun.write(...) patterns so the tests follow the
repo’s Bun-specific I/O guideline.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 33d9756c-11d6-436b-8465-7e750f438029

📥 Commits

Reviewing files that changed from the base of the PR and between 292e92f and 4f53e9f.

📒 Files selected for processing (34)
  • .changeset/mcp-install.md
  • README.md
  • packages/cli-core/src/cli-program.ts
  • packages/cli-core/src/commands/doctor/README.md
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/doctor/context.test.ts
  • packages/cli-core/src/commands/doctor/index.ts
  • packages/cli-core/src/commands/mcp/README.md
  • packages/cli-core/src/commands/mcp/clients/claude-code.ts
  • packages/cli-core/src/commands/mcp/clients/clients.test.ts
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.ts
  • packages/cli-core/src/commands/mcp/clients/paths.ts
  • packages/cli-core/src/commands/mcp/clients/registry.ts
  • packages/cli-core/src/commands/mcp/clients/types.ts
  • packages/cli-core/src/commands/mcp/clients/user-scope.test.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/commands/mcp/collect.ts
  • packages/cli-core/src/commands/mcp/index.ts
  • packages/cli-core/src/commands/mcp/install.test.ts
  • packages/cli-core/src/commands/mcp/install.ts
  • packages/cli-core/src/commands/mcp/list.test.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/mcp/probe.test.ts
  • packages/cli-core/src/commands/mcp/probe.ts
  • packages/cli-core/src/commands/mcp/shared.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts
  • packages/cli-core/src/commands/mcp/uninstall.ts
  • packages/cli-core/src/lib/environment.ts
  • packages/cli-core/src/lib/errors.ts
✅ Files skipped from review due to trivial changes (4)
  • .changeset/mcp-install.md
  • packages/cli-core/src/commands/doctor/README.md
  • README.md
  • packages/cli-core/src/commands/mcp/README.md
🚧 Files skipped from review as they are similar to previous changes (26)
  • packages/cli-core/src/commands/mcp/list.test.ts
  • packages/cli-core/src/commands/mcp/clients/registry.ts
  • packages/cli-core/src/commands/mcp/index.ts
  • packages/cli-core/src/commands/mcp/clients/claude-code.ts
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/commands/mcp/clients/types.ts
  • packages/cli-core/src/commands/mcp/probe.test.ts
  • packages/cli-core/src/lib/errors.ts
  • packages/cli-core/src/commands/doctor/index.ts
  • packages/cli-core/src/commands/mcp/clients/paths.ts
  • packages/cli-core/src/commands/mcp/install.ts
  • packages/cli-core/src/lib/environment.ts
  • packages/cli-core/src/commands/mcp/collect.ts
  • packages/cli-core/src/commands/mcp/uninstall.ts
  • packages/cli-core/src/commands/mcp/clients/clients.test.ts
  • packages/cli-core/src/commands/mcp/probe.ts
  • packages/cli-core/src/commands/mcp/install.test.ts
  • packages/cli-core/src/cli-program.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts

Comment threadpackages/cli-core/src/commands/mcp/clients/user-scope.test.ts Outdated
@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@2.0.1-snapshot.bdf8faf
PackageVersion
clerk2.0.1-snapshot.bdf8faf

Published from bdf8faf

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli-core/src/commands/mcp/clients/clients.test.ts`:
- Around line 65-96: The VS Code test is leaking to the real config because
vscodeUserDir() respects XDG_CONFIG_HOME/APPDATA; update the tests to isolate
that by setting XDG_CONFIG_HOME and APPDATA to a directory under mockHome (or
delete them) before running the VS Code-specific assertions and restore them
after; ensure the expectation for the VS Code path does not call vscodeUserDir()
(i.e. compute the expected path explicitly inside the test instead of reusing
expectedPath()/vscodeUserDir()) so the test verifies the function under test
rather than mirroring the same helper.
In `@packages/cli-core/src/commands/mcp/clients/paths.ts`:
- Around line 26-35: vscodeUserDir currently uses the nullish coalescing
operator (??) with process.env.APPDATA and process.env.XDG_CONFIG_HOME which
treats empty strings as set; update vscodeUserDir to treat empty or
whitespace-only env vars as unset by checking truthiness/trim (e.g. derive local
vars like appData = process.env.APPDATA?.trim() ? process.env.APPDATA :
undefined and xdg = process.env.XDG_CONFIG_HOME?.trim() ?
process.env.XDG_CONFIG_HOME : undefined) and then fall back to join(homedir(),
...) in the switch cases; reference the vscodeUserDir function and its uses of
process.env.APPDATA and process.env.XDG_CONFIG_HOME, and ensure platform(),
homedir(), and join() behavior is preserved.
In `@packages/cli-core/src/lib/environment.ts`:
- Around line 154-165: getMcpUrl currently lets an empty CLERK_MCP_URL ("") win
due to the nullish coalescing operator; treat empty/whitespace-only env values
as unset by trimming and checking truthiness before falling back. Update
getMcpUrl (function name: getMcpUrl) to compute const env =
process.env.CLERK_MCP_URL?.trim(); then return env && env.length ? env :
getCurrentEnv().mcpUrl ?? DEFAULT_MCP_URL so blank overrides no longer produce
an unusable empty string.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2826916f-5886-46c5-96c7-da4b8e7b1a92

📥 Commits

Reviewing files that changed from the base of the PR and between bdf8faf and d80abcd.

📒 Files selected for processing (23)
  • .changeset/mcp-install.md
  • packages/cli-core/src/cli-program.ts
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/mcp/README.md
  • packages/cli-core/src/commands/mcp/clients/claude-code.ts
  • packages/cli-core/src/commands/mcp/clients/clients.test.ts
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.ts
  • packages/cli-core/src/commands/mcp/clients/paths.ts
  • packages/cli-core/src/commands/mcp/clients/user-scope.test.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/commands/mcp/install.test.ts
  • packages/cli-core/src/commands/mcp/install.ts
  • packages/cli-core/src/commands/mcp/list.test.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/mcp/shared.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts
  • packages/cli-core/src/lib/environment.ts
  • packages/cli-core/src/lib/errors.ts
💤 Files with no reviewable changes (3)
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/cli-program.ts
✅ Files skipped from review due to trivial changes (2)
  • .changeset/mcp-install.md
  • packages/cli-core/src/commands/mcp/README.md
🚧 Files skipped from review as they are similar to previous changes (12)
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/lib/errors.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/clients/user-scope.test.ts
  • packages/cli-core/src/commands/mcp/shared.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.ts
  • packages/cli-core/src/commands/mcp/install.test.ts

Comment threadpackages/cli-core/src/commands/mcp/clients/clients.test.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/paths.ts Outdated
Comment threadpackages/cli-core/src/lib/environment.ts
@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@2.0.1-snapshot.d637eb0
PackageVersion
clerk2.0.1-snapshot.d637eb0

Published from d637eb0

@wyattjoh
wyattjoh self-requested a review June 12, 2026 16:26
@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 2 times, most recently from 19b2820 to 0d5281dCompareJune 15, 2026 12:22

@wyattjohwyattjoh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid, well-tested feature surface. The error handling, URL hardening, and per-client config abstraction are all carefully done. A few non-blocking observations below, mostly around concurrency edge cases in the stdio bridge and the shared ~/.claude.json write path.

Comment threadpackages/cli-core/src/commands/mcp/clients/claude.ts
Comment threadpackages/cli-core/src/commands/mcp/run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/run.ts
Comment threadpackages/cli-core/src/lib/input-json.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/toml-config.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/shared.ts
Comment threadpackages/cli-core/src/commands/mcp/install.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.test.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.test.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/gemini.ts Outdated
@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 3 times, most recently from 161868b to c13e55aCompareJune 27, 2026 12:17

@wyattjohwyattjoh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strong, well-tested PR. The main cleanup is leftover --url surface from the "drop --url from public CLI" commit (a help example that now errors, an unactionable error message, the README, and the changeset), plus a couple of narrow correctness edge cases noted inline.

Comment threadpackages/cli-core/src/commands/mcp/index.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/README.md Outdated
Comment thread.changeset/mcp-install.md Outdated
Comment threadpackages/cli-core/src/commands/mcp/run.ts
Comment threadpackages/cli-core/src/commands/mcp/clients/json-config.ts
Comment threadpackages/cli-core/src/commands/mcp/install.ts
Comment threadpackages/cli-core/src/commands/mcp/run.ts
Comment threadpackages/cli-core/src/commands/mcp/shared.ts
@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 4 times, most recently from c70a7f1 to 89d5811CompareJuly 15, 2026 12:16
@rafa-thaytorafa-thayto changed the title feat(mcp): add clerk mcp install/list/uninstall + doctor MCP checkfeat(mcp): clerk mcp install/list/uninstall/run across 10 clients + doctor MCP checkJul 21, 2026
@rafa-thaytorafa-thayto changed the title feat(mcp): clerk mcp install/list/uninstall/run across 10 clients + doctor MCP checkfeat(mcp): clerk mcp install/list/uninstall/run + doctor MCP checkJul 21, 2026
… + doctor MCP check
Registers the Clerk remote MCP server (https://mcp.clerk.com/mcp) in AI
editors and agent CLIs, with an MCP reachability check in `clerk doctor`.
Supported clients (all user-global): Claude Code, Cursor, GitHub Copilot /
VS Code (`--client vscode` or `copilot`), Windsurf, Gemini, Codex, opencode,
OpenClaw, Warp, and Hermes Agent.
Registration is delegated to each client's own CLI where a non-interactive
one exists (`claude`/`gemini`/`codex`/`openclaw`/`hermes` `mcp add`,
`code --add-mcp`), with PATH-based detection and no file-write fallback;
Cursor, Windsurf, Warp, and opencode (interactive-only CLI) get atomic
file writes. Install always converges via remove-then-add. Client CLIs are
spawned with stdin closed and a 15s timeout; Windows `.cmd` shims run
through `cmd.exe /c`; Hermes gets its confirm prompt answered via stdin and
a post-add verification because its cancel path exits 0 without saving.
Every client stores the same URL-less bridge descriptor
`{command: "clerk", args: ["mcp", "run"]}`. `clerk mcp run` is a built-in
stdio<->Streamable-HTTP bridge (replaces npx mcp-remote) that resolves its
target at spawn time (CLERK_MCP_URL > env profile mcpUrl > hosted default),
serializes stdout frames, caps both stdin lines and upstream SSE/JSON
bodies at 16 MiB, and surfaces pre-session 401/403 as a clear error while
answering in-session auth failures per-request.
Reads stay file-based (JSON/TOML/YAML codecs; TOML and YAML are read-only
since those clients' CLIs own both mutations). `--name` is allowlisted
([A-Za-z0-9_-], no leading dash) because it is spliced into client CLI argv
and used as a config key. `list --json` reports { entries, failures } so an
unreadable config surfaces structurally; install/uninstall carry the same
failures array and exit non-zero only when every targeted client fails.
Agent mode never prompts and always emits JSON with machine-readable error
codes plus a docs URL (clerk.com/docs/guides/ai/mcp/clerk-mcp-server).
Verified live against the real client CLIs on macOS: full uninstall/install
round-trips with zero failures; opencode reports "clerk connected" and
Hermes "clerk enabled" through their own CLIs; OpenClaw's dialect verified
in a sandboxed OPENCLAW_STATE_DIR; bridge handshake against the hosted
server returns "Clerk MCP Server".
Comment threadpackages/cli-core/src/commands/doctor/check-mcp.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/make-cli-client.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/make-client.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/probe.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/make-client.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/toml-config.ts
Comment threadpackages/cli-core/src/commands/mcp/probe.ts
Comment threadpackages/cli-core/src/commands/doctor/check-mcp.ts
Comment threadpackages/cli-core/src/commands/mcp/clients/json-config.ts
@rafa-thayto
rafa-thayto merged commit 8bf74ab into mainJul 22, 2026
9 of 10 checks passed
@rafa-thayto
rafa-thayto deleted the add-mcp-server-cli branch July 22, 2026 14:37
@github-actionsgithub-actionsBot mentioned this pull request Jul 22, 2026
Sign up for freeto 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.

2 participants

@rafa-thayto@wyattjoh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(mcp): clerk mcp install/list/uninstall/run + doctor MCP check - #307

Merged
rafa-thayto merged 4 commits into
mainfrom
add-mcp-server-cli
Jul 22, 2026
Merged

feat(mcp): clerk mcp install/list/uninstall/run + doctor MCP check#307
rafa-thayto merged 4 commits into
mainfrom
add-mcp-server-cli

Conversation

@rafa-thayto

@rafa-thaytorafa-thayto commented May 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a clerk mcp command group that connects the Clerk remote MCP server (https://mcp.clerk.com/mcp) to 10 AI clients, plus an MCP health check in clerk doctor.

  • clerk mcp install — register the server. Human mode: interactive multiselect over detected clients; agent mode / --all / --client <id> (repeatable): non-interactive. Install always converges: whatever entry sits under the name is replaced (remove-then-add through the client's CLI, or an overwrite for file-backed clients) — there is no --force and no conflict state.
  • clerk mcp list — show every Clerk-flavored entry (named clerk or pointing at a *.clerk.com host). JSON output is { entries, failures }, so an unreadable client config surfaces structurally instead of reading as "nothing installed".
  • clerk mcp uninstall — remove the entry. Human mode prompts with the clients that actually have it (nothing pre-checked); agent mode / --all targets every client. Nothing matched → warm hint, exit 0.
  • clerk mcp run — a built-in stdio↔Streamable-HTTP bridge that editors spawn (replaces npx mcp-remote; no npx dependency). Every client config stores the same URL-less descriptor { "command": "clerk", "args": ["mcp", "run"] }; the bridge resolves its target at spawn time (CLERK_MCP_URL > env profile mcpUrl > hosted default).
  • clerk doctor — MCP check probes each distinct configured URL via the initialize handshake when an entry is installed (skips otherwise; warns, never fails), and distinguishes "config unreadable" from "nothing installed".

Registration is delegated to each client's own CLI

Clients that ship a non-interactive registration CLI are driven through it — the client owns its config format and write safety. The rest get direct file writes. There is no file-write fallback for CLI-backed clients: a missing binary fails that client with an actionable error (mcp_client_cli_not_found + docs link), and detection is PATH-based so the picker only offers clients we can actually drive.

Client--client idRegistered viaRemoved via
Claude Codeclaudeclaude mcp add --scope userclaude mcp remove
Cursorcursorfile write (no CLI exists)file write
GitHub Copilot / VS Codevscode (copilot)code --add-mcp '<json>'file write (add-only CLI)
Windsurfwindsurffile write (no CLI exists)file write
Gemini CLIgeminigemini mcp add --scope usergemini mcp remove
Codexcodexcodex mcp add (TOML config)codex mcp remove
opencodeopencodefile write (its mcp add is an interactive-only wizard)file write
OpenClawopenclawopenclaw mcp add --no-probeopenclaw mcp unset
Warpwarpfile write to ~/.warp/.mcp.json (no registration CLI)file write
Hermes Agenthermeshermes mcp add (stdin-piped confirm + post-add verify)hermes mcp remove

All entries are user-global, so the server is available in every project.

Design notes

  • Client CLIs are spawned safely: stdin closed (a prompting CLI EOF-errors instead of hanging agent runs) + a 15s hard timeout. Windows .cmd/.bat shims are launched through cmd.exe /c.
  • Hermes hardening: its mcp add ends in a confirm prompt and exits 0 on cancel without saving — so the affirmative answer is piped to stdin and the config is re-read after add; a silent no-op becomes mcp_client_cli_failed. Its YAML config is read-only to us (both mutations go through its CLI), so hand-written YAML is never rewritten.
  • Reads stay ours:list, doctor, and the uninstall picker parse config files directly (JSON/TOML/YAML codecs, incl. OpenClaw's nested mcp.servers map). Legacy descriptor shapes ({url}, {serverUrl}, mcp-remote args, clerk mcp run --url) still round-trip.
  • Agent mode throughout: non-TTY runs auto-emit JSON, never prompt, and every registration-blocking error carries a machine-readable code plus a docsUrl to the Clerk MCP setup docs (agent mode gets the .md variant) for the manual fallback.
  • Partial failure is structural: per-client failures land in a failures: [{ client, error }] array in install/uninstall/list JSON; the command exits non-zero only when every targeted client fails.
  • Auth (current limitation): the bridge is transport-only. A 401/403 on the initial handshake exits with a clear error; on later requests it's surfaced to the editor as a per-request JSON-RPC error without killing the bridge.

Test plan

  • bun run format:check, bun run lint, bun run typecheck — green
  • bun run test — 2095 tests green, including per-client argv contracts for all six CLI dialects, file shapes for the four file-backed clients, nested-topKey + YAML codec units, corrupt-config failure threading, Hermes verify-add, and Windows .cmd-shim spawning
  • Live verification (macOS): every CLI dialect verified against the real client CLI — claude/gemini/codex/code (duplicate-add/remove semantics), hermes (prompt + lying exit code discovered this way), openclaw (sandboxed OPENCLAW_STATE_DIR)
  • Real round-trip: uninstall --allinstall --all across all detected clients with zero failures; opencode's own CLI reports ✓ clerk connected and Hermes reports clerk ✓ enabled; bridge handshake against the hosted server returns Clerk MCP Server

@changeset-bot

changeset-botBot commented May 25, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e22734c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkMinor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented May 25, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds MCP server support to the Clerk CLI: new subcommands (clerk mcp install/list/uninstall), a JSON-backed client factory and five client integrations (Claude Code, Cursor, VS Code, Windsurf, Gemini), per-client config read/write helpers, collection of installed entries, an initialize-handshake probe supporting JSON and SSE with a 10s timeout, a doctor check (checkMcp), environment/profile mcpUrl wiring, CLI wiring, comprehensive tests, and documentation including a changeset and README updates.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 24.44% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check⚠️ WarningThe title matches the MCP install/list/uninstall and doctor work, but it incorrectly includes a run command that isn't in this changeset.Remove the unsupported "run" reference and keep the title focused on install/list/uninstall plus the doctor MCP check.
✅ Passed checks (3 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
Description check✅ PassedThe description is about the same MCP feature area and doctor check, so it is related despite several inaccurate details.

Comment @coderabbitai help to get the list of available commands.

@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 2 times, most recently from b4bc06c to 4f53e9fCompareMay 30, 2026 12:14

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/cli-core/src/commands/mcp/clients/user-scope.test.ts (1)

2-2: ⚡ Quick win

Use Bun file APIs here instead of node:fs read/write helpers.

These tests currently use readFile/writeFile from node:fs/promises, which goes against the repo’s Bun-specific file I/O guideline. Bun.file(...).json()/text() and Bun.write(...) would keep this aligned with the rest of the codebase. As per coding guidelines, **/*.{ts,tsx,js,jsx}: Prefer Bun.file over node:fs's readFile/writeFile for file operations.

Also applies to: 45-46, 64-66

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/cli-core/src/commands/mcp/clients/user-scope.test.ts` at line 2,
Replace node:fs/promises read/write helpers imported as mkdir, mkdtemp,
readFile, rm, writeFile in user-scope.test.ts with Bun file APIs: use
Bun.file(path).text()/json() for reads and Bun.write/Bun.writeSync (or
Bun.file(path).write(...)) for writes, and keep using mkdtemp/mkdir/rm
equivalents via Bun or the existing helpers if available; update all call sites
that use readFile/writeFile (and any promise-based usages) to the corresponding
Bun.file(...).text()/json() and Bun.write(...) patterns so the tests follow the
repo’s Bun-specific I/O guideline.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli-core/src/commands/mcp/clients/user-scope.test.ts`:
- Around line 7-12: The top-level module mock of node:os (mock.module("node:os",
...)) is never torn down causing cross-test leakage; add a cleanup via
afterAll(() => mock.restoreModule("node:os")) (or equivalent) to restore the
original module and avoid order-dependent failures, and update file I/O in this
test to use Bun APIs instead of node:fs/promises—replace readFile/writeFile
calls with Bun.file(path).json()/text() for reads and Bun.file(path).write(...)
or Bun.write(...) for writes so the test uses Bun's file operations correctly;
reference mock.module, mock.restoreModule, mockHome and the test file's existing
fs read/write usages when making the changes.
---
Nitpick comments:
In `@packages/cli-core/src/commands/mcp/clients/user-scope.test.ts`:
- Line 2: Replace node:fs/promises read/write helpers imported as mkdir,
mkdtemp, readFile, rm, writeFile in user-scope.test.ts with Bun file APIs: use
Bun.file(path).text()/json() for reads and Bun.write/Bun.writeSync (or
Bun.file(path).write(...)) for writes, and keep using mkdtemp/mkdir/rm
equivalents via Bun or the existing helpers if available; update all call sites
that use readFile/writeFile (and any promise-based usages) to the corresponding
Bun.file(...).text()/json() and Bun.write(...) patterns so the tests follow the
repo’s Bun-specific I/O guideline.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 33d9756c-11d6-436b-8465-7e750f438029

📥 Commits

Reviewing files that changed from the base of the PR and between 292e92f and 4f53e9f.

📒 Files selected for processing (34)
  • .changeset/mcp-install.md
  • README.md
  • packages/cli-core/src/cli-program.ts
  • packages/cli-core/src/commands/doctor/README.md
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/doctor/context.test.ts
  • packages/cli-core/src/commands/doctor/index.ts
  • packages/cli-core/src/commands/mcp/README.md
  • packages/cli-core/src/commands/mcp/clients/claude-code.ts
  • packages/cli-core/src/commands/mcp/clients/clients.test.ts
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.ts
  • packages/cli-core/src/commands/mcp/clients/paths.ts
  • packages/cli-core/src/commands/mcp/clients/registry.ts
  • packages/cli-core/src/commands/mcp/clients/types.ts
  • packages/cli-core/src/commands/mcp/clients/user-scope.test.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/commands/mcp/collect.ts
  • packages/cli-core/src/commands/mcp/index.ts
  • packages/cli-core/src/commands/mcp/install.test.ts
  • packages/cli-core/src/commands/mcp/install.ts
  • packages/cli-core/src/commands/mcp/list.test.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/mcp/probe.test.ts
  • packages/cli-core/src/commands/mcp/probe.ts
  • packages/cli-core/src/commands/mcp/shared.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts
  • packages/cli-core/src/commands/mcp/uninstall.ts
  • packages/cli-core/src/lib/environment.ts
  • packages/cli-core/src/lib/errors.ts
✅ Files skipped from review due to trivial changes (4)
  • .changeset/mcp-install.md
  • packages/cli-core/src/commands/doctor/README.md
  • README.md
  • packages/cli-core/src/commands/mcp/README.md
🚧 Files skipped from review as they are similar to previous changes (26)
  • packages/cli-core/src/commands/mcp/list.test.ts
  • packages/cli-core/src/commands/mcp/clients/registry.ts
  • packages/cli-core/src/commands/mcp/index.ts
  • packages/cli-core/src/commands/mcp/clients/claude-code.ts
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/commands/mcp/clients/types.ts
  • packages/cli-core/src/commands/mcp/probe.test.ts
  • packages/cli-core/src/lib/errors.ts
  • packages/cli-core/src/commands/doctor/index.ts
  • packages/cli-core/src/commands/mcp/clients/paths.ts
  • packages/cli-core/src/commands/mcp/install.ts
  • packages/cli-core/src/lib/environment.ts
  • packages/cli-core/src/commands/mcp/collect.ts
  • packages/cli-core/src/commands/mcp/uninstall.ts
  • packages/cli-core/src/commands/mcp/clients/clients.test.ts
  • packages/cli-core/src/commands/mcp/probe.ts
  • packages/cli-core/src/commands/mcp/install.test.ts
  • packages/cli-core/src/cli-program.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts

Comment threadpackages/cli-core/src/commands/mcp/clients/user-scope.test.ts Outdated
@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@2.0.1-snapshot.bdf8faf
PackageVersion
clerk2.0.1-snapshot.bdf8faf

Published from bdf8faf

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli-core/src/commands/mcp/clients/clients.test.ts`:
- Around line 65-96: The VS Code test is leaking to the real config because
vscodeUserDir() respects XDG_CONFIG_HOME/APPDATA; update the tests to isolate
that by setting XDG_CONFIG_HOME and APPDATA to a directory under mockHome (or
delete them) before running the VS Code-specific assertions and restore them
after; ensure the expectation for the VS Code path does not call vscodeUserDir()
(i.e. compute the expected path explicitly inside the test instead of reusing
expectedPath()/vscodeUserDir()) so the test verifies the function under test
rather than mirroring the same helper.
In `@packages/cli-core/src/commands/mcp/clients/paths.ts`:
- Around line 26-35: vscodeUserDir currently uses the nullish coalescing
operator (??) with process.env.APPDATA and process.env.XDG_CONFIG_HOME which
treats empty strings as set; update vscodeUserDir to treat empty or
whitespace-only env vars as unset by checking truthiness/trim (e.g. derive local
vars like appData = process.env.APPDATA?.trim() ? process.env.APPDATA :
undefined and xdg = process.env.XDG_CONFIG_HOME?.trim() ?
process.env.XDG_CONFIG_HOME : undefined) and then fall back to join(homedir(),
...) in the switch cases; reference the vscodeUserDir function and its uses of
process.env.APPDATA and process.env.XDG_CONFIG_HOME, and ensure platform(),
homedir(), and join() behavior is preserved.
In `@packages/cli-core/src/lib/environment.ts`:
- Around line 154-165: getMcpUrl currently lets an empty CLERK_MCP_URL ("") win
due to the nullish coalescing operator; treat empty/whitespace-only env values
as unset by trimming and checking truthiness before falling back. Update
getMcpUrl (function name: getMcpUrl) to compute const env =
process.env.CLERK_MCP_URL?.trim(); then return env && env.length ? env :
getCurrentEnv().mcpUrl ?? DEFAULT_MCP_URL so blank overrides no longer produce
an unusable empty string.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2826916f-5886-46c5-96c7-da4b8e7b1a92

📥 Commits

Reviewing files that changed from the base of the PR and between bdf8faf and d80abcd.

📒 Files selected for processing (23)
  • .changeset/mcp-install.md
  • packages/cli-core/src/cli-program.ts
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/mcp/README.md
  • packages/cli-core/src/commands/mcp/clients/claude-code.ts
  • packages/cli-core/src/commands/mcp/clients/clients.test.ts
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.ts
  • packages/cli-core/src/commands/mcp/clients/paths.ts
  • packages/cli-core/src/commands/mcp/clients/user-scope.test.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/commands/mcp/install.test.ts
  • packages/cli-core/src/commands/mcp/install.ts
  • packages/cli-core/src/commands/mcp/list.test.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/mcp/shared.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts
  • packages/cli-core/src/lib/environment.ts
  • packages/cli-core/src/lib/errors.ts
💤 Files with no reviewable changes (3)
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/cli-program.ts
✅ Files skipped from review due to trivial changes (2)
  • .changeset/mcp-install.md
  • packages/cli-core/src/commands/mcp/README.md
🚧 Files skipped from review as they are similar to previous changes (12)
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/lib/errors.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/clients/user-scope.test.ts
  • packages/cli-core/src/commands/mcp/shared.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.ts
  • packages/cli-core/src/commands/mcp/install.test.ts

Comment threadpackages/cli-core/src/commands/mcp/clients/clients.test.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/paths.ts Outdated
Comment threadpackages/cli-core/src/lib/environment.ts
@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@2.0.1-snapshot.d637eb0
PackageVersion
clerk2.0.1-snapshot.d637eb0

Published from d637eb0

@wyattjoh
wyattjoh self-requested a review June 12, 2026 16:26
@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 2 times, most recently from 19b2820 to 0d5281dCompareJune 15, 2026 12:22

@wyattjohwyattjoh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid, well-tested feature surface. The error handling, URL hardening, and per-client config abstraction are all carefully done. A few non-blocking observations below, mostly around concurrency edge cases in the stdio bridge and the shared ~/.claude.json write path.

Comment threadpackages/cli-core/src/commands/mcp/clients/claude.ts
Comment threadpackages/cli-core/src/commands/mcp/run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/run.ts
Comment threadpackages/cli-core/src/lib/input-json.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/toml-config.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/shared.ts
Comment threadpackages/cli-core/src/commands/mcp/install.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.test.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.test.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/gemini.ts Outdated
@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 3 times, most recently from 161868b to c13e55aCompareJune 27, 2026 12:17

@wyattjohwyattjoh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strong, well-tested PR. The main cleanup is leftover --url surface from the "drop --url from public CLI" commit (a help example that now errors, an unactionable error message, the README, and the changeset), plus a couple of narrow correctness edge cases noted inline.

Comment threadpackages/cli-core/src/commands/mcp/index.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/README.md Outdated
Comment thread.changeset/mcp-install.md Outdated
Comment threadpackages/cli-core/src/commands/mcp/run.ts
Comment threadpackages/cli-core/src/commands/mcp/clients/json-config.ts
Comment threadpackages/cli-core/src/commands/mcp/install.ts
Comment threadpackages/cli-core/src/commands/mcp/run.ts
Comment threadpackages/cli-core/src/commands/mcp/shared.ts
@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 4 times, most recently from c70a7f1 to 89d5811CompareJuly 15, 2026 12:16
@rafa-thaytorafa-thayto changed the title feat(mcp): add clerk mcp install/list/uninstall + doctor MCP checkfeat(mcp): clerk mcp install/list/uninstall/run across 10 clients + doctor MCP checkJul 21, 2026
@rafa-thaytorafa-thayto changed the title feat(mcp): clerk mcp install/list/uninstall/run across 10 clients + doctor MCP checkfeat(mcp): clerk mcp install/list/uninstall/run + doctor MCP checkJul 21, 2026
… + doctor MCP check
Registers the Clerk remote MCP server (https://mcp.clerk.com/mcp) in AI
editors and agent CLIs, with an MCP reachability check in `clerk doctor`.
Supported clients (all user-global): Claude Code, Cursor, GitHub Copilot /
VS Code (`--client vscode` or `copilot`), Windsurf, Gemini, Codex, opencode,
OpenClaw, Warp, and Hermes Agent.
Registration is delegated to each client's own CLI where a non-interactive
one exists (`claude`/`gemini`/`codex`/`openclaw`/`hermes` `mcp add`,
`code --add-mcp`), with PATH-based detection and no file-write fallback;
Cursor, Windsurf, Warp, and opencode (interactive-only CLI) get atomic
file writes. Install always converges via remove-then-add. Client CLIs are
spawned with stdin closed and a 15s timeout; Windows `.cmd` shims run
through `cmd.exe /c`; Hermes gets its confirm prompt answered via stdin and
a post-add verification because its cancel path exits 0 without saving.
Every client stores the same URL-less bridge descriptor
`{command: "clerk", args: ["mcp", "run"]}`. `clerk mcp run` is a built-in
stdio<->Streamable-HTTP bridge (replaces npx mcp-remote) that resolves its
target at spawn time (CLERK_MCP_URL > env profile mcpUrl > hosted default),
serializes stdout frames, caps both stdin lines and upstream SSE/JSON
bodies at 16 MiB, and surfaces pre-session 401/403 as a clear error while
answering in-session auth failures per-request.
Reads stay file-based (JSON/TOML/YAML codecs; TOML and YAML are read-only
since those clients' CLIs own both mutations). `--name` is allowlisted
([A-Za-z0-9_-], no leading dash) because it is spliced into client CLI argv
and used as a config key. `list --json` reports { entries, failures } so an
unreadable config surfaces structurally; install/uninstall carry the same
failures array and exit non-zero only when every targeted client fails.
Agent mode never prompts and always emits JSON with machine-readable error
codes plus a docs URL (clerk.com/docs/guides/ai/mcp/clerk-mcp-server).
Verified live against the real client CLIs on macOS: full uninstall/install
round-trips with zero failures; opencode reports "clerk connected" and
Hermes "clerk enabled" through their own CLIs; OpenClaw's dialect verified
in a sandboxed OPENCLAW_STATE_DIR; bridge handshake against the hosted
server returns "Clerk MCP Server".
Comment threadpackages/cli-core/src/commands/doctor/check-mcp.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/make-cli-client.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/make-client.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/probe.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/make-client.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/toml-config.ts
Comment threadpackages/cli-core/src/commands/mcp/probe.ts
Comment threadpackages/cli-core/src/commands/doctor/check-mcp.ts
Comment threadpackages/cli-core/src/commands/mcp/clients/json-config.ts
@rafa-thayto
rafa-thayto merged commit 8bf74ab into mainJul 22, 2026
9 of 10 checks passed
@rafa-thayto
rafa-thayto deleted the add-mcp-server-cli branch July 22, 2026 14:37
@github-actionsgithub-actionsBot mentioned this pull request Jul 22, 2026
Sign up for freeto 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.

2 participants

@rafa-thayto@wyattjoh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat(mcp): clerk mcp install/list/uninstall/run + doctor MCP check - #307

Merged
rafa-thayto merged 4 commits into
mainfrom
add-mcp-server-cli
Jul 22, 2026
Merged

feat(mcp): clerk mcp install/list/uninstall/run + doctor MCP check#307
rafa-thayto merged 4 commits into
mainfrom
add-mcp-server-cli

Conversation

@rafa-thayto

@rafa-thaytorafa-thayto commented May 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a clerk mcp command group that connects the Clerk remote MCP server (https://mcp.clerk.com/mcp) to 10 AI clients, plus an MCP health check in clerk doctor.

  • clerk mcp install — register the server. Human mode: interactive multiselect over detected clients; agent mode / --all / --client <id> (repeatable): non-interactive. Install always converges: whatever entry sits under the name is replaced (remove-then-add through the client's CLI, or an overwrite for file-backed clients) — there is no --force and no conflict state.
  • clerk mcp list — show every Clerk-flavored entry (named clerk or pointing at a *.clerk.com host). JSON output is { entries, failures }, so an unreadable client config surfaces structurally instead of reading as "nothing installed".
  • clerk mcp uninstall — remove the entry. Human mode prompts with the clients that actually have it (nothing pre-checked); agent mode / --all targets every client. Nothing matched → warm hint, exit 0.
  • clerk mcp run — a built-in stdio↔Streamable-HTTP bridge that editors spawn (replaces npx mcp-remote; no npx dependency). Every client config stores the same URL-less descriptor { "command": "clerk", "args": ["mcp", "run"] }; the bridge resolves its target at spawn time (CLERK_MCP_URL > env profile mcpUrl > hosted default).
  • clerk doctor — MCP check probes each distinct configured URL via the initialize handshake when an entry is installed (skips otherwise; warns, never fails), and distinguishes "config unreadable" from "nothing installed".

Registration is delegated to each client's own CLI

Clients that ship a non-interactive registration CLI are driven through it — the client owns its config format and write safety. The rest get direct file writes. There is no file-write fallback for CLI-backed clients: a missing binary fails that client with an actionable error (mcp_client_cli_not_found + docs link), and detection is PATH-based so the picker only offers clients we can actually drive.

Client--client idRegistered viaRemoved via
Claude Codeclaudeclaude mcp add --scope userclaude mcp remove
Cursorcursorfile write (no CLI exists)file write
GitHub Copilot / VS Codevscode (copilot)code --add-mcp '<json>'file write (add-only CLI)
Windsurfwindsurffile write (no CLI exists)file write
Gemini CLIgeminigemini mcp add --scope usergemini mcp remove
Codexcodexcodex mcp add (TOML config)codex mcp remove
opencodeopencodefile write (its mcp add is an interactive-only wizard)file write
OpenClawopenclawopenclaw mcp add --no-probeopenclaw mcp unset
Warpwarpfile write to ~/.warp/.mcp.json (no registration CLI)file write
Hermes Agenthermeshermes mcp add (stdin-piped confirm + post-add verify)hermes mcp remove

All entries are user-global, so the server is available in every project.

Design notes

  • Client CLIs are spawned safely: stdin closed (a prompting CLI EOF-errors instead of hanging agent runs) + a 15s hard timeout. Windows .cmd/.bat shims are launched through cmd.exe /c.
  • Hermes hardening: its mcp add ends in a confirm prompt and exits 0 on cancel without saving — so the affirmative answer is piped to stdin and the config is re-read after add; a silent no-op becomes mcp_client_cli_failed. Its YAML config is read-only to us (both mutations go through its CLI), so hand-written YAML is never rewritten.
  • Reads stay ours:list, doctor, and the uninstall picker parse config files directly (JSON/TOML/YAML codecs, incl. OpenClaw's nested mcp.servers map). Legacy descriptor shapes ({url}, {serverUrl}, mcp-remote args, clerk mcp run --url) still round-trip.
  • Agent mode throughout: non-TTY runs auto-emit JSON, never prompt, and every registration-blocking error carries a machine-readable code plus a docsUrl to the Clerk MCP setup docs (agent mode gets the .md variant) for the manual fallback.
  • Partial failure is structural: per-client failures land in a failures: [{ client, error }] array in install/uninstall/list JSON; the command exits non-zero only when every targeted client fails.
  • Auth (current limitation): the bridge is transport-only. A 401/403 on the initial handshake exits with a clear error; on later requests it's surfaced to the editor as a per-request JSON-RPC error without killing the bridge.

Test plan

  • bun run format:check, bun run lint, bun run typecheck — green
  • bun run test — 2095 tests green, including per-client argv contracts for all six CLI dialects, file shapes for the four file-backed clients, nested-topKey + YAML codec units, corrupt-config failure threading, Hermes verify-add, and Windows .cmd-shim spawning
  • Live verification (macOS): every CLI dialect verified against the real client CLI — claude/gemini/codex/code (duplicate-add/remove semantics), hermes (prompt + lying exit code discovered this way), openclaw (sandboxed OPENCLAW_STATE_DIR)
  • Real round-trip: uninstall --allinstall --all across all detected clients with zero failures; opencode's own CLI reports ✓ clerk connected and Hermes reports clerk ✓ enabled; bridge handshake against the hosted server returns Clerk MCP Server

@changeset-bot

changeset-botBot commented May 25, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e22734c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkMinor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented May 25, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds MCP server support to the Clerk CLI: new subcommands (clerk mcp install/list/uninstall), a JSON-backed client factory and five client integrations (Claude Code, Cursor, VS Code, Windsurf, Gemini), per-client config read/write helpers, collection of installed entries, an initialize-handshake probe supporting JSON and SSE with a 10s timeout, a doctor check (checkMcp), environment/profile mcpUrl wiring, CLI wiring, comprehensive tests, and documentation including a changeset and README updates.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 24.44% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check⚠️ WarningThe title matches the MCP install/list/uninstall and doctor work, but it incorrectly includes a run command that isn't in this changeset.Remove the unsupported "run" reference and keep the title focused on install/list/uninstall plus the doctor MCP check.
✅ Passed checks (3 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
Description check✅ PassedThe description is about the same MCP feature area and doctor check, so it is related despite several inaccurate details.

Comment @coderabbitai help to get the list of available commands.

@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 2 times, most recently from b4bc06c to 4f53e9fCompareMay 30, 2026 12:14

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/cli-core/src/commands/mcp/clients/user-scope.test.ts (1)

2-2: ⚡ Quick win

Use Bun file APIs here instead of node:fs read/write helpers.

These tests currently use readFile/writeFile from node:fs/promises, which goes against the repo’s Bun-specific file I/O guideline. Bun.file(...).json()/text() and Bun.write(...) would keep this aligned with the rest of the codebase. As per coding guidelines, **/*.{ts,tsx,js,jsx}: Prefer Bun.file over node:fs's readFile/writeFile for file operations.

Also applies to: 45-46, 64-66

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/cli-core/src/commands/mcp/clients/user-scope.test.ts` at line 2,
Replace node:fs/promises read/write helpers imported as mkdir, mkdtemp,
readFile, rm, writeFile in user-scope.test.ts with Bun file APIs: use
Bun.file(path).text()/json() for reads and Bun.write/Bun.writeSync (or
Bun.file(path).write(...)) for writes, and keep using mkdtemp/mkdir/rm
equivalents via Bun or the existing helpers if available; update all call sites
that use readFile/writeFile (and any promise-based usages) to the corresponding
Bun.file(...).text()/json() and Bun.write(...) patterns so the tests follow the
repo’s Bun-specific I/O guideline.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli-core/src/commands/mcp/clients/user-scope.test.ts`:
- Around line 7-12: The top-level module mock of node:os (mock.module("node:os",
...)) is never torn down causing cross-test leakage; add a cleanup via
afterAll(() => mock.restoreModule("node:os")) (or equivalent) to restore the
original module and avoid order-dependent failures, and update file I/O in this
test to use Bun APIs instead of node:fs/promises—replace readFile/writeFile
calls with Bun.file(path).json()/text() for reads and Bun.file(path).write(...)
or Bun.write(...) for writes so the test uses Bun's file operations correctly;
reference mock.module, mock.restoreModule, mockHome and the test file's existing
fs read/write usages when making the changes.
---
Nitpick comments:
In `@packages/cli-core/src/commands/mcp/clients/user-scope.test.ts`:
- Line 2: Replace node:fs/promises read/write helpers imported as mkdir,
mkdtemp, readFile, rm, writeFile in user-scope.test.ts with Bun file APIs: use
Bun.file(path).text()/json() for reads and Bun.write/Bun.writeSync (or
Bun.file(path).write(...)) for writes, and keep using mkdtemp/mkdir/rm
equivalents via Bun or the existing helpers if available; update all call sites
that use readFile/writeFile (and any promise-based usages) to the corresponding
Bun.file(...).text()/json() and Bun.write(...) patterns so the tests follow the
repo’s Bun-specific I/O guideline.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 33d9756c-11d6-436b-8465-7e750f438029

📥 Commits

Reviewing files that changed from the base of the PR and between 292e92f and 4f53e9f.

📒 Files selected for processing (34)
  • .changeset/mcp-install.md
  • README.md
  • packages/cli-core/src/cli-program.ts
  • packages/cli-core/src/commands/doctor/README.md
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/doctor/context.test.ts
  • packages/cli-core/src/commands/doctor/index.ts
  • packages/cli-core/src/commands/mcp/README.md
  • packages/cli-core/src/commands/mcp/clients/claude-code.ts
  • packages/cli-core/src/commands/mcp/clients/clients.test.ts
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.ts
  • packages/cli-core/src/commands/mcp/clients/paths.ts
  • packages/cli-core/src/commands/mcp/clients/registry.ts
  • packages/cli-core/src/commands/mcp/clients/types.ts
  • packages/cli-core/src/commands/mcp/clients/user-scope.test.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/commands/mcp/collect.ts
  • packages/cli-core/src/commands/mcp/index.ts
  • packages/cli-core/src/commands/mcp/install.test.ts
  • packages/cli-core/src/commands/mcp/install.ts
  • packages/cli-core/src/commands/mcp/list.test.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/mcp/probe.test.ts
  • packages/cli-core/src/commands/mcp/probe.ts
  • packages/cli-core/src/commands/mcp/shared.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts
  • packages/cli-core/src/commands/mcp/uninstall.ts
  • packages/cli-core/src/lib/environment.ts
  • packages/cli-core/src/lib/errors.ts
✅ Files skipped from review due to trivial changes (4)
  • .changeset/mcp-install.md
  • packages/cli-core/src/commands/doctor/README.md
  • README.md
  • packages/cli-core/src/commands/mcp/README.md
🚧 Files skipped from review as they are similar to previous changes (26)
  • packages/cli-core/src/commands/mcp/list.test.ts
  • packages/cli-core/src/commands/mcp/clients/registry.ts
  • packages/cli-core/src/commands/mcp/index.ts
  • packages/cli-core/src/commands/mcp/clients/claude-code.ts
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/commands/mcp/clients/types.ts
  • packages/cli-core/src/commands/mcp/probe.test.ts
  • packages/cli-core/src/lib/errors.ts
  • packages/cli-core/src/commands/doctor/index.ts
  • packages/cli-core/src/commands/mcp/clients/paths.ts
  • packages/cli-core/src/commands/mcp/install.ts
  • packages/cli-core/src/lib/environment.ts
  • packages/cli-core/src/commands/mcp/collect.ts
  • packages/cli-core/src/commands/mcp/uninstall.ts
  • packages/cli-core/src/commands/mcp/clients/clients.test.ts
  • packages/cli-core/src/commands/mcp/probe.ts
  • packages/cli-core/src/commands/mcp/install.test.ts
  • packages/cli-core/src/cli-program.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts

Comment threadpackages/cli-core/src/commands/mcp/clients/user-scope.test.ts Outdated
@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@2.0.1-snapshot.bdf8faf
PackageVersion
clerk2.0.1-snapshot.bdf8faf

Published from bdf8faf

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli-core/src/commands/mcp/clients/clients.test.ts`:
- Around line 65-96: The VS Code test is leaking to the real config because
vscodeUserDir() respects XDG_CONFIG_HOME/APPDATA; update the tests to isolate
that by setting XDG_CONFIG_HOME and APPDATA to a directory under mockHome (or
delete them) before running the VS Code-specific assertions and restore them
after; ensure the expectation for the VS Code path does not call vscodeUserDir()
(i.e. compute the expected path explicitly inside the test instead of reusing
expectedPath()/vscodeUserDir()) so the test verifies the function under test
rather than mirroring the same helper.
In `@packages/cli-core/src/commands/mcp/clients/paths.ts`:
- Around line 26-35: vscodeUserDir currently uses the nullish coalescing
operator (??) with process.env.APPDATA and process.env.XDG_CONFIG_HOME which
treats empty strings as set; update vscodeUserDir to treat empty or
whitespace-only env vars as unset by checking truthiness/trim (e.g. derive local
vars like appData = process.env.APPDATA?.trim() ? process.env.APPDATA :
undefined and xdg = process.env.XDG_CONFIG_HOME?.trim() ?
process.env.XDG_CONFIG_HOME : undefined) and then fall back to join(homedir(),
...) in the switch cases; reference the vscodeUserDir function and its uses of
process.env.APPDATA and process.env.XDG_CONFIG_HOME, and ensure platform(),
homedir(), and join() behavior is preserved.
In `@packages/cli-core/src/lib/environment.ts`:
- Around line 154-165: getMcpUrl currently lets an empty CLERK_MCP_URL ("") win
due to the nullish coalescing operator; treat empty/whitespace-only env values
as unset by trimming and checking truthiness before falling back. Update
getMcpUrl (function name: getMcpUrl) to compute const env =
process.env.CLERK_MCP_URL?.trim(); then return env && env.length ? env :
getCurrentEnv().mcpUrl ?? DEFAULT_MCP_URL so blank overrides no longer produce
an unusable empty string.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2826916f-5886-46c5-96c7-da4b8e7b1a92

📥 Commits

Reviewing files that changed from the base of the PR and between bdf8faf and d80abcd.

📒 Files selected for processing (23)
  • .changeset/mcp-install.md
  • packages/cli-core/src/cli-program.ts
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/mcp/README.md
  • packages/cli-core/src/commands/mcp/clients/claude-code.ts
  • packages/cli-core/src/commands/mcp/clients/clients.test.ts
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.ts
  • packages/cli-core/src/commands/mcp/clients/paths.ts
  • packages/cli-core/src/commands/mcp/clients/user-scope.test.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/commands/mcp/install.test.ts
  • packages/cli-core/src/commands/mcp/install.ts
  • packages/cli-core/src/commands/mcp/list.test.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/mcp/shared.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts
  • packages/cli-core/src/lib/environment.ts
  • packages/cli-core/src/lib/errors.ts
💤 Files with no reviewable changes (3)
  • packages/cli-core/src/commands/mcp/clients/gemini.ts
  • packages/cli-core/src/commands/mcp/clients/windsurf.ts
  • packages/cli-core/src/cli-program.ts
✅ Files skipped from review due to trivial changes (2)
  • .changeset/mcp-install.md
  • packages/cli-core/src/commands/mcp/README.md
🚧 Files skipped from review as they are similar to previous changes (12)
  • packages/cli-core/src/commands/mcp/clients/cursor.ts
  • packages/cli-core/src/lib/errors.ts
  • packages/cli-core/src/commands/mcp/clients/vscode.ts
  • packages/cli-core/src/commands/mcp/list.ts
  • packages/cli-core/src/commands/doctor/check-mcp.ts
  • packages/cli-core/src/commands/mcp/uninstall.test.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.test.ts
  • packages/cli-core/src/commands/mcp/clients/user-scope.test.ts
  • packages/cli-core/src/commands/mcp/shared.ts
  • packages/cli-core/src/commands/mcp/clients/json-config.ts
  • packages/cli-core/src/commands/mcp/clients/make-json-client.ts
  • packages/cli-core/src/commands/mcp/install.test.ts

Comment threadpackages/cli-core/src/commands/mcp/clients/clients.test.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/paths.ts Outdated
Comment threadpackages/cli-core/src/lib/environment.ts
@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@2.0.1-snapshot.d637eb0
PackageVersion
clerk2.0.1-snapshot.d637eb0

Published from d637eb0

@wyattjoh
wyattjoh self-requested a review June 12, 2026 16:26
@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 2 times, most recently from 19b2820 to 0d5281dCompareJune 15, 2026 12:22

@wyattjohwyattjoh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid, well-tested feature surface. The error handling, URL hardening, and per-client config abstraction are all carefully done. A few non-blocking observations below, mostly around concurrency edge cases in the stdio bridge and the shared ~/.claude.json write path.

Comment threadpackages/cli-core/src/commands/mcp/clients/claude.ts
Comment threadpackages/cli-core/src/commands/mcp/run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/run.ts
Comment threadpackages/cli-core/src/lib/input-json.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/toml-config.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/shared.ts
Comment threadpackages/cli-core/src/commands/mcp/install.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.test.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.test.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/gemini.ts Outdated
@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 3 times, most recently from 161868b to c13e55aCompareJune 27, 2026 12:17

@wyattjohwyattjoh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strong, well-tested PR. The main cleanup is leftover --url surface from the "drop --url from public CLI" commit (a help example that now errors, an unactionable error message, the README, and the changeset), plus a couple of narrow correctness edge cases noted inline.

Comment threadpackages/cli-core/src/commands/mcp/index.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/README.md Outdated
Comment thread.changeset/mcp-install.md Outdated
Comment threadpackages/cli-core/src/commands/mcp/run.ts
Comment threadpackages/cli-core/src/commands/mcp/clients/json-config.ts
Comment threadpackages/cli-core/src/commands/mcp/install.ts
Comment threadpackages/cli-core/src/commands/mcp/run.ts
Comment threadpackages/cli-core/src/commands/mcp/shared.ts
@rafa-thayto
rafa-thaytoforce-pushed the add-mcp-server-cli branch 4 times, most recently from c70a7f1 to 89d5811CompareJuly 15, 2026 12:16
@rafa-thaytorafa-thayto changed the title feat(mcp): add clerk mcp install/list/uninstall + doctor MCP checkfeat(mcp): clerk mcp install/list/uninstall/run across 10 clients + doctor MCP checkJul 21, 2026
@rafa-thaytorafa-thayto changed the title feat(mcp): clerk mcp install/list/uninstall/run across 10 clients + doctor MCP checkfeat(mcp): clerk mcp install/list/uninstall/run + doctor MCP checkJul 21, 2026
… + doctor MCP check
Registers the Clerk remote MCP server (https://mcp.clerk.com/mcp) in AI
editors and agent CLIs, with an MCP reachability check in `clerk doctor`.
Supported clients (all user-global): Claude Code, Cursor, GitHub Copilot /
VS Code (`--client vscode` or `copilot`), Windsurf, Gemini, Codex, opencode,
OpenClaw, Warp, and Hermes Agent.
Registration is delegated to each client's own CLI where a non-interactive
one exists (`claude`/`gemini`/`codex`/`openclaw`/`hermes` `mcp add`,
`code --add-mcp`), with PATH-based detection and no file-write fallback;
Cursor, Windsurf, Warp, and opencode (interactive-only CLI) get atomic
file writes. Install always converges via remove-then-add. Client CLIs are
spawned with stdin closed and a 15s timeout; Windows `.cmd` shims run
through `cmd.exe /c`; Hermes gets its confirm prompt answered via stdin and
a post-add verification because its cancel path exits 0 without saving.
Every client stores the same URL-less bridge descriptor
`{command: "clerk", args: ["mcp", "run"]}`. `clerk mcp run` is a built-in
stdio<->Streamable-HTTP bridge (replaces npx mcp-remote) that resolves its
target at spawn time (CLERK_MCP_URL > env profile mcpUrl > hosted default),
serializes stdout frames, caps both stdin lines and upstream SSE/JSON
bodies at 16 MiB, and surfaces pre-session 401/403 as a clear error while
answering in-session auth failures per-request.
Reads stay file-based (JSON/TOML/YAML codecs; TOML and YAML are read-only
since those clients' CLIs own both mutations). `--name` is allowlisted
([A-Za-z0-9_-], no leading dash) because it is spliced into client CLI argv
and used as a config key. `list --json` reports { entries, failures } so an
unreadable config surfaces structurally; install/uninstall carry the same
failures array and exit non-zero only when every targeted client fails.
Agent mode never prompts and always emits JSON with machine-readable error
codes plus a docs URL (clerk.com/docs/guides/ai/mcp/clerk-mcp-server).
Verified live against the real client CLIs on macOS: full uninstall/install
round-trips with zero failures; opencode reports "clerk connected" and
Hermes "clerk enabled" through their own CLIs; OpenClaw's dialect verified
in a sandboxed OPENCLAW_STATE_DIR; bridge handshake against the hosted
server returns "Clerk MCP Server".
Comment threadpackages/cli-core/src/commands/doctor/check-mcp.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/clerk-run.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/make-cli-client.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/make-client.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/probe.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/make-client.ts Outdated
Comment threadpackages/cli-core/src/commands/mcp/clients/toml-config.ts
Comment threadpackages/cli-core/src/commands/mcp/probe.ts
Comment threadpackages/cli-core/src/commands/doctor/check-mcp.ts
Comment threadpackages/cli-core/src/commands/mcp/clients/json-config.ts
@rafa-thayto
rafa-thayto merged commit 8bf74ab into mainJul 22, 2026
9 of 10 checks passed
@rafa-thayto
rafa-thayto deleted the add-mcp-server-cli branch July 22, 2026 14:37
@github-actionsgithub-actionsBot mentioned this pull request Jul 22, 2026
Sign up for freeto 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.

2 participants

@rafa-thayto@wyattjoh