fix: preserve PATHEXT for stdio mcp servers on windows#2
Conversation
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a crucial fix for Windows environments, specifically addressing how stdio MCP servers handle executable resolution. It ensures that the PATHEXT environment variable, vital for locating executables, is correctly propagated to subprocesses. By automatically supplementing PATHEXT from the parent process when it's not explicitly set, the change prevents failures in commands that rely on system executables, such as uvx attempting to find git.exe. This enhancement improves the robustness and reliability of AstrBot on Windows without overriding user-defined configurations. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
审阅者指南添加了一个 Windows 专用的辅助函数,用于在启动基于 stdio 的 MCP 服务器时,确保在保留/合并 PATHEXT 的同时,不覆盖用户配置;并将其接入 stdio 启动路径,同时添加了跨平台和不同环境配置下行为的单元测试。 带 PATHEXT 保留的 stdio MCP 服务器启动时序图sequenceDiagram
participant Agent
participant MCPClient
participant _prepare_stdio_env
participant StdioServerParameters
participant OSSubprocess
Agent->>MCPClient: start_mcp_server(config)
MCPClient->>MCPClient: detect_stdio_mode(cfg)
alt stdio_mode
MCPClient->>_prepare_stdio_env: call(cfg)
alt platform_is_win32_and_PATHEXT_present
_prepare_stdio_env->>_prepare_stdio_env: copy_config_and_env
_prepare_stdio_env-->>MCPClient: cfg_with_PATHEXT
else non_win32_or_no_PATHEXT
_prepare_stdio_env-->>MCPClient: cfg_unmodified
end
MCPClient->>StdioServerParameters: create(**cfg)
StdioServerParameters->>OSSubprocess: spawn_stdio_process(env)
OSSubprocess-->>MCPClient: process_handle
else non_stdio_mode
MCPClient->>MCPClient: use_other_server_parameters
end
MCPClient-->>Agent: server_handle
|
| 变更 | 详情 | 文件 |
|---|---|---|
| 确保 Windows 上的 stdio MCP 子进程从父进程环境继承或合并 PATHEXT,且不覆盖用户提供的值。 |
|
astrbot/core/agent/mcp_client.py |
添加单元测试,用于验证 _prepare_stdio_env 在不同平台及环境设置下的行为。 |
|
tests/unit/test_mcp_client.py |
提示与命令
与 Sourcery 交互
- 触发一次新的审查: 在 pull request 上评论
@sourcery-ai review。 - 继续讨论: 直接回复 Sourcery 的审查评论即可。
- 从审查评论生成 GitHub issue: 通过回复某条审查评论,请求 Sourcery 从该评论创建 issue。你也可以直接回复该评论
@sourcery-ai issue来创建对应的 issue。 - 生成 pull request 标题: 在 pull request 标题的任意位置写上
@sourcery-ai,即可在任意时间生成标题。你也可以在 pull request 中评论@sourcery-ai title,在任意时间(重新)生成标题。 - 生成 pull request 摘要: 在 pull request 正文的任意位置写上
@sourcery-ai summary,即可在你希望的位置生成 PR 摘要。你也可以在 pull request 中评论@sourcery-ai summary,在任意时间(重新)生成摘要。 - 生成审阅者指南: 在 pull request 上评论
@sourcery-ai guide,即可在任意时间(重新)生成审阅者指南。 - 解决所有 Sourcery 评论: 在 pull request 上评论
@sourcery-ai resolve,以解决所有 Sourcery 评论。当你已经处理完所有评论且不希望再看到它们时非常有用。 - 忽略所有 Sourcery 审查: 在 pull request 上评论
@sourcery-ai dismiss,即可忽略所有现有的 Sourcery 审查。当你希望从头开始一轮新的审查时尤其有用——别忘了再评论@sourcery-ai review来触发新的审查!
自定义你的体验
访问你的 控制面板 以:
- 启用或禁用诸如 Sourcery 生成的 pull request 摘要、审阅者指南等审查功能。
- 修改审查语言。
- 添加、删除或编辑自定义审查说明。
- 调整其他审查设置。
获取帮助
Original review guide in English
Reviewer's Guide
Adds a Windows-specific helper to ensure PATHEXT is preserved/merged when launching stdio-based MCP servers, and wires it into the stdio path along with unit tests covering the behavior across platforms and env configurations.
Sequence diagram for stdio MCP server startup with PATHEXT preservation
sequenceDiagram
participant Agent
participant MCPClient
participant _prepare_stdio_env
participant StdioServerParameters
participant OSSubprocess
Agent->>MCPClient: start_mcp_server(config)
MCPClient->>MCPClient: detect_stdio_mode(cfg)
alt stdio_mode
MCPClient->>_prepare_stdio_env: call(cfg)
alt platform_is_win32_and_PATHEXT_present
_prepare_stdio_env->>_prepare_stdio_env: copy_config_and_env
_prepare_stdio_env-->>MCPClient: cfg_with_PATHEXT
else non_win32_or_no_PATHEXT
_prepare_stdio_env-->>MCPClient: cfg_unmodified
end
MCPClient->>StdioServerParameters: create(**cfg)
StdioServerParameters->>OSSubprocess: spawn_stdio_process(env)
OSSubprocess-->>MCPClient: process_handle
else non_stdio_mode
MCPClient->>MCPClient: use_other_server_parameters
end
MCPClient-->>Agent: server_handle
Flow diagram for _prepare_stdio_env Windows PATHEXT merge logic
flowchart TD
A[start_config_dict] --> B{sys.platform == win32}
B -->|no| C[return_original_config]
B -->|yes| D{PATHEXT_in_parent_env?}
D -->|no| C
D -->|yes| E[prepared = copy_of_config]
E --> F["env = dict(prepared.env_or_empty)"]
F --> G[env.setdefault PATHEXT_from_parent]
G --> H[prepared.env = env]
H --> I[return_prepared_config]
File-Level Changes
| Change | Details | Files |
|---|---|---|
| Ensure stdio MCP subprocesses on Windows inherit or merge PATHEXT from the parent environment without overriding user-provided values. |
|
astrbot/core/agent/mcp_client.py |
| Add unit tests to validate _prepare_stdio_env behavior under various platform and environment setups. |
|
tests/unit/test_mcp_client.py |
Tips and commands
Interacting with Sourcery
- Trigger a new review: Comment
@sourcery-ai reviewon the pull request. - Continue discussions: Reply directly to Sourcery's review comments.
- Generate a GitHub issue from a review comment: Ask Sourcery to create an
issue from a review comment by replying to it. You can also reply to a
review comment with@sourcery-ai issueto create an issue from it. - Generate a pull request title: Write
@sourcery-aianywhere in the pull
request title to generate a title at any time. You can also comment
@sourcery-ai titleon the pull request to (re-)generate the title at any time. - Generate a pull request summary: Write
@sourcery-ai summaryanywhere in
the pull request body to generate a PR summary at any time exactly where you
want it. You can also comment@sourcery-ai summaryon the pull request to
(re-)generate the summary at any time. - Generate reviewer's guide: Comment
@sourcery-ai guideon the pull
request to (re-)generate the reviewer's guide at any time. - Resolve all Sourcery comments: Comment
@sourcery-ai resolveon the
pull request to resolve all Sourcery comments. Useful if you've already
addressed all the comments and don't want to see them anymore. - Dismiss all Sourcery reviews: Comment
@sourcery-ai dismisson the pull
request to dismiss all existing Sourcery reviews. Especially useful if you
want to start fresh with a new review - don't forget to comment
@sourcery-ai reviewto trigger a new review!
Customizing Your Experience
Access your dashboard to:
- Enable or disable review features such as the Sourcery-generated pull request
summary, the reviewer's guide, and others. - Change the review language.
- Add, remove or edit custom review instructions.
- Adjust other review settings.
Getting Help
- Contact our support team for questions or feedback.
- Visit our documentation for detailed guides and information.
- Keep in touch with the Sourcery team by following us on X/Twitter, LinkedIn or GitHub.
There was a problem hiding this comment.
Hey - 我发现了 2 个问题
用于 AI Agents 的提示
Please address the comments from this code review:
## 单独评论
### 评论 1
<location path="tests/unit/test_mcp_client.py" line_range="6-14" />
<code_context>
+from astrbot.core.agent import mcp_client
+
+
+def test_prepare_stdio_env_adds_pathext_on_windows(monkeypatch: pytest.MonkeyPatch):
+ monkeypatch.setattr(mcp_client.sys, "platform", "win32")
+ monkeypatch.setenv("PATHEXT", ".COM;.EXE")
+
+ cfg = {"command": "uvx", "args": ["mcp-server-fetch"]}
+
+ prepared = mcp_client._prepare_stdio_env(cfg)
+
+ assert prepared["env"]["PATHEXT"] == ".COM;.EXE"
+
+
</code_context>
<issue_to_address>
**suggestion (testing):** 为 Windows 上未设置 PATHEXT 的情况添加一个测试,以确保返回的配置不发生变化。
当前所有 Windows 相关测试都假定 PATHEXT 已被设置。请添加一个测试(例如 `test_prepare_stdio_env_does_not_set_env_when_pathext_missing_on_windows`),在其中将 `sys.platform` 设为 `win32` 并清除 `PATHEXT`,然后断言返回的配置保持不变(不会出现新的 `env` 条目或 PATHEXT 值)。这将防止未来引入默认 PATHEXT 时产生回归问题。
</issue_to_address>
### 评论 2
<location path="tests/unit/test_mcp_client.py" line_range="3" />
<code_context>
+ }
+
+
+def test_prepare_stdio_env_preserves_user_defined_pathext(
+ monkeypatch: pytest.MonkeyPatch,
+):
+ monkeypatch.setattr(mcp_client.sys, "platform", "win32")
+ monkeypatch.setenv("PATHEXT", ".COM;.EXE")
+
+ cfg = {
+ "command": "uvx",
+ "args": ["mcp-server-fetch"],
+ "env": {"PATHEXT": ".BAT"},
+ }
+
+ prepared = mcp_client._prepare_stdio_env(cfg)
+
+ assert prepared["env"]["PATHEXT"] == ".BAT"
+
+
</code_context>
<issue_to_address>
**suggestion (testing):** 添加一个测试,在 PATHEXT 缺失且已存在 env 字典的情况下,验证该 env 保持不变。
目前你已经覆盖了:当 PATHEXT 存在时合并 env,以及保留用户显式指定的 PATHEXT。请再补充一种情况:`sys.platform == 'win32'`,配置中已有 `env`,但父环境中没有 PATHEXT,并断言 `_prepare_stdio_env` 不会向 `env` 中注入 PATHEXT。该测试将用来记录并锁定这一行为。
```suggestion
from astrbot.core.agent import mcp_client
def test_prepare_stdio_env_does_not_inject_pathext_when_missing_in_parent_env(
monkeypatch: pytest.MonkeyPatch,
):
"""On Windows, do not inject PATHEXT into an explicit env when parent env lacks it."""
monkeypatch.setattr(mcp_client.sys, "platform", "win32")
# Ensure PATHEXT is not present in the parent environment
monkeypatch.delenv("PATHEXT", raising=False)
cfg = {
"command": "uvx",
"args": ["mcp-server-fetch"],
"env": {"HTTP_PROXY": "http://127.0.0.1:7890"},
}
prepared = mcp_client._prepare_stdio_env(cfg)
# The explicit env dict should be left untouched; no PATHEXT injected
assert prepared["env"] == {"HTTP_PROXY": "http://127.0.0.1:7890"}
```
</issue_to_address>帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据反馈改进后续的评审。
Original comment in English
Hey - I've found 2 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="tests/unit/test_mcp_client.py" line_range="6-14" />
<code_context>
+from astrbot.core.agent import mcp_client
+
+
+def test_prepare_stdio_env_adds_pathext_on_windows(monkeypatch: pytest.MonkeyPatch):
+ monkeypatch.setattr(mcp_client.sys, "platform", "win32")
+ monkeypatch.setenv("PATHEXT", ".COM;.EXE")
+
+ cfg = {"command": "uvx", "args": ["mcp-server-fetch"]}
+
+ prepared = mcp_client._prepare_stdio_env(cfg)
+
+ assert prepared["env"]["PATHEXT"] == ".COM;.EXE"
+
+
</code_context>
<issue_to_address>
**suggestion (testing):** Add a test for the case where PATHEXT is not set on Windows to ensure the config is returned unchanged.
All current Windows tests assume PATHEXT is set. Please add a test (e.g. `test_prepare_stdio_env_does_not_set_env_when_pathext_missing_on_windows`) that sets `sys.platform` to `win32` and unsets `PATHEXT`, then asserts the returned config is unchanged (no new `env` entry or PATHEXT value). This will guard against future regressions that inject a default PATHEXT.
</issue_to_address>
### Comment 2
<location path="tests/unit/test_mcp_client.py" line_range="3" />
<code_context>
+ }
+
+
+def test_prepare_stdio_env_preserves_user_defined_pathext(
+ monkeypatch: pytest.MonkeyPatch,
+):
+ monkeypatch.setattr(mcp_client.sys, "platform", "win32")
+ monkeypatch.setenv("PATHEXT", ".COM;.EXE")
+
+ cfg = {
+ "command": "uvx",
+ "args": ["mcp-server-fetch"],
+ "env": {"PATHEXT": ".BAT"},
+ }
+
+ prepared = mcp_client._prepare_stdio_env(cfg)
+
+ assert prepared["env"]["PATHEXT"] == ".BAT"
+
+
</code_context>
<issue_to_address>
**suggestion (testing):** Add a test where PATHEXT is missing and an existing env dict is present to verify it is left untouched.
Right now you cover merging env when PATHEXT exists and preserving a user-specified PATHEXT. Please also cover `sys.platform == 'win32'` with an existing `env` but no PATHEXT in the parent environment, and assert that `_prepare_stdio_env` does not inject PATHEXT into `env`. That test will document and lock in this behavior.
```suggestion
from astrbot.core.agent import mcp_client
def test_prepare_stdio_env_does_not_inject_pathext_when_missing_in_parent_env(
monkeypatch: pytest.MonkeyPatch,
):
"""On Windows, do not inject PATHEXT into an explicit env when parent env lacks it."""
monkeypatch.setattr(mcp_client.sys, "platform", "win32")
# Ensure PATHEXT is not present in the parent environment
monkeypatch.delenv("PATHEXT", raising=False)
cfg = {
"command": "uvx",
"args": ["mcp-server-fetch"],
"env": {"HTTP_PROXY": "http://127.0.0.1:7890"},
}
prepared = mcp_client._prepare_stdio_env(cfg)
# The explicit env dict should be left untouched; no PATHEXT injected
assert prepared["env"] == {"HTTP_PROXY": "http://127.0.0.1:7890"}
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| def test_prepare_stdio_env_adds_pathext_on_windows(monkeypatch: pytest.MonkeyPatch): | ||
| monkeypatch.setattr(mcp_client.sys, "platform", "win32") | ||
| monkeypatch.setenv("PATHEXT", ".COM;.EXE") | ||
|
|
||
| cfg = {"command": "uvx", "args": ["mcp-server-fetch"]} | ||
|
|
||
| prepared = mcp_client._prepare_stdio_env(cfg) | ||
|
|
||
| assert prepared["env"]["PATHEXT"] == ".COM;.EXE" |
There was a problem hiding this comment.
suggestion (testing): 为 Windows 上未设置 PATHEXT 的情况添加一个测试,以确保返回的配置不发生变化。
当前所有 Windows 相关测试都假定 PATHEXT 已被设置。请添加一个测试(例如 test_prepare_stdio_env_does_not_set_env_when_pathext_missing_on_windows),在其中将 sys.platform 设为 win32 并清除 PATHEXT,然后断言返回的配置保持不变(不会出现新的 env 条目或 PATHEXT 值)。这将防止未来引入默认 PATHEXT 时产生回归问题。
Original comment in English
suggestion (testing): Add a test for the case where PATHEXT is not set on Windows to ensure the config is returned unchanged.
All current Windows tests assume PATHEXT is set. Please add a test (e.g. test_prepare_stdio_env_does_not_set_env_when_pathext_missing_on_windows) that sets sys.platform to win32 and unsets PATHEXT, then asserts the returned config is unchanged (no new env entry or PATHEXT value). This will guard against future regressions that inject a default PATHEXT.
| @@ -0,0 +1,64 @@ | |||
| import pytest | |||
|
|
|||
| from astrbot.core.agent import mcp_client | |||
There was a problem hiding this comment.
suggestion (testing): 添加一个测试,在 PATHEXT 缺失且已存在 env 字典的情况下,验证该 env 保持不变。
目前你已经覆盖了:当 PATHEXT 存在时合并 env,以及保留用户显式指定的 PATHEXT。请再补充一种情况:sys.platform == 'win32',配置中已有 env,但父环境中没有 PATHEXT,并断言 _prepare_stdio_env 不会向 env 中注入 PATHEXT。该测试将用来记录并锁定这一行为。
| from astrbot.core.agent import mcp_client | |
| from astrbot.core.agent import mcp_client | |
| def test_prepare_stdio_env_does_not_inject_pathext_when_missing_in_parent_env( | |
| monkeypatch: pytest.MonkeyPatch, | |
| ): | |
| """On Windows, do not inject PATHEXT into an explicit env when parent env lacks it.""" | |
| monkeypatch.setattr(mcp_client.sys, "platform", "win32") | |
| # Ensure PATHEXT is not present in the parent environment | |
| monkeypatch.delenv("PATHEXT", raising=False) | |
| cfg = { | |
| "command": "uvx", | |
| "args": ["mcp-server-fetch"], | |
| "env": {"HTTP_PROXY": "http://127.0.0.1:7890"}, | |
| } | |
| prepared = mcp_client._prepare_stdio_env(cfg) | |
| # The explicit env dict should be left untouched; no PATHEXT injected | |
| assert prepared["env"] == {"HTTP_PROXY": "http://127.0.0.1:7890"} |
Original comment in English
suggestion (testing): Add a test where PATHEXT is missing and an existing env dict is present to verify it is left untouched.
Right now you cover merging env when PATHEXT exists and preserving a user-specified PATHEXT. Please also cover sys.platform == 'win32' with an existing env but no PATHEXT in the parent environment, and assert that _prepare_stdio_env does not inject PATHEXT into env. That test will document and lock in this behavior.
| from astrbot.core.agent import mcp_client | |
| from astrbot.core.agent import mcp_client | |
| def test_prepare_stdio_env_does_not_inject_pathext_when_missing_in_parent_env( | |
| monkeypatch: pytest.MonkeyPatch, | |
| ): | |
| """On Windows, do not inject PATHEXT into an explicit env when parent env lacks it.""" | |
| monkeypatch.setattr(mcp_client.sys, "platform", "win32") | |
| # Ensure PATHEXT is not present in the parent environment | |
| monkeypatch.delenv("PATHEXT", raising=False) | |
| cfg = { | |
| "command": "uvx", | |
| "args": ["mcp-server-fetch"], | |
| "env": {"HTTP_PROXY": "http://127.0.0.1:7890"}, | |
| } | |
| prepared = mcp_client._prepare_stdio_env(cfg) | |
| # The explicit env dict should be left untouched; no PATHEXT injected | |
| assert prepared["env"] == {"HTTP_PROXY": "http://127.0.0.1:7890"} |
There was a problem hiding this comment.
Code Review
This pull request introduces a fix to ensure the PATHEXT environment variable is preserved for stdio MCP servers on Windows, which can resolve issues with executable resolution in subprocesses. The implementation is well-contained in a new _prepare_stdio_env function. This function is only called for stdio servers on Windows and correctly respects existing user-defined environment configurations. The change is accompanied by a comprehensive set of unit tests that cover various scenarios, including different platforms and pre-existing environment variables. The code is clean and the approach is sound.
There was a problem hiding this comment.
Pull request overview
Fixes Windows stdio MCP server startup issues by ensuring PATHEXT is available to the spawned subprocess when the user hasn’t explicitly provided it, preserving Windows executable resolution (e.g., git.exe) for commands like uvx --from git+....
Changes:
- Add
_prepare_stdio_env()to injectPATHEXTinto stdio server env on Windows without overwriting user-provided values. - Apply
_prepare_stdio_env()before constructingmcp.StdioServerParameters. - Add unit tests covering Windows/non-Windows behavior and env merge semantics.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
astrbot/core/agent/mcp_client.py |
Adds and uses a Windows-only env preparation step to preserve PATHEXT for stdio MCP subprocesses. |
tests/unit/test_mcp_client.py |
Adds unit tests validating PATHEXT injection/merge behavior and non-Windows no-op behavior. |
Modifications / 改动点
改动 astrbot/core/agent/mcp_client.py 当:
Windows 下启动 stdio MCP 服务时,如果用户没有显式传 env.PATHEXT,AstrBot 会从父进程补上 PATHEXT,避免 uvx --from git+... 无法解析 git.exe。用户自己配置的 PATHEXT 不会被覆盖。
Screenshots or Test Results / 运行截图或测试结果
Checklist / 检查清单
requirements.txt和pyproject.toml文件相应位置。/ I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations inrequirements.txtandpyproject.toml.由 Sourcery 提供的摘要
确保 Windows stdio MCP 子进程从父进程继承适当的可执行文件解析设置。
错误修复:
PATHEXT环境变量。测试:
PATHEXT注入、环境变量合并、用户自定义PATHEXT保留,以及在非 Windows 环境下的 stdio MCP 配置行为。Original summary in English
Summary by Sourcery
Ensure Windows stdio MCP subprocesses inherit appropriate executable resolution settings from the parent process.
Bug Fixes:
Tests: