From f6005e0d670667245ce1f7b02a0e5e03b1e40fa5 Mon Sep 17 00:00:00 2001 From: roboomp Date: Fri, 26 Jun 2026 06:58:19 +0000 Subject: [PATCH] fix(mcp): hid stdio windows subprocess consoles Set windowsHide for every Windows stdio MCP spawn path so direct .exe servers no longer open a visible cmd.exe window. Added a regression test covering direct Windows executable MCP server launch options. Fixes #3535 --- packages/coding-agent/CHANGELOG.md | 4 +++ .../src/mcp/transports/stdio.test.ts | 28 +++++++++++++++++++ .../coding-agent/src/mcp/transports/stdio.ts | 7 +++-- 3 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 packages/coding-agent/src/mcp/transports/stdio.test.ts diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 5cbb09c44..2f9ad861a 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixed Windows stdio MCP server launches showing a separate `cmd.exe` window for direct executable servers; MCP subprocesses now set `windowsHide` on every Windows spawn path. ([#3535](https://github.com/can1357/oh-my-pi/issues/3535)) + ## [16.1.21] - 2026-06-26 ### Fixed diff --git a/packages/coding-agent/src/mcp/transports/stdio.test.ts b/packages/coding-agent/src/mcp/transports/stdio.test.ts new file mode 100644 index 000000000..3e668796f --- /dev/null +++ b/packages/coding-agent/src/mcp/transports/stdio.test.ts @@ -0,0 +1,28 @@ +import { describe, expect, it } from "bun:test"; + +import { resolveStdioSpawnCommand } from "./stdio"; + +describe("resolveStdioSpawnCommand", () => { + it("hides direct Windows executable MCP servers", async () => { + await expect( + resolveStdioSpawnCommand( + { command: "server.exe", args: ["--stdio"] }, + { cwd: process.cwd(), env: {}, platform: "win32" }, + ), + ).resolves.toEqual({ + cmd: ["server.exe", "--stdio"], + windowsHide: true, + }); + }); + + it("keeps off-Windows spawn options unchanged", async () => { + await expect( + resolveStdioSpawnCommand( + { command: "server.exe", args: ["--stdio"] }, + { cwd: process.cwd(), env: {}, platform: "linux" }, + ), + ).resolves.toEqual({ + cmd: ["server.exe", "--stdio"], + }); + }); +}); diff --git a/packages/coding-agent/src/mcp/transports/stdio.ts b/packages/coding-agent/src/mcp/transports/stdio.ts index 23b46f0e3..1e7f0cf47 100644 --- a/packages/coding-agent/src/mcp/transports/stdio.ts +++ b/packages/coding-agent/src/mcp/transports/stdio.ts @@ -239,12 +239,15 @@ export async function resolveStdioSpawnCommand( // Direct-spawn only when we resolved to a concrete file AND its extension // is not a batch script. Everything else (resolved .cmd/.bat, or an // unresolved extensionless command) goes through cmd.exe so PATHEXT runs. + // Every Windows stdio server launch hides its console window; otherwise + // direct .exe servers pop a visible cmd window while the MCP server lives. + const windowsHide = true; const needsCmdExe = resolved === null || isWindowsBatchCommand(resolvedCommand); - if (!needsCmdExe) return { cmd: [resolvedCommand, ...args] }; + if (!needsCmdExe) return { cmd: [resolvedCommand, ...args], windowsHide }; return { cmd: [resolveComSpec(options.env), "/d", "/s", "/c", buildCmdExeCommand(resolvedCommand, args)], - windowsHide: true, + windowsHide, }; }