MCP 工具设计原则
MCP 工具设计原则
学习目标
本章将学习 Chrome DevTools MCP 的七项设计原则,并理解这些原则如何具体落实到代码中。你将能够:
- 掌握面向 Agent 的工具设计原则
- 将抽象原则映射到具体的代码实现
- 在自己的 MCP 工具开发中复用这些模式
前置知识
- MCP 协议与生态集成 — Model Context Protocol 的传输层与客户端/服务器架构
- 工具调用协议与执行模型 — Function Calling 协议与工具系统四层架构
- Token 优化与 Agent 响应策略 — 四种 Token 优化策略
项目实践
Chrome DevTools MCP 在 docs/design-principles.md 中明确列出了七项设计原则。这些不是空泛的口号,而是直接体现在代码结构和 API 设计中。
原则一:Agent-Agnostic API
使用 MCP 等标准协议。不要锁定单一 LLM。互操作性是关键。
代码体现:
// 使用 @modelcontextprotocol/sdk 作为唯一协议层const server = new McpServer( { name: 'chrome_devtools', version: VERSION }, { capabilities: { logging: {} } },);// 通过 StdioServerTransport 与任何 MCP Client 通信项目不依赖任何特定 AI 厂商的 SDK——它只使用 MCP 协议的 McpServer 类,通过 stdio 传输 JSON-RPC 消息。这意味着 Claude Code、Cursor、Copilot、Codex 等任何 MCP Client 都能使用同一套工具。
原则二:Token-Optimized
返回语义摘要。“LCP was 3.2s” 比 50k 行 JSON 更好。
代码体现:
McpResponse 类不直接返回原始 Trace JSON,而是通过 trace-processing/parse.ts 中的 getTraceSummary() 和 getInsightOutput() 提取摘要:
// McpResponse 调用链attachTraceSummary(trace) → getTraceSummary(trace) → 返回精简摘要attachTraceInsight(trace, insightSetId, insightName) → getInsightOutput(...) → 返回单一 Insight原则三:Small, Deterministic Blocks
为 Agent 提供可组合的工具(点击、截图),而非魔法按钮。
代码体现:
Chrome DevTools MCP 将浏览器操作拆分为 40+ 个细粒度工具:
- 点击、拖拽、填写、键盘输入、悬浮——每个操作独立
- 截图、快照、脚本执行——每个获取信息的方式独立
- 性能 Trace 录制、停止、分析——分为三个步骤
这种设计让 Agent 灵活组合工具,而不是依赖一个 doEverything() 工具。
原则四:Self-Healing Errors
返回可操作的错误,包含上下文和潜在修复方案。
代码体现:
当工具因 Category 未启用而不可用时,ToolHandler 返回包含具体启用命令的错误信息:
Tool install_extension is in category Extensions which is currently disabled.Enable it by running chrome-devtools start --categoryExtensions=true.For more information check the README.当未知参数传入时:
Unknown argument for tool "navigate_page": "timeout_ms".Expected arguments: "pageId", "url", "type". Remove it and retry.这种错误信息直接告诉 Agent 哪里错了 + 怎么修 + 应该用什么参数。
原则五:Human-Agent Collaboration
输出必须对机器可读(结构化)且对人类可读(摘要)。
代码体现:
每个工具的响应通过 McpResponse.appendResponseLine() 追加人类可读的摘要行,同时通过 structuredContent 字段(--experimentalStructuredContent 标志启用)为机器提供结构化数据:
// 文本响应(人类可读)"Lighthouse audit completed. Performance: 85, Accessibility: 92."
// structuredContent(机器可读,实验性功能){ "scores": [{"id": "performance", "score": 85}] }原则六:Progressive Complexity
工具默认简单(高级操作),但提供高级可选参数供进阶用户。
代码体现:
// 基础调用:只需必需参数take_screenshot()
// 进阶调用:添加可选参数take_screenshot({ filePath: "/path/to/save.png", pageId: 2 })
// ToolDefinition 中可选参数用 .optional() 标记schema: { pageId: zod.number().optional().describe("..."), filePath: zod.string().optional().describe("..."),}工具分类(ToolCategory)本身也是渐进式复杂度的体现:核心工具(input、navigation、debugging)默认启用,高级工具(extensions、third-party、webmcp)默认关闭,需显式启用。
原则七:Reference over Value
对于重量级资源(截图、Trace、视频),返回文件路径或 URI,而非原始数据流。
代码体现:
// 截图保存到临时文件,返回路径而非 Base64const {filepath} = await context.saveTemporaryFile(screenshot, `screenshot.png`);response.appendResponseLine(filepath);对于网络请求详情:
// 请求体/响应体保存到 .network-response 文件response.attachNetworkRequest(reqId, { requestFilePath: "/path/request.json", responseFilePath: "/path/response.json",});设计取舍
| 取舍 | 选择 | 代价 |
|---|---|---|
| 大工具 vs 小工具 | 小工具 | Agent 需要更多步骤完成任务 |
| 结构化 vs 文本 | 文本为主,结构化可选 | Agent 需要解析文本或依赖实验性功能 |
| 内嵌数据 vs 文件引用 | 文件引用 | Agent 需要额外工具读取文件 |
陷阱与对策
| 陷阱 | 后果 | 对策 |
|---|---|---|
| 一个”万能工具” | Agent 无法组合操作,调试困难 | 每个操作拆分为独立工具 |
| 错误信息只返回异常堆栈 | Agent 无法理解问题 | 返回人类可读的错误 + 修复建议 |
| 所有参数都是必填的 | Agent 无法简单使用 | 用 .optional() 标记可选参数 |