跳转到内容

MCP 工具设计原则

MCP 工具设计原则

学习目标

本章将学习 Chrome DevTools MCP 的七项设计原则,并理解这些原则如何具体落实到代码中。你将能够:

  • 掌握面向 Agent 的工具设计原则
  • 将抽象原则映射到具体的代码实现
  • 在自己的 MCP 工具开发中复用这些模式

前置知识


项目实践

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

详见 Token 优化与 Agent 响应策略

原则三: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,而非原始数据流。

代码体现

// 截图保存到临时文件,返回路径而非 Base64
const {filepath} = await context.saveTemporaryFile(screenshot, `screenshot.png`);
response.appendResponseLine(filepath);

对于网络请求详情:

// 请求体/响应体保存到 .network-response 文件
response.attachNetworkRequest(reqId, {
requestFilePath: "/path/request.json",
responseFilePath: "/path/response.json",
});

详见 Token 优化与 Agent 响应策略

设计取舍

取舍选择代价
大工具 vs 小工具小工具Agent 需要更多步骤完成任务
结构化 vs 文本文本为主,结构化可选Agent 需要解析文本或依赖实验性功能
内嵌数据 vs 文件引用文件引用Agent 需要额外工具读取文件

陷阱与对策

陷阱后果对策
一个”万能工具”Agent 无法组合操作,调试困难每个操作拆分为独立工具
错误信息只返回异常堆栈Agent 无法理解问题返回人类可读的错误 + 修复建议
所有参数都是必填的Agent 无法简单使用.optional() 标记可选参数

参考来源