fix(plugin): enrich + memoize shell PATH for MCP stdio subprocesses#4268
Merged
Conversation
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 环境无副作用 - 向后兼容:现有工作配置不受影响
resolveStdioExecutable now probes the login shell for every stdio plugin, not just on a not-found fallback. The interactive PATH is stable for the process, so cache the first non-empty probe behind the injectable stdioShellPATH seam — one login shell per process instead of one per server. Empty probes are not cached, so a shell that becomes resolvable later is still picked up.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Lands #4220 (by @weikejia123) and adds probe memoization. Closes #4220.
The bug (#4220)
An MCP stdio server launched by absolute path (e.g. a
wrapper.sh) ran with aPATHthat lacked the user's interactive-shell directories (/opt/homebrew/bin, …). GUI launches (Finder / Dock /open(1)) don't inherit~/.zshrc's PATH, so the wrapper'sexec uvx/npxfailed withexec: uvx: not found.resolveStdioExecutableonly enriched PATH from the shell on the command-not-found fallback path, so absolute-path commands (which resolve immediately) never got it — even though their child processes need it just as much.Fix (commit 1, @weikejia123)
Hoist the shell-PATH enrichment into an unconditional first step (
enrichStdioShellPATH) so every stdio subprocess inherits the interactive PATH. Bare-command and Windows paths are unchanged.Memoization (commit 2)
Because the probe now runs for every stdio plugin, and
defaultStdioShellPATHspawns an interactive login shell (zsh -l -i -c …, 2s timeout), N servers meant N shell spawns at startup. The interactive PATH is stable for the process, so the first non-empty probe is cached behind the injectablestdioShellPATHseam — one login shell per process instead of per server. Empty probes aren't cached, so a shell that becomes resolvable later is still picked up. Tests override the seam, so isolation is preserved.Validation
go build ./internal/plugin/,go vet,go test ./internal/plugin/(existing 11 + 2 new cache tests) all pass.