Skip to content

MCP 工具注册表 ​

本文引用的源码文件

本文引用的文件

目录 ​

  1. 引言
  2. 项目结构
  3. 核心组件
  4. 架构总览
  5. 详细组件分析
  6. 依赖关系分析
  7. 性能与并发控制
  8. 安全与错误处理
  9. 生命周期管理
  10. 分组、命名空间与版本机制
  11. 最佳实践
  12. 自定义工具开发指南
  13. 故障排查
  14. 结论

引言 ​

本文聚焦 Agent 端的 MCP 工具注册表,解释其如何从配置中加载 MCP Server、建立连接、发现并缓存工具、暴露 HTTP API,以及扩展端如何消费这些工具。需要特别强调:Agent 是独立 Node.js 进程;修改 Agent 源码后必须重启进程,新接口和逻辑才会生效。

项目结构 ​

MCP 相关代码主要分布在 Agent 端 agent/src/mcp 三个文件,并由 agent/src/server.js 暴露 HTTP 接口;扩展端通过 background 脚本拉取工具列表并动态注入到工具执行器。

graph TB
subgraph "Agent 端"
REG["mcp/registry.js<br/>注册表"]
CLI["mcp/client.js<br/>MCP 客户端"]
CFG["mcp/mcp-config.js<br/>配置缓存与写锁"]
SRV["server.js<br/>HTTP 路由"]
end
subgraph "扩展端"
BE["background/tool-executor.js<br/>拉取并注入 MCP 工具"]
UI["side_panel/tool-panel.js<br/>服务级开关 UI"]
AM["side_panel/agent-manager.js<br/>按服务聚合工具"]
end
SRV --> REG
REG --> CLI
REG --> CFG
BE --> SRV
UI --> BE
AM --> BE

图表来源

章节来源

核心组件 ​

  • 注册表(registry.js):维护已连接的 MCP Client 实例,提供初始化、查询工具、调用工具、连接/断开服务器、关闭所有连接等能力。
  • MCP 客户端(client.js):封装 Model Context Protocol SDK,负责创建传输层(stdio/SSE/streamableHttp/websocket)、连接远端 MCP Server、列出工具、调用工具、断开连接。
  • 配置管理(mcp-config.js):读写 ~/.ai-helper-agent/mcp_servers.json,带内存缓存、mtime 失效、写入互斥锁。
  • HTTP 路由(server.js):将 MCP 管理操作映射为 /api/mcp/* 接口,并在请求入口同步语言设置。
  • 扩展端工具注入(tool-executor.js):根据全局开关、禁用服务器列表、强制下发列表,把 MCP 工具动态注入到扩展的工具执行体系。
  • UI 分组与过滤(tool-panel.js、agent-manager.js):MCP 工具按服务维度展示,支持“服务级开放/关闭”。

章节来源

架构总览 ​

MCP 工具注册表不是传统意义上的“文件系统扫描 + 模块加载”系统,而是“配置驱动 + 协议客户端 + 运行时连接池”的系统。工具发现来自远端 MCP Server 的 listTools,而不是本地目录扫描。

sequenceDiagram
participant Ext as "扩展端 Background"
participant Srv as "Agent server.js"
participant Reg as "registry.js"
participant Cli as "client.js"
participant Mps as "远端 MCP Server"
Ext->>Srv : GET /api/mcp/tools
Srv->>Reg : getMcpTools()
Reg-->>Srv : { success, tools[] }
Srv-->>Ext : JSON 工具列表
Ext->>Srv : POST /api/mcp/call { serverId, toolName, args }
Srv->>Reg : callMcpTool(serverId, toolName, args)
Reg->>Cli : client.callTool(toolName, args)
Cli->>Mps : MCP callTool
Mps-->>Cli : result
Cli-->>Reg : { success, content, raw }
Reg-->>Srv : 结果
Srv-->>Ext : JSON 响应

图表来源

详细组件分析 ​

注册表:registry.js ​

注册表是 MCP 能力的统一入口,职责包括:

  • 启动时遍历配置中的启用服务器,逐个连接并缓存 Client。
  • 提供扁平化工具列表,包含 serverId、serverName、name、description、inputSchema。
  • 转发工具调用到对应 Client。
  • 提供连接/断开/关闭全部连接的生命周期方法。
  • 重新导出配置管理函数,供 server.js 直接调用。

关键行为要点:

  • 未启用的服务器在初始化阶段被跳过。
  • 连接失败会记录失败服务器列表并输出警告日志。
  • 工具调用前校验 Client 是否存在且已连接。
  • 关闭时对所有 Client 调用 disconnect,并清空 Map。
classDiagram
class Registry {
+initializeMcpRegistry() Promise
+getMcpServersStatus() Object
+getMcpTools(serverId?) Object
+callMcpTool(serverId, toolName, args) Promise
+connectMcpServer(serverId) Promise
+disconnectMcpServer(serverId) Promise
+shutdownMcpRegistry() Promise
}
class McpClient {
+connect() Promise
+callTool(toolName, args) Promise
+disconnect() Promise
+getStatus() Object
}
class Config {
+loadMcpConfig() Object
+addMcpServer(server) Promise
+removeMcpServer(serverId) Promise
+toggleMcpServer(serverId, enabled) Promise
}
Registry --> McpClient : "管理多个实例"
Registry --> Config : "读取/更新配置"

图表来源

章节来源

MCP 客户端:client.js ​

McpClient 封装了 MCP SDK 的使用方式,支持多种传输协议:

  • stdio:通过命令、参数、环境变量和工作目录启动子进程。
  • SSE:需要 URL,可选 headers。
  • streamableHttp:需要 URL,可选 headers。
  • websocket:需要 URL。

连接流程:

  1. 根据配置创建传输层。
  2. 创建 SDK Client 并 connect。
  3. 获取 serverInfo 和 capabilities。
  4. 调用 listTools 缓存工具元数据。
  5. 标记 connected。

工具调用流程:

  1. 检查 connected。
  2. 调用 callTool。
  3. 解析 result.content 中的 text 内容。
  4. 返回 success/content/raw。
flowchart TD
Start(["connect()"]) --> CreateTransport["根据 transport 类型创建传输层"]
CreateTransport --> Validate["校验必要字段<br/>url/command"]
Validate --> ConnectSDK["SDK Client.connect()"]
ConnectSDK --> FetchMeta["获取 serverInfo / capabilities"]
FetchMeta --> ListTools["listTools()"]
ListTools --> CacheTools["缓存 tools 数组"]
CacheTools --> MarkConnected["connected = true"]
MarkConnected --> Done(["返回成功"])

图表来源

章节来源

配置管理:mcp-config.js ​

配置路径位于用户主目录下的 .ai-helper-agent/mcp_servers.json。该模块提供:

  • 带 mtime 的内存缓存。
  • 首次缺失时自动创建默认空配置。
  • 解析失败时降级为空 servers 数组。
  • 写入互斥锁,避免并发写破坏 JSON。
  • add/remove/toggle 等原子化配置变更。
flowchart TD
Load["loadMcpConfig()"] --> CheckMtime{"缓存有效?"}
CheckMtime --> |是| ReturnCache["返回缓存"]
CheckMtime --> |否| Exists{"文件存在?"}
Exists --> |否| CreateDefault["创建默认配置并缓存"]
Exists --> |是| ReadFile["读取并解析 JSON"]
ReadFile --> ParseOk{"解析成功?"}
ParseOk --> |是| UpdateCache["更新缓存 mtime"]
ParseOk --> |否| Fallback["降级为空 servers 并记录错误"]
UpdateCache --> ReturnData["返回配置"]
Fallback --> ReturnData

图表来源

章节来源

HTTP 路由:server.js ​

server.js 将 MCP 能力暴露为 REST 接口,主要包括:

  • /api/mcp/servers:GET 获取服务器状态。
  • /api/mcp/tools:GET 获取工具列表,可传 serverId 过滤。
  • /api/mcp/call:POST 调用工具。
  • /api/mcp/servers/connect、/api/mcp/servers/disconnect、/api/mcp/servers/toggle、/api/mcp/servers(POST/DELETE):管理服务器配置与连接。

每个请求进入时,server.js 会将当前请求的语言同步到各子模块,包括 MCP 客户端和注册表。

sequenceDiagram
participant C as "客户端"
participant R as "server.js 路由"
participant G as "registry.js"
participant L as "client.js"
C->>R : POST /api/mcp/call
R->>R : 校验 serverId/toolName
R->>G : callMcpTool(serverId, toolName, args)
G->>L : client.callTool(toolName, args)
L-->>G : { success, content, raw }
G-->>R : 结果
R-->>C : JSON 响应

图表来源

章节来源

扩展端工具注入:tool-executor.js ​

扩展端不会直接调用 MCP Server,而是先通过 Agent 的 /api/mcp/tools 拉取工具定义,再注入到扩展的工具执行器中。关键策略:

  • 有全局 MCP 开关,关闭则跳过加载。
  • 每次加载前先清理之前注册的 MCP 工具。
  • 并行拉取工具列表和服务器状态。
  • 忽略 disabled 服务器的工具。
  • 支持 forcedMcpServerIds 强制下发,绕过 deny-list。
flowchart TD
Entry(["loadMcpTools()"]) --> GlobalCheck{"全局 MCP 开启?"}
GlobalCheck --> |否| Skip["跳过加载"]
GlobalCheck --> |是| Clean["清理旧 MCP 工具"]
Clean --> FetchAll["并行获取 tools 和 servers"]
FetchAll --> HasTools{"tools 非空?"}
HasTools --> |否| Skip
HasTools --> |是| BuildDisabled["构建禁用服务器集合"]
BuildDisabled --> Inject["注入可用工具到执行器"]
Inject --> Done(["完成"])

图表来源

章节来源

UI 分组与服务级开关:tool-panel.js 与 agent-manager.js ​

  • tool-panel.js 明确说明:MCP 工具不参与工具级勾选,而是按“服务级开关”判定。
  • agent-manager.js 将工具按 serverId 聚合为服务对象,便于 UI 展示和计数。

这意味着 MCP 的“分组”本质上是“服务分组”,而不是传统的 category 分类。

章节来源

依赖关系分析 ​

  • registry.js 依赖 client.js 和 mcp-config.js。
  • server.js 依赖 registry.js 暴露的函数。
  • 扩展端 tool-executor.js 依赖 Agent 的 HTTP API。
  • UI 层依赖 tool-executor.js 提供的工具列表和服务聚合结果。
graph LR
ToolExecutor["tool-executor.js"] --> ServerAPI["server.js /api/mcp/*"]
ServerAPI --> Registry["registry.js"]
Registry --> Client["client.js"]
Registry --> Config["mcp-config.js"]
UIPanel["tool-panel.js / agent-manager.js"] --> ToolExecutor

图表来源

章节来源

性能与并发控制 ​

  • 配置缓存:mcp-config.js 使用内存缓存和 mtime 判断,避免频繁读盘。
  • 写入互斥:saveMcpConfig 使用 Promise 链式锁,保证并发写串行化。
  • 工具列表缓存:client.js 在 connect 后缓存 tools 数组,后续 getMcpTools 直接读取内存。
  • 扩展端并发拉取:tool-executor.js 并行拉取 tools 和 servers,减少等待时间。
  • 连接复用:registry.js 以 serverId 为键缓存 McpClient,避免重复连接。

需要注意:

  • 当前实现没有显式的工具调用限流或超时控制,工具调用耗时取决于远端 MCP Server。
  • 如果某个 MCP Server 不稳定,建议通过 toggle 禁用,而不是无限重试。

章节来源

安全与错误处理 ​

  • 认证与 CORS:server.js 对大多数接口要求 Bearer Token,并对 Chrome 扩展来源放行 CORS。
  • 输入校验:/api/mcp/call 校验 serverId 和 toolName。
  • 错误传播:client.js 将 SDK 错误包装为国际化消息;registry.js 对未连接或未找到服务器返回错误。
  • 日志:server.js 记录 mcp_tool_call 等系统事件,便于审计。
  • 配置解析失败保护:mcp-config.js 解析失败时降级为空配置,不阻断启动。

潜在风险点:

  • stdio 模式下 command/args/env 由配置决定,应视为可信配置来源;若配置来源不可信,需额外校验。
  • MCP Server 返回的 tool description 和 inputSchema 会被透传到扩展端,UI 渲染时应做基本防护。

章节来源

生命周期管理 ​

  • 启动初始化:server.js 启动时可通过 initializeMcpRegistry 连接所有启用的 MCP Server。
  • 动态连接:connectMcpServer 支持运行时连接指定服务器,先断开已有连接再新建。
  • 动态断开:disconnectMcpServer 清理 Client 并从 Map 删除。
  • 关闭清理:shutdownMcpRegistry 遍历所有 Client 调用 disconnect 并清空 Map。
  • 热重载限制:由于 MCP 连接和工具缓存存在于进程内存中,修改 Agent 源码后必须重启进程才能生效。
stateDiagram-v2
[*] --> 未连接
未连接 --> 已连接 : "connectMcpServer()"
已连接 --> 已连接 : "callMcpTool()"
已连接 --> 未连接 : "disconnectMcpServer()"
未连接 --> 已连接 : "initializeMcpRegistry()"
已连接 --> 已关闭 : "shutdownMcpRegistry()"
未连接 --> 已关闭 : "shutdownMcpRegistry()"

图表来源

章节来源

分组、命名空间与版本机制 ​

  • 分组:MCP 工具按 serverId 分组,UI 层按服务聚合展示。
  • 命名空间:工具名称来自远端 MCP Server,registry.js 在扁平化时保留 serverId 和 serverName,形成“服务名/工具名”的隐式命名空间。
  • 版本:client.js 在连接后获取 serverInfo 和 capabilities,但当前注册表并未将版本信息合并到工具列表中;serverInfo 主要用于状态展示。

因此,当前版本的“版本管理”更偏向“服务端能力探测”,而非严格的语义化版本控制。

章节来源

最佳实践 ​

  • 错误处理
    • 始终检查工具调用结果中的 success 字段。
    • 对未连接服务器优先调用 disconnect 或 toggle 禁用。
    • 捕获 MCP Server 返回的 error.message,避免向用户暴露内部堆栈。
  • 性能优化
    • 避免频繁重连同一 MCP Server;利用 registry.js 的连接复用。
    • 批量刷新工具列表时,先清理旧工具再注入,避免脏状态。
    • 对不常用的 MCP Server,按需连接,不要全部保持长连接。
  • 安全检查
    • 不要信任远端 MCP Server 返回的描述和 schema 作为执行依据。
    • stdio 模式的 command/args/env 应由可信配置源生成。
    • 对工具调用结果进行长度和内容过滤,防止注入。
  • i18n
    • 扩展端 UI 文案应从 shared locales 动态读取,不应硬编码中文。
    • Agent 端语言通过 server.js 的请求入口同步到各子模块。

章节来源

自定义工具开发指南 ​

如果你要开发一个 MCP Server,使其能被 ai-helper-agent 发现和调用,应遵循以下契约:

  1. 实现 MCP Server

    • 支持 MCP 协议(JSON-RPC 2.0)。
    • 暴露 listTools,返回工具元数据,至少包含 name、description、inputSchema。
    • 暴露 callTool,接收 name 和 arguments,返回 content 和 isError。
  2. 选择传输方式

    • stdio:适合本地命令行工具,配置 command、args、env、cwd。
    • SSE/streamableHttp/websocket:适合网络服务,配置 url 和可选 headers。
  3. 在 Agent 端添加配置

    • 编辑 ~/.ai-helper-agent/mcp_servers.json,或通过 Agent 的 /api/mcp/servers 接口添加。
    • 确保 id 唯一,enabled 为 true。
  4. 验证连接与工具

    • 调用 /api/mcp/servers 查看连接状态。
    • 调用 /api/mcp/tools 确认工具列表。
    • 调用 /api/mcp/call 测试工具执行。
  5. 扩展端集成

    • 扩展端会自动拉取工具列表并注入执行器。
    • UI 上按服务维度展示,可在侧边栏或服务管理器中启用/禁用服务。

章节来源

故障排查 ​

常见问题与定位思路:

  • MCP 工具列表为空

    • 检查全局 MCP 开关是否开启。
    • 检查服务器是否 enabled。
    • 检查 /api/mcp/servers 中 connected 是否为 true。
    • 检查远端 MCP Server 是否正确实现 listTools。
  • 工具调用失败

    • 检查 serverId 和 toolName 是否正确。
    • 检查 Client 是否 connected。
    • 查看 server.js 中 mcp_tool_call 日志。
    • 检查 client.js 返回的错误消息。
  • 配置保存失败

    • 检查 ~/.ai-helper-agent/mcp_servers.json 权限。
    • 检查 JSON 格式是否合法。
    • 观察 mcp-config.js 的解析错误日志。
  • 连接反复失败

    • 检查 command/url 是否正确。
    • 检查网络可达性。
    • 考虑暂时 toggle 禁用该服务,避免影响其他工具。
  • 扩展端看不到 MCP 工具

    • 检查 tool-executor.js 是否成功拉取 tools。
    • 检查 disabledServerIds 是否包含目标服务。
    • 检查 UI 层的服务级开关是否排除该服务。

章节来源

结论 ​

MCP 工具注册表是一个以配置为中心、以协议客户端为核心的工具扩展机制。它不依赖本地文件系统扫描,而是通过 MCP 协议从远端 Server 发现工具;它通过注册表统一管理连接、缓存工具、转发调用;扩展端则负责拉取工具定义并注入执行体系。对于开发者而言,最重要的是保证 MCP Server 协议正确、配置可信、错误可观测,并在 Agent 进程变更后及时重启。