<formtoolname="search_products"tooldescription="Search the product catalog by keyword and optional category filter"action="/search"method="GET"><labelfor="query">Search term</label><inputtype="text"name="query"id="query"requiredtoolparamdescription="The keyword or phrase to search for in product titles and descriptions"><labelfor="category">Category</label><selectname="category"id="category"toolparamtitle="Product Category"toolparamdescription="Filter results to a specific product category. Use 'all' for no filter."><optionvalue="all">All categories</option><optionvalue="electronics">Electronics</option><optionvalue="books">Books</option><optionvalue="clothing">Clothing</option></select><buttontype="submit">Search</button></form>技术支持
浏览器会基于这些属性在内部生成 JSON 架构。工具名、参数类型、来源于 <select> 选项的枚举值,以及来自 HTML 的必填标记,都会无需额外配置地传递过去。
表单能覆盖许多场景,但某些工具需要与应用状态交互、发起 API 调用,或返回不适合传统表单提交的计算结果。以下是一个向待办清单添加条目的基础工具:
window.navigator.modelContext.registerTool({name:"addTodo",description:"Add a new item to the user's to-do list. Use when the user asks to create, add, or remember a task.",inputSchema:{type:"object",properties:{text:{type:"string",description:"The task description"},priority:{type:"string",enum:["low","medium","high"],description:"Task priority level. Default to 'medium' if not specified by the user."}},required:["text"]},execute:({ text, priority ="medium"})=>{const newItem ={id: Date.now(), text, priority,done:false};
todoApp.addItem(newItem);
todoApp.renderList();return{content:[{type:"text",text: Added task:"${text}"with ${priority} priority. The to-do list now has ${todoApp.getCount()} items.}]};}});技术支持
functiononUserLogin(user){
navigator.modelContext.registerTool({name:"viewOrderHistory",description:"Show the user's past orders with dates and totals",inputSchema:{type:"object",properties:{limit:{type:"number",description:"Maximum number of orders to return. Defaults to 10."}}},execute:async({ limit =10})=>{const orders =awaitfetchOrders(user.id, limit);return{content:[{type:"text",text:JSON.stringify(orders)}]};}});}functiononUserLogout(){
navigator.modelContext.unregisterTool("viewOrderHistory");}技术支持
navigator.modelContext.registerTool({name:"checkAvailability",description:"Check table availability for a given date and party size at this restaurant. Returns available time slots.",inputSchema:{type:"object",properties:{date:{type:"string",description:"The desired reservation date in YYYY-MM-DD format"},partySize:{type:"number",description:"Number of guests, between 1 and 12"}},required:["date","partySize"]},execute:async({ date, partySize })=>{if(partySize <1|| partySize >12){return{content:[{type:"text",text:"Party size must be between 1 and 12. For larger groups, please call the restaurant directly at (555) 123-4567."}]};}const parsedDate =newDate(date);if(isNaN(parsedDate.getTime())|| parsedDate <newDate()){return{content:[{type:"text",text:"Please provide a valid future date in YYYY-MM-DD format."}]};}const slots =await api.getAvailableSlots(date, partySize);renderAvailabilityCalendar(date, slots);return{content:[{type:"text",text:JSON.stringify({
date,
partySize,availableSlots: slots.map(s=> s.time),message: slots.length >0? Found ${slots.length} available time slots.:"No availability for this date. Try a different date or smaller party."})}]};}});技术支持
document.querySelector("form").addEventListener("submit",(e)=>{if(e.agentInvoked){
console.log("AI agent submitted the form");if(!isWithinAgentRateLimit()){
e.preventDefault();
e.respondWith(Promise.resolve({error:"Too many requests. Please wait before trying again."}));return;}}});技术支持
form:tool-form-active{border: 2px solid #2563eb;background-color: #eff6ff;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">1rem;border-radius: 8px;}form:tool-form-active::before{content:"AI agent is filling this form";display: block;font-size: 0.875rem;color: #2563eb;font-weight: 600;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">0.5rem;}技术支持
应用了 :tool-form-active 样式的表单,显示蓝色边框以及 "AI agent is filling this form" 标签。
命令式工具则是另一种挑战。因为没有表单与提交按钮,所以在智能体调用工具时会立即执行。
对于不应在未经人类批准下执行的任何操作,您需要将确认流程内置到工具本身:
navigator.modelContext.registerTool({name:"deleteAccount",description:"Permanently delete the user's account and all associated data. Cannot be undone.",inputSchema:{type:"object",properties:{confirmPhrase:{type:"string",description:"The user must type 'DELETE MY ACCOUNT' to confirm."}},required:["confirmPhrase"]},execute:({ confirmPhrase })=>{if(confirmPhrase !=="DELETE MY ACCOUNT"){return{content:[{type:"text",text:"Confirmation phrase does not match. Ask the user to confirm explicitly."}]};}returnnewPromise((resolve)=>{showConfirmationModal("Are you sure?",{onConfirm:async()=>{await api.deleteAccount();resolve({content:[{type:"text",text:"Account deleted."}]});},onCancel:()=>{resolve({content:[{type:"text",text:"Deletion cancelled by user."}]});}});});}});技术支持
现在,AI 智能体浏览网页就像游客看外文菜单:眯着眼认像素,猜按钮作用,还得祈祷页面不要在点击中途变换布局。Google 和 Microsoft 认为这个问题值得被好好解决。
2026 年 2 月,Chrome 146 率先发布了 WebMCP 的早期预览版——这是一个拟议中的网页标准,允许网站明确告知智能体自己能做什么以及如何去做。
不再需要屏幕抓取。
不再需要对 DOM 进行逆向工程。
在本教程中,我将带您了解 WebMCP 究竟是什么、如何搭建环境,并使用基于 HTML 和基于 JavaScript 的两种方式构建可运行的集成。
我还会介绍处理智能体调用的安全要点,以及如何使用 Chrome 的调试工具测试全部流程。
什么是 WebMCP?
WebMCP(Web Model Context Protocol,网页模型上下文协议)是一项新的浏览器标准,让网站可以将自身能力声明为结构化的工具,供 AI 智能体直接调用。与其强迫 AI “阅读”并点击可视化界面,WebMCP 将网页本身转换为一个干净的、基于客户端的 API。
想象一下,如今一个智能体尝试在旅游网站订票时会发生什么。它必须弄清哪个字段输入出发城市、猜日期格式是 MM/DD 还是 YYYY-MM-DD、找到搜索按钮,并祈祷页面不会重新加载成它从未见过的布局。如果网站能发布一个名为
searchFlights的工具,并为出发地、目的地、日期和乘客数提供类型化参数,那么这些猜测都会消失。WebMCP 让这一切成为可能。站点通过名为
navigator.modelContext的浏览器 API 注册工具,每个工具包含名称、自然语言描述和其输入输出的 JSON 架构。智能体发现这些工具,根据架构发送参数,并接收结构化数据作为返回。Google 与 Microsoft 的工程师在 W3C Web Machine Learning 社区组下共同制定了该规范。其架构完全在客户端侧运行,这与典型 MCP 部署不同——后者通常由独立后端服务器执行工具。标准 MCP 通过 JSON-RPC 将智能体连接到后端服务器,而 WebMCP 则将一切保留在浏览器标签页内。
工具在页面的 JavaScript 中执行;它们共享用户已有的会话,由浏览器调控智能体可执行的操作。如果您此前用过 MCP 服务器(例如我们 MCP 模型上下文协议教程 中的示例),工具注册的理念会很眼熟,但部署模型截然不同。
WebMCP 的关键特性与能力
可用两种 API,且用途不同:
<form>元素上添加toolname与tooldescription属性,并可在各字段上添加可选的toolparamdescription。浏览器会基于现有表单结构生成 JSON 架构。已有完善表单的网站仅需极少改动即可对智能体开放。navigator.modelContext.registerTool(),定义工具名称、描述、输入架构与execute回调。对于普通表单难以覆盖的动态场景,如多步流程、API 调用、计算结果与自定义校验逻辑,应选择此路径。两种 API 都支持 动态注册,即工具可随页面状态变化而出现或消失。在用户登录前,您也许只暴露
login工具;认证之后,再注册与账户相关的工具。:tool-form-active应用于当前由智能体填写的任何表单,并将:tool-submit-active应用于该表单的提交按钮。观看屏幕的人可以清楚看到智能体正在操作哪一个表单。目前尚无内置的发现机制。智能体必须先访问您的页面,才能了解您提供的工具。Chrome 团队讨论过通过
.well-known/webmcp清单文件实现访问前发现的想法,但尚未有类似规范。由于 WebMCP 仍处于早期预览,您应预期会有一些粗糙之处与持续变化,尤其是规范已经在不同版本间演进,包括在 2026 年 3 月移除了
provideContext和clearContext方法。工具的可发现性仅限已访问过的页面;规范中也承认并讨论了安全问题,如通过工具链的提示注入与数据外泄,但尚未完全解决。在开始之前,您可以查看 Chrome 文档中的变更日志,因为在本教程撰写后,API 表面可能已有变化。
WebMCP 前置条件与环境配置
环境配置几分钟即可完成,但装错 Chrome 版本会让您浪费更多时间。
需要 Chrome Canary 版本 146.0.7672.0 或更高。Chrome 的稳定版、Beta 与 Dev 通道不包含 WebMCP 标志。在 Chrome 论坛中已有多位开发者提到,安装了错误版本而困惑为何找不到该标志。请务必从 Canary 页面下载。
Chrome Canary 下载页面
除浏览器外,您需要具备 HTML 表单与 JavaScript 事件处理的基础知识。如需复习,DataCamp 的 JavaScript 入门课程涵盖了基础内容。
虽然不是必需,但建议安装Model Context Tool Inspector 扩展。它会列出当前页面注册的所有工具,允许您手动输入参数执行工具,并内置 Gemini API,便于用自然语言测试工具调用。
启用 WebMCP 实验性标志
安装好 Chrome Canary 后:
chrome://flags/#enable-webmcp-testing。chrome://flags 页面中突出显示并设置为 Enabled 的 WebMCP 标志
重启后,打开开发者工具(F12),转到 Console 选项卡,运行:
DevTools 控制台显示 navigator.modelContext 对象。
输出为对象(而非
undefined)即确认 API 可用。接下来,您可以安装Model Context Tool Inspector 扩展,并打开 Google 的旅行演示,在真实页面上查看工具注册的实际效果。在那里,您只需启动扩展,即可看到类似如下界面:
使用Chrome 的 Model Context Tool Inspector 扩展在Google 的 Flight Search 演示网站中检查 searchFlights 工具。
如您所见,Model Context Tool Inspector 能检测到 searchFlights 工具并展示其输入架构,从而让模型更清楚地理解如何与页面交互。
构建您的第一个 WebMCP 集成
构建首个 WebMCP 集成有两种方式,下面分别介绍:
声明式方法(HTML)
注册 WebMCP 工具的最简单方式,是给页面上已有的表单添加注解。以下是一个没有添加 WebMCP 注解的标准产品搜索表单:
四个属性即可将其变为可由智能体调用的工具。toolname 和 tooldescription 加在 <form> 标签本身。对于各个字段,当默认标签不足以让智能体理解时,可用 toolparamtitle 与 toolparamdescription 覆盖默认标签:
浏览器会基于这些属性在内部生成 JSON 架构。工具名、参数类型、来源于 <select> 选项的枚举值,以及来自 HTML 的必填标记,都会无需额外配置地传递过去。
要测试它,请在本地用任意文本编辑器创建一个 .html 文件,粘贴以上代码,并用 Chrome 通过 file:///{your_document_path.html} 打开;为使扩展能处理本地文件,您还需要打开扩展设置,进入“详细信息”,启用对文件 URL 的访问,如下图所示。
允许扩展访问本地文件。
否则,您的浏览器将无法与本地 HTML 文件交互。完成后,用 Chrome 打开该文件并检查 WebMCP 扩展,您应能看到类似如下的截图。
添加注解后,扩展显示为 search_products 工具生成的架构。
那么,当智能体调用该工具时实际发生了什么?
Chrome 会聚焦该表单,并用智能体提供的值填充字段,然后停在这里。用户仍需点击“提交”。这种默认行为是有意为之,因为应由人类确认操作再执行。
在表单标签上添加
toolautosubmit属性可以移除这一步确认。对于只读操作(如搜索)很合适。凡是修改数据、触发购买或更改账户设置的操作,请不要开启它。也可以在提交时拦截,并向智能体返回结构化结果。submit 事件上的 respondWith() 方法接收一个 promise,您 resolve 的内容会成为工具的输出:
submit 事件上的 agentInvoked 布尔值在智能体触发提交时为 true,普通用户点击则为 false。您可以据此在智能体专用响应格式与常规表单处理之间分支。
当字段上未提供 toolparamdescription 时,浏览器会从关联的 <label> 元素中提取描述;若无,则从 aria-description 属性获取。因此,遵循无障碍规范的标记无需额外工作即可提供合理的参数描述。
当前有一个小怪癖:对于一组 <input type="radio"> 元素,toolparamdescription 必须放在该组的第一个单选按钮上,并作用于整个参数。
命令式方法(JavaScript)
表单能覆盖许多场景,但某些工具需要与应用状态交互、发起 API 调用,或返回不适合传统表单提交的计算结果。以下是一个向待办清单添加条目的基础工具:
execute 回调会接收智能体发送的参数(应与 inputSchema 匹配),运行您的应用逻辑,并返回一个 content 数组。
有个重要细节:请在返回前更新 UI。
智能体有时会在调用工具后检查页面是否发生变化以确认是否成功,如果 UI 尚未更新,智能体可能会判断调用失败。您可以随着页面状态演变动态注册和注销工具:
一个更贴近现实的场景:餐厅订位页面检查桌位可用性。这个示例还展示了代码内校验。Chrome 文档建议这样做,因为虽然架构约束对智能体很有帮助,但并非所有智能体都会强制执行这些约束。
有几点来自 Chrome 文档的建议值得强调。尽可能接收原始的用户输入。如果有人说“11:00 到 15:00”,您的工具应直接接收这些字符串,而不是要求智能体先把它们转换成例如“自午夜起的分钟数”之类的内部格式。
请在代码中进行校验,而不仅依赖架构,因为不同智能体对架构的执行力度不一。并且务必在 UI 更新后再返回结果,因为智能体可能会查看页面以确认操作是否完成。
若要在客户端 WebMCP 的同时补充服务端 MCP 工具,请参阅我们的 使用 FastMCP 构建 MCP 服务器指南,获取详细设置说明。
在 WebMCP 中处理智能体调用与安全
WebMCP 规范假定有人在旁监督。Google 与 Microsoft 的作者将其设计为协作式工作流,而非完全自治。以下机制反映了这种理念。
检测 AI 与人类用户
在
SubmitEvent接口上新增了一个名为 agentInvoked 的布尔属性。由智能体触发表单提交时,该属性为 true;由人正常点击提交时,则为 false。除表单提交外,还有两个窗口级事件用于追踪智能体活动。
toolactivated会在智能体调用工具并由 Chrome 预填关联表单字段后触发。若用户取消操作或重置表单,则触发toolcancel。这两个事件都不可取消,但都带有
toolName属性,方便您了解涉及的是哪个工具。强制落实“人参与环”的权限
对于声明式工具,默认行为是:智能体填完字段后,需要人类点击“提交”。对于有实际后果的操作,应保留该默认设置。
toolautosubmit属性会移除此步骤。它适用于只读或低风险的工具:搜索、可用性查询、状态检查。凡是创建、修改或删除数据的操作,都应要求手动点击。
您可以用 CSS 伪类为智能体交互状态设置样式:
应用了 :tool-form-active 样式的表单,显示蓝色边框以及 "AI agent is filling this form" 标签。
命令式工具则是另一种挑战。因为没有表单与提交按钮,所以在智能体调用工具时会立即执行。
对于不应在未经人类批准下执行的任何操作,您需要将确认流程内置到工具本身:
整个 MCP 生态的模式是一致的:智能体表达意图,由人类确认执行。想要了解这种模式在不同 MCP 实现中的运作,请参阅我们的 MCP 服务器与客户端精选概览。
测试您的 WebMCP 实现
当您完成首个 WebMCP 集成后,就可以开始测试了。
使用 Chrome DevTools 智能体模拟器
安装 Model Context Tool Inspector 扩展后,其图标上的小徽标会显示当前页面注册了多少工具。打开弹窗可查看各工具的名称、描述与输入架构。如果您注册的工具未出现在这里,说明注册未成功。
扩展面板显示多个已注册工具列表,并展开了它们的架构。
要试用智能体模拟器,让我们回到Google 的 Flight Search 演示网站。现在我们可以测试如何手动或使用自然语言来执行该应用。
手动执行方法
手动执行去除了智能体测试中的不确定因素。在扩展面板中从下拉列表选择一个工具,在 “Input Arguments” 字段中输入一个 JSON 对象,然后点击 “Execute Tool”。扩展会用您提供的参数直接调用该工具。
扩展中选择了 searchFlights 工具、填写了 JSON 参数,并显示 Execute Tool 按钮。
这对于定位问题非常有价值。如果工具在手写参数下工作正常,但智能体调用时失败,您就知道问题出在工具描述或架构上,而不是 execute 函数。例如,使用 Google 的旅行演示:
手动执行工具后,旅行演示页面显示搜索结果。
自然语言测试
自然语言测试使用 Gemini API。在扩展设置中粘贴一把来自 AI Studio 的密钥,然后输入如下提示词
扩展会把该提示词与您的工具定义一并发送给 Gemini 2.5 Flash,由模型决定调用哪个工具以及传递哪些参数。
扩展的自然语言输入框中输入了提示词,下方显示智能体调用工具的响应。
如果智能体选择了错误的工具或参数映射不正确,您的工具描述可能需要更具体。好的描述应以正面表述工具的用途(“用于查询航班可用性”),而不是强调不该用于什么。
一个实用的测试序列:
关于 Google 生态内 MCP 工具的背景,我们的 Google MCP 服务器教程涵盖了多个相关模式。若想了解超出 Chrome 的更广泛协议格局,请查看 Top Remote MCP Servers。
结语
WebMCP 为网站与 AI 智能体提供了一条直接的沟通通道。网站发布结构化的工具定义,智能体用类型化参数调用它们,而不再凭猜测穿行页面。声明式 API 仅用几个 HTML 属性就能将现有表单变为可调用工具;命令式 API 则覆盖更复杂的场景。安全模型确保在人们真正重要的操作中,仍由人类掌控。
该规范仍处早期阶段,仅在启用标志的 Chrome Canary 中可用,尚不适合生产环境。但事实是,Google 与 Microsoft 共同编写,且由 W3C 治理,说明其有望成为稳定的网页标准。现在投入时间熟悉该 API,意味着等到稳定版到来时,您已掌握工具设计模式与安全机制。
如果您想现在开始准备,请审视现有站点中的表单,找出最适合开放给智能体的部分。搜索表单与可用性查询这类低风险入口是良好起点。当您在这些场景中熟练掌握声明式 API 后,再把需要自定义逻辑的工作流迁移到命令式工具。
若想进一步探索该主题,我推荐: