メインコンテンツまでスキップ

连接池管理

McpService 是 MCP 模块的核心服务,管理所有 MCP 服务器的连接生命周期。它维护一个客户端连接池,采用懒加载策略(首次使用时才建立连接),并提供健康检查和自动重连能力。

文件路径packages/desktop/app/main/services/capabilities/tools/mcp-users/McpService.ts

类结构

class McpService extends EventEmitter {
private clients: Map<string, Client>;
private connectionStates: Map<string, ConnectionState>;
private toolsCache: Map<string, { tools: MCPTool[]; timestamp: number }>;
private readonly CACHE_TTL = 5 * 60 * 1000; // 5 分钟

constructor(private logger: LoggerService);
}

内部状态

属性类型说明
clientsMap<string, Client>服务器 ID → MCP Client 实例映射
connectionStatesMap<string, ConnectionState>服务器 ID → 连接状态映射
toolsCacheMap<string, { tools, timestamp }>服务器 ID → 工具列表缓存(含时间戳)

连接状态

stateDiagram-v2
[*] --> disconnected
disconnected --> connecting: initClient()
connecting --> connected: 连接成功
connecting --> error: 连接失败
connected --> disconnected: disconnect()
error --> connecting: reconnect()
connected --> connecting: reconnect()
状态说明
disconnected未连接或已断开
connecting正在建立连接
connected连接正常
error连接失败

连接生命周期

initClient — 初始化客户端

initClient(server: McpServerRecord): Promise<Client>

核心连接方法,执行流程:

  1. 检查复用 — 如果 clients Map 中已有该 ID 的客户端,先做健康检查
  2. 健康检查 — 调用 client.listTools() 验证连接是否有效
  3. 不健康处理 — 如果健康检查失败,先断开再重建
  4. 创建传输层 — 根据 server.type 创建对应的 Transport
  5. 创建客户端 — 创建 MCP SDK Client 实例,配置 capabilities
  6. 建立连接 — 调用 client.connect(transport)
  7. 缓存客户端 — 存入 clients Map
  8. 发送事件 — 发出 server:connected 事件
const client = new Client(
{ name: 'elftia', version: '1.0.0' },
{
capabilities: {
roots: { listChanged: true },
sampling: {}
}
}
);
await client.connect(transport);

disconnect — 断开连接

disconnect(serverId: string): Promise<void>
  1. 获取 Client 实例
  2. 调用 client.close()(带错误捕获)
  3. clients Map 中移除
  4. 状态设为 disconnected

reconnect — 重新连接

reconnect(serverId: string): Promise<void>

disconnect(),然后如果服务器 isActive,再 initClient()

cleanup — 全部清理

cleanup(): Promise<void>

断开所有连接,清空 clientsconnectionStatestoolsCache 三个 Map。用于应用退出时。

传输层实现

StdioClientTransport

用于本地进程通信,通过标准输入/输出(stdin/stdout)与子进程通信。

new StdioClientTransport({
command: server.config.command,
args: server.config.args || [],
env: {
...process.env, // 继承系统环境
...server.config.env // 覆盖自定义变量
}
});

特点

  • 自动启动子进程
  • 环境变量完全继承当前进程,再叠加自定义配置
  • 进程退出时连接自动关闭

SSEClientTransport

基于 HTTP Server-Sent Events 的长连接传输,使用 Electron 的 net.fetch 替代 Node.js 原生 fetch。

new SSEClientTransport(new URL(server.config.url), {
fetch: (url, init) => {
return net.fetch(
typeof url === 'string' ? url : url.toString(),
init
);
},
requestInit: {
headers: server.config.headers || {}
}
});

使用 net.fetch 的原因

  • Electron 的 net 模块遵循 Chromium 的网络栈
  • 更好的 SSL/TLS 支持和证书管理
  • 自动使用系统代理设置

StreamableHTTPClientTransport

基于 MCP 2025-03-26 规范的 Streamable HTTP 传输。配置方式与 SSE 类似:

new StreamableHTTPClientTransport(new URL(server.config.url), {
fetch: (url, init) => {
return net.fetch(
typeof url === 'string' ? url : url.toString(),
init
);
},
requestInit: {
headers: server.config.headers || {}
}
});

配置存储

MCP 服务器配置存储在 Electron Store 中:

const configStore = new Store<{
mcpServers: McpServerRecord[];
}>({
name: 'mcp-config',
defaults: { mcpServers: [] }
});

文件位置{userData}/mcp-config.json

McpServerRecord 完整字段

字段类型默认值说明
idstring自动生成唯一标识,格式 mcp_{timestamp}_{random}
namestring-服务器名称(不可重复)
typeMcpServerTransport-stdio / sse / http
scopeMcpServerScope'user'user(全局)/ local(项目级)
configMcpServerConfig-连接配置(command/args/env/url/headers)
isActivebooleantrue是否启用
isTrustedbooleanfalse是否受信任
disabledToolsstring[][]禁用的工具名列表
disabledAutoApproveToolsstring[][]禁止自动审批的工具名列表
installSourcestring'manual'安装来源:builtin / manual / protocol / unknown
projectPathstring?-关联的项目路径(local scope 时使用)
createdAtnumberDate.now()创建时间戳
updatedAtnumber?-最后更新时间戳

核心方法

list — 列出服务器

async list(): Promise<McpServerRecord[]>

直接从 configStore 读取服务器列表,不涉及连接。

add — 添加服务器

async add(input: McpServerInput): Promise<McpActionResult>
  1. 检查名称唯一性
  2. 创建 McpServerRecord,生成唯一 ID
  3. 保存到 configStore
  4. 如果 isActive,尝试建立连接(连接失败不阻塞添加)
  5. 发出 servers:changed 事件

addJson — JSON 批量添加

async addJson(input: McpServerJsonInput): Promise<McpActionResult>

解析 JSON 字符串,遍历 { [name]: config } 对象,对每个条目调用 add()。返回汇总结果。

update — 更新服务器

async update(id: string, updates: Partial<McpServerRecord>): Promise<McpActionResult>
  1. 按 ID 查找服务器
  2. 合并更新(保持 ID 不变)
  3. 保存并设置 updatedAt
  4. 如果 configtype 变更,触发 reconnect()
  5. 发出 servers:changed 事件

listServerTools — 获取服务器工具

async listServerTools(serverId: string): Promise<MCPTool[]>
  1. 检查缓存是否命中且未过期
  2. 命中则直接返回
  3. 未命中则 initClient()client.listTools()
  4. 转换为 MCPTool[] 格式,过滤 disabledTools
  5. 写入缓存

工具 ID 生成规则:mcp__${cleanServerName}__${cleanToolName}(非字母数字字符替换为 _

callTool — 调用工具

async callTool(serverId: string, toolName: string, args: any, callId: string): Promise<MCPCallToolResponse>
  1. initClient() 确保连接
  2. client.callTool({ name, arguments })
  3. 记录调用耗时
  4. 发出 tool:called 事件
  5. 返回 { isError, content } 或错误信息

test — 测试连接

async test(input: McpServerRemoveInput): Promise<McpTestResult>

尝试连接并列出工具,返回成功/失败和工具名列表。

discover — 发现能力

async discover(input: McpServerRemoveInput): Promise<McpDiscoverResult>

连接后分别调用 listTools()listPrompts()listResources()(后两者可选,允许部分失败),返回完整的能力清单。

事件

McpService 继承自 EventEmitter,发出以下事件:

事件参数触发时机
server:connectedserverId服务器连接成功
connection:stateserverId, state连接状态变更
servers:changed服务器列表变更(增/删/改)
tool:called{ serverId, toolName, duration, success }工具调用完成

扩展点

  • 新增传输类型 — 在 createTransport() 的 switch 语句中添加新的 case 分支
  • 自定义健康检查 — 修改 isClientHealthy() 方法
  • 缓存策略 — 修改 CACHE_TTL 或实现更复杂的缓存失效策略
  • 连接事件 — 监听 EventEmitter 事件实现自定义监控

相关文件