如何在 Puppeteer 中启用 WebMCP 并发现、执行页面注册的 MCP 工具
【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteer
WebMCP 是一个实验性 API,允许网页注册工具,再由浏览器或外部 Agent(如 LLM)发现并调用。Puppeteer 提供了对应的实验性 API:page.webmcp属性,用于读取页面上已注册的工具列表、监听工具增减,以及直接执行工具并拿到返回结果。本文的任务是:在 Chrome 151+ 上通过 Puppeteer 启动带--enable-features=WebMCP标志的浏览器,完成「注册工具 → 发现工具 → 执行工具 → 读取结果」这条完整链路。
前提条件(来自 WebMCP 指南 与 Page API 的明确说明):
- 浏览器必须是Chrome 151+,且需要支持 WebMCP CDP domain;
- 启动时必须加
--enable-features=WebMCP标志; - WebMCP 是实验性 API,接口可能变化。
启用 WebMCP:带标志启动浏览器
在 Puppeteer 中,WebMCP 支持通过page.webmcp属性访问,当浏览器支持该能力时,它会随页面导航自动初始化,无需手动调用构造函数:
import puppeteer from 'puppeteer'; const browser = await puppeteer.launch({ args: ['--enable-features=WebMCP'], }); const page = await browser.newPage(); // page.webmcp is now available console.log(page.webmcp);如果启动时漏掉--enable-features=WebMCP标志,页面侧的document.modelContext工具注册不会生效,后续page.webmcp.tools()也将拿不到任何工具。
在页面中注册工具
Puppeteer 只负责「发现与执行」,工具本身需要在页面里注册。文档给出两种注册方式(见 WebMCP 指南)。
命令式注册(JavaScript)
通过page.evaluate在页面内调用document.modelContext.registerTool:
await page.evaluate(async () => { await document.modelContext?.registerTool({ name: 'calculate_sum', description: 'Calculates the sum of two numbers', inputSchema: { type: 'object', properties: { a: {type: 'number'}, b: {type: 'number'}, }, required: ['a', 'b'], }, execute: ({a, b}) => { return a + b; }, }); });registerTool还支持第二个参数传入AbortSignal:调用该 signal 的abort()后可以取消这个工具的注册,此时 Puppeteer 侧会收到toolsremoved事件(该行为在仓库测试 webmcp.test.ts 中有验证)。
声明式注册(HTML form)
WebMCP 支持将带特定属性的 HTML form 识别为工具:
await page.setContent(` <form toolname="search_products" tooldescription="Search for products in the catalog" > <input name="query" type="text" /> <button type="submit">Search</button> </form> `);通过 form 注册的工具,可以拿到对应的表单元素句柄:
const tools = page.webmcp.tools(); const searchTool = tools.find(t => t.name === 'search_products'); const formHandle = await searchTool.formElement;注意:form 必须带tooldescription属性。测试代码显示,缺少该属性的 form(如<form toolname="mytool"></form>)会触发page的issue事件,其genericIssueDetails.errorType为FormModelContextMissingToolDescription。
发现已注册的工具
page.webmcp.tools()返回当前页面注册的所有工具,每个WebMCPTool对象带有name、description、inputSchema、frame等属性(详见 WebMCPTool API):
const tools = page.webmcp.tools(); for (const tool of tools) { console.log(`Tool found: ${tool.name} - ${tool.description}`); }如果工具是动态注册的,用事件监听代替轮询。page.webmcp是一个EventEmitter,事件包括toolsadded、toolsremoved、toolinvoked、toolresponded(见 WebMCP API):
// Listen for new tools page.webmcp.on('toolsadded', event => { for (const tool of event.tools) { console.log(`New tool added: ${tool.name}`); } }); // Listen for removed tools page.webmcp.on('toolsremoved', event => { for (const tool of event.tools) { console.log(`Tool removed: ${tool.name}`); } });toolsadded/toolsremoved事件的负载是一个tools数组(WebMCPToolsAddedEvent、WebMCPToolsRemovedEvent)。
一个需要注意的边界:工具注册归属于页面上下文。仓库测试 webmcp.test.ts 验证了——整页导航(再次page.goto到其他页面)会触发toolsremoved并使page.webmcp.tools()变回空列表;而同文档导航(如仅改 hash)不会清空已注册的工具。如果你的自动化流程中会频繁跳转,发现逻辑要按「每次导航后重新检查」来写。
执行工具并判断结果
对发现到的工具调用execute(input, options),第一个参数是匹配该工具inputSchema的输入对象,返回一个 Promise,resolve 为WebMCPToolCallResult(见 WebMCPTool.execute):
const tools = page.webmcp.tools(); const tool = tools.find(t => t.name === 'calculate_sum'); if (tool) { const result = await tool.execute({a: 5, b: 10}); if (result.status === 'Completed') { console.log('Result:', result.output); } else { console.error('Error:', result.errorText); } }判断结果时主要看WebMCPToolCallResult的几个字段(见 WebMCPToolCallResult API):
status:调用状态,如Completed、Canceled、Error;output:status为Completed时的工具输出,其他状态下不存在;errorText:错误文本;exception:如果工具执行的 JavaScript 抛出异常,这里携带异常对象;call:对应的WebMCPToolCall(含id、input、tool),可用id把一次调用和它的响应关联起来。
仓库测试里有一条可直接参照的成功用例:注册一个execute: ({text}) => 'hello ' + text的工具后执行tool.execute({text: 'world'}),返回status为Completed、output为'hello world',且errorText与exception均为 undefined。这就是本文场景的验证标准:execute的 Promise resolve 后,result.status === 'Completed'且result.output与页面内execute函数的返回值一致,即整条链路跑通。
取消执行中的工具
execute的第二个参数options.signal接收一个AbortSignal,用于取消进行中的执行(见 WebMCPToolExecuteOptions):
const controller = new AbortController(); // Cancel execution after 2 seconds setTimeout(() => { controller.abort(); }, 2000); const result = await tool.execute( {query: 'large data processing'}, {signal: controller.signal}, ); if (result.status === 'Canceled') { console.log('Tool execution was canceled.'); }测试验证了取消后的结果形态:status为Canceled,output不存在,errorText为空字符串。如果传入的 signal 在调用前就已经 abort,同样会得到Canceled结果。
观测工具的调用与响应
除了自己主动execute,还可以监听页面或浏览器主动发起的调用:
page.webmcp.on('toolinvoked', call => { console.log(`Tool ${call.tool.name} was invoked with input:`, call.input); }); page.webmcp.on('toolresponded', response => { console.log( `Tool ${response.call?.tool.name} responded with status: ${response.status}`, ); if (response.status === 'Completed') { console.log('Output:', response.output); } else if (response.status === 'Canceled') { console.log('Invocation was canceled'); } else { console.log('Error:', response.errorText); } });toolinvoked事件的负载是 WebMCPToolCall,包含调用id、输入参数和被调用的工具对象;toolresponded的负载就是上文提到的WebMCPToolCallResult。WebMCPTool对象本身也带toolinvoked事件,可以只监听某一个工具的调用。
限制与验证清单
- 只支持 Chrome 151+ 的 CDP 模式,且必须显式传
--enable-features=WebMCP;API 为实验性质,可能随版本变化(Page API 中webmcp属性标注为 Experimental)。 WebMCP与WebMCPTool的构造函数均为内部实现,不应直接new或继承,统一通过page.webmcp获取。- 验证顺序可以按仓库测试的方式:
page.goto一个页面 → 注册工具 → 等待toolsadded→page.webmcp.tools()检查name/description/inputSchema→execute后断言status === 'Completed'和output值。完整用例可参考 test/src/cdp/webmcp.test.ts。 - 更多细节见 docs/guides/webmcp.md 与 WebMCP API 文档。
【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteer
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考