From 5c7641a120e6551c8a56c67328bd4732876ac88a Mon Sep 17 00:00:00 2001 From: Sebastien Tardif Date: Fri, 28 Aug 2026 08:11:18 -0700 Subject: [PATCH] fix: write Cursor MCP config under mcpServers Cursor reads .cursor/mcp.json as mcpServers. Configure MCP wrote the VS Code servers key, so Cursor ignored Patchloom while the status bar showed configured. Keep inspecting legacy servers.patchloom. Signed-off-by: Sebastien Tardif --- AGENTS.md | 4 ++-- src/mcp/config.ts | 20 +++++++++++++++++--- test/unit/mcpConfig.test.ts | 22 +++++++++++++++++++++- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 0630b8a..e257f15 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -55,7 +55,7 @@ test/ binaryDiscovery.test.ts Real executable discovery on PATH (13 tests) initializeProject.test.ts Status display, agents file classification, formatError (69 tests) managedLifecycle.test.ts Managed install with real file I/O (26 tests) - mcpConfig.test.ts MCP config with real temp directories (14 tests) + mcpConfig.test.ts MCP config with real temp directories (15 tests) managedInstall.test.ts Managed Update compares latest vs managed binary (10 tests) mcpRegister.test.ts Native MCP definition helper for binary path (6 tests) statusRefresh.test.ts Status and MCP refresh order after input change (1 test) @@ -130,7 +130,7 @@ All I/O-dependent functions accept an `inputs` object with injectable callbacks | Target | Config file | Key | |--------|------------|-----| | VS Code workspace | `.vscode/mcp.json` | `servers` | -| Cursor workspace | `.cursor/mcp.json` | `servers` | +| Cursor workspace | `.cursor/mcp.json` | `mcpServers` | | Windsurf user | `~/.codeium/windsurf/mcp_config.json` | `mcpServers` | ## Coding conventions diff --git a/src/mcp/config.ts b/src/mcp/config.ts index fa05f1a..6113230 100644 --- a/src/mcp/config.ts +++ b/src/mcp/config.ts @@ -144,6 +144,10 @@ export function buildPatchloomMcpEntry( return entry; } +function usesMcpServersKey(kind: McpTargetKind): boolean { + return kind === "windsurf-user" || kind === "cursor-workspace"; +} + function withPatchloomEntry( kind: McpTargetKind, config: Record, @@ -151,7 +155,7 @@ function withPatchloomEntry( mcpSurface: McpSurface = "full" ): Record { const entry = buildPatchloomMcpEntry(commandPath, mcpSurface); - if (kind === "windsurf-user") { + if (usesMcpServersKey(kind)) { const servers = objectValue(config.mcpServers); return { ...config, @@ -173,8 +177,18 @@ function withPatchloomEntry( } function hasPatchloomEntry(kind: McpTargetKind, config: Record): boolean { - const key = kind === "windsurf-user" ? "mcpServers" : "servers"; - const root = objectValue(config[key]); + if (usesMcpServersKey(kind)) { + const modern = objectValue(config.mcpServers); + if (typeof modern.patchloom === "object" && modern.patchloom !== null) { + return true; + } + if (kind === "cursor-workspace") { + const legacy = objectValue(config.servers); + return typeof legacy.patchloom === "object" && legacy.patchloom !== null; + } + return false; + } + const root = objectValue(config.servers); return typeof root.patchloom === "object" && root.patchloom !== null; } diff --git a/test/unit/mcpConfig.test.ts b/test/unit/mcpConfig.test.ts index 4d15fc6..0db0c36 100644 --- a/test/unit/mcpConfig.test.ts +++ b/test/unit/mcpConfig.test.ts @@ -157,6 +157,26 @@ test("configureMcpTargets preserves existing servers in the config file", async }); }); +test("configureMcpTargets writes Cursor config with mcpServers key", async () => { + await withTempDir(async (workspace) => { + await configureMcpTargets({ + workspaceFolderPath: workspace, + homeDir: workspace, + includeKinds: ["cursor-workspace"], + patchloomPathSetting: "patchloom", + writeFile: async (filePath, content) => { + await fs.mkdir(path.dirname(filePath), { recursive: true }); + await fs.writeFile(filePath, content, "utf8"); + } + }); + + const written = await readJson(path.join(workspace, ".cursor", "mcp.json")); + assert.equal(written.servers, undefined, "Cursor does not read the VS Code servers key"); + const servers = written.mcpServers as Record; + assert.ok(servers.patchloom); + }); +}); + test("configureMcpTargets creates both vscode and cursor configs", async () => { await withTempDir(async (workspace) => { const results = await configureMcpTargets({ @@ -176,7 +196,7 @@ test("configureMcpTargets creates both vscode and cursor configs", async () => { const vscodeConfig = await readJson(path.join(workspace, ".vscode", "mcp.json")); const cursorConfig = await readJson(path.join(workspace, ".cursor", "mcp.json")); assert.ok((vscodeConfig.servers as Record).patchloom); - assert.ok((cursorConfig.servers as Record).patchloom); + assert.ok((cursorConfig.mcpServers as Record).patchloom); }); });