diff --git a/src/main/services/MCPService.ts b/src/main/services/MCPService.ts index e3b93889..749750fc 100644 --- a/src/main/services/MCPService.ts +++ b/src/main/services/MCPService.ts @@ -1,5 +1,6 @@ import os from 'node:os' import path from 'node:path' +import fs from 'node:fs' import { isLinux, isMac, isWin } from '@main/constant' import { createInMemoryMCPServer } from '@main/mcpServers/factory' @@ -13,7 +14,7 @@ import { nanoid } from '@reduxjs/toolkit' import { GetMCPPromptResponse, GetResourceResponse, MCPPrompt, MCPResource, MCPServer, MCPTool } from '@types' import { app } from 'electron' import Logger from 'electron-log' - +import { memoize } from 'lodash' import { CacheService } from './CacheService' import { StreamableHTTPClientTransport, type StreamableHTTPClientTransportOptions } from './MCPStreamableHttpClient' @@ -184,7 +185,7 @@ class McpService { args, env: { ...getDefaultEnvironment(), - PATH: this.getEnhancedPath(process.env.PATH || ''), + PATH: await this.getEnhancedPath(process.env.PATH || ''), ...server.env }, stderr: 'pipe' @@ -470,13 +471,93 @@ class McpService { return await cachedGetResource(server, uri) } + private getSystemPath = memoize(async (): Promise => { + return new Promise((resolve, reject) => { + let command: string + let shell: string + + if (process.platform === 'win32') { + shell = 'powershell.exe' + command = '$env:PATH' + } else { + // 尝试获取当前用户的默认 shell + + let userShell = process.env.SHELL + if (!userShell) { + if (fs.existsSync('/bin/zsh')) { + userShell = '/bin/zsh' + } else if (fs.existsSync('/bin/bash')) { + userShell = '/bin/bash' + } else if (fs.existsSync('/bin/fish')) { + userShell = '/bin/fish' + } else { + userShell = '/bin/sh' + } + } + shell = userShell + + // 根据不同的 shell 构建不同的命令 + if (userShell.includes('zsh')) { + shell = '/bin/zsh' + command = + 'source /etc/zshenv 2>/dev/null || true; source ~/.zshenv 2>/dev/null || true; source /etc/zprofile 2>/dev/null || true; source ~/.zprofile 2>/dev/null || true; source /etc/zshrc 2>/dev/null || true; source ~/.zshrc 2>/dev/null || true; source /etc/zlogin 2>/dev/null || true; source ~/.zlogin 2>/dev/null || true; echo $PATH' + } else if (userShell.includes('bash')) { + shell = '/bin/bash' + command = + 'source /etc/profile 2>/dev/null || true; source ~/.bash_profile 2>/dev/null || true; source ~/.bash_login 2>/dev/null || true; source ~/.profile 2>/dev/null || true; source ~/.bashrc 2>/dev/null || true; echo $PATH' + } else if (userShell.includes('fish')) { + shell = '/bin/fish' + command = + 'source /etc/fish/config.fish 2>/dev/null || true; source ~/.config/fish/config.fish 2>/dev/null || true; source ~/.config/fish/config.local.fish 2>/dev/null || true; echo $PATH' + } else { + // 默认使用 zsh + shell = '/bin/zsh' + command = + 'source /etc/zshenv 2>/dev/null || true; source ~/.zshenv 2>/dev/null || true; source /etc/zprofile 2>/dev/null || true; source ~/.zprofile 2>/dev/null || true; source /etc/zshrc 2>/dev/null || true; source ~/.zshrc 2>/dev/null || true; source /etc/zlogin 2>/dev/null || true; source ~/.zlogin 2>/dev/null || true; echo $PATH' + } + } + + console.log(`Using shell: ${shell} with command: ${command}`) + const child = require('child_process').spawn(shell, ['-c', command], { + env: { ...process.env }, + cwd: app.getPath('home') + }) + + let path = '' + child.stdout.on('data', (data) => { + path += data.toString() + }) + + child.stderr.on('data', (data) => { + console.error('Error getting PATH:', data.toString()) + }) + + child.on('close', (code) => { + if (code === 0) { + const trimmedPath = path.trim() + resolve(trimmedPath) + } else { + reject(new Error(`Failed to get system PATH, exit code: ${code}`)) + } + }) + }) + }) + /** * Get enhanced PATH including common tool locations */ - private getEnhancedPath(originalPath: string): string { + private async getEnhancedPath(originalPath: string): Promise { + let systemPath = '' + try { + systemPath = await this.getSystemPath() + } catch (error) { + Logger.error('[MCP] Failed to get system PATH:', error) + } // 将原始 PATH 按分隔符分割成数组 const pathSeparator = process.platform === 'win32' ? ';' : ':' - const existingPaths = new Set(originalPath.split(pathSeparator).filter(Boolean)) + const existingPaths = new Set( + [...systemPath.split(pathSeparator), ...originalPath.split(pathSeparator)].filter(Boolean) + ) const homeDir = process.env.HOME || process.env.USERPROFILE || '' // 定义要添加的新路径