chrome-devtools-mcp 第三方开发者工具(3P Developer Tools):让页面把自己的运行时数据暴露给 AI Agent
chrome-devtools-mcp 第三方开发者工具3P Developer Tools让页面把自己的运行时数据暴露给 AI Agent【免费下载链接】chrome-devtools-mcpChrome DevTools for coding agents项目地址: https://gitcode.com/GitHub_Trending/chr/chrome-devtools-mcp本文基于 chrome-devtools-mcp 仓库中的 docs/third-party-developer-tools.md 展开讲解如何通过devtoolstooldiscovery事件在网页中注册ToolGroup使 AI Agent 能够读取静态分析无法推断的运行时状态组件树、内部调试数据等并结合仓库源码剖析工具发现、参数校验AJV、DOM 元素 UID 映射与结果可序列化处理的完整链路。读完本文你可以为自己的 Web 应用编写可被 MCP 客户端调用的页面内工具并理解其在 chrome-devtools-mcp 中的真实执行机制。功能定位为什么需要第三方开发者工具第三方开发者工具Third-party developer tools允许你的 Web 应用把内部状态、组件层级或特定调试数据直接暴露给 Chrome DevTools for Agents即 chrome-devtools-mcp。这些信息的共同特征是无法通过静态分析读 HTML、读源码、看网络请求推断出来。例如 React 组件的内部 state、前端路由的当前匹配结果、应用内缓存的 feature flag 等。通过该机制Agent 在调试会话中能获得更丰富、更可操作的上下文——它不再只是看着页面 DOM而是可以询问页面本身。工具发现机制基于事件的一次握手chrome-devtools-mcp 使用事件驱动机制来发现页面暴露的工具整体流程分三步与 docs/third-party-developer-tools.md 的 How It Works 一节一致事件分发Event Dispatchchrome-devtools-mcp 在全局window对象上派发一个devtoolstooldiscovery自定义事件监听Listener你的应用监听该事件并在回调中提供工具定义响应Response你的应用必须调用event.respondWith()传入一个ToolGroup对象完成注册。触发时机chrome-devtools-mcp 会在页面导航类操作后自动请求这份工具列表例如new_page、navigate_page也支持通过list_3p_developer_tools()这个 MCP 工具显式请求。这一点在源码中得到印证src/tools/pages.ts 中多个页面工具新建页、导航、选择页等的 handler 都会调用response.setListThirdPartyDeveloperTools()而 src/McpResponse.ts 中的#handleThirdPartyDevelopeTools()会在开关开启且标记置位时调用page.getToolGroups()真正发起发现流程。源码级解析getToolGroups 如何完成发现发现逻辑集中在 src/McpPage.ts 的getToolGroups()中从源码结构看它比文档描述的三步更防御先探测监听器是否存在。它通过 Puppeteer 内部客户端调用 CDP 命令DOMDebugger.getEventListeners检查window上是否已注册devtoolstooldiscovery监听器若没有则直接返回空数组避免无谓地派发事件。注入respondWith并派发事件。在页面上下文中构造new CustomEvent(devtoolstooldiscovery)为其挂上自定义的event.respondWith方法然后window.dispatchEvent(event)。校验 ToolGroup 结构。respondWith内部会做类型检查toolGroup.name必须是字符串tools必须是数组每个tool的name、description必须是字符串inputSchema必须是对象execute必须是函数。校验失败的 group 或 tool 会被console.error(Invalid toolGroup: / Invalid tool:)报告并丢弃——这意味着页面里一个畸形定义不会阻断其他合法工具注册。同步/异步响应兼容。若页面同步调用了respondWithPromise 立即 resolve否则退回setTimeout(0)给微任务和异步回调比如await后再respondWith留出一帧时间最终兜底 resolve 空数组。把可执行工具组存进页面。首次收到合法ToolGroup时代码会把整个含execute函数的 toolGroups 缓存在window.__dtmcp.toolGroups并挂载一个统一的执行助手window.__dtmcp.executeTool(toolName, args)——这正是文档中 Tool Invocation 一节提到的evaluate_script直调入口。DOM 参数的 UID 化。对收集到的每个工具的inputSchema调用replaceHtmlElementsWithUids把其中的 DOM 元素替换为可序列化的 UID 引用保证 schema 能安全传回 MCP 层。需要注意一个细节MCP 服务端的接口定义src/tools/thirdPartyDeveloper.ts中的ToolDefinition不含execute字段ToolGroup则是泛型ToolGroupT extends ToolDefinition可执行版本写作ToolDefinition {execute: ...}。这是由架构决定的execute始终在页面 JS 上下文中运行MCP 进程只持有其序列化镜像。实现步骤在你的应用中注册工具类型定义你的工具必须遵循ToolDefinition与ToolGroup接口源自原文档与 src/tools/thirdPartyDeveloper.ts 中的定义对应export interface ToolDefinition { name: string; description: string; inputSchema: JSONSchema7; execute: (args: Recordstring, unknown) unknown; } export interface ToolGroup { name: string; description: string; tools: ToolDefinition[]; }其中inputSchema是标准 JSON Schema 7它有两个作用一是让 LLM 理解工具签名二是在服务端被 AJV 编译后用于运行时参数校验见下文工具调用一节。完整示例在页面加载后注册一个监听器提供包含工具定义的ToolGroupwindow.addEventListener( devtoolstooldiscovery, (event: DevtoolsToolDiscoveryEvent) { event.respondWith({ name: Page-specific DevTools, description: Provide runtime info directly from the pages JavaScript, tools: [ { name: add, description: Calculates the sum of two numbers., inputSchema: { type: object, properties: { a: {type: number}, b: {type: number}, }, required: [a, b], }, execute: async (input: {a: number; b: number}) { return input.a input.b; }, }, ], }); }, );测试用例 tests/tools/thirdPartyDeveloper.test.ts 中的list_3p_developer_tools用例采用了同样的写法在页面里window.addEventListener(devtoolstooldiscovery, ...)并在事件中调用e.respondWith(mockToolGroup)然后断言 MCP 返回的structuredContent.thirdPartyDeveloperTools中 group 名、描述、工具名与 inputSchema 全部原样回传——可以确认发现链路对字段是透传的。工具调用两条执行路径工具被发现后MCP 客户端有两种方式执行它1.execute_3p_developer_tool标准方式按名称调用特定注册工具参数经过校验。对应 MCP 层定义见 src/tools/thirdPartyDeveloper.ts其入参 schema 为参数类型必填说明toolNamestring是要执行的工具名paramsstringJSON 字符串否传给工具的参数需为 JSON 序列化的对象handler 的执行链路值得逐步拆解解析参数若提供了params先JSON.parse且解析结果必须是普通对象typeof parsed object parsed ! null否则抛出Failed to parse params as JSON或Parsed params is not an object。按名查找工具遍历request.page.getThirdPartyDeveloperTools()缓存的 toolGroups跨 group 查找首个同名工具找不到则抛Tool ${toolName} not found。AJV 校验new ajv()编译工具的inputSchema后校验 params校验失败时抛出Invalid parameters for tool ${toolName}: ...并附带ajv.errorsText的详细信息。委派页面执行最终调用request.page.executeThirdPartyDeveloperTool(toolName, params, response)进入页面上下文运行。list_3p_developer_tools则被标记为readOnlyHint: true只读execute_3p_developer_tool为readOnlyHint: false两者都归属于ToolCategory.THIRD_PARTY枚举值为experimentalThirdParty见 src/tools/categories.ts。2.evaluate_script直调组合方式对于更复杂的交互可以运行自定义脚本直接在脚本中调用window.__dtmcp.executeTool(toolName, params)list_3p_developer_tools的工具描述src/tools/thirdPartyDeveloper.ts明确建议了两种适用场景工具返回不可序列化的值时直调时结果留在页面内可继续加工需要把多个第三方工具组合成更高级功能时。从源码看window.__dtmcp.executeTool是在首次respondWith时挂载的src/McpPage.ts它遍历window.__dtmcp.toolGroups找到同名工具并await tool.execute(args)找不到工具时抛Tool ${toolName} not found页面没有任何工具时抛No tools found on the page。DOM 元素如何作为参数和返回值文档特别指出如果工具需要以 DOM 元素作为输入或输出它们通过在可访问性树中引用的特殊 UID来处理。源码在 src/McpPage.ts 的executeThirdPartyDeveloperTool()中实现了完整的往返映射输入方向UID → 真实元素遍历params的所有值凡形如{uid: ...}且仅此一个 key 的对象都通过getElementByUid依赖先前take_snapshot建立的TextSnapshot解析为ElementHandle元素句柄作为额外参数传入页面内evaluate在页面侧再把参数中对应的{uid}占位符逐一替换回真实的 DOM 元素args[key] elements.shift()最后调用window.__dtmcp.executeTool(name, args)。输出方向元素 → 暂存 → UID页面侧先对工具结果做递归清洗processToolResultElement实例被暂存到window.__dtmcp.stashedElements数组原位替换为{stashedId: stashed-N}结果回到 MCP 层后逐个取出暂存元素拿到backendNodeId并通过TextSnapshot重建快照解析出对应的cdpElementId最后递归地把{stashedId: stashed-N}替换为{uid: cdpElementId}与标准快照中的元素 UID 体系统一——Agent 拿到的 UID 可直接用于其他工具如点击、检查。这套机制解释了为什么 UID 是特殊的它让 DOM 元素穿越了页面上下文 ↔ 浏览器进程的序列化边界同时保持了 chrome-devtools-mcp 全局快照 UID 体系的一致性。返回值的可序列化约束processToolResultsrc/McpPage.ts定义了一条明确的清洗规则编写工具时应提前预期值类型处理方式DOMElement暂存并替换为 UID 引用见上节数组递归处理每个元素循环引用替换为字符串Circular reference非纯对象Object.getPrototypeOf(data) ! Object.prototype替换为XxxClass instance形式的字符串函数替换为Function object原始类型字符串、数字、布尔原样返回如果你的工具需要返回复杂结构最稳妥的做法是返回纯对象 原始类型 需要引用的 DOM 元素其余信息自行序列化为字符串。当工具需要返回不可序列化内容且希望进一步加工时改用evaluate_script直调路径。安全模型与边界实验性Experimental Status该功能目前处于实验阶段API 可能变化不保证稳定性。作用域Scope第三方开发者工具只在定义它的页面上下文中执行不跨源origin持久化。页面导航后工具列表会随新的发现流程重新建立。权限Capabilities这些工具不授予任何额外权限——它们能执行的代码本质上等同于已经能在那个页面上运行代码的攻击者所能运行的代码。换句话说注册工具不会扩大 MCP 的攻击面只是把页面自身已有的能力显式化。如何开启--categoryExperimentalThirdParty 标志该功能被命令行标志--categoryExperimentalThirdPartytrue门控。在仓库中可以看到完整的证据链类别定义在 src/config/category-options.tsToolCategory.THIRD_PARTY的描述为 Set to true to enable third-party developer tools exposed by the inspected page itself且offByDefault: true默认关闭CLI 工具描述中直接标注了开启要求如execute_3p_developer_tool描述末尾的 (requires flag: --categoryExperimentalThirdPartytrue)src/config/cli-options.ts运行时开关检查位于 src/McpResponse.ts只有this.#args.categoryExperimentalThirdParty为真、请求了列表且当前存在页面时才会执行getToolGroups()所有相关测试tests/tools/thirdPartyDeveloper.test.ts均以{categoryExperimentalThirdParty: true}作为启动参数。因此使用前提是启动 chrome-devtools-mcp 时显式传入--categoryExperimentalThirdPartytrue或等价的 MCP 选项配置否则list_3p_developer_tools/execute_3p_developer_tool相关链路不会被触发。小结与延伸阅读第三方开发者工具是 chrome-devtools-mcp 中页面参与式调试的入口页面通过监听devtoolstooldiscovery并respondWith注册ToolGroupMCP 侧完成发现、AJV 校验与 UID 化的 DOM 往返映射最终让 Agent 既能按名调用工具也能经window.__dtmcp.executeTool组合调用。关键源码索引如下职责文件MCP 工具定义list / execute与参数校验src/tools/thirdPartyDeveloper.ts发现流程getToolGroups与页面内执行executeThirdPartyDeveloperToolsrc/McpPage.ts开关检查#handleThirdPartyDevelopeToolssrc/McpResponse.ts工具类别与默认关闭配置src/config/category-options.ts、src/tools/categories.ts行为验证测试tests/tools/thirdPartyDeveloper.test.ts原始文档docs/third-party-developer-tools.md【免费下载链接】chrome-devtools-mcpChrome DevTools for coding agents项目地址: https://gitcode.com/GitHub_Trending/chr/chrome-devtools-mcp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考