Skip to content

fix(cli): quote the cmd.exe command path with real quotes on Windows - #280

Open
KassaSana wants to merge 3 commits into
firecrawl:mainfrom
KassaSana:fix/windows-cmd-command-quoting
Open

KassaSana wants to merge 3 commits into
firecrawl:mainfrom
KassaSana:fix/windows-cmd-command-quoting

Conversation

@KassaSana

@KassaSana KassaSana commented Sep 24, 2026 •

Copy link
Copy Markdown

Problem

On Windows, firecrawl setup mcp (and every other setup step that runs npx) fails whenever Node is installed under C:\Program Files\, which is the default install location:

'"C:\Program' is not recognized as an internal or external command,
operable program or batch file.
Error: Failed to configure Firecrawl MCP.

Fixes #188

Root cause

runClientCommand() launches .cmd shims through cmd.exe /d /s /c and escaped the resolved program path with the same escapeCmdArg() used for arguments, producing ^"C:\Program Files\nodejs\npx.cmd^".

That escaping is correct for arguments: cmd.exe strips the carets and passes the quotes on to the program's C-runtime parser. The program path, though, is parsed by cmd.exe itself, where ^" is a literal character rather than a quote, so the path splits at the first space.

Changes

  • Add quoteCmdCommand() and use it for the command token only. It wraps the program path in plain quotes (Windows paths cannot contain ", and one would be rejected). cmd.exe still expands %VAR%/!VAR! inside quotes, where a caret is literal, so each %/! is moved outside the quotes and caret-escaped: "C:\a"^%"X"^%"b\npx.cmd". Argument escaping is unchanged.
  • Export runClientCommand so it can be tested against a real cmd.exe.
  • Tests:
    • The existing win32 test in setup.test.ts mocks child_process and asserted the ^"…^" form, so it passed while the real spawn failed. Updated it to assert a plainly quoted command token.
    • New setup-windows-spawn.test.ts (describe.runIf(win32)) spawns a real cmd.exe with a .cmd shim under Program Files (x86) and checks that the argv arrives exactly, including & and ${FIRECRAWL_API_KEY}. It also covers a path containing %USERNAME%, with and without spaces (thanks cubic for flagging that case).

This deliberately differs from the patch suggested in #188, which changed escapeCmdArg for every argument. With unescaped outer quotes cmd.exe stops consuming the carets, so https://x.dev/mcp?a=1&b=2 would arrive as a=1^&b=2. Thanks to @matheusjosedesouzabispo-blip for pinpointing escapeCmdArg.

Note: #187 moves runClientCommand into src/utils/run-client-command.ts with the same escapeCmdArg(resolved) line. If that lands first, the same one-line change applies there, and I'm happy to rebase.

Testing

Windows 11, Node 26, pnpm 10.12.1 (the repo's pinned version)

  • New real-spawn tests (3 cases): Program Files (x86) fails before the fix with '"C:\...\Program' is not recognized, and the %USERNAME% cases fail with the plain-quote version. All 3 pass now.
  • End to end, in a temp dir: firecrawl setup mcp --project --agent claude-code -y --keyless
    • published 1.24.4: '"C:\Program' is not recognized … → Failed to configure Firecrawl MCP
    • this branch: Done!, writes .mcp.json with https://mcp.firecrawl.dev/v2/mcp
  • Full suite, built: the same 28 tests fail on main and on this branch (Windows path assumptions in setup, credentials and web-defaults tests). This PR adds no new failures.

Linux, Node 22 (container), pnpm build then vitest run

  • main: 619 passed. This branch: 619 passed, plus 3 skipped (the win32-only tests).
  • pnpm type-check and pnpm build pass. Prettier is clean on the changed files, and the pre-commit lint-staged hook ran.

runClientCommand() launches .cmd shims such as npx.cmd through
`cmd.exe /d /s /c`, and escaped the resolved program path with the same
escapeCmdArg() used for arguments. That produces ^"C:\Program Files\...^".
For arguments this works, because cmd.exe strips the carets and passes the
quotes on to the program. The program path, though, is parsed by cmd.exe
itself, where ^" is a literal character rather than a quote, so the path
splits at the first space:

  '"C:\Program' is not recognized as an internal or external command

This broke `firecrawl setup mcp`, skills installs and every other setup
step that runs npx whenever Node lives under C:\Program Files, which is
the default install location.

Quote the program path with plain quotes (Windows paths cannot contain
`"`) and leave argument escaping unchanged. Escaping the arguments with
unescaped outer quotes as well would stop the carets from being consumed,
so an argument like `https://host/mcp?a=1&b=2` would arrive as
`a=1^&b=2`.

The existing win32 test mocked child_process and asserted the ^"...^"
form, so it could not see the failure. Update that assertion, and add a
win32-only test that spawns a real cmd.exe with a .cmd shim under
"Program Files (x86)" and checks the argv it receives.

Fixes firecrawl#188
Copilot AI lite review requested due to automatic review settings September 24, 2026 08:45

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 3 files

Shadow auto-approve: would not auto-approve because issues were found.

Fix all with cubic | Re-trigger cubic

Comment thread src/commands/setup.ts Outdated
cmd.exe expands %VAR% (and !VAR! under delayed expansion) even inside
double quotes, where a caret is a literal character. With the command
path now in plain quotes, a launcher under a directory such as
`dir%USERNAME%x` was rewritten to the variable's value and failed with
"The system cannot find the path specified" (the previous caret-escaped
form handled this, but only for paths without spaces).

Close the quotes around each % and ! and caret-escape it outside them,
e.g. "C:\a"^%"X"^%"b\npx.cmd", so the path is passed through literally
whether or not it contains spaces. Extend the real cmd.exe test to cover
%USERNAME% in the path, with and without spaces.
@KassaSana

Copy link
Copy Markdown
Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 24, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@KassaSana I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 3 files

Shadow auto-approve: would not auto-approve because issues were found.

Fix all with cubic | Re-trigger cubic

Comment thread src/__tests__/commands/setup-windows-spawn.test.ts
setup-windows-spawn.test.ts only runs on win32, and the main test job
runs on ubuntu-latest, so those tests were always skipped in CI. Add a
windows-latest job that runs just that file, reusing the existing Node
and pnpm setup steps.

It targets the one file rather than adding Windows to the main job,
because the full suite has pre-existing Windows-only failures (path
assumptions in setup, credentials and web-defaults tests) that are
identical on main.
@KassaSana

Copy link
Copy Markdown
Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 24, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@KassaSana I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 1 file (changes from recent commits).

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Shadow auto-approve: would auto-approve. Fixes Windows cmd.exe quoting for CLI commands by using real quotes on the command token while keeping caret-escaping for arguments, verified by real cmd.exe spawn tests and CI. The fix is focused and clearly beneficial.

Re-trigger cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 4 files

Confidence score: 5/5

  • In .github/workflows/test.yml, the new job runs on pushes to main/master, using a Windows VM and full pnpm install for only three tests; restrict it to pull requests to avoid unnecessary CI cost.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".github/workflows/test.yml">

<violation number="1" location=".github/workflows/test.yml:46">
P3: This new job runs on every push to main/master as well as PRs, spending a Windows VM and a full `pnpm install` to execute only three tests. Gate it with `if: github.event_name == 'pull_request'`, matching the other add-on jobs in this file (`test-binary` and `test-npm-package`).</violation>
</file>

Shadow auto-approve: would not auto-approve because issues were found.

Fix all with cubic | Re-trigger cubic

# The main test job runs on Linux, where the real cmd.exe spawn tests are
# skipped. Run them on Windows so command quoting stays covered.
test-windows-spawn:
runs-on: windows-latest

@cubic-dev-ai cubic-dev-ai Bot Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: This new job runs on every push to main/master as well as PRs, spending a Windows VM and a full pnpm install to execute only three tests. Gate it with if: github.event_name == 'pull_request', matching the other add-on jobs in this file (test-binary and test-npm-package).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/test.yml, line 46:

<comment>This new job runs on every push to main/master as well as PRs, spending a Windows VM and a full `pnpm install` to execute only three tests. Gate it with `if: github.event_name == 'pull_request'`, matching the other add-on jobs in this file (`test-binary` and `test-npm-package`).</comment>

<file context>
@@ -40,6 +40,31 @@ jobs:
+  # The main test job runs on Linux, where the real cmd.exe spawn tests are
+  # skipped. Run them on Windows so command quoting stays covered.
+  test-windows-spawn:
+    runs-on: windows-latest
+
+    steps:
</file context>
Suggested change
runs-on: windows-latest
if: github.event_name == 'pull_request'
runs-on: windows-latest
Fix with cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Windows: setup mcp fails with path quoting (C:\Program not recognized, v1.20.0)

2 participants