首页
/ chrome-devtools-mcp 设计原则解析:如何为 AI Agent 构建可靠的 Chrome DevTools MCP 服务

chrome-devtools-mcp 设计原则解析:如何为 AI Agent 构建可靠的 Chrome DevTools MCP 服务

2026-09-06 21:40:06作者:房伟宁

本文以仓库中的 docs/design-principles.md 为核心,逐条剖析 chrome-devtools-mcp(Chrome DevTools for coding agents)为 MCP 服务器制定功能时遵循的七条设计原则,并对照源码说明每条原则在工具定义、响应组装与错误处理中的具体落地方式。读完本文,你将掌握:如何判断一个面向 LLM 的工具 API 是否符合 Agent 友好设计,以及如何在自己的 MCP 工具中实践“Token 优化、可组合工具、自修复错误”等关键做法。

该文档原文开宗明义:“These are rough guidelines to follow when shipping features for the MCP server. Apply them with nuance.”(这些是向 MCP 服务器交付功能时应遵循的粗略指导原则,需结合具体情境灵活运用。)下面七条原则即完整出自该文档,每一条都将先给出原文表述,再结合仓库源码印证其实现路径。

一、Agent-Agnostic API:面向标准而非特定模型

原文原则:Agent-Agnostic API —— 使用 MCP 这类标准,不锁定任何一家 LLM,互操作性是关键。

在 chrome-devtools-mcp 中,这条原则体现在整个通信边界上:

  • 服务器以标准 MCP 协议对外暴露工具,任何符合 MCP 规范的客户端(文档中提到的 Antigravity、Claude、Cursor、Copilot 等编码 Agent)都可以接入,见 README
  • 所有工具的入参用标准 JSON Schema 描述。每个工具通过 ToolDefinition 声明 schemaToolHandler 在构造时用 zod 将其编译为 registeredInputSchema,注册进 MCP 协议层;参数校验由 schema 驱动,与调用方是哪类模型无关。
  • 返回值统一采用 MCP 的内容块模型:content 数组包含 textimage 两种块,structuredContent 作为可选的结构化载荷,定义见 src/McpResponse.tshandle() 的返回类型。

从源码结构看,服务器内部完全不假设“调用方是某家的 Agent”——它只依赖 MCP 规范定义的能力(如 image 内容块、structuredContent 扩展字段),这正是“不锁定单一 LLM”的直接体现。

二、Token-Optimized:返回语义摘要而非原始数据

原文原则:Token-Optimized —— 返回语义摘要。“LCP was 3.2s” 优于 5 万行 JSON;大量数据的正确归宿是文件。

这是七条原则中最“可量化”的一条,源码中有三处典型落地:

1. 摘要优先的输出层

McpResponse.format() 是所有工具响应汇聚的最终出口。它把各类数据渲染成人可读的文本行,而不是倾倒原始对象。例如 Lighthouse 结果被压缩为类别分数、通过/失败计数与报告清单,而非完整审计 JSON:

response.push('## Lighthouse Audit Results');
response.push(`Mode: ${summary.mode}`);
response.push(`URL: ${summary.url}`);
response.push('### Category Scores');
for (const score of summary.scores) {
  response.push(`- ${score.title}: ${(score.score ?? 0) * 100} (${score.id})`);
}
response.push('### Audit Summary');
response.push(`Passed: ${summary.audits.passed}`);
response.push(`Failed: ${summary.audits.failed}`);

性能追踪(trace)同理:attachTraceSummary() 挂载的是解析后的 TraceResultformat() 阶段调用 getTraceSummary() 生成摘要文本写入响应(见 src/McpResponse.tssrc/processors/PerformanceTrace.ts)。

2. 大列表分页,而不是全量回传

网络请求、控制台消息、堆快照聚合数据等列表型数据,在 src/utils/pagination.tspaginate() 支持下按页返回,并在响应中附带导航提示:

Showing 1-20 of 150 (Page 1 of 8).
Next page: 2

实现见 McpResponse.#dataWithPagination()。这让 Agent 可以按需翻页,而不是被一次性灌入全量日志。

3. 紧凑编码:toon / gcf 数据格式

对于仍需保留结构化信息的场景,format() 支持通过 --experimentalDataFormat(或其旧别名 --experimentalToonFormat)选择紧凑编码器,将结构化对象编码为比 JSON 更省 Token 的文本形式,格式解析入口在 src/McpResponse.ts。若缺少对应的 peer 依赖包,报错信息会直接给出可复制的安装命令:

The `@toon-format/toon` package is required to use --experimentalDataFormat=toon.
- For npx: npx --package chrome-devtools-mcp@latest --package @toon-format/toon@latest chrome-devtools-mcp --experimentalDataFormat=toon
- For npm: npm install @toon-format/toon

4. 大数据落文件

“Files are the right location for large amounts of data” 在快照工具中体现得最完整:McpResponse.#handleSnapshot() 支持 filePath 参数,指定后快照文本经 context.saveFile() 写入磁盘,响应中只回传一句 Saved snapshot to ...;未指定路径时才把 SnapshotFormatter 的渲染结果内联返回。文件写入统一走 McpContext.saveFile()/saveTemporaryFile(),其中 saveTemporaryFile() 使用 0o600 权限写入临时目录,兼顾安全与隔离。

三、Small, Deterministic Blocks:可组合的小工具而非魔法按钮

原文原则:Small, Deterministic Blocks —— 给 Agent 可组合的工具(Click、Screenshot),而不是魔法按钮。

源码中工具粒度刻意保持得很细:

  • 输入类动作拆分为独立的 click / fill / 按键等操作,每个操作有独立 schema 与独立 handler,见 src/tools/input.ts(如 click 工具的参数仅 uiddblClickincludeSnapshot 三项)。
  • 页面类动作(导航、弹窗处理等)独立于输入与调试工具,见 src/tools/pages.ts
  • “slim” 模式把工具面进一步压缩到最小集合:screenshot(无参数)、navigate(仅 url)、evaluate(仅 script),定义在 src/tools/slim/tools.ts

这种拆分的确定性来自两点:其一,每个 handler 只做一件事且副作用明确(readOnlyHint 注解如实标注,例如 screenshotfilePath 参数会写文件而被标为 readOnlyHint: false);其二,Agent 拿到的是可复用的原子操作,可以自由编排“导航 → 点击 → 截图”这类流程,而不必依赖一个行为不可预测的复合接口。src/tools/tools.ts 负责按类别汇总全部工具并交给 ToolHandler 注册。

四、Self-Healing Errors:可自修复的错误信息

原文原则:Self-Healing Errors —— 返回包含上下文与潜在修复方法的可执行错误(actionable errors)。

ToolHandler 是这条原则最集中的体现。它没有把“工具不可用”静默掉,而是把“为什么不可用 + 怎么修好”直接写进错误文本:

function buildDisabledMessage(toolName, flag, categoryLabel?) {
  // "Tool X is in category Y which is currently disabled.
  //  Enable it by running chrome-devtools start --flag=true."
}

(见 src/ToolHandler.ts)。Agent 读到这句话后,下一步动作是明确的:带上对应 flag 重启服务器,而不是盲目重试。

同类设计还有:

  • 未知参数错误buildUnknownArgumentsMessage() 会列出未知参数、期望参数全集,并明确指示“Remove it and retry”,见 src/ToolHandler.ts
  • 保留错误因果链:外层 catch 中若错误带 cause,会追加 Cause: ... 一行再返回,避免底层信息丢失,见 src/ToolHandler.ts
  • 超时错误带上下文:元素交互失败时抛出 Failed to interact with the element with uid X. The element did not become interactive within the configured timeout.src/tools/input.ts),Agent 由此知道该重试或先处理阻塞。
  • 浏览器重连自愈提示:浏览器重启后 page id 会变化,setReconnectNotice() 会让下一次响应开头附上一句 Note: the browser was restarted or reconnected since the last call. Page ids have changed. Call list_pages to see open pages.src/McpResponse.ts),直接告诉 Agent 如何恢复状态。

五、Human-Agent Collaboration:机器可读,人也读得懂

原文原则:Human-Agent Collaboration —— 输出必须机器可读(结构化),同时人类可读(摘要)。

McpResponse.format() 的组装过程本身就是该原则的样板:同一次调用中,它一边往 response 数组里 push 人类可读的文本行(## PagesEmulating viewport: {...}Showing 1-20 of 150 ...),一边把对应数据填充进 structuredContentpagesviewportpagination 等字段),最后返回:

return {
  content: [text, ...images],      // 给 LLM 的文本/图片块
  structuredContent,                // 给程序消费的结构化对象
};

结构化载荷的对外暴露由 --experimentalStructuredContent 开关控制(见 src/ToolHandler.ts),默认关闭以保持协议兼容性——这又是一个“默认简单、按需增强”的实例。此外,分页状态、navigatedToUrldialog 等状态也同时出现在文本与结构化两侧,保证人类在日志里排查问题时与 Agent 看到的是同一事实。

六、Progressive Complexity:默认简单,高级参数留给进阶用户

原文原则:Progressive Complexity —— 工具默认简单(高层动作),但为高级用户提供可选的进阶参数。

仓库中有三层递进的复杂度设计:

  1. slim 模式:最简工具集,例如 src/tools/slim/tools.ts 中的 screenshot 工具 schema 为空对象,调用零参数、语义单一。
  2. 完整工具的可选参数take_screenshot 在默认行为(视口截图)之上提供 format(png/jpeg/webp)、quality(0-100,仅 JPEG/WebP)、uidfullPagefilePath 等可选项(src/tools/screenshot.ts);click 提供 dblClickincludeSnapshot 等可选项(src/tools/input.ts)。不传参数即是最常见路径。
  3. 服务器级 flag 门控:实验类能力通过注解条件隐藏,未开启时工具直接不可调用,且错误信息指路(见上文自修复错误一节):
annotations: {
  category: ToolCategory.DEBUGGING,
  conditions: ['javascriptEvaluation'],   // 需开启对应 flag
},

(见 src/tools/slim/tools.ts;条件校验逻辑在 src/ToolHandler.ts)。

从源码结构看,这种分层让轻度用户只暴露最小攻击面与最小认知负担,而深度调试场景(脚本求值、扩展、WebMCP 等)不会被强制启用。

七、Reference over Value:重资产只返回引用

原文原则:Reference over Value —— 对截图、trace、视频等重资产,返回文件路径或资源 URI,绝不返回原始数据流。例外:部分 MCP 客户端支持对重资产的原生处理(例如直接显示图片)。

take_screenshot 的 handler 是三原则(Token 优化、引用优先、渐进复杂度)交汇的最完整案例,其输出策略按优先级为:

if (request.params.filePath) {
  // 1) 用户显式指定路径 → 落盘,仅回传 "Saved screenshot to ..."
  const result = await context.saveFile(screenshot, request.params.filePath, extension);
  response.appendResponseLine(`Saved screenshot to ${result.filename}.`);
} else if (screenshot.length >= 2_000_000) {
  // 2) 超过 2MB → 存临时文件,仅回传路径
  const {filepath} = await context.saveTemporaryFile(screenshot, `screenshot${extension}`);
  response.appendResponseLine(`Saved screenshot to ${filepath}.`);
} else {
  // 3) 小图 → 作为 image 内容块直接附加(文档所述“例外”)
  response.attachImage({mimeType: `image/${format}`, data: base64});
}

(见 src/tools/screenshot.ts

这正对应文档中 “Some MCP clients support a built-in handling of heavy assets e.g. directly displaying images. This could be an exception” 的表述:小尺寸图片以内联 image 块返回,让支持图片的客户端直接展示;大尺寸则退化为文件引用,保护上下文窗口。快照、trace 等其他重资产同样遵循“写文件、回引用”的路线(见第二节 filePath 分支与 storeTraceRecording()src/McpContext.ts 中的存储)。

小结:七条原则如何构成一条工程纪律

回看 docs/design-principles.md 的七条原则,它们在 chrome-devtools-mcp 源码中形成了闭环:

原则 源码落点
Agent-Agnostic API MCP 协议工具注册与 JSON Schema(src/ToolHandler.ts
Token-Optimized 摘要化 format()、分页、紧凑编码(src/McpResponse.ts
Small, Deterministic Blocks 细粒度工具定义(src/tools/input.tssrc/tools/slim/tools.ts
Self-Healing Errors 可执行的禁用/参数/重连提示(src/ToolHandler.ts
Human-Agent Collaboration 文本与 structuredContent 双通道输出(src/McpResponse.ts
Progressive Complexity slim 模式、可选参数、flag 门控(src/tools/slim/tools.ts
Reference over Value 2MB 阈值落盘 + 小图内联(src/tools/screenshot.ts

文档同时提醒这些原则是 “rough guidelines”,需要 “apply with nuance”——例如 take_screenshot 中对“小于 2MB 直接内联”就是一次有意识的权衡:既守住 Token 预算,又保留了客户端直显图片的体验。对任何要构建“给 LLM 用的工具接口”的团队,这七条原则及其源码证据,都是一份可直接对标的检查清单。

登录后查看全文
热门项目推荐
相关项目推荐