Skip to content

Commit 8fb8ec9

Browse files
author
soddygo
committed
fix: 优化 mcp-proxy 环境变量,避免 Windows 长度限制
- 为 getAppEnv() 添加 includeSystemPath 选项,允许排除系统 PATH - mcp-proxy 使用精简环境变量(仅应用内工具),避免 ENAMETOOLONG 错误 - 保留 Windows 关键系统变量(SystemRoot, COMSPEC 等) - 当 includeSystemPath=false 时,跳过 ORIGINAL_PATH 和注册表 PATH
1 parent a2066d4 commit 8fb8ec9

File tree

2 files changed

+82
-62
lines changed

2 files changed

+82
-62
lines changed

crates/agent-electron-client/src/main/services/packages/mcp.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -682,9 +682,10 @@ class McpProxyManager {
682682
return null;
683683
}
684684

685-
// 构建完整环境变量,确保 mcp-proxy 进程能正确启动子进程
686-
// 包含 Windows 系统变量 (SystemRoot, COMSPEC 等) 和应用内工具路径
687-
const proxyEnv = getAppEnv();
685+
// 构建精简环境变量,避免 Windows 环境变量长度限制 (32,767)
686+
// mcp-proxy 只需要应用内集成的工具(node, npm, npx, uv, uvx, git)
687+
// 不需要用户的系统 PATH,这样可以显著减少环境变量长度
688+
const proxyEnv = getAppEnv({ includeSystemPath: false });
688689

689690
// 构建 proxy 启动参数,使用 --config-file 避免命令行长度限制
690691
const proxyArgs = [scriptPath, "--config-file", configFilePath];

crates/agent-electron-client/src/main/services/system/dependencies.ts

Lines changed: 78 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,19 @@ export function getBundledGitBashPath(): string {
484484
* - 清除用户 npm 配置文件,避免读取用户全局设置
485485
* - 禁用 uv 自动安装到全局目录
486486
*/
487-
export function getAppEnv(): Record<string, string> {
487+
export interface GetAppEnvOptions {
488+
/**
489+
* 是否包含系统 PATH 环境变量。
490+
* - true: 包含系统 PATH(默认行为,适用于需要访问系统工具的进程)
491+
* - false: 只包含应用内集成的 PATH(适用于 MCP 代理等需要精简环境的进程)
492+
* @default true
493+
*/
494+
includeSystemPath?: boolean;
495+
}
496+
497+
export function getAppEnv(opts?: GetAppEnvOptions): Record<string, string> {
498+
const { includeSystemPath = true } = opts ?? {};
499+
488500
const appDataDir = getAppDataDir();
489501
const nodeModulesBin = path.join(appDataDir, "node_modules", ".bin");
490502
const appBin = getAppBinDir();
@@ -510,24 +522,24 @@ export function getAppEnv(): Record<string, string> {
510522
// 镜像配置
511523
const mirror = getMirrorConfig();
512524

513-
// 构建系统 PATH 的回退路径(仅包含常用系统工具目录)
514-
// 这样 agent 可以使用 bash/git/grep 等系统工具
515-
const systemPathPaths = getSystemPaths();
516-
517525
// 获取内置 Node.js 24、Git 和 Electron Node 路径
518526
const bundledNodeBinDir = getBundledNodeBinDir();
519527
const bundledGitBinDir = getBundledGitBinDir();
520528
const bundledGitBashPath = getBundledGitBashPath();
521529
const electronNodeBinDir = getElectronNodeBinDir();
522530

531+
// 构建系统 PATH 的回退路径(仅包含常用系统工具目录)
532+
// 这样 agent 可以使用 bash/git/grep 等系统工具
533+
const systemPathPaths = includeSystemPath ? getSystemPaths() : [];
534+
523535
// PATH 优先级:应用内 uv/uvx 优先,再应用内 node/npm,最后系统回退
524536
// - bundledNodeBinDir: 内置 Node.js 24(仅 Windows)
525537
// - bundledGitBinDir: 内置 Git bin(仅 Windows)
526538
// - electronNodeBinDir: Electron 内置的 npm/npx
527539
// - uvBin/uvToolBinDir: 应用内 uv/uvx(优先,保证 MCP 等子进程用应用内版本)
528540
// - nodeModulesBin: 应用内 node_modules/.bin
529541
// - appBin: 应用内 bin
530-
// - systemPathPaths: 系统工具回退
542+
// - systemPathPaths: 系统工具回退(可选,由 includeSystemPath 控制)
531543
const priorityPath = [
532544
bundledNodeBinDir,
533545
electronNodeBinDir,
@@ -667,27 +679,29 @@ export function getAppEnv(): Record<string, string> {
667679
}
668680
}
669681

670-
// 2. 确保 Windows 系统目录在 PATH 中
682+
// 2. 确保 Windows 系统目录在 PATH 中(始终添加,这是系统运行必需的)
671683
const windowsSystemPathEntries = [
672684
"C:\\Windows\\System32",
673685
"C:\\Windows\\System32\\Wbem",
674686
"C:\\Windows\\System32\\WindowsPowerShell\\v1.0",
675687
"C:\\Windows\\System32\\OpenSSH",
676688
];
677689

678-
const currentPath = cleanEnv.PATH || "";
690+
let currentPath = cleanEnv.PATH || "";
679691
const currentPathLower = currentPath.split(";").map((p) => p.toLowerCase());
680692

681693
for (const sysPath of windowsSystemPathEntries) {
682694
if (!currentPathLower.includes(sysPath.toLowerCase())) {
683-
cleanEnv.PATH = currentPath + ";" + sysPath;
695+
currentPath = currentPath + ";" + sysPath;
696+
cleanEnv.PATH = currentPath;
684697
}
685698
}
686699

687700
// 3. 设置 ORIGINAL_PATH(POSIX 格式)供 git-bash 使用
701+
// 注意:当 includeSystemPath 为 false 时,跳过此步骤以精简环境变量
688702
// 参考 LobsterAI: 确保 git-bash 的 /etc/profile 正确处理 PATH
689703
// 注意:限制条目数量以避免超过 Windows 环境变量长度限制 (32,767)
690-
if (bundledGitBashPath) {
704+
if (includeSystemPath && bundledGitBashPath) {
691705
const MAX_ORIGINAL_PATH_ENTRIES = 20; // 限制条目数量
692706
const pathEntries = (cleanEnv.PATH || "").split(";").filter(Boolean);
693707
const limitedEntries = pathEntries.slice(0, MAX_ORIGINAL_PATH_ENTRIES);
@@ -701,62 +715,67 @@ export function getAppEnv(): Record<string, string> {
701715
}
702716

703717
// 4. 从注册表读取最新 PATH(解决用户后安装的工具不在 PATH 中的问题)
704-
try {
705-
const { execSync } = require("child_process");
706-
const psScript = [
707-
'$machinePath = [Environment]::GetEnvironmentVariable("Path", "Machine")',
708-
'$userPath = [Environment]::GetEnvironmentVariable("Path", "User")',
709-
'[Console]::Write("$machinePath;$userPath")',
710-
].join("; ");
711-
const encodedCommand = Buffer.from(psScript, "utf16le").toString(
712-
"base64",
713-
);
714-
const result = execSync(
715-
`powershell -NoProfile -NonInteractive -EncodedCommand ${encodedCommand}`,
716-
{
717-
encoding: "utf-8",
718-
timeout: 10000,
719-
windowsHide: true,
720-
},
721-
);
722-
723-
const registryPath = result.trim();
724-
if (registryPath) {
725-
const registryEntries = registryPath
726-
.split(";")
727-
.map((entry: string) => entry.trim())
728-
.filter(Boolean);
729-
730-
// 去重并追加到 PATH 末尾
731-
// 注意:限制追加的条目数量以避免超过 Windows 环境变量长度限制
732-
const MAX_REGISTRY_PATH_ENTRIES = 10; // 最多从注册表追加10个条目
733-
const existingPaths = new Set(
734-
currentPath.split(";").map((p) => p.toLowerCase()),
718+
// 注意:当 includeSystemPath 为 false 时,跳过此步骤以精简环境变量
719+
if (includeSystemPath) {
720+
try {
721+
const { execSync } = require("child_process");
722+
const psScript = [
723+
'$machinePath = [Environment]::GetEnvironmentVariable("Path", "Machine")',
724+
'$userPath = [Environment]::GetEnvironmentVariable("Path", "User")',
725+
'[Console]::Write("$machinePath;$userPath")',
726+
].join("; ");
727+
const encodedCommand = Buffer.from(psScript, "utf16le").toString(
728+
"base64",
735729
);
736-
const missingEntries: string[] = [];
730+
const result = execSync(
731+
`powershell -NoProfile -NonInteractive -EncodedCommand ${encodedCommand}`,
732+
{
733+
encoding: "utf-8",
734+
timeout: 10000,
735+
windowsHide: true,
736+
},
737+
);
738+
739+
const registryPath = result.trim();
740+
if (registryPath) {
741+
const registryEntries = registryPath
742+
.split(";")
743+
.map((entry: string) => entry.trim())
744+
.filter(Boolean);
745+
746+
// 去重并追加到 PATH 末尾
747+
// 注意:限制追加的条目数量以避免超过 Windows 环境变量长度限制
748+
const MAX_REGISTRY_PATH_ENTRIES = 10; // 最多从注册表追加10个条目
749+
const existingPaths = new Set(
750+
currentPath.split(";").map((p) => p.toLowerCase()),
751+
);
752+
const missingEntries: string[] = [];
753+
754+
for (const entry of registryEntries) {
755+
if (missingEntries.length >= MAX_REGISTRY_PATH_ENTRIES) {
756+
log.info(
757+
`[getAppEnv] 已达到最大注册表 PATH 条目限制 (${MAX_REGISTRY_PATH_ENTRIES}),跳过剩余条目`,
758+
);
759+
break;
760+
}
761+
if (!existingPaths.has(entry.toLowerCase())) {
762+
missingEntries.push(entry);
763+
existingPaths.add(entry.toLowerCase());
764+
}
765+
}
737766

738-
for (const entry of registryEntries) {
739-
if (missingEntries.length >= MAX_REGISTRY_PATH_ENTRIES) {
767+
if (missingEntries.length > 0) {
768+
cleanEnv.PATH = currentPath + ";" + missingEntries.join(";");
740769
log.info(
741-
`[getAppEnv] 已达到最大注册表 PATH 条目限制 (${MAX_REGISTRY_PATH_ENTRIES}),跳过剩余条目`,
770+
`[getAppEnv] 从注册表追加 ${missingEntries.length} 个 PATH 条目`,
742771
);
743-
break;
744-
}
745-
if (!existingPaths.has(entry.toLowerCase())) {
746-
missingEntries.push(entry);
747-
existingPaths.add(entry.toLowerCase());
748772
}
749773
}
750-
751-
if (missingEntries.length > 0) {
752-
cleanEnv.PATH = currentPath + ";" + missingEntries.join(";");
753-
log.info(
754-
`[getAppEnv] 从注册表追加 ${missingEntries.length} 个 PATH 条目`,
755-
);
756-
}
774+
} catch (error) {
775+
log.warn(`[getAppEnv] 读取注册表 PATH 失败: ${error}`);
757776
}
758-
} catch (error) {
759-
log.warn(`[getAppEnv] 读取注册表 PATH 失败: ${error}`);
777+
} else {
778+
log.info(`[getAppEnv] 跳过注册表 PATH 读取(includeSystemPath=false)`);
760779
}
761780
}
762781

0 commit comments

Comments
 (0)