Skip to content

fix(mcp): resolve bare npx/npm/node against nvm/fnm/volta install dirs - #67873

Open
HaoNgo232 wants to merge 4 commits into
NousResearch:mainfrom
HaoNgo232:fix/mcp-resolve-node-version-managers
Open

fix(mcp): resolve bare npx/npm/node against nvm/fnm/volta install dirs#67873
HaoNgo232 wants to merge 4 commits into
NousResearch:mainfrom
HaoNgo232:fix/mcp-resolve-node-version-managers

Conversation

@HaoNgo232

@HaoNgo232 HaoNgo232 commented Jul 20, 2026

Copy link
Copy Markdown

Summary

A bare command: npx (or npm/node) MCP server fails to start with:

FileNotFoundError: [Errno 2] No such file or directory: 'npx'

when Node.js is installed via a version manager. The root cause is that the Hermes daemon runs MCP servers with a fixed PATH that does not source shell rc files, so the version-manager install dirs are invisible to it. This is not distro-specific — it reproduces on any Linux distro and macOS wherever Node lives under a version manager, because the failure is purely a PATH-resolution issue, independent of the distro.

#34186 already added fallback candidates for $HERMES_HOME/node/bin, ~/.local/bin, and /usr/local/bin, but none of those cover the version-manager install locations that interactive dev machines almost always use. So the failure persists for the common setups.

Fix

Extend _resolve_stdio_command's candidate list with _node_version_manager_dirs(), which collects:

  • ~/.nvm/versions/<version>/bin (one dir per installed version)
  • ~/.fnm/node-versions/<version>/installation/bin
  • ~/.volta/bin

The resolver still prepends the resolved dir to PATH (existing behavior), so npx's /usr/bin/env node shebang finds node in the same directory. The change is surgical: it only activates when the bare command isn't otherwise locatable through the user's PATH or the existing candidates.

This also makes the manual ~/.local/bin symlink workaround (used until now) unnecessary for version-manager users.

Test plan

Regression tests added in tests/tools/test_mcp_tool_issue_948.py, mirroring the existing /usr/local/bin fallback test, one per version manager:

  • test_resolve_stdio_command_falls_back_to_nvm
  • test_resolve_stdio_command_falls_back_to_fnm
  • test_resolve_stdio_command_falls_back_to_volta

Full file: uv run pytest tests/tools/test_mcp_tool_issue_948.py10 passed.

Verification note

Verified on: Linux Mint 22 (kernel 6.8), nvm Node v22.21.1. The fnm and volta paths are covered by the new unit tests (which construct the exact install-dir layouts those managers use). The distro-independence claim rests on the PATH-resolution mechanism, not on any distro-specific path.

Related

Node installed via a version manager (nvm, fnm, volta) lives outside the
Hermes daemon PATH, so a bare `command: npx` MCP server fails with ENOENT
at execvp on every Linux distro and macOS. Extend `_resolve_stdio_command`
candidates with `_node_version_manager_dirs()` and add a regression test.
Copilot AI review requested due to automatic review settings July 20, 2026 05:04

Copilot AI 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.

Pull request overview

This PR aims to make stdio MCP servers configured with bare Node.js commands (npx/npm/node) start reliably when Node is installed via version managers (nvm/fnm/volta), by extending _resolve_stdio_command()’s fallback candidate list to include version-manager install directories.

Changes:

  • Add _node_version_manager_dirs() to enumerate likely Node binary directories from common version managers.
  • Extend _resolve_stdio_command() to try those directories when resolving bare npx/npm/node.
  • Add a regression test covering the new nvm fallback behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
tools/mcp_tool.py Adds version-manager directory discovery and uses it as additional fallback candidates for resolving bare npx/npm/node.
tests/tools/test_mcp_tool_issue_948.py Adds a regression test intended to validate the new nvm fallback path resolution behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tools/mcp_tool.py Outdated
Comment thread tools/mcp_tool.py
Comment thread tests/tools/test_mcp_tool_issue_948.py
Extends regression coverage for the version-manager fallback beyond nvm to
fnm (~/.fnm/node-versions/<v>/installation/bin) and volta (~/.volta/bin),
confirming _node_version_manager_dirs() resolves all three.
@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint tool/mcp MCP client and OAuth P2 Medium — degraded but workaround exists labels Jul 20, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Related to merged #34186: this adds the missing nvm/fnm/Volta installation roots to its existing stdio MCP command-resolution fallback.

nvm installs under ~/.nvm/versions/node/<version>/bin (and rarely io.js),
not ~/.nvm/versions/<version>/bin. The previous layout skipped the 'node'
segment, so the fallback never triggered for real nvm installs. Also sort
version entries for deterministic resolution. Updates the regression test to
model nvm's actual layout so it no longer passes against a broken fallback.
@HaoNgo232

Copy link
Copy Markdown
Author

Thanks @copilot — you were right. nvm installs under ~/.nvm/versions/node/<version>/bin (and rarely io.js), not ~/.nvm/versions/<version>/bin. The earlier test mirrored the wrong layout and passed against a broken fallback (false positive).

Fixed in e4170f6:

  • _node_version_manager_dirs() now walks ~/.nvm/versions/{node,io.js}/<version>/bin and sorts version entries for deterministic resolution.
  • Updated the doc comment in _resolve_stdio_command to the correct path.
  • Rewrote the nvm regression test to model nvm's real layout, so it now actually exercises the fixed fallback. fnm/volta tests unchanged (their layouts were already correct).

All 10 tests in tests/tools/test_mcp_tool_issue_948.py pass.

The version is arbitrary (the resolver scans all versions dynamically);
v20.0.0 avoids implying support is limited to one version.
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the focused MCP resolver fix. The premise remains valid on current main: tools/mcp_tool.py:659-684 only falls back to $HERMES_HOME/node/bin, ~/.local/bin, and /usr/local/bin after the configured PATH lookup fails, while _run_stdio() sends the filtered environment through that resolver at tools/mcp_tool.py:2390-2391.

The final diff correctly incorporates the earlier nvm-layout correction by traversing ~/.nvm/versions/{node,io.js}/<version>/bin, and preserves the existing resolved-directory PATH prepend so an npx shebang can locate its sibling node. The regression coverage exercises nvm, fnm, and Volta fallback resolution without changing configured-PATH precedence.

Automated hermes-sweeper review.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform area/install-update Installer, updater, packaging, wheels, doctor labels Jul 30, 2026
@alt-glitch alt-glitch removed sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades area/install-update Installer, updater, packaging, wheels, doctor labels Aug 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform tool/mcp MCP client and OAuth type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants