[GH-ISSUE #5811] [Bug] rea mcp add fails on Windows with spawn npx ENOENT #3516

Open
opened 2026-10-31 00:41:22 +00:00 by bsakaguchi · 5 comments
Owner

Originally created by @IdrisMalik on GitHub (Oct 21, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/5811

Summary

rea mcp add fails on Windows with spawn npx ENOENT. The registration path spawns the bare command npx, which resolves to the npm .cmd shim on Windows. Node cannot execute a .cmd shim without a shell, so the spawn fails before any client config is written.

rea setup is unaffected and succeeds on the same machine, so this appears limited to the mcp add path.

Environment

  • REA: rea-agents@6.7.0 (latest on npm), installed globally
  • OS: Microsoft Windows 11 Home Single Language, 10.0.26300 (64-bit)
  • Node: v24.20.0
  • npm: 11.16.0
  • npm global bin: C:\Users\Idris\AppData\Roaming\npm (contains npx.cmd, npx.ps1, npx)

Reproduction

> rea mcp add --agent opencode
Registering MCP server...code: MCP_ADD_FAILED
message: spawn npx ENOENT

Also fails identically with --no-global in an empty scratch directory, so the failure happens before any target config is touched:

> cd C:\Temp\rea-probe
> rea mcp add --agent opencode --no-global
Registering MCP server...code: MCP_ADD_FAILED
message: spawn npx ENOENT

No client config file is created or modified in either case.

Expected

rea mcp add --agent <client> registers the REA MCP server entry for the requested client.

Actual

MCP_ADD_FAILED with spawn npx ENOENT, and no registration performed.

This looks like the same family of bug as #504 and #526 (both closed), where rea doctor/rea setup aborted with spawn EINVAL while probing npm.cmd/npx.cmd version shims on Windows. The error code differs here (ENOENT rather than EINVAL) and the affected command differs (mcp add rather than doctor/setup), so it may not be covered by those fixes.

#4994 (fix(setup): register a stable launcher from a version-scoped store) also touches launcher resolution and may be relevant.

Suggested direction

Resolve the npm shim explicitly on Windows (spawn npx.cmd through cmd /c, or use the already-installed rea.mjs entry point directly with node <path> mcp), rather than spawning a bare npx.

Workaround

Use rea setup --all-detected instead. It resolves the launcher through the installed package path and correctly registered REA with Codex, OpenCode, VS Code, and DeepSeek Harness on this machine.

Originally created by @IdrisMalik on GitHub (Oct 21, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5811 ### Summary `rea mcp add` fails on Windows with `spawn npx ENOENT`. The registration path spawns the bare command `npx`, which resolves to the npm `.cmd` shim on Windows. Node cannot execute a `.cmd` shim without a shell, so the spawn fails before any client config is written. `rea setup` is unaffected and succeeds on the same machine, so this appears limited to the `mcp add` path. ### Environment - REA: `rea-agents@6.7.0` (latest on npm), installed globally - OS: Microsoft Windows 11 Home Single Language, 10.0.26300 (64-bit) - Node: v24.20.0 - npm: 11.16.0 - npm global bin: `C:\Users\Idris\AppData\Roaming\npm` (contains `npx.cmd`, `npx.ps1`, `npx`) ### Reproduction ``` > rea mcp add --agent opencode Registering MCP server...code: MCP_ADD_FAILED message: spawn npx ENOENT ``` Also fails identically with `--no-global` in an empty scratch directory, so the failure happens before any target config is touched: ``` > cd C:\Temp\rea-probe > rea mcp add --agent opencode --no-global Registering MCP server...code: MCP_ADD_FAILED message: spawn npx ENOENT ``` No client config file is created or modified in either case. ### Expected `rea mcp add --agent <client>` registers the REA MCP server entry for the requested client. ### Actual `MCP_ADD_FAILED` with `spawn npx ENOENT`, and no registration performed. ### Related This looks like the same family of bug as #504 and #526 (both closed), where `rea doctor`/`rea setup` aborted with `spawn EINVAL` while probing `npm.cmd`/`npx.cmd` version shims on Windows. The error code differs here (ENOENT rather than EINVAL) and the affected command differs (`mcp add` rather than `doctor`/`setup`), so it may not be covered by those fixes. #4994 (`fix(setup): register a stable launcher from a version-scoped store`) also touches launcher resolution and may be relevant. ### Suggested direction Resolve the npm shim explicitly on Windows (spawn `npx.cmd` through `cmd /c`, or use the already-installed `rea.mjs` entry point directly with `node <path> mcp`), rather than spawning a bare `npx`. ### Workaround Use `rea setup --all-detected` instead. It resolves the launcher through the installed package path and correctly registered REA with Codex, OpenCode, VS Code, and DeepSeek Harness on this machine.
Author
Owner

@XinwangTao commented on GitHub (Oct 21, 2026):

Confirmed — this is the classic Windows .cmd shim problem: spawn() without a shell can't execute npx.cmd, so ENOENT fires before any client config is written. The suggested direction is right, and the "already-installed rea.mjs entry point directly with node mcp" option is the more robust one, because resolving npx.cmd via cmd /c reintroduces shell-quoting concerns and depends on npm's bin layout (npx.ps1 vs npx.cmd can also mismatch if the caller's PATH prefers a different shim). Since rea setup already resolves the launcher through the installed package path and works on the same machine, reusing that exact resolution in mcp add would keep the two paths consistent. A regression test on Windows CI that runs rea mcp add --no-global in a scratch dir and asserts a client config is created would have caught this.

<!-- gh-comment-id:6311282326 --> @XinwangTao commented on GitHub (Oct 21, 2026): Confirmed — this is the classic Windows .cmd shim problem: spawn() without a shell can't execute npx.cmd, so ENOENT fires before any client config is written. The suggested direction is right, and the "already-installed rea.mjs entry point directly with node <path> mcp" option is the more robust one, because resolving npx.cmd via cmd /c reintroduces shell-quoting concerns and depends on npm's bin layout (npx.ps1 vs npx.cmd can also mismatch if the caller's PATH prefers a different shim). Since rea setup already resolves the launcher through the installed package path and works on the same machine, reusing that exact resolution in mcp add would keep the two paths consistent. A regression test on Windows CI that runs rea mcp add --no-global in a scratch dir and asserts a client config is created would have caught this.
Author
Owner

@IdrisMalik commented on GitHub (Oct 21, 2026):

Root cause located, with the fix location pinned. It is not in REA's own code — it is in the incur CLI framework that REA vendors.

Where it actually fails

rea mcp add is implemented by incur (github.com/wevm/incur), so the failure path is:

  • incur/dist/SyncMcp.js → register() → await exec(cmd, [...prefix, 'add-mcp', ...args])
  • incur/dist/SyncMcp.js:150-153 → that exec helper is a promisified execFile(cmd, args) — no shell
  • cmd is 'npx', which on Windows is npx.cmd → spawn npx ENOENT
  • incur/dist/Cli.js catches it and emits MCP_ADD_FAILED

Upstream source: src/SyncMcp.ts L62-L63 (the add-mcp invocation) and L340-L347 (the promisified execFile helper). The vendored incur@0.6.1 and npm latest incur@0.7.1 are byte-identical in this file (SHA-256 428b462df6f857b62656f9dca0844e1519d9d2496ff8642273f7b8cb460d9ef4), so a REA dependency bump would not fix it.

Why rea setup works but rea mcp add does not

REA's own registration code already handles Windows correctly — dist/application/ClientRegistrationIdentity.js:28-36 wraps the runner as ["cmd.exe", "/d", "/c", ...command] on win32, with the comment "Both runners may be .cmd shims on Windows, which clients cannot spawn directly."

rea setup goes through that path. rea mcp add does not: it delegates to incur's add-mcp invocation, which never reaches REA's Windows-safe resolution. So the suggested direction — reuse the resolution setup already performs — is exactly right, and the fix belongs in incur (or REA must bypass incur's runner invocation on win32).

The underlying tool is fine

add-mcp itself works on Windows; only the shell-less spawn is at fault. Measured on the affected machine (Windows 11 10.0.26300 x64, Node v24.20.0, npm 11.16.0):

execFile('npx', ...)             : FAIL  spawn npx ENOENT
execFile('cmd.exe','/d','/c')   : OK    (stdout 3906 bytes)

npx -y add-mcp --help also runs fine directly, so add-mcp is not the problem.

Cross-references

  • https://github.com/wevm/incur/issues/268 — same root cause, different call site (npm/pnpm in the --update path, src/internal/update.ts L396-L403). Fixing only that file leaves mcp add broken, so both call sites need the same treatment or a shared shell-safe exec helper.
  • https://github.com/wevm/incur/issues/275 — new issue filed with the full reproduction, package versions, and source references for this call site.

On the Windows CI regression test

The suggested test — run rea mcp add --no-global in a scratch dir and assert a client config is created — is the right shape. Worth noting that it would have to exercise the incur path rather than only rea setup, since the two use different launcher resolution; a setup-only test would pass today and would not catch this.

Happy to provide the exact reproduction steps or to test a patch locally if that is useful.

<!-- gh-comment-id:6311464677 --> @IdrisMalik commented on GitHub (Oct 21, 2026): Root cause located, with the fix location pinned. It is not in REA's own code — it is in the `incur` CLI framework that REA vendors. ## Where it actually fails `rea mcp add` is implemented by `incur` (github.com/wevm/incur), so the failure path is: - `incur/dist/SyncMcp.js` → `register()` → `await exec(cmd, [...prefix, 'add-mcp', ...args])` - `incur/dist/SyncMcp.js:150-153` → that `exec` helper is a promisified `execFile(cmd, args)` — **no shell** - `cmd` is `'npx'`, which on Windows is `npx.cmd` → `spawn npx ENOENT` - `incur/dist/Cli.js` catches it and emits `MCP_ADD_FAILED` Upstream source: `src/SyncMcp.ts` L62-L63 (the `add-mcp` invocation) and L340-L347 (the promisified `execFile` helper). The vendored `incur@0.6.1` and npm latest `incur@0.7.1` are byte-identical in this file (SHA-256 `428b462df6f857b62656f9dca0844e1519d9d2496ff8642273f7b8cb460d9ef4`), so **a REA dependency bump would not fix it**. ## Why `rea setup` works but `rea mcp add` does not REA's own registration code already handles Windows correctly — `dist/application/ClientRegistrationIdentity.js:28-36` wraps the runner as `["cmd.exe", "/d", "/c", ...command]` on `win32`, with the comment "Both runners may be .cmd shims on Windows, which clients cannot spawn directly." `rea setup` goes through that path. `rea mcp add` does not: it delegates to `incur`'s `add-mcp` invocation, which never reaches REA's Windows-safe resolution. So the suggested direction — reuse the resolution `setup` already performs — is exactly right, and the fix belongs in `incur` (or REA must bypass `incur`'s runner invocation on `win32`). ## The underlying tool is fine `add-mcp` itself works on Windows; only the shell-less spawn is at fault. Measured on the affected machine (Windows 11 10.0.26300 x64, Node v24.20.0, npm 11.16.0): ``` execFile('npx', ...) : FAIL spawn npx ENOENT execFile('cmd.exe','/d','/c') : OK (stdout 3906 bytes) ``` `npx -y add-mcp --help` also runs fine directly, so `add-mcp` is not the problem. ## Cross-references - https://github.com/wevm/incur/issues/268 — same root cause, different call site (`npm`/`pnpm` in the `--update` path, `src/internal/update.ts` L396-L403). Fixing only that file leaves `mcp add` broken, so both call sites need the same treatment or a shared shell-safe exec helper. - https://github.com/wevm/incur/issues/275 — new issue filed with the full reproduction, package versions, and source references for this call site. ## On the Windows CI regression test The suggested test — run `rea mcp add --no-global` in a scratch dir and assert a client config is created — is the right shape. Worth noting that it would have to exercise the `incur` path rather than only `rea setup`, since the two use different launcher resolution; a `setup`-only test would pass today and would not catch this. Happy to provide the exact reproduction steps or to test a patch locally if that is useful.
Author
Owner

@IdrisMalik commented on GitHub (Oct 21, 2026):

Update: the fix has been implemented upstream and is up for review.

The defect turned out to be in incur (github.com/wevm/incur), which REA vendors. Two call sites had it:

Both now share a helper built on cross-spawn, which resolves .cmd/.bat shims and preserves argument boundaries. A windows-latest CI lane was added so the shim regression test actually runs, and a Windows .cmd shim test asserts that an argument containing spaces arrives as a single argument.

PR: https://github.com/wevm/incur/pull/276

Verified end to end against the locally vendored incur@0.6.1:

# before
Registering MCP server...code: MCP_ADD_FAILED
message: spawn npx ENOENT

# after
Registering MCP server...
✓ Registered rea as MCP server
  Agents: OpenCode

The generated config uses the Windows-safe launcher already produced by REA's own registration code — ["cmd.exe", "/d", "/c", "npx", "-y", "rea-agents@6.7.0", "mcp"].

rea setup was never affected and continues to work. The mcp add path will pick this up once incur ships the fix and REA bumps its dependency.

<!-- gh-comment-id:6311907769 --> @IdrisMalik commented on GitHub (Oct 21, 2026): Update: the fix has been implemented upstream and is up for review. The defect turned out to be in `incur` (github.com/wevm/incur), which REA vendors. Two call sites had it: - `src/SyncMcp.ts` — `mcp add` (`npx`), reported in https://github.com/wevm/incur/issues/275 - `src/internal/update.ts` — `--update` (`npm`/`pnpm`), reported in https://github.com/wevm/incur/issues/268 Both now share a helper built on `cross-spawn`, which resolves `.cmd`/`.bat` shims and preserves argument boundaries. A `windows-latest` CI lane was added so the shim regression test actually runs, and a Windows `.cmd` shim test asserts that an argument containing spaces arrives as a single argument. PR: https://github.com/wevm/incur/pull/276 Verified end to end against the locally vendored `incur@0.6.1`: ``` # before Registering MCP server...code: MCP_ADD_FAILED message: spawn npx ENOENT # after Registering MCP server... ✓ Registered rea as MCP server Agents: OpenCode ``` The generated config uses the Windows-safe launcher already produced by REA's own registration code — `["cmd.exe", "/d", "/c", "npx", "-y", "rea-agents@6.7.0", "mcp"]`. `rea setup` was never affected and continues to work. The `mcp add` path will pick this up once `incur` ships the fix and REA bumps its dependency.
Author
Owner

@IdrisMalik commented on GitHub (Oct 21, 2026):

Follow-up: the pull request referenced above has been withdrawn at my request and is closed, so nothing is awaiting review.

The diagnosis in this thread is unchanged and still verified — rea mcp add fails on Windows because the registration path spawns npx through node:child_process.execFile, which cannot launch a .cmd shim without a shell, while rea setup uses a different path that already handles this correctly. The defect lives in the incur CLI framework that REA vendors, and it is tracked upstream at https://github.com/wevm/incur/issues/275.

rea setup is unaffected and remains the working path on Windows. The mcp add path will pick up a fix when incur ships one and REA bumps its dependency.

<!-- gh-comment-id:6312711787 --> @IdrisMalik commented on GitHub (Oct 21, 2026): Follow-up: the pull request referenced above has been withdrawn at my request and is closed, so nothing is awaiting review. The diagnosis in this thread is unchanged and still verified — `rea mcp add` fails on Windows because the registration path spawns `npx` through `node:child_process.execFile`, which cannot launch a `.cmd` shim without a shell, while `rea setup` uses a different path that already handles this correctly. The defect lives in the `incur` CLI framework that REA vendors, and it is tracked upstream at https://github.com/wevm/incur/issues/275. `rea setup` is unaffected and remains the working path on Windows. The `mcp add` path will pick up a fix when `incur` ships one and REA bumps its dependency.
Author
Owner

@XinwangTao commented on GitHub (Oct 22, 2026):

The diagnosis is airtight — shell-less execFile cannot launch the npx.cmd shim, and the contrast with rea setup (which already wraps the runner via ClientRegistrationIdentity on win32) proves the fix belongs in incur's add-mcp invocation, not REA's registration logic. Agreed the regression test must exercise the incur path specifically; a setup-only test would pass today and miss this. One thing worth adding to the upstream ask: since both SyncMcp.ts and update.ts need the same shell-safe helper, a shared cross-spawn-based util (as #276 proposed) is the right shape so the two call sites can't drift again. With #276 withdrawn, tracking wevm/incur#275/#268 upstream and bumping the vendored dependency when they land seems the correct path forward.

<!-- gh-comment-id:6330322502 --> @XinwangTao commented on GitHub (Oct 22, 2026): The diagnosis is airtight — shell-less execFile cannot launch the npx.cmd shim, and the contrast with rea setup (which already wraps the runner via ClientRegistrationIdentity on win32) proves the fix belongs in incur's add-mcp invocation, not REA's registration logic. Agreed the regression test must exercise the incur path specifically; a setup-only test would pass today and miss this. One thing worth adding to the upstream ask: since both SyncMcp.ts and update.ts need the same shell-safe helper, a shared cross-spawn-based util (as #276 proposed) is the right shape so the two call sites can't drift again. With #276 withdrawn, tracking wevm/incur#275/#268 upstream and bumping the vendored dependency when they land seems the correct path forward.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#3516
No description provided.