主题
MCP 工具集成
本文引用的源码文件
本文引用的文件
目录
引言
本文件系统性梳理 AI Helper 的 MCP(Model Context Protocol)工具集成方案,覆盖客户端实现、远程工具发现、协议适配、错误处理、服务分组与开关控制、deny-list 模型、缓存同步与竞态处理、用户界面展示与安全控制等关键主题。文档面向开发者与高级用户,既提供高层架构图,也给出代码级流程与关键路径引用,便于定位与二次扩展。
项目结构
本项目采用双进程架构:Chrome 扩展端(Manifest V3,位于 src/)与独立本地代理端 Agent(Node.js,位于 agent/)。MCP 能力由 Agent 端负责连接外部 MCP Server,并通过 HTTP API 暴露给扩展端;扩展端在 Background 中动态注册 MCP 工具,并在侧边栏与选项页提供管理与展示能力。
graph TB subgraph "扩展端Chrome 扩展" BG["Background<br/>tool-executor.js"] SP["Side Panel<br/>tool-panel.js / skill-selector.js / agent-manager.js"] OPT["Options 页面<br/>toolbox-mcp.js / toolbox-config.js"] end subgraph "Agent 端Node.js" SRV["HTTP 路由<br/>server.js"] REG["注册表<br/>registry.js"] CFG["配置管理<br/>mcp-config.js"] CLI["MCP 客户端<br/>client.js"] end EXT["外部 MCP Server"] OPT --> SRV SP --> BG BG --> SRV SRV --> REG REG --> CLI CLI --> EXT
图表来源
- src/background/tool-executor.js#L547-L651
- agent/src/server.js#L1867-L1923
- agent/src/mcp/registry.js#L1-L55
- agent/src/mcp/client.js#L35-L127
章节来源
核心组件
- Agent 端 MCP 客户端:封装多种传输层(stdio/SSE/streamableHttp/websocket),统一 connect/listTools/callTool/disconnect 生命周期。
- Agent 端 MCP 注册表:维护已连接 Client 实例,提供工具扁平化查询、按 serverId 过滤、工具调用、连接/断开、启停状态查询。
- Agent 端 MCP 配置:持久化 mcp_servers.json,支持增删改查、启用/禁用、写锁保护与内存缓存。
- 扩展端 Background 工具注入:拉取 MCP 工具列表,构建 RAW_TOOLS/BUILTIN_TOOLS/TOOL_HANDLERS,并写入 chrome.storage.local.mcpTools。
- 扩展端 Side Panel 工具面板:从 storage 读取缓存,监听变更实时刷新,渲染服务分组、展开收起、工具详情与状态指示器。
- 扩展端 Options 页面:MCP 服务器增删改查、连接/断开、启用/禁用、环境变量与请求头编辑。
章节来源
- agent/src/mcp/client.js#L35-L184
- agent/src/mcp/registry.js#L1-L181
- agent/src/mcp/mcp-config.js#L1-L129
- src/background/tool-executor.js#L547-L739
- src/side_panel/tool-panel.js#L45-L239
- src/options/toolbox-mcp.js#L1-L601
架构总览
MCP 工具链路的端到端流程如下:用户在 Options 页面配置 MCP 服务器 → Agent 端通过 HTTP API 持久化配置并建立连接 → Registry 维护 Client 实例并暴露工具列表 → Background 拉取工具并动态注册为内置工具 → Side Panel 从 storage 读取缓存并渲染分组 UI。
sequenceDiagram
participant User as "用户"
participant Options as "Options 页面<br/>toolbox-mcp.js"
participant Server as "Agent HTTP 路由<br/>server.js"
participant Registry as "MCP 注册表<br/>registry.js"
participant Client as "MCP 客户端<br/>client.js"
participant ExtBG as "Background<br/>tool-executor.js"
participant Storage as "chrome.storage.local"
participant SidePanel as "Side Panel<br/>tool-panel.js"
User->>Options : 添加/编辑/连接 MCP 服务器
Options->>Server : POST /api/mcp/servers / connect / toggle
Server->>Registry : add/connect/toggle
Registry->>Client : connect()
Client-->>Registry : {success, toolCount}
Registry-->>Server : {success, servers/tools}
Server-->>Options : 返回结果
ExtBG->>Server : GET /api/mcp/tools + /servers
Server-->>ExtBG : tools[] + servers[]
ExtBG->>Storage : set({mcpTools})
SidePanel->>Storage : get(mcpTools) + onChanged
SidePanel-->>User : 渲染服务分组与工具详情
图表来源
- src/options/toolbox-mcp.js#L510-L560
- agent/src/server.js#L1867-L1923
- agent/src/mcp/registry.js#L133-L167
- agent/src/mcp/client.js#L96-L127
- src/background/tool-executor.js#L547-L651
- src/side_panel/tool-panel.js#L81-L102
详细组件分析
Agent 端 MCP 客户端(协议适配与错误处理)
- 传输适配:根据 transport 字段选择 stdio/sse/streamableHttp/websocket,校验必要参数(command/url),支持 headers/env/cwd/args。
- 连接流程:创建 Client,connect 后获取 serverInfo/capabilities,listTools 填充 tools,设置 connected=true。
- 工具调用:callTool 将 name/arguments 转发至远端,解析 content.text 拼接文本内容,统一返回 success/content/raw 或 error。
- 错误处理:连接失败时 disconnect 清理资源;调用失败时返回结构化错误信息;disconnect/close 均做 try/catch 保护。
classDiagram
class McpClient {
+string serverId
+string serverName
+object serverConfig
-object client
-object transport
+boolean connected
+array tools
+object serverInfo
+connect() Promise~object~
+callTool(toolName, args) Promise~object~
+disconnect() Promise~void~
+getStatus() object
-_createTransport() object
}
图表来源
章节来源
Agent 端 MCP 注册表(工具发现与调用)
- 初始化:启动时遍历 enabled 的服务器,逐个 connect,统计成功/失败数量并输出日志。
- 工具查询:getMcpTools 扁平化所有已连接 Client 的工具,支持按 serverId 过滤。
- 工具调用:callMcpTool 校验 serverId 存在且 connected,转发到对应 Client.callTool。
- 连接管理:connectMcpServer 先断开旧连接再新建;disconnectMcpServer 清理 Map 条目;shutdownMcpRegistry 关闭全部。
flowchart TD
Start(["调用 callMcpTool(serverId, toolName, args)"]) --> Lookup["查找 clients.get(serverId)"]
Lookup --> Exists{"是否存在?"}
Exists --> |否| Err1["返回 serverNotFound"]
Exists --> |是| Connected{"是否已连接?"}
Connected --> |否| Err2["返回 serverNotConnected"]
Connected --> |是| Call["client.callTool(toolName, args)"]
Call --> Return["返回 {success, content/raw/error}"]
图表来源
章节来源
- agent/src/mcp/registry.js#L26-L55
- agent/src/mcp/registry.js#L89-L108
- agent/src/mcp/registry.js#L133-L167
Agent 端 MCP 配置(持久化与写锁)
- 存储位置:~/.ai-helper-agent/mcp_servers.json。
- 读写策略:内存缓存 + mtime 失效;写操作使用 withWriteLock 串行化,避免并发写损坏。
- 功能接口:load/save/add/remove/toggle,统一返回 success 或带 i18n 的错误消息。
flowchart TD
A["addMcpServer(server)"] --> B["loadMcpConfig()"]
B --> C{"id 已存在?"}
C --> |是| D["合并更新"]
C --> |否| E["push 新项"]
D --> F["saveMcpConfig(config)"]
E --> F
F --> G["withWriteLock 串行写入 + 更新缓存"]
图表来源
章节来源
扩展端 Background 工具注入与调度
- 全局开关:检查 mcpEnabled,未开启则跳过加载。
- 互斥加载:mcpLoadLock 保证多次 load/unload 串行执行,避免竞态。
- 并行拉取:Promise.all 同时获取 tools 与 servers,构建 disabledServerIds 集合。
- 动态注册:为每个工具生成 id=mcp_{serverId}_{name},注入 RAW_TOOLS/BUILTIN_TOOLS/TOOL_HANDLERS,并记录元信息映射。
- 缓存同步:将过滤后的工具列表写入 chrome.storage.local.mcpTools,供 Side Panel 消费。
- 会话强制下发:sessionForcedMcpServers 保存“/”触发器指定的服务,绕过关闭/排除列表。
flowchart TD
S(["loadMcpTools()"]) --> Lock["等待 mcpLoadLock"]
Lock --> CheckGlobal{"mcpEnabled === true ?"}
CheckGlobal --> |否| End1["返回 0"]
CheckGlobal --> |是| Clean["unloadMcpToolsInternal()"]
Clean --> Fetch["Promise.all(getMcpTools(), getMcpServers())"]
Fetch --> Filter["过滤 disabledServerIds"]
Filter --> Register["注册 RAW_TOOLS/BUILTIN_TOOLS/HANDLERS"]
Register --> Cache["set(chrome.storage.local.mcpTools)"]
Cache --> Release["释放 mcpLoadLock"]
图表来源
- src/background/tool-executor.js#L547-L651
- src/background/tool-executor.js#L653-L684
- src/background/tool-executor.js#L711-L739
章节来源
服务分组与 deny-list 模型
- 分组维度:按 serverId 聚合工具,形成服务级行,显示 serverName、工具数、ID 与展开按钮。
- 三层判定:
- closed:用户在工具配置弹窗关闭的服务(agentMcpClosedServers_${agentId})。
- excluded:助手编辑器排除的服务(agent.mcpExcludedServerIds)。
- forced:/ 触发器手动指定的服务,强制下发并绕过前两者。
- 生效规则:forced > 非 closed/excluded;closed/excluded 为 deny-list,不在列表中即视为开放。
flowchart TD
T["工具过滤"] --> CheckMcp{"是否为 MCP 工具?"}
CheckMcp --> |否| Keep["保留"]
CheckMcp --> |是| Global{"mcpEnabled && agentConnected && registered ?"}
Global --> |否| Drop["丢弃"]
Global --> Forced{"forcedMcpServerIds 包含?"}
Forced --> |是| Keep
Forced --> |否| Closed{"closed 包含?"}
Closed --> |是| Drop
Closed --> |否| Excluded{"excluded 包含?"}
Excluded --> |是| Drop
Excluded --> |否| Keep
图表来源
章节来源
扩展端 Side Panel 工具展示与交互
- 数据源:优先通过 runtime.sendMessage('GET_MCP_TOOLS') 直接从 Background 获取,否则回退到 chrome.storage.local.mcpTools。
- 实时更新:监听 storage.onChanged,当 mcpTools/mcpEnabled 变化时刷新分类计数、标题与工具栏状态点。
- 服务分组渲染:按 serverId 分组,计算 effectiveOpen(未被 closed 且未被 excluded),未开放的服务排在后面并附角标。
- 交互行为:
- 服务行 checkbox:切换服务级开关(denied/open)。
- 展开按钮:展开只读工具列表(名称+描述)。
- 全选/取消全选:仅对当前可见且可操作的项生效。
- 计数与角标:按服务粒度统计 open/total,区分被助手排除的行不计入 open。
sequenceDiagram
participant SP as "Side Panel"
participant BG as "Background"
participant Store as "chrome.storage.local"
SP->>BG : sendMessage('GET_MCP_TOOLS')
BG-->>SP : {success, tools}
alt 获取失败
SP->>Store : get(['mcpTools'])
Store-->>SP : mcpTools[]
end
SP->>Store : onChanged 监听 mcpTools/mcpEnabled
SP-->>User : 渲染服务分组/工具详情/状态指示器
图表来源
- src/side_panel/tool-panel.js#L81-L102
- src/side_panel/tool-panel.js#L148-L202
- src/side_panel/skill-selector.js#L574-L599
章节来源
- src/side_panel/tool-panel.js#L45-L239
- src/side_panel/tool-panel.js#L560-L759
- src/side_panel/skill-selector.js#L560-L759
- src/side_panel/agent-manager.js#L920-L1000
扩展端 Options 页面:MCP 服务器管理
- 列表渲染:显示连接状态、启用状态、transport、命令/URL、工具列表折叠区。
- 表单编辑:支持 stdio/SSE/streamableHttp/websocket 四种传输方式,按需显示 command/args/url,以及 env/headers 多行键值对。
- 操作接口:add/remove/connect/disconnect/toggle,统一通过 agentApi 调用后端路由。
- 搜索与筛选:支持按 name/id/command/url/args 模糊搜索,以及 enabled/disabled 状态筛选。
章节来源
Agent 端 HTTP 路由(API 契约)
- 服务器管理:POST /api/mcp/servers(新增)、DELETE /api/mcp/servers(删除)。
- 连接控制:POST /api/mcp/servers/connect、/disconnect、/toggle(enabled 切换时若禁用会先断开)。
- 工具调用:POST /api/mcp/call(serverId + toolName + args)。
- 审计日志:所有关键操作均调用 logSystem 记录 serverId、success 等上下文。
章节来源
依赖关系分析
- Agent 端:
- registry.js 依赖 client.js(连接与调用)、mcp-config.js(配置读写)、i18n(多语言错误提示)。
- server.js 作为 HTTP 入口,依赖 registry.js 提供的工具与连接能力。
- 扩展端:
- background/tool-executor.js 依赖 AgentClient(HTTP 调用)、chrome.storage.local(缓存)、内部工具注册表。
- side_panel/tool-panel.js 与 skill-selector.js 依赖 chrome.storage.local 与 runtime message 机制。
- options/toolbox-mcp.js 依赖 toolbox-shared.agentApi 与 i18n。
graph LR REG["registry.js"] --> CLI["client.js"] REG --> CFG["mcp-config.js"] SRV["server.js"] --> REG BG["tool-executor.js"] --> SRV SP["tool-panel.js"] --> BG OPT["toolbox-mcp.js"] --> SRV
图表来源
- agent/src/mcp/registry.js#L1-L5
- agent/src/server.js#L1867-L1923
- src/background/tool-executor.js#L547-L651
- src/options/toolbox-mcp.js#L510-L560
章节来源
- agent/src/mcp/registry.js#L1-L181
- agent/src/server.js#L1867-L1923
- src/background/tool-executor.js#L547-L739
- src/options/toolbox-mcp.js#L1-L601
性能与并发特性
- 互斥锁:
- Background 侧 mcpLoadLock 串行化 load/unload,避免重复注册与缓存不一致。
- Agent 侧 mcp-config.js 使用 withWriteLock 串行化写操作,防止并发写损坏配置文件。
- 并行拉取:Background 侧 Promise.all 并行获取 tools 与 servers,减少首屏延迟。
- 缓存策略:
- Background 将工具列表写入 chrome.storage.local.mcpTools,Side Panel 优先走 runtime message 直取,失败再回退 storage。
- Agent 侧 mcp-config.js 使用内存缓存 + mtime 失效,降低频繁 IO。
- 工具定义压缩:description 与 inputSchema 经过压缩函数处理,减少发送给模型的负载。
[本节为通用性能建议,不直接分析具体文件]
故障排查指南
- 无法加载 MCP 工具:
- 检查全局开关 mcpEnabled 是否为 true。
- 确认 Agent 已连接且支持 MCP。
- 查看 Background 日志:[Background] load MCP tool failed...
- 工具未出现在侧边栏:
- 确认该工具所属服务未被 disabledServerIds 过滤。
- 检查服务是否在 closed/excluded 列表中,或被助手排除。
- 验证 chrome.storage.local.mcpTools 是否已写入。
- 连接失败:
- 检查 transport 必填参数(stdio 需要 command,SSE/streamableHttp/websocket 需要 url)。
- 查看 Agent 端日志与 i18n 错误消息(如 mcp.sseRequiresUrl、mcp.stdioRequiresCommand)。
- 工具调用失败:
- 确认 serverId 存在且 connected。
- 检查 callTool 返回的 error.message,必要时重试或降级。
章节来源
- src/background/tool-executor.js#L547-L651
- agent/src/mcp/client.js#L96-L127
- agent/src/mcp/registry.js#L116-L128
- src/shared/locales/en.js#L959-L985
结论
本方案以 Agent 端为核心,统一封装 MCP 协议客户端与注册表,通过 HTTP API 向扩展端暴露工具发现与调用能力;扩展端在 Background 动态注册工具并缓存,Side Panel 与 Options 提供直观的管理与展示界面。通过 deny-list 模型与三层判定(closed/excluded/forced),在保证安全可控的前提下,灵活支持服务级开关与会话级强制下发。整体设计兼顾可扩展性、可观测性与用户体验。
[本节为总结性内容,不直接分析具体文件]
附录:配置与调用示例路径
- 配置 MCP 服务器连接(Options 页面):
- 处理工具调用响应与错误恢复(Background):
- 动态注册与处理器:src/background/tool-executor.js#L597-L623
- 调用 Agent 并返回结果:src/background/tool-executor.js#L616-L619
- 服务分组与 deny-list 判定:
- 过滤逻辑与 forced 强制下发:src/background/tool-executor.js#L1208-L1247
- Side Panel 工具展示与交互:
- 缓存加载与 onChanged 监听:src/side_panel/tool-panel.js#L81-L102, src/side_panel/tool-panel.js#L148-L202
- 服务分组与展开收起:src/side_panel/tool-panel.js#L560-L759
- 服务列表排序与有效开放标记:src/side_panel/skill-selector.js#L574-L599
- Agent 端 MCP 客户端与注册表:
- 客户端连接/调用/断开:agent/src/mcp/client.js#L96-L184
- 注册表工具查询与调用:agent/src/mcp/registry.js#L89-L128
- 配置持久化与写锁:agent/src/mcp/mcp-config.js#L69-L98