Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
20 changes: 17 additions & 3 deletions src/mcp/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,14 +144,18 @@ export function buildPatchloomMcpEntry(
return entry;
}

function usesMcpServersKey(kind: McpTargetKind): boolean {
return kind === "windsurf-user" || kind === "cursor-workspace";
}

function withPatchloomEntry(
kind: McpTargetKind,
config: Record<string, unknown>,
commandPath: string,
mcpSurface: McpSurface = "full"
): Record<string, unknown> {
const entry = buildPatchloomMcpEntry(commandPath, mcpSurface);
if (kind === "windsurf-user") {
if (usesMcpServersKey(kind)) {
const servers = objectValue(config.mcpServers);
return {
...config,
Expand All @@ -173,8 +177,18 @@ function withPatchloomEntry(
}

function hasPatchloomEntry(kind: McpTargetKind, config: Record<string, unknown>): 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;
}

Expand Down
22 changes: 21 additions & 1 deletion test/unit/mcpConfig.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, unknown>;
assert.ok(servers.patchloom);
});
});

test("configureMcpTargets creates both vscode and cursor configs", async () => {
await withTempDir(async (workspace) => {
const results = await configureMcpTargets({
Expand All @@ -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<string, unknown>).patchloom);
assert.ok((cursorConfig.servers as Record<string, unknown>).patchloom);
assert.ok((cursorConfig.mcpServers as Record<string, unknown>).patchloom);
});
});

Expand Down
Loading