diff --git a/packages/coding-agent/src/mcp/transports/stdio.ts b/packages/coding-agent/src/mcp/transports/stdio.ts index d6e6ebe60..d3d0e2212 100644 --- a/packages/coding-agent/src/mcp/transports/stdio.ts +++ b/packages/coding-agent/src/mcp/transports/stdio.ts @@ -110,23 +110,17 @@ async function resolveWindowsCommandPath( function quoteCmdArg(value: string): string { if (value.length === 0) return '""'; let result = '"'; - let backslashes = 0; for (const char of value) { - if (char === "\\") { - backslashes++; - continue; - } if (char === '"') { - result += "\\".repeat(backslashes * 2 + 1); - result += '"'; - backslashes = 0; - continue; + result += '^"'; + } else if (char === "^") { + result += "^^"; + } else if (char === "%") { + result += "^%"; + } else { + result += char; } - result += "\\".repeat(backslashes); - backslashes = 0; - result += char === "%" ? "^%" : char; } - result += "\\".repeat(backslashes * 2); return `${result}"`; } diff --git a/packages/coding-agent/test/mcp-stdio-transport.test.ts b/packages/coding-agent/test/mcp-stdio-transport.test.ts index ffb2159c1..5f3396c74 100644 --- a/packages/coding-agent/test/mcp-stdio-transport.test.ts +++ b/packages/coding-agent/test/mcp-stdio-transport.test.ts @@ -62,6 +62,37 @@ describe("resolveStdioSpawnCommand", () => { } }); + it("escapes quoted JSON args before routing .cmd shims through cmd.exe", async () => { + const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "omp-mcp-quotes-")); + try { + const shim = path.join(tempDir, "codegraph.cmd"); + await Bun.write(shim, "@echo off\r\n"); + + const result = await resolveStdioSpawnCommand( + { type: "stdio", command: "codegraph", args: ["--config", '{"a":"b&c|d"}'] }, + { + cwd: tempDir, + env: { + COMSPEC: "C:\\Windows\\System32\\cmd.exe", + PATH: tempDir, + PATHEXT: ".cmd", + }, + platform: "win32", + }, + ); + + expect(result.cmd).toEqual([ + "C:\\Windows\\System32\\cmd.exe", + "/d", + "/s", + "/c", + `"${shim}" "--config" "{^"a^":^"b&c|d^"}"`, + ]); + } finally { + await fs.rm(tempDir, { recursive: true, force: true }); + } + }); + it("resolves extension-less absolute Windows paths to the sibling .cmd shim", async () => { // Mirrors npm's Windows shim layout: bare `codegraph` (shebang script), // `codegraph.cmd` (cmd.exe wrapper), and `codegraph.ps1` siblings under