跳转到内容

三层架构:Server → Handler → Context → Page

三层架构:Server → Handler → Context → Page

学习目标

本章分析 Chrome DevTools MCP 的四层分离架构。你将理解:

  • createMcpServer() 的职责:McpServer 实例化、浏览器启动、工具注册
  • ToolHandler 的职责:参数校验、工具启用检查、Mutex 获取
  • McpContext 的职责:全局状态、浏览器实例、事件收集器
  • McpPage 的职责:每页状态封装
  • 延迟初始化模式如何降低启动成本

前置知识


项目实践

整体架构

Layer 1: createMcpServer() — 入口与注册

src/index.tscreateMcpServer() 是整个系统的入口:

createMcpServer(args, options):
1. 初始化遥测(ClearcutLogger)
2. 创建 McpServer 实例
3. 设置 roots/list 请求处理器
4. 定义 getContext() —— 延迟加载浏览器
5. 创建 Mutex(全局工具执行锁)
6. createTools() —— 生成所有工具定义
7. 遍历工具列表,registerTool() 注册到 McpServer
8. 加载 issue descriptions(DevTools 问题描述)

延迟初始化模式

getContext() 是一个懒加载函数,只在首次工具调用时创建 McpContext

let context: McpContext;
async function getContext() {
// 根据参数决定连接还是启动
const browser = (hasConnectionParams)
? await ensureBrowserConnected(...)
: await ensureBrowserLaunched(...);
// 复用已有 context(browser 相同时)
if (context?.browser !== browser) {
context = await McpContext.from(browser, logger, opts);
}
return context;
}

这种设计避免了 MCP Server 启动时立即打开浏览器的开销。

Layer 2: ToolHandler — 执行管道

ToolHandler 是每次工具调用的执行中枢:

ToolHandler.handle(params):
1. 检查工具是否被禁用(category/condition)
2. 校验未知参数(unknownArgumentNames)
3. 获取 Mutex(确保顺序执行)
4. 获取 McpContext
5. 检测 DevTools 窗口
6. 创建 McpResponse 或 SlimMcpResponse
7. 文件路径校验(verifyFilesSchema)
8. 执行工具 handler
9. 生成响应内容
10. 记录遥测
11. 释放 Mutex(finally 块)

Layer 3: McpContext — 全局状态

McpContext 封装了所有全局状态:

状态字段说明
浏览器browserPuppeteer Browser 实例
页面列表#pages, #mcpPages当前页面快照
选中页面#selectedPage当前操作的页面
Isolated contexts#isolatedContexts隔离的 BrowserContext
网络请求#networkCollector按页面收集请求
控制台#consoleCollector按页面收集消息
Trace 结果#traceResults最近一次 Trace 数据
Roots#rootsMCP workspace roots

Layer 4: McpPage — 每页状态

McpPage 封装了每个页面的独立状态:

状态字段说明
Puppeteer 页面pptrPage底层 CDP 页面引用
页面 IDid自增数字标识
文本快照textSnapshot无障碍树快照
模拟设置emulationSettingsCPU/网络/视口等模拟状态
Isolated contextisolatedContextName所属隔离上下文
DevTools 页面devToolsPage关联的 DevTools 窗口
对话框#dialog当前打开的对话框

设计取舍

取舍选择原因
单体 Context vs 分层 Context分层每个层级职责清晰,易于测试
每次新建 Context vs 复用复用避免重复初始化事件监听器
工具注册时检查 vs 执行时检查两者结合注册时决定是否暴露,执行时做参数校验

陷阱与对策

陷阱后果对策
Context 与浏览器断开后仍被使用工具调用全部失败getContext() 每次检查 browser 实例
页面关闭后 McpPage 未被清理内存泄漏createPagesSnapshot() 中的 prune 逻辑清理孤儿
Mutex 未释放(异常场景)后续工具全部阻塞finally 块中 guard.dispose()

参考来源