跳转到内容

工具系统设计:ToolDefinition + ToolHandler + McpResponse

工具系统设计:ToolDefinition + ToolHandler + McpResponse

学习目标

本章分析 Chrome DevTools MCP 的工具系统三层流水线。你将理解:

  • ToolDefinition 的工具元数据规范(Zod Schema、Category、Annotations)
  • defineTool() / definePageTool() 工厂函数的设计意图
  • ToolHandler 的执行管道:校验 → 启用检查 → 执行 → 响应
  • McpResponse 的构建器模式(appendResponseLine、attachImage、includeSnapshot)
  • 工具模块按功能分类的组织方式

前置知识


项目实践

ToolDefinition:工具元数据规范

每个工具通过 ToolDefinition 接口定义:

interface ToolDefinition {
name: string; // 工具名称(如 "take_screenshot")
description: string; // 工具描述(Agent 用来判断是否调用)
annotations: {
category: ToolCategory; // 分类(input/navigation/performance 等)
readOnlyHint: boolean; // 是否只读(影响 Agent 的调用决策)
conditions?: string[]; // 启用条件(如 "experimentalVision")
};
schema: ZodRawShape; // Zod 参数 schema
blockedByDialog: boolean; // 对话框打开时是否阻塞
verifyFilesSchema: Array<keyof Schema>; // 需要路径校验的参数
handler: (request, response, context) => Promise<void>;
}

defineTool / definePageTool 工厂函数

项目提供两种工厂函数:

defineTool(definition)
→ 注册到全局工具列表
→ handler 接收 { params }
definePageTool(definition)
→ 自动设置 pageScoped: true
→ handler 接收 { params, page }
→ 在启用 pageIdRouting 时自动获得 pageId 参数

工厂函数的优势

  • 统一工具定义的接口
  • 自动附加 pageScoped 标记
  • 支持参数化工厂((args) => ToolDefinition

工具模块组织

工具按功能分组到 13 个模块文件中:

模块文件工具类别工具数
input.ts输入自动化10
pages.ts导航自动化6
emulation.ts设备模拟2
performance.ts性能分析3
network.ts网络调试2
console.ts / screenshot.ts / script.ts / snapshot.ts调试工具8
memory.ts内存检测5
lighthouse.tsLighthouse 审计1
extensions.ts扩展调试5
screencast.ts屏幕录制2
thirdPartyDeveloper.ts第三方工具2
webmcp.tsWebMCP2
slim/tools.tsSlim 模式3

ToolHandler:执行管道

每次工具调用的完整执行链:

handle(params):
1. 检查 disabled → 返回错误(含启用命令)
2. 检查 unknownArgumentNames → 返回错误(含正确参数列表)
3. await toolMutex.acquire() → FIFO 排队
4. await getContext() → 确保浏览器就绪
5. context.detectOpenDevToolsWindows() → 更新 DevTools 状态
6. new McpResponse() / SlimMcpResponse() → 响应构建器
7. verifyFilesSchema → 路径白名单校验
8. isPageScopedTool ? handler({ params, page }) : handler({ params })
9. response.handle() → 生成最终 content
10. ClearcutLogger.logToolInvocation() → 遥测
11. guard.dispose() → 释放 Mutex

McpResponse:构建器模式

McpResponse 使用构建器模式逐步组装响应:

const response = new McpResponse();
// 工具 handler 调用这些方法
response.appendResponseLine("Navigation completed.");
response.attachImage({ data: "...", mimeType: "image/png" });
response.includeSnapshot({ verbose: true });
response.setIncludeNetworkRequests(true, { pageIdx: 0, pageSize: 50 });
response.setIncludeConsoleData(true, { types: ["error", "warn"] });
response.attachTraceSummary(traceResult);
response.attachLighthouseResult(lighthouseData);
response.setError(err); // 可选,标记错误状态

响应生成

response.handle() 方法将所有追加的内容合并为最终的 CallToolResult,包括:

  • content 数组(TextContent + ImageContent
  • isError 标志
  • structuredContent(可选,实验性)

设计取舍

取舍选择原因
定义时检查 vs 运行时检查两者定义时决定是否注册,运行时做参数校验
统一 Handler vs 每个工具自建管道统一 Handler复用 Mutex、遥测、错误处理逻辑
构建器 vs 一次性返回构建器Handler 按需追加内容,不强制全部生成

陷阱与对策

陷阱后果对策
Handler 中抛出未捕获异常Mutex 未释放finally 块保证 guard.dispose()
参数 schema 描述不完整Agent 无法正确使用参数每个 schema 字段必须有 .describe()
readOnlyHint 设置错误Agent 错误判断是否需要确认有副作用的工具(文件写入、页面操作)设为 false

参考来源