Skip to content

fix: unconditionally enrich shell PATH for MCP stdio subprocesses#4220

Closed
weikejia123 wants to merge 1 commit into
esengine:main-v2from
weikejia123:fix/mcp-shell-path
Closed

fix: unconditionally enrich shell PATH for MCP stdio subprocesses#4220
weikejia123 wants to merge 1 commit into
esengine:main-v2from
weikejia123:fix/mcp-shell-path

Conversation

@weikejia123

Copy link
Copy Markdown
Contributor

问题

MCP stdio transport 启动子进程时,如果命令是绝对路径(如 /path/to/wrapper.sh),子进程环境变量 PATH 不包含用户交互式 shell 的完整路径(如 /opt/homebrew/bin),导致 wrapper 脚本中 exec 的子工具(uvxnpx 等)找不到。

报错示例:

plugin "MiniMax": read: EOF: stderr: /path/to/minimax-mcp-wrapper.sh: line 4: exec: uvx: not found

根因

resolveStdioExecutableinternal/plugin/transport_stdio.go)中有三条路径:

情况 行为
命令含路径分隔符(绝对/相对路径,即 wrapper.sh 场景) 直接返回,PATH 不富化
裸命令名 + 当前 PATH 找到 直接返回,PATH 不富化
裸命令名 + 没找到 探测 shell PATH,富化后再找

shell PATH 探测(defaultStdioShellPATH)的触发条件是"命令未在 PATH 中找到",但"命令本身能否被找到"和"命令的运行时依赖能否被找到"是两件事。用绝对路径写的命令入口同样需要正确的 PATH 才能让它的子进程正常工作。

此外,macOS GUI 应用(Finder / Dock / open(1))启动时不继承用户 ~/.zshrc 中配置的 PATH(如 /opt/homebrew/bin),defaultStdioShellPATH 正是为弥补此缺陷而设计的。但它被错误地限定在了"命令未找到"的回退路径中。

修复

将 shell PATH 富化从"命令查找失败的回退路径"提升为 resolveStdioExecutable独立的、无条件的初始化步骤。提取了辅助函数 enrichStdioShellPATH,在 hasPathSeparator 检查和 lookPathInEnv 查找之前执行,确保每个 MCP stdio 子进程都继承用户交互式 shell 的完整 PATH。

修复前:

resolveStdioExecutable
  → hasPathSeparator?      → 返回(无 PATH 富化)
  → lookPathInEnv?         → 返回(无 PATH 富化)
  → 都没找到 → 才探测 shell PATH → 再找一次
  → Windows fallback
  → 错误

修复后:

resolveStdioExecutable
  → enrichStdioShellPATH   → 无条件富化 PATH [新增]
  → hasPathSeparator?      → 返回(PATH 已富化)
  → lookPathInEnv?         → 返回(PATH 已富化)
  → Windows fallback
  → 错误

影响范围

  • ✅ 使用绝对/相对路径命令的 MCP stdio plugins 获得完整 shell PATH
  • ✅ 裸命令名路径不受影响(行为不变)
  • ✅ Windows 行为不变(由独立的 windowsStdioFallbackPATH 处理)
  • ✅ shell 探测有 2 秒超时保护,无 shell 环境无副作用
  • ✅ 向后兼容:现有工作配置不受影响

测试

  • 编译通过
  • go vet 无警告
  • go test ./internal/plugin/ 全部 11 个测试通过

MCP plugin 使用绝对路径命令(如 /path/to/wrapper.sh)时,
resolveStdioExecutable 识别到路径中含分隔符后直接返回,
跳过了 defaultStdioShellPATH 对子进程环境变量 PATH 的富化。

这导致 wrapper 脚本中 exec 的子工具(如 uvx、npx)在
macOS GUI 启动环境下找不到,报类似这样的错误:
  plugin "MiniMax": read: EOF: stderr: uvx: not found

将 shell PATH 探测从"命令查找失败的回退路径"提升为
resolveStdioExecutable 中独立的、无条件的初始化步骤。
新辅助函数 enrichStdioShellPATH 在 hasPathSeparator 检查
和 lookPathInEnv 之前执行,确保每个 MCP stdio 子进程
都继承用户交互式 shell 的完整 PATH。

影响范围:
- 使用绝对/相对路径命令的 MCP stdio plugins 获得完整 shell PATH
- 裸命令名路径不受影响(之前已能正确查找)
- Windows 行为不变(由独立的 windowsStdioFallbackPATH 处理)
- shell 探测有 2 秒超时保护,无 shell 环境无副作用
- 向后兼容:现有工作配置不受影响
@github-actions github-actions Bot added v2 Go rewrite (1.x) — main-v2 branch, active development mcp MCP servers / plugins (internal/plugin, codegraph) labels Jun 12, 2026
@esengine

Copy link
Copy Markdown
Owner

Thanks @weikejia123 — sharp diagnosis and the right fix. The point that "the command resolving" and "the command's children resolving" are two different things is exactly it.

Landed in #4268 with your commit preserved, plus one small follow-on: since the enrichment now probes the login shell for every stdio server, I memoized the first non-empty probe so it's a single shell spawn per process instead of one per server. Appreciate the clear root-cause writeup.

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

Labels

mcp MCP servers / plugins (internal/plugin, codegraph) v2 Go rewrite (1.x) — main-v2 branch, active development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants