Skip to content

Commit 1d034e6

Browse files
authored
Merge pull request #46 from supermemoryai/vorflux/fix-windows-powershell
fix: wrap commands with `cmd /c` on Windows for MCP client compatibility
2 parents dbc56c9 + 62548c8 commit 1d034e6

1 file changed

Lines changed: 26 additions & 5 deletions

File tree

src/commands/install.ts

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { ArgumentsCamelCase, Argv } from "yargs"
2+
import process from "node:process"
23
import { logger } from "../logger"
34
import { blue, green, red } from "picocolors"
45
import {
@@ -12,6 +13,18 @@ import {
1213
} from "../client-config"
1314
import { spawn } from "node:child_process"
1415

16+
const isWindows = process.platform === "win32"
17+
18+
// On Windows, shell scripts like `npx` cannot be spawned directly by MCP clients
19+
// because they use child_process.spawn() without shell:true. The standard workaround
20+
// is to wrap the command with `cmd /c` so Windows can resolve the .cmd/.ps1 shim.
21+
function wrapCommandForPlatform(command: string, args: Array<string>): { command: string; args: Array<string> } {
22+
if (isWindows) {
23+
return { command: "cmd", args: ["/c", command, ...args] }
24+
}
25+
return { command, args }
26+
}
27+
1528
// Helper to set a server config in a nested structure
1629
function setServerConfig(
1730
config: ClientConfig,
@@ -53,7 +66,12 @@ function setServerConfig(
5366
}
5467
} else if (client === "opencode") {
5568
// OpenCode has a different config structure for MCP servers
56-
if (serverConfig.command === "npx" && serverConfig.args?.includes("mcp-remote@latest")) {
69+
// Check for npx directly or wrapped via cmd /c npx (Windows)
70+
const isNpxCommand =
71+
serverConfig.command === "npx" ||
72+
(serverConfig.command === "cmd" && serverConfig.args?.[0] === "/c" && serverConfig.args?.[1] === "npx")
73+
const isNpxMcpRemote = isNpxCommand && serverConfig.args?.includes("mcp-remote@latest")
74+
if (isNpxMcpRemote) {
5775
// For remote MCP servers, OpenCode uses a different structure
5876
const urlIndex = serverConfig.args.indexOf("mcp-remote@latest") + 1
5977
const url = serverConfig.args[urlIndex]
@@ -232,6 +250,7 @@ async function runAuthentication(url: string): Promise<void> {
232250
return new Promise((resolve, reject) => {
233251
const child = spawn("npx", ["-y", "-p", "mcp-remote@latest", "mcp-remote-client", url], {
234252
stdio: ["ignore", "ignore", "ignore"], // Hide all output
253+
shell: isWindows, // Required on Windows where npx is a .cmd script
235254
})
236255

237256
child.on("close", (code) => {
@@ -381,9 +400,10 @@ export async function handler(argv: ArgumentsCamelCase<InstallArgv>) {
381400
if (projectHeader) {
382401
args.push("--header", projectHeader)
383402
}
403+
const wrapped = wrapCommandForPlatform("npx", args)
384404
const serverConfig: ClientConfig = {
385-
command: "npx",
386-
args: args,
405+
command: wrapped.command,
406+
args: wrapped.args,
387407
}
388408
if (envVars) {
389409
serverConfig.env = envVars
@@ -392,9 +412,10 @@ export async function handler(argv: ArgumentsCamelCase<InstallArgv>) {
392412
} else {
393413
// Command-based installation (including simple package names)
394414
const cmdParts = command.split(" ")
415+
const wrapped = wrapCommandForPlatform(cmdParts[0] || command, cmdParts.slice(1))
395416
const serverConfig: ClientConfig = {
396-
command: cmdParts[0],
397-
args: cmdParts.slice(1),
417+
command: wrapped.command,
418+
args: wrapped.args,
398419
}
399420
if (envVars) {
400421
serverConfig.env = envVars

0 commit comments

Comments
 (0)