Skip to content

[AI-699] Auto-register kcap MCP servers for Cursor - #296

Merged
alexeyzimarev merged 9 commits into
mainfrom
tonyyoung/ai-699-cursor-mcp
Jul 8, 2026
Merged

alexeyzimarev merged 9 commits into
mainfrom
tonyyoung/ai-699-cursor-mcp

Conversation

@realtonyyoung

Copy link
Copy Markdown
Collaborator

First per-harness rollout of the MCP auto-config epic (AI-1224) — wires the merged config-writer foundation (AI-1225) into the Cursor install path.

What

  • kcap setup and kcap plugin install --cursor now auto-register the 4 kcap MCP servers into ~/.cursor/mcp.json (Standard shape: mcpServers map, command: "kcap" + args, no cwd — it's a user-global config). Uses JsonMcpConfigWriter + KcapMcpServers.All (Cursor is Claude-capable → includes kcap-flows) + McpConfigShape.Standard + McpMarker("cursor").
  • Non-destructive (preserves user-authored servers), idempotent, atomic; kcap plugin remove --cursor / kcap uninstall unregister the kcap entries + clear the sidecar marker.
  • New --skip-cursor-mcp flag; wired through CodingAgentsStep (delegate + result flag + HandleCursorMcp), SetupCommand, PluginCommand (InstallCursor/RemoveCursor), UninstallCommand. Mirrors the existing Codex MCP-registration wiring.
  • Docs: --skip-cursor-mcp added to help text; README's MCP auto-registration section now lists Cursor.

Tests

  • Unit: CodingAgentsStep (Cursor MCP registered / not-when-hooks-fail / skipped / failure-warning), PluginCommandCursor (install writes mcp.json + preserves a user server, --if-installed skips, remove unregisters), CursorPaths, UninstallCommand marker-clear. Full suite 2641/2641, build clean (0 warnings, AOT-safe).
  • Live E2E (dev build vs real Cursor): config write/idempotent/non-destructive/remove all verified against a real ~/.cursor; stdio handshake confirmed AI-1233 behavior (version negotiation, resources/list/prompts/list/ping answered); and a real search_sessions call ("have we worked on ACP before") executed through Cursor and returned results.

Part of AI-1224. Depends on the merged foundations AI-1225 (#282) + AI-1233 (#284).

🤖 Generated with Claude Code

…son)

Wires the merged JSON MCP foundation (JsonMcpConfigWriter + McpConfigShape.Standard
+ KcapMcpServers.All + McpMarker) into the Cursor install path — the first harness
to use it. `kcap setup` and `kcap plugin install --cursor` now register all 4 kcap
MCP servers into ~/.cursor/mcp.json non-destructively (idempotent, preserves
user-authored servers); `--skip-cursor-mcp` opts out. `kcap plugin remove --cursor`
and `kcap uninstall` unregister them and clear the ownership marker sidecar.
@linear-code

linear-code Bot commented Jul 8, 2026

Copy link
Copy Markdown

AI-699

AI-1224

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Auto-register kcap MCP servers for Cursor (~/.cursor/mcp.json)

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Auto-register 4 kcap MCP servers in Cursor’s ~/.cursor/mcp.json during setup/install.
• Add --skip-cursor-mcp plus uninstall/remove cleanup via sidecar ownership marker.
• Expand unit tests and docs to cover Cursor MCP registration and opt-out behavior.
Diagram

graph TD
  A["kcap setup / plugin --cursor"] --> B["CodingAgentsStep / PluginCommand"] --> C["JsonMcpConfigWriter"] --> D[("~/.cursor/mcp.json")]
  E["kcap plugin remove --cursor"] --> B --> F["Unregister + clear marker"] --> D
  G["kcap uninstall"] --> H["UninstallCommand"] --> F
  I["--skip-cursor-mcp"] --> B
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Register Cursor MCP only when Cursor hooks are enabled (current)
  • ➕ Keeps “Cursor integration” as a single opt-in surface area
  • ➕ Avoids touching user config unless the user opted into Cursor support
  • ➕ Matches existing Codex MCP wiring pattern and warning-only failure behavior
  • ➖ Users who want MCP but not hooks can’t get that via setup defaults
2. Separate prompts/flags for Cursor hooks vs Cursor MCP registration
  • ➕ Lets users enable MCP servers without enabling hooks
  • ➕ More explicit consent for writing ~/.cursor/mcp.json
  • ➖ Adds prompt/flag complexity and more branching in setup UX
  • ➖ Harder to maintain parity with other harness installers
3. Use Cursor-native plugin/extension mechanism (no direct file writes)
  • ➕ Avoids direct manipulation of user config files
  • ➕ Potentially more future-proof if Cursor changes config formats
  • ➖ May not exist or may be less reliable/portable than mcp.json today
  • ➖ More engineering effort and likely a different distribution model

Recommendation: Keep the current approach: writing ~/.cursor/mcp.json in a non-destructive, idempotent way with a sidecar ownership marker and a --skip-cursor-mcp escape hatch is the best tradeoff of UX simplicity and safety. If demand emerges for “MCP without hooks”, consider adding an explicit advanced flag later rather than complicating the default setup flow now.

Files changed (13) +360 / -34

Enhancement (6) +117 / -22
CursorPaths.csAdd CursorPaths.UserMcpJson() for ~/.cursor/mcp.json +6/-0

Add CursorPaths.UserMcpJson() for ~/.cursor/mcp.json

• Introduces a helper to compute the user-scope Cursor MCP config path consistently across OSes. Used by CLI commands to read/write Cursor’s MCP configuration.

src/Capacitor.Cli.Core/Cursor/CursorPaths.cs

CodingAgentsStep.csWire Cursor MCP registration into setup flow +46/-5

Wire Cursor MCP registration into setup flow

• Adds SkipCursorMcp option, CursorMcpPath, a RegisterCursorMcp installer delegate, and a CursorMcpRegistered result flag. Implements HandleCursorMcp to register MCP servers only when Cursor hooks install succeeded and opt-out is not set, emitting success/unchanged/warning messages.

src/Capacitor.Cli/Commands/CodingAgentsStep.cs

PluginCommand.csRegister/unregister Cursor MCP servers on plugin install/remove +53/-14

Register/unregister Cursor MCP servers on plugin install/remove

• On --cursor install, registers all kcap MCP servers into ~/.cursor/mcp.json unless --skip-cursor-mcp is provided, treating write failures as warnings. On --cursor remove, unregisters owned kcap entries from mcp.json independently of hooks.json and returns failure if either operation fails.

src/Capacitor.Cli/Commands/PluginCommand.cs

PluginEnvironment.csAdd CursorMcpJson path to PluginEnvironment +1/-0

Add CursorMcpJson path to PluginEnvironment

• Exposes env.CursorMcpJson computed via CursorPaths so PluginCommand can target the correct user home (including tests using a fake home).

src/Capacitor.Cli/Commands/PluginEnvironment.cs

SetupCommand.csAdd --skip-cursor-mcp and call JsonMcpConfigWriter during setup +9/-3

Add --skip-cursor-mcp and call JsonMcpConfigWriter during setup

• Parses --skip-cursor-mcp, passes it into CodingAgentsStep.Options, and provides CodingAgentsStep with the Cursor MCP path and a delegate that registers servers using the standard JSON shape and a Cursor marker.

src/Capacitor.Cli/Commands/SetupCommand.cs

UninstallCommand.csClear Cursor MCP ownership marker on uninstall +2/-0

Clear Cursor MCP ownership marker on uninstall

• Adds a belt-and-braces cleanup step that clears the Cursor McpMarker for ~/.cursor/mcp.json during uninstall, preventing marker leakage after manual edits.

src/Capacitor.Cli/Commands/UninstallCommand.cs

Tests (4) +222 / -1
CodingAgentsStepTests.csAdd CodingAgentsStep coverage for Cursor MCP registration +75/-1

Add CodingAgentsStep coverage for Cursor MCP registration

• Adds tests validating Cursor MCP registration triggers only after hooks install, can be skipped via flag, and emits warnings on failure. Extends test paths/installers to include Cursor MCP path and delegate behavior.

test/Capacitor.Cli.Tests.Unit/CodingAgentsStepTests.cs

CursorPathsTests.csTest CursorPaths.UserMcpJson() path construction +5/-0

Test CursorPaths.UserMcpJson() path construction

• Adds a unit test ensuring ~/.cursor/mcp.json is computed under the provided home directory.

test/Capacitor.Cli.Tests.Unit/Cursor/CursorPathsTests.cs

PluginCommandCursorTests.csAdd install/remove Cursor MCP integration tests +116/-0

Add install/remove Cursor MCP integration tests

• Adds tests that plugin install registers MCP servers while preserving user entries, that --if-installed short-circuits without writing mcp.json when not opted in, that --skip-cursor-mcp avoids writes, and that plugin remove unregisters kcap servers while preserving user servers. Introduces a test PluginEnvironment rooted at a temp home.

test/Capacitor.Cli.Tests.Unit/PluginCommandCursorTests.cs

UninstallCommandTests.csEnsure uninstall clears Cursor MCP marker even if JSON is hand-edited +26/-0

Ensure uninstall clears Cursor MCP marker even if JSON is hand-edited

• Adds a regression test proving uninstall clears the Cursor McpMarker even when mcp.json no longer contains kcap entries (simulating manual edits).

test/Capacitor.Cli.Tests.Unit/UninstallCommandTests.cs

Documentation (3) +21 / -11
README.mdDocument Cursor MCP auto-registration and opt-out flag +10/-9

Document Cursor MCP auto-registration and opt-out flag

• Updates MCP server docs to include Cursor alongside Claude Code and Codex. Adds explicit mention of ~/.cursor/mcp.json registration and the --skip-cursor-mcp opt-out in relevant sections.

README.md

help-plugin.txtExpose --skip-cursor-mcp and describe Cursor MCP behavior +9/-2

Expose --skip-cursor-mcp and describe Cursor MCP behavior

• Adds CLI help text for --skip-cursor-mcp and clarifies that --cursor installs hooks.json and registers MCP servers in mcp.json by default.

src/Capacitor.Cli.Core/Resources/help-plugin.txt

help-setup.txtAdd --skip-cursor-mcp to setup help +2/-0

Add --skip-cursor-mcp to setup help

• Extends setup command help output to include the new Cursor MCP opt-out flag and its target path.

src/Capacitor.Cli.Core/Resources/help-setup.txt

@realtonyyoung
realtonyyoung removed the request for review from alexeyzimarev July 8, 2026 14:01
@qodo-code-review

qodo-code-review Bot commented Jul 8, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Linear ID in test comment ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
Newly added test comments reference the Linear identifier AI-699, which is disallowed in code
comments and can introduce non-public/non-canonical references in the repo.
Code

test/Capacitor.Cli.Tests.Unit/PluginCommandCursorTests.cs[R37-44]

+    // AI-699: `plugin install/remove --cursor` also (un)registers the kcap MCP
+    // servers in ~/.cursor/mcp.json. These use an explicit PluginEnvironment
+    // (not PluginEnvironment.FromProcess()) so env.CursorMcpJson resolves under
+    // a temp home instead of the real machine's ~/.cursor — mirrors
+    // PluginCommandCodexInstallIntegrationTests.TestEnv. The `--if-installed`
+    // refresh branch is used (pre-marker hooks.json seeded) rather than a bare
+    // `install --cursor`, so the AgentDetector "kcap on PATH" precheck (which
+    // only runs on the non-refresh path) never comes into play.
Evidence
PR Compliance ID 4 forbids including Linear issue identifiers in code comments. The added comment
blocks in test/Capacitor.Cli.Tests.Unit/PluginCommandCursorTests.cs and
test/Capacitor.Cli.Tests.Unit/UninstallCommandTests.cs explicitly contain the Linear ID AI-699,
directly violating that requirement.

CLAUDE.md: Keep code comments minimal; prefer self-explanatory code and avoid Linear issue numbers in comments
test/Capacitor.Cli.Tests.Unit/PluginCommandCursorTests.cs[37-44]
test/Capacitor.Cli.Tests.Unit/UninstallCommandTests.cs[445-449]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Newly-added comments in unit tests reference a Linear issue ID (`AI-699`), which violates the compliance requirement (PR Compliance ID 4) to avoid Linear identifiers in code comments.

## Issue Context
Per the checklist, comments should not include Linear identifiers because they can create non-public/non-canonical references in the repository. Prefer removing the issue reference entirely, or if a reference is truly necessary, use a GitHub issue number (e.g., `#123`).

## Fix Focus Areas
- test/Capacitor.Cli.Tests.Unit/PluginCommandCursorTests.cs[37-44]
- test/Capacitor.Cli.Tests.Unit/UninstallCommandTests.cs[445-449]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Cursor MCP marker not cleared ✓ Resolved 🐞 Bug ☼ Reliability
Description
PluginCommand.RemoveCursor relies on JsonMcpConfigWriter.Unregister to clear the Cursor MCP
sidecar marker, but Unregister only clears the marker when it actually removed JSON entries. If
the user manually deleted the kcap entries from ~/.cursor/mcp.json (leaving the marker), `kcap
plugin remove --cursor` will not clear the marker, leaving stale ownership state behind.
Code

src/Capacitor.Cli/Commands/PluginCommand.cs[R753-754]

+        var mcpChange = JsonMcpConfigWriter.Unregister(env.CursorMcpJson, McpConfigShape.Standard, new McpMarker("cursor"));
+        var mcpFailed = mcpChange == JsonMcpConfigWriter.Change.Failed;
Evidence
RemoveCursor calls Unregister but never clears the marker itself, while Unregister only clears
the marker when it actually removes entries. Therefore, if no JSON changes are needed (e.g., kcap
entries already gone), the marker will remain.

src/Capacitor.Cli/Commands/PluginCommand.cs[728-763]
src/Capacitor.Cli.Core/Mcp/JsonMcpConfigWriter.cs[44-56]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`kcap plugin remove --cursor` should clear the Cursor MCP sidecar marker even when there are no kcap MCP entries left to remove (e.g., the user hand-edited `~/.cursor/mcp.json`). Today, `PluginCommand.RemoveCursor` calls `JsonMcpConfigWriter.Unregister(...)`, but `Unregister` only clears the marker when it actually mutates the JSON file.

### Issue Context
- `PluginCommand.RemoveCursor` calls `JsonMcpConfigWriter.Unregister(env.CursorMcpJson, ..., new McpMarker("cursor"))` and then returns based on `mcpChange`.
- `JsonMcpConfigWriter.Unregister` calls `marker.Clear(configPath)` only when `changed` is true.

### Fix Focus Areas
- src/Capacitor.Cli/Commands/PluginCommand.cs[728-763]
- src/Capacitor.Cli.Core/Mcp/JsonMcpConfigWriter.cs[44-56]

### Suggested fix
In `RemoveCursor`, after calling `Unregister`, if `mcpChange != JsonMcpConfigWriter.Change.Failed`, explicitly clear the marker (best-effort) to ensure cleanup even when `Unregister` returns `Unchanged`.

### Add/adjust tests
Add a unit test in `PluginCommandCursorTests` that:
1) Creates `~/.cursor/mcp.json` with `{ "mcpServers": {} }`.
2) Creates a Cursor `McpMarker("cursor")` record for at least one server name.
3) Runs `plugin remove --cursor`.
4) Asserts the marker no longer reports any owned servers (marker file deleted/cleared).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread test/Capacitor.Cli.Tests.Unit/PluginCommandCursorTests.cs Outdated
Comment thread src/Capacitor.Cli/Commands/PluginCommand.cs
… from test comments

JsonMcpConfigWriter.Unregister only cleared the sidecar ownership
marker when it removed JSON entries, orphaning the marker when a user
hand-deleted the kcap entries. Clear the marker whenever unregister
runs (any harness), skipping only a hard Failed. Also drops AI-699
references from new test comments per the no-Linear-ids-in-comments
rule.
Comment thread src/Capacitor.Cli/Commands/UninstallCommand.cs Outdated
realtonyyoung and others added 2 commits July 8, 2026 13:36
…ailed

uninstall's belt-and-braces marker sweep unconditionally cleared the Cursor
MCP ownership marker, defeating the retry-safety that JsonMcpConfigWriter.
Unregister deliberately provides: it retains the marker on Failed so a later
retry (after the user fixes/permits the file) can still identify the kcap-*
entries as kcap-owned. Clearing it here orphaned those entries — the retry
preserved them as user-authored.

`plugin remove --cursor` already owns the marker cleanup via Unregister,
which clears it on any non-Failed outcome (including hand-pruned entries) and
retains it on Failed. Drop the sweep line (+ its now-unused using) and let
RemoveCursor own it. Add failure-path (uninstall keeps marker) + retry
(remove → fail → fix → remove clears) regression tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…mment

Rephrase "Regression (AI-699 self-review)" to "Regression (self-review)"
to keep the newly-added test comment free of Linear identifiers (matches
the ef5425d cleanup; PR-compliance / CLAUDE.md convention).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread test/Capacitor.Cli.Tests.Unit/PluginCommandCursorTests.cs
…ys under temp

PluginCommandCursorTests redirected PluginEnvironment.HomeDirectory (via
TestEnv) but not the process HOME. McpMarker resolves its storage from
Environment.GetFolderPath(UserProfile) (→ $HOME on Unix), not from
PluginEnvironment.HomeDirectory, so on Unix a temp-dir config was treated as
non-user-scope and ownership markers leaked into the real ~/.kcap/mcp-markers
(the install test never unregisters, leaving files in the dev/CI home).

TempDir now pins HOME to itself for its lifetime and restores on Dispose, so
all marker state — for both the test's direct McpMarker calls and the
production plugin --cursor path — stays under the temp home and is cleaned up.
Safe under the existing [NotInParallel("HomeEnvVarMutation")]. Verified: the
run adds no files to the real ~/.kcap/mcp-markers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread test/Capacitor.Cli.Tests.Unit/PluginCommandCursorTests.cs Outdated
…a sidecar

The prior fix pinned $HOME, but McpMarker resolves user-scope via
Environment.GetFolderPath(UserProfile), which ignores $HOME on Windows — and
Path.GetTempPath() may sit outside the profile (e.g. TEMP=D:\Temp), so the
ownership marker could still fall back to the real ~/.kcap/mcp-markers there
(the install test never unregisters, leaving a file behind).

Root the fake home under Environment.GetFolderPath(UserProfile) instead. The
config is then always user-scope, so McpMarker writes its marker as a sidecar
under the test dir on every OS — covering both the test's direct McpMarker
calls and the production plugin --cursor path — and it's removed with the dir
on Dispose. Drops the now-unnecessary HOME mutation. Verified on Unix: a full
PluginCommandCursorTests run adds zero files to the real ~/.kcap/mcp-markers
and leaves no test dirs under the home.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread test/Capacitor.Cli.Tests.Unit/PluginCommandCursorTests.cs Outdated
… home)

Rooting the fake home under UserProfile makes the marker a contained sidecar
in the common case, but if the real profile is itself a git repo (~/.git,
e.g. tracked dotfiles), McpMarker.IsInsideRepo walks up to it and classifies
the config as non-user-scope — so the production plugin --cursor path writes
the marker centrally under the real ~/.kcap/mcp-markers, which the install
test (no unregister) would leave behind.

Stop depending on the user-scope classification for cleanup: TempDir.Dispose
now explicitly clears the marker for its cursor config. McpMarker.Clear
resolves the exact same path the production code used to write it (sidecar or
central), so the marker can't persist past the test on any OS or repo layout.
Verified on Unix (non-repo home): run adds zero central markers, no leftover
home dirs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@realtonyyoung

Copy link
Copy Markdown
Collaborator Author

NO FINDINGS

@realtonyyoung

Copy link
Copy Markdown
Collaborator Author

@alexeyzimarev — ready for your review (you're already the requested reviewer). The automated review cycle is complete on e789ce5.

What it does: first per-harness consumer of the merged MCP config-writer foundation — wires kcap MCP auto-registration into the Cursor install path so kcap setup and kcap plugin install/remove --cursor write/remove the 4 kcap servers in ~/.cursor/mcp.json (mirrors the Codex MCP wiring), with a --skip-cursor-mcp opt-out.

Review status: Qodo findings resolved; a full Codex review returned NO FINDINGS; all review threads resolved; CI green (AOT + ubuntu/windows build+test).

Notable fixes made during review:

  • JsonMcpConfigWriter.Unregister clears the ownership marker on any non-Failed outcome but RETAINS it on Failed, so a retry can still identify the kcap-owned entries.
  • uninstall no longer over-clears the Cursor MCP marker (cleanup rides on plugin remove --cursor).
  • Test isolation hardened so the ownership marker never leaks into the real ~/.kcap/mcp-markers (cleared in test teardown regardless of OS/home layout).

Thanks!

… to it

The per-harness plugin test suites all need the same MCP-marker isolation
(don't leak an ownership marker into the real ~/.kcap/mcp-markers). Rather
than re-derive it in each new harness suite, extract it once as a reusable
FakeUserHome:

- Roots the fake home under Environment.GetFolderPath(UserProfile) so the
  common case is a contained sidecar on every OS.
- On Dispose, deletes the home AND sweeps ~/.kcap/mcp-markers for any central
  marker whose config points under it — covering the edge where the real
  profile is itself a git repo (McpMarker.IsInsideRepo → non-user-scope →
  central marker). Harness-agnostic: no per-suite config knowledge needed.

Replaces PluginCommandCursorTests' inline TempDir. Subsequent per-harness
suites (Copilot, Gemini, …) reuse FakeUserHome directly. Full unit suite
2644/2644; PluginCommandCursorTests adds zero files to the real
~/.kcap/mcp-markers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@realtonyyoung

Copy link
Copy Markdown
Collaborator Author

NO FINDINGS

@alexeyzimarev
alexeyzimarev merged commit 0d9c273 into main Jul 8, 2026
5 checks passed
@alexeyzimarev
alexeyzimarev deleted the tonyyoung/ai-699-cursor-mcp branch July 8, 2026 19:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants