From 3b13bb7f8320117a910e570d3c1784210aa34625 Mon Sep 17 00:00:00 2001 From: Sushanth012 Date: Sun, 21 Jun 2026 17:48:11 -0700 Subject: [PATCH] fix: avoid overwriting invalid MCP JSON configs --- src/cli/connect/json-mcp-adapter.ts | 10 +++++- test/connect-new-agents.test.ts | 55 ++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/cli/connect/json-mcp-adapter.ts b/src/cli/connect/json-mcp-adapter.ts index 35998e0dc..10cc8b3a8 100644 --- a/src/cli/connect/json-mcp-adapter.ts +++ b/src/cli/connect/json-mcp-adapter.ts @@ -60,7 +60,15 @@ export function createJsonMcpAdapter( }, async install(opts: ConnectOptions): Promise { + const configExists = existsSync(config.configPath); const existing = readJsonSafe(config.configPath); + if (configExists && existing === null) { + p.log.error( + `Could not parse ${config.configPath}; leaving it unchanged. Fix the JSON and retry.`, + ); + return { kind: "skipped", reason: "invalid-json" }; + } + const next: McpConfig = existing ? { ...existing } : {}; const servers: Record = { ...((next[wrapperKey] as Record) ?? {}), @@ -80,7 +88,7 @@ export function createJsonMcpAdapter( } let backupPath: string | undefined; - if (existsSync(config.configPath)) { + if (configExists) { backupPath = backupFile(config.configPath, config.name); logBackup(backupPath); } else { diff --git a/test/connect-new-agents.test.ts b/test/connect-new-agents.test.ts index 9ac4485ab..956a9aca0 100644 --- a/test/connect-new-agents.test.ts +++ b/test/connect-new-agents.test.ts @@ -1,5 +1,12 @@ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; -import { mkdtempSync, mkdirSync, rmSync, readFileSync, existsSync } from "node:fs"; +import { + mkdtempSync, + mkdirSync, + rmSync, + readFileSync, + existsSync, + writeFileSync, +} from "node:fs"; import { tmpdir, platform } from "node:os"; import { join } from "node:path"; @@ -218,13 +225,16 @@ describe("connect: Droid (Factory.ai)", () => { describe("connect: Zed", () => { let home: string; const ORIG = process.env["HOME"]; + const ORIG_USERPROFILE = process.env["USERPROFILE"]; beforeEach(() => { home = freshHome(); vi.resetModules(); process.env["HOME"] = home; + process.env["USERPROFILE"] = home; }); afterEach(() => { process.env["HOME"] = ORIG; + process.env["USERPROFILE"] = ORIG_USERPROFILE; rmSync(home, { recursive: true, force: true }); }); @@ -246,6 +256,49 @@ describe("connect: Zed", () => { expect(cfg.context_servers.agentmemory.args).toContain("@agentmemory/mcp"); expect(cfg.mcpServers).toBeUndefined(); }); + + it("preserves existing Zed settings and context servers", async () => { + const zedDir = join(home, ".config", "zed"); + const settingsPath = join(zedDir, "settings.json"); + mkdirSync(zedDir, { recursive: true }); + writeFileSync( + settingsPath, + JSON.stringify({ + theme: "One Dark", + context_servers: { + other: { + command: "node", + args: ["server.js"], + }, + }, + }), + ); + + const { adapter } = await import("../src/cli/connect/zed.js"); + const result = await adapter.install({ dryRun: false, force: false }); + + expect(result.kind).toBe("installed"); + const cfg = JSON.parse(readFileSync(settingsPath, "utf-8")); + expect(cfg.theme).toBe("One Dark"); + expect(cfg.context_servers.other.command).toBe("node"); + expect(cfg.context_servers.agentmemory.command).toBe("npx"); + expect(cfg.context_servers.agentmemory.args).toContain("@agentmemory/mcp"); + }); + + it("does not overwrite existing Zed settings when the file cannot be parsed", async () => { + const zedDir = join(home, ".config", "zed"); + const settingsPath = join(zedDir, "settings.json"); + const originalSettings = '{\n // Zed allows JSONC-style settings\n "theme": "One Dark",\n}\n'; + mkdirSync(zedDir, { recursive: true }); + writeFileSync(settingsPath, originalSettings); + + const { adapter } = await import("../src/cli/connect/zed.js"); + const result = await adapter.install({ dryRun: false, force: false }); + + expect(result).toEqual({ kind: "skipped", reason: "invalid-json" }); + expect(readFileSync(settingsPath, "utf-8")).toBe(originalSettings); + expect(existsSync(join(home, ".agentmemory", "backups"))).toBe(false); + }); }); describe("connect: Continue.dev", () => {