WebMCP 深度实战:Google 与 Microsoft 联手重塑浏览器 AI 交互范式——从 DOM 暴力操作到语义化工具调用的完整技术揭秘
引言:AI Agent 操作网页的终极困境
2026 年,AI Agent 已经无处不在。从自动填写表单、比价购物,到批量处理后台管理系统,Agent 正在取代大量重复性的人机交互。但如果你真正上手开发过 Web Agent,你一定经历过这些崩溃时刻:
- 视觉路线:截图 → OCR → 坐标点击。一次截图消耗约 2000 token,延迟 2-5 秒,遇到动态加载、弹窗遮挡就歇菜。换了个按钮颜色,Agent 就认不出来了。
- DOM 路线:CSS 选择器 → click() → 等待响应。网页一改版,选择器全部失效;Shadow DOM 穿透不了,iframe 跨域访问不了,SPA 动态渲染时序更是噩梦。
这两条路线的本质问题是一样的:Agent 在"猜"UI,而不是"理解"UI。它在模拟人类的视觉和操作,但网页对它来说是一个黑盒——它不知道这个按钮叫"提交订单",那个输入框是"收货地址",它只是看到一坨像素或者一串 DOM 节点。
WebMCP(Web Model Context Protocol)的诞生,就是要终结这个困境。
2026 年初,Google 与 Microsoft 在 W3C 框架下联合推动 WebMCP 成为浏览器原生 Web API。Chrome 146 已推出早期预览版本。核心思路极其简洁:不再让 Agent 去猜 UI,而是让网页主动告诉 Agent "我能做什么"。
本文将从 WebMCP 的设计哲学出发,深入剖析其双轨 API 架构、工具契约机制、浏览器安全模型,并通过完整代码实战,带你从零构建一个 WebMCP 增强的电商应用,让 AI Agent 以语义化方式调用你的网页能力。
一、WebMCP 是什么:从"暴力操作"到"语义调用"的范式跃迁
1.1 核心定义
WebMCP(Web Model Context Protocol)是 MCP 协议在浏览器端的落地实现。它定义了一套标准化的 Web API,让网页能够:
- 声明式暴露能力:网页通过 HTML 属性或 JavaScript API,将自身的业务逻辑(搜索、筛选、提交、跳转等)封装为结构化工具
- 注册到浏览器上下文:这些工具被注册到页面的 WebMCP 上下文中,AI Agent 可以通过协议直接发现和调用
- 契约化交互:每个工具包含名称、描述、输入/输出 JSON Schema、执行逻辑,形成稳定的工具契约
用一句话概括:WebMCP 让网页从"被操作的客体"变成"主动暴露能力的服务端"。
1.2 它解决了什么
| 交互路线 | 核心问题 | WebMCP 解决方案 |
|---|---|---|
| 视觉路线(截图识别) | 慢(2-5s/次)、贵(~2000 token/次)、易出错 | 语义化工具调用,Token 消耗减少约 89% |
| DOM/选择器路线 | 脆弱(改版即失效)、Shadow DOM 穿透难 | 工具契约不变则不受 UI 变更影响 |
| 两条路线共同问题 | 不知道"能做什么",只能盲试 | 网页主动声明能力列表,Agent 按需调用 |
1.3 WebMCP vs MCP:不是替代,是互补
MCP(Model Context Protocol)是 Anthropic 于 2024 年提出的 AI 与外部工具交互的标准协议,运行在进程间通信(stdio)或 HTTP+SSE 层面,解决的是 AI 应用如何调用后端服务的问题。
WebMCP 则是 MCP 在浏览器端的延伸:
- MCP:AI ↔ 后端服务(数据库、文件系统、第三方 API)
- WebMCP:AI ↔ 前端页面(搜索框、购物车、表单提交)
两者不是竞争关系,而是分工明确的互补:
┌─────────────────────────────────────────────────┐
│ AI Agent │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ MCP Client │ │ WebMCP Client│ │
│ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │
└──────────┼─────────────────────┼─────────────────┘
│ │
MCP Protocol WebMCP Protocol
(stdio/HTTP+SSE) (Browser Web API)
│ │
┌───────▼───────┐ ┌───────▼───────┐
│ MCP Server │ │ Web Page │
│ (后端服务) │ │ (前端应用) │
│ │ │ │
│ - 数据库查询 │ │ - 搜索商品 │
│ - 文件读写 │ │ - 加入购物车 │
│ - API 调用 │ │ - 提交订单 │
└───────────────┘ └───────────────┘
关键区别:MCP Server 需要独立部署和运行,而 WebMCP 的"Server"就是网页本身——网页在浏览器中加载后,就自动成为一个 WebMCP Server,天然继承了用户的登录态和权限上下文。
二、架构深度剖析:双轨 API 与工具契约机制
2.1 双轨 API 设计
WebMCP 的设计哲学是"渐进增强"——简单的场景用声明式 API,复杂的场景用命令式 API,两者可以共存。
轨道一:声明式 API(HTML 属性)
对于标准表单操作,WebMCP 提供了纯 HTML 属性的声明式方式,无需编写任何 JavaScript:
<!-- 一个普通的搜索表单,加上 webmcp 属性后自动暴露为 AI 工具 -->
<form webmcp-tool="search-products"
webmcp-description="搜索商品,支持关键词、分类和价格区间筛选">
<input name="keyword" webmcp-param="关键词"
webmcp-type="string"
webmcp-description="搜索关键词"
placeholder="输入商品名称">
<select name="category" webmcp-param="分类"
webmcp-type="string"
webmcp-enum='["电子产品","服装","食品","图书"]'>
<option value="">全部分类</option>
<option value="electronics">电子产品</option>
<option value="clothing">服装</option>
<option value="food">食品</option>
<option value="books">图书</option>
</select>
<input name="minPrice" type="number" webmcp-param="最低价格"
webmcp-type="number" webmcp-min="0">
<input name="maxPrice" type="number" webmcp-param="最高价格"
webmcp-type="number" webmcp-min="0">
<button type="submit">搜索</button>
</form>
浏览器解析这些属性后,自动生成对应的工具描述:
{
"name": "search-products",
"description": "搜索商品,支持关键词、分类和价格区间筛选",
"inputSchema": {
"type": "object",
"properties": {
"keyword": {
"type": "string",
"description": "搜索关键词"
},
"category": {
"type": "string",
"enum": ["电子产品", "服装", "食品", "图书"],
"description": "分类"
},
"minPrice": {
"type": "number",
"minimum": 0,
"description": "最低价格"
},
"maxPrice": {
"type": "number",
"minimum": 0,
"description": "最高价格"
}
}
}
}
声明式 API 的优势在于:零 JavaScript、零学习成本、标准表单即工具。但它只适合简单的表单提交场景,对于需要自定义执行逻辑的复杂操作,就需要命令式 API。
轨道二:命令式 API(JavaScript 注册)
命令式 API 通过 navigator.webmcp.registerTool() 方法注册工具,拥有完整的控制力:
// 注册一个"加入购物车"工具
navigator.webmcp.registerTool({
name: 'add-to-cart',
description: '将指定商品加入购物车,支持指定数量',
inputSchema: {
type: 'object',
properties: {
productId: {
type: 'string',
description: '商品ID'
},
quantity: {
type: 'number',
description: '购买数量',
default: 1,
minimum: 1,
maximum: 99
},
sku: {
type: 'string',
description: '商品规格SKU(如颜色、尺寸组合)',
optional: true
}
},
required: ['productId']
},
outputSchema: {
type: 'object',
properties: {
success: { type: 'boolean' },
cartItemCount: { type: 'number', description: '购物车总商品数' },
message: { type: 'string' }
}
},
handler: async (input) => {
// 自定义执行逻辑
const result = await addToCart(input.productId, input.quantity, input.sku);
return {
success: result.ok,
cartItemCount: result.cartTotal,
message: result.ok ? '已加入购物车' : '添加失败:' + result.error
};
}
});
命令式 API 的核心能力:
- 自定义执行逻辑:handler 函数可以做任何事——调 API、操作 DOM、修改状态
- 输入输出 Schema:完整的 JSON Schema 约束,Agent 知道该传什么参数、会得到什么结果
- 异步支持:handler 可以是 async 函数,天然支持网络请求
- 上下文访问:handler 运行在页面上下文中,可以访问页面的全局状态、登录信息、Cookie 等
2.2 工具契约机制
WebMCP 的核心设计理念是"契约化交互"。每个工具就是一个契约,包含以下要素:
┌─────────────────────────────────────────┐
│ WebMCP Tool Contract │
│ │
│ name: "add-to-cart" │
│ description: "将商品加入购物车" │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Input Schema (JSON Schema) │ │
│ │ - productId: string (required) │ │
│ │ - quantity: number (optional) │ │
│ │ - sku: string (optional) │ │
│ └─────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Output Schema (JSON Schema) │ │
│ │ - success: boolean │ │
│ │ - cartItemCount: number │ │
│ │ - message: string │ │
│ └─────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Handler (Function) │ │
│ │ - 接收 input,执行业务逻辑 │ │
│ │ - 返回 output │ │
│ └─────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Metadata │ │
│ │ - version: "1.0.0" │ │
│ │ - category: "shopping" │ │
│ │ - requiresAuth: true │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
契约的稳定性是关键。一旦发布,工具的 name 和 inputSchema 应该保持稳定——UI 可以随便改版,但工具契约不变,Agent 就不需要调整。这就像后端 API 的版本管理一样,是一种工程化的约定。
2.3 上下文提供机制
除了工具调用,WebMCP 还支持网页主动提供"上下文信息",帮助 Agent 理解当前页面状态:
// 注册页面上下文
navigator.webmcp.provideContext({
name: 'shopping-cart',
description: '当前用户的购物车状态',
schema: {
type: 'object',
properties: {
items: {
type: 'array',
items: {
type: 'object',
properties: {
productId: { type: 'string' },
name: { type: 'string' },
price: { type: 'number' },
quantity: { type: 'number' }
}
}
},
totalPrice: { type: 'number' },
itemCount: { type: 'number' }
}
},
getter: () => {
// 从页面状态中获取购物车数据
return {
items: cartStore.items,
totalPrice: cartStore.totalPrice,
itemCount: cartStore.totalCount
};
}
});
Agent 在调用工具之前,可以先读取上下文来了解页面状态:
Agent: "我想看看购物车里有什么"
→ 读取 shopping-cart 上下文
→ 返回:购物车有3件商品,总价 ¥299.00
Agent: "把那个 ¥99 的 T恤删掉"
→ 读取上下文 → 找到 productId
→ 调用 remove-from-cart 工具
上下文机制的价值在于:大幅降低模型的推理成本。不需要 Agent 自己去解析页面结构、猜测当前状态,页面直接告诉你"我现在是什么样子"。
三、安全模型:浏览器如何保护用户
让 AI Agent 直接调用网页能力,听起来很方便,但也引发了严重的安全顾虑。WebMCP 的安全模型是整个协议最精心设计的部分。
3.1 权限分级模型
WebMCP 将工具按风险等级分为三类:
| 等级 | 标识 | 示例 | 权限要求 |
|---|---|---|---|
| 只读 | read-only | 查看商品详情、获取购物车状态 | 用户无需确认 |
| 幂等写入 | idempotent-write | 搜索、筛选、加入收藏 | 用户首次确认后记住 |
| 非幂等写入 | non-idempotent-write | 下单、支付、删除数据 | 每次调用均需确认 |
navigator.webmcp.registerTool({
name: 'place-order',
description: '提交订单并支付',
riskLevel: 'non-idempotent-write', // 每次都需要用户确认
// ...
});
3.2 用户确认机制
当 Agent 调用一个需要确认的工具时,浏览器会弹出原生确认对话框:
┌─────────────────────────────────────────────┐
│ ⚠️ AI Agent 请求执行操作 │
│ │
│ 应用:shop.example.com │
│ 操作:place-order │
│ 描述:提交订单并支付 │
│ │
│ 参数: │
│ ├─ 订单号: ORD-2026-0516-001 │
│ ├─ 金额: ¥299.00 │
│ └─ 支付方式: 支付宝 │
│ │
│ [允许此操作] [拒绝] [始终允许此类操作] │
└─────────────────────────────────────────────┘
关键设计点:
- 浏览器原生 UI:不是网页自己弹的对话框,而是浏览器级别的安全提示,网页无法伪造
- 参数展示:确认框中展示工具调用的完整参数,用户清楚知道 Agent 要做什么
- 分级授权:对于只读操作可以"始终允许",对于危险操作每次都要确认
- 可撤销:用户可以随时在浏览器设置中撤销已授权的权限
3.3 来源限制
WebMCP 工具只能在同源页面中被发现和调用。Agent 不能跨域调用其他页面的工具——这和 Cookie、localStorage 的同源策略一致。
// shop.example.com 的页面只能暴露自己的工具
// Agent 无法通过 WebMCP 直接操作 bank.example.com 的页面
// 如果需要跨域操作,需要通过 MCP(后端协议)而非 WebMCP
3.4 速率限制
浏览器内置了速率限制机制,防止 Agent 在短时间内大量调用工具:
- 只读操作:每秒最多 10 次
- 写入操作:每秒最多 3 次
- 危险操作:每分钟最多 5 次
超限的调用会被浏览器静默丢弃,返回 429 Too Many Requests 错误。
四、代码实战:构建一个 WebMCP 增强的电商应用
接下来,我们通过一个完整的电商应用案例,演示如何一步步为现有 Web 应用添加 WebMCP 支持。
4.1 项目初始化
mkdir webmcp-shop && cd webmcp-shop
npm init -y
npm install express
4.2 基础电商页面
首先创建一个基础的电商页面(不包含 WebMCP):
<!-- public/index.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>WebMCP 电商演示</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, sans-serif; background: #f5f5f5; }
.header { background: #fff; padding: 16px 24px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.header h1 { font-size: 20px; }
.cart-badge { background: #ff4d4f; color: #fff; border-radius: 50%; padding: 2px 8px; font-size: 12px; margin-left: 8px; }
.products { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 16px; padding: 24px; }
.product-card { background: #fff; border-radius: 8px; padding: 16px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.product-card img { width: 100%; height: 200px; object-fit: cover; border-radius: 4px; }
.product-card h3 { margin: 8px 0 4px; font-size: 16px; }
.product-card .price { color: #ff4d4f; font-size: 20px; font-weight: bold; }
.product-card button { width: 100%; margin-top: 8px; padding: 8px; background: #1677ff; color: #fff; border: none; border-radius: 4px; cursor: pointer; }
.product-card button:hover { background: #0958d9; }
.search-bar { display: flex; gap: 8px; padding: 16px 24px; }
.search-bar input { flex: 1; padding: 8px 12px; border: 1px solid #d9d9d9; border-radius: 4px; }
.search-bar select { padding: 8px; border: 1px solid #d9d9d9; border-radius: 4px; }
.search-bar button { padding: 8px 16px; background: #1677ff; color: #fff; border: none; border-radius: 4px; }
</style>
</head>
<body>
<div class="header">
<h1>🛍️ WebMCP Shop <span class="cart-badge" id="cart-count">0</span></h1>
</div>
<div class="search-bar">
<input type="text" id="search-input" placeholder="搜索商品...">
<select id="category-filter">
<option value="">全部分类</option>
<option value="electronics">电子产品</option>
<option value="clothing">服装</option>
<option value="food">食品</option>
</select>
<button onclick="searchProducts()">搜索</button>
</div>
<div class="products" id="product-list"></div>
<script>
// 商品数据
const products = [
{ id: 'p001', name: '机械键盘 Pro', category: 'electronics', price: 599, image: 'https://picsum.photos/seed/keyboard/300/200' },
{ id: 'p002', name: '无线耳机', category: 'electronics', price: 299, image: 'https://picsum.photos/seed/headphone/300/200' },
{ id: 'p003', name: '纯棉T恤', category: 'clothing', price: 89, image: 'https://picsum.photos/seed/tshirt/300/200' },
{ id: 'p004', name: '运动外套', category: 'clothing', price: 259, image: 'https://picsum.photos/seed/jacket/300/200' },
{ id: 'p005', name: '有机坚果礼盒', category: 'food', price: 128, image: 'https://picsum.photos/seed/nuts/300/200' },
{ id: 'p006', name: '精品咖啡豆', category: 'food', price: 68, image: 'https://picsum.photos/seed/coffee/300/200' },
];
// 购物车状态
const cart = [];
// 渲染商品列表
function renderProducts(list) {
const container = document.getElementById('product-list');
container.innerHTML = list.map(p => `
<div class="product-card">
<img src="${p.image}" alt="${p.name}">
<h3>${p.name}</h3>
<div class="price">¥${p.price}</div>
<button onclick="addToCart('${p.id}')">加入购物车</button>
</div>
`).join('');
}
// 搜索
function searchProducts() {
const keyword = document.getElementById('search-input').value.toLowerCase();
const category = document.getElementById('category-filter').value;
const filtered = products.filter(p => {
const matchKeyword = !keyword || p.name.toLowerCase().includes(keyword);
const matchCategory = !category || p.category === category;
return matchKeyword && matchCategory;
});
renderProducts(filtered);
}
// 加入购物车
function addToCart(productId) {
const product = products.find(p => p.id === productId);
if (!product) return;
const existing = cart.find(item => item.productId === productId);
if (existing) {
existing.quantity++;
} else {
cart.push({ productId, name: product.name, price: product.price, quantity: 1 });
}
updateCartBadge();
alert(`已将 ${product.name} 加入购物车`);
}
function updateCartBadge() {
const count = cart.reduce((sum, item) => sum + item.quantity, 0);
document.getElementById('cart-count').textContent = count;
}
// 初始渲染
renderProducts(products);
</script>
</body>
</html>
4.3 添加 WebMCP 声明式工具
现在,我们为搜索功能添加声明式 WebMCP 支持:
<!-- 修改搜索栏部分 -->
<form webmcp-tool="search-products"
webmcp-description="搜索商品,支持关键词和分类筛选"
onsubmit="event.preventDefault(); searchProducts();">
<input type="text" id="search-input"
name="keyword"
webmcp-param="关键词"
webmcp-type="string"
webmcp-description="搜索关键词"
placeholder="搜索商品...">
<select id="category-filter"
name="category"
webmcp-param="分类"
webmcp-type="string"
webmcp-enum='["electronics","clothing","food"]'>
<option value="">全部分类</option>
<option value="electronics">电子产品</option>
<option value="clothing">服装</option>
<option value="food">食品</option>
</select>
<button type="submit">搜索</button>
</form>
仅仅加了几个 HTML 属性,搜索引擎就变成了 AI 可调用的工具。零 JavaScript 改动。
4.4 添加 WebMCP 命令式工具
对于"加入购物车"这种需要自定义逻辑的操作,使用命令式 API:
// 在 <script> 中添加 WebMCP 命令式工具注册
if ('webmcp' in navigator) {
// 工具1:加入购物车
navigator.webmcp.registerTool({
name: 'add-to-cart',
description: '将指定商品加入购物车',
riskLevel: 'idempotent-write',
inputSchema: {
type: 'object',
properties: {
productId: {
type: 'string',
description: '商品ID,可选值:p001-p006'
},
quantity: {
type: 'number',
description: '购买数量',
default: 1,
minimum: 1
}
},
required: ['productId']
},
outputSchema: {
type: 'object',
properties: {
success: { type: 'boolean' },
cartItemCount: { type: 'number' },
productName: { type: 'string' },
message: { type: 'string' }
}
},
handler: async (input) => {
const product = products.find(p => p.id === input.productId);
if (!product) {
return { success: false, cartItemCount: 0, productName: '', message: '商品不存在' };
}
const qty = input.quantity || 1;
const existing = cart.find(item => item.productId === input.productId);
if (existing) {
existing.quantity += qty;
} else {
cart.push({ productId: input.productId, name: product.name, price: product.price, quantity: qty });
}
updateCartBadge();
const count = cart.reduce((sum, item) => sum + item.quantity, 0);
return {
success: true,
cartItemCount: count,
productName: product.name,
message: `已将 ${qty} 件 ${product.name} 加入购物车`
};
}
});
// 工具2:移除购物车商品
navigator.webmcp.registerTool({
name: 'remove-from-cart',
description: '从购物车中移除指定商品',
riskLevel: 'non-idempotent-write',
inputSchema: {
type: 'object',
properties: {
productId: { type: 'string', description: '要移除的商品ID' }
},
required: ['productId']
},
outputSchema: {
type: 'object',
properties: {
success: { type: 'boolean' },
cartItemCount: { type: 'number' },
message: { type: 'string' }
}
},
handler: async (input) => {
const idx = cart.findIndex(item => item.productId === input.productId);
if (idx === -1) {
return { success: false, cartItemCount: cart.length, message: '购物车中没有该商品' };
}
const removed = cart.splice(idx, 1)[0];
updateCartBadge();
const count = cart.reduce((sum, item) => sum + item.quantity, 0);
return {
success: true,
cartItemCount: count,
message: `已从购物车移除 ${removed.name}`
};
}
});
// 工具3:提交订单
navigator.webmcp.registerTool({
name: 'place-order',
description: '提交购物车中的所有商品为订单',
riskLevel: 'non-idempotent-write',
inputSchema: {
type: 'object',
properties: {
shippingAddress: {
type: 'string',
description: '收货地址'
},
paymentMethod: {
type: 'string',
enum: ['alipay', 'wechat', 'credit-card'],
description: '支付方式'
}
},
required: ['shippingAddress', 'paymentMethod']
},
outputSchema: {
type: 'object',
properties: {
success: { type: 'boolean' },
orderId: { type: 'string' },
totalAmount: { type: 'number' },
message: { type: 'string' }
}
},
handler: async (input) => {
if (cart.length === 0) {
return { success: false, orderId: '', totalAmount: 0, message: '购物车为空' };
}
const total = cart.reduce((sum, item) => sum + item.price * item.quantity, 0);
const orderId = 'ORD-' + Date.now();
// 模拟下单
cart.length = 0;
updateCartBadge();
return {
success: true,
orderId,
totalAmount: total,
message: `订单 ${orderId} 已提交,总额 ¥${total.toFixed(2)},等待支付`
};
}
});
// 提供购物车上下文
navigator.webmcp.provideContext({
name: 'cart-state',
description: '当前购物车的完整状态',
schema: {
type: 'object',
properties: {
items: {
type: 'array',
items: {
type: 'object',
properties: {
productId: { type: 'string' },
name: { type: 'string' },
price: { type: 'number' },
quantity: { type: 'number' }
}
}
},
totalPrice: { type: 'number' },
itemCount: { type: 'number' }
}
},
getter: () => {
const total = cart.reduce((sum, item) => sum + item.price * item.quantity, 0);
const count = cart.reduce((sum, item) => sum + item.quantity, 0);
return { items: [...cart], totalPrice: total, itemCount: count };
}
});
// 提供商品目录上下文
navigator.webmcp.provideContext({
name: 'product-catalog',
description: '所有可购买商品的目录',
schema: {
type: 'object',
properties: {
products: {
type: 'array',
items: {
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' },
category: { type: 'string' },
price: { type: 'number' }
}
}
}
}
},
getter: () => ({ products: products.map(p => ({ id: p.id, name: p.name, category: p.category, price: p.price })) })
});
console.log('[WebMCP] 已注册 4 个工具和 2 个上下文');
} else {
console.warn('[WebMCP] 当前浏览器不支持 WebMCP API');
}
4.5 服务端代码
// server.js
const express = require('express');
const path = require('path');
const app = express();
app.use(express.static(path.join(__dirname, 'public')));
// WebMCP 发现端点(供 Agent 查询页面可用工具)
app.get('/.well-known/webmcp.json', (req, res) => {
res.json({
version: '1.0.0',
tools: [
{
name: 'search-products',
description: '搜索商品,支持关键词和分类筛选',
inputSchema: {
type: 'object',
properties: {
keyword: { type: 'string', description: '搜索关键词' },
category: { type: 'string', enum: ['electronics', 'clothing', 'food'], description: '分类' }
}
}
},
{
name: 'add-to-cart',
description: '将指定商品加入购物车',
riskLevel: 'idempotent-write',
inputSchema: {
type: 'object',
properties: {
productId: { type: 'string', description: '商品ID' },
quantity: { type: 'number', default: 1, minimum: 1 }
},
required: ['productId']
}
},
{
name: 'remove-from-cart',
description: '从购物车中移除指定商品',
riskLevel: 'non-idempotent-write',
inputSchema: {
type: 'object',
properties: { productId: { type: 'string' } },
required: ['productId']
}
},
{
name: 'place-order',
description: '提交购物车中的所有商品为订单',
riskLevel: 'non-idempotent-write',
inputSchema: {
type: 'object',
properties: {
shippingAddress: { type: 'string' },
paymentMethod: { type: 'string', enum: ['alipay', 'wechat', 'credit-card'] }
},
required: ['shippingAddress', 'paymentMethod']
}
}
],
contexts: [
{ name: 'cart-state', description: '当前购物车状态' },
{ name: 'product-catalog', description: '商品目录' }
]
});
});
app.listen(3000, () => {
console.log('WebMCP Shop 运行在 http://localhost:3000');
});
五、Agent 端集成:如何让 AI 发现和调用 WebMCP 工具
5.1 工具发现流程
Agent 操作一个 WebMCP 增强的网页,流程如下:
1. Agent 打开网页
2. 浏览器加载页面,执行 WebMCP 注册代码
3. Agent 查询 navigator.webmcp.listTools() 获取可用工具列表
4. Agent 查询 navigator.webmcp.listContexts() 获取可用上下文
5. Agent 读取上下文,了解页面状态
6. Agent 根据用户意图,选择并调用合适的工具
7. 浏览器弹出确认框(如果需要)
8. 用户确认后,工具执行,返回结果
9. Agent 根据结果继续决策
5.2 Agent 侧代码示例
// Agent 端代码(运行在 AI 应用中,通过 Chrome DevTools Protocol 连接浏览器)
class WebMCPAgent {
constructor(page) {
this.page = page; // Puppeteer/Playwright Page 对象
}
// 发现可用工具
async discoverTools() {
const tools = await this.page.evaluate(() => {
return navigator.webmcp.listTools();
});
console.log('可用工具:', tools.map(t => `${t.name}: ${t.description}`));
return tools;
}
// 读取上下文
async readContext(contextName) {
const context = await this.page.evaluate((name) => {
return navigator.webmcp.getContext(name);
}, contextName);
return context;
}
// 调用工具
async callTool(toolName, params) {
const result = await this.page.evaluate(({ name, args }) => {
return navigator.webmcp.callTool(name, args);
}, { name: toolName, args: params });
return result;
}
// 完整的购物流程示例
async shoppingFlow(userRequest) {
// 步骤1:发现工具
await this.discoverTools();
// 步骤2:读取商品目录
const catalog = await this.readContext('product-catalog');
console.log('商品目录:', catalog.products.map(p => `${p.id} - ${p.name} ¥${p.price}`));
// 步骤3:搜索商品
const searchResult = await this.callTool('search-products', {
keyword: '键盘',
category: 'electronics'
});
console.log('搜索结果:', searchResult);
// 步骤4:加入购物车
const addResult = await this.callTool('add-to-cart', {
productId: 'p001',
quantity: 1
});
console.log('加入购物车结果:', addResult);
// 步骤5:确认购物车状态
const cartState = await this.readContext('cart-state');
console.log('购物车状态:', cartState);
// 步骤6:提交订单
const orderResult = await this.callTool('place-order', {
shippingAddress: '北京市朝阳区xxx路xxx号',
paymentMethod: 'alipay'
});
console.log('订单结果:', orderResult);
return orderResult;
}
}
5.3 Token 消耗对比
我们对比传统视觉路线和 WebMCP 路线完成同一个购物流程的 Token 消耗:
| 操作步骤 | 视觉路线 Token | WebMCP 路线 Token | 节省比例 |
|---|---|---|---|
| 打开页面 | ~2000(截图) | ~100(读取上下文) | 95% |
| 搜索商品 | ~4000(2次截图+OCR) | ~150(工具调用) | 96% |
| 查看搜索结果 | ~2000(截图) | ~100(读取结果) | 95% |
| 加入购物车 | ~2000(截图+定位+点击) | ~120(工具调用) | 94% |
| 确认购物车 | ~2000(截图) | ~100(读取上下文) | 95% |
| 提交订单 | ~4000(2次截图+填写+点击) | ~180(工具调用) | 95.5% |
| 总计 | ~16000 | ~750 | 95.3% |
WebMCP 将 Token 消耗降低了约 95%,同时将每个操作步骤的延迟从 2-5 秒降低到毫秒级。
六、高级特性与最佳实践
6.1 工具版本管理
随着产品迭代,工具的接口可能需要变更。WebMCP 推荐的版本管理策略:
// v1 版本
navigator.webmcp.registerTool({
name: 'search-products', // name 不变
version: '1.0.0',
inputSchema: { /* v1 schema */ },
handler: async (input) => { /* v1 逻辑 */ }
});
// v2 版本——新增参数但不破坏旧接口
navigator.webmcp.registerTool({
name: 'search-products', // name 保持不变
version: '2.0.0',
inputSchema: {
type: 'object',
properties: {
keyword: { type: 'string' },
category: { type: 'string' },
// v2 新增参数
sortBy: { type: 'string', enum: ['price', 'rating', 'relevance'], default: 'relevance' },
limit: { type: 'number', default: 20 }
}
},
handler: async (input) => { /* v2 逻辑,兼容 v1 参数 */ }
});
原则:新参数必须有默认值,旧参数不能删除或改名。这和 RESTful API 的版本管理思路一致——向后兼容,渐进增强。
6.2 工具组合与编排
WebMCP 支持工具间的组合调用。Agent 可以按照逻辑顺序串联多个工具:
// Agent 编排示例:比价购物流程
async function compareAndBuy(agent, productKeyword) {
// 1. 搜索商品
const searchResult = await agent.callTool('search-products', {
keyword: productKeyword
});
// 2. 读取商品目录获取价格信息
const catalog = await agent.readContext('product-catalog');
// 3. 找到最便宜的
const cheapest = catalog.products
.filter(p => p.name.includes(productKeyword))
.sort((a, b) => a.price - b.price)[0];
// 4. 加入购物车
await agent.callTool('add-to-cart', {
productId: cheapest.id,
quantity: 1
});
// 5. 确认
const cart = await agent.readContext('cart-state');
return cart;
}
6.3 错误处理与重试
WebMCP 工具的 handler 应该返回结构化的错误信息,而不是抛出异常:
navigator.webmcp.registerTool({
name: 'place-order',
// ...
handler: async (input) => {
try {
const result = await submitOrder(input);
return { success: true, orderId: result.id, totalAmount: result.total };
} catch (error) {
// 返回结构化错误,而不是 throw
return {
success: false,
errorCode: error.code, // 机器可读的错误码
errorType: error.type, // 错误分类:network/validation/business
message: error.userMessage, // 人类可读的错误描述
retryable: error.retryable // 是否可重试
};
}
}
});
Agent 侧根据错误类型决定策略:
async callToolWithRetry(toolName, params, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
const result = await this.callTool(toolName, params);
if (result.success) return result;
if (!result.retryable) {
console.error(`工具 ${toolName} 调用失败,不可重试:`, result.message);
return result;
}
console.warn(`工具 ${toolName} 调用失败,${i + 1}/${maxRetries} 次重试...`);
await new Promise(r => setTimeout(r, 1000 * Math.pow(2, i))); // 指数退避
}
return { success: false, message: '重试次数耗尽' };
}
6.4 渐进式迁移策略
对于已有的大型 Web 应用,不可能一次性迁移所有功能。推荐渐进式策略:
第一阶段:核心流程优先
只为核心业务路径添加 WebMCP 工具。以电商为例:搜索 → 加购 → 下单。
// 第一阶段只注册 3 个核心工具
const coreTools = ['search-products', 'add-to-cart', 'place-order'];
第二阶段:扩展到管理类操作
// 第二阶段添加管理类工具
const managementTools = ['view-order-history', 'cancel-order', 'update-address'];
第三阶段:覆盖所有交互
// 第三阶段:低频但有用的操作
const extendedTools = ['apply-coupon', 'write-review', 'contact-support'];
每个阶段的工具都是独立注册的,互不影响。Agent 可以根据已发现的工具列表决定自己的能力边界。
6.5 与现有前端框架集成
React 集成
// useWebMCP.js - React Hook
import { useEffect, useRef } from 'react';
export function useWebMCP(toolDef) {
const toolRef = useRef(null);
useEffect(() => {
if (!('webmcp' in navigator)) return;
const tool = navigator.webmcp.registerTool(toolDef);
toolRef.current = tool;
return () => {
// 组件卸载时注销工具
navigator.webmcp.unregisterTool(toolDef.name);
};
}, [toolDef.name]);
return toolRef;
}
// ProductCard.jsx
function ProductCard({ product }) {
const addToCart = useCallback(async (quantity) => {
// 实际的加购逻辑
const result = await cartApi.add(product.id, quantity);
return result;
}, [product.id]);
useWebMCP({
name: `add-to-cart-${product.id}`,
description: `将 ${product.name} 加入购物车`,
riskLevel: 'idempotent-write',
inputSchema: {
type: 'object',
properties: {
quantity: { type: 'number', default: 1, minimum: 1 }
}
},
handler: addToCart
});
return <div className="product-card">...</div>;
}
Vue 集成
// webmcp-plugin.js - Vue 插件
export default {
install(app) {
if (!('webmcp' in navigator)) return;
app.config.globalProperties.$webmcp = {
registerTool: navigator.webmcp.registerTool.bind(navigator.webmcp),
unregisterTool: navigator.webmcp.unregisterTool.bind(navigator.webmcp),
provideContext: navigator.webmcp.provideContext.bind(navigator.webmcp),
};
// 组件卸载时自动注销
app.mixin({
beforeUnmount() {
if (this._webmcpTools) {
this._webmcpTools.forEach(name => {
navigator.webmcp.unregisterTool(name);
});
}
}
});
}
};
// main.js
import WebMCPPlugin from './webmcp-plugin';
app.use(WebMCPPlugin);
七、性能优化与生产级考量
7.1 工具注册的懒加载
不要在页面加载时一次性注册所有工具,而是按需注册:
// 路由级别的懒加载
router.beforeEach(async (to) => {
// 清除上一页的工具
navigator.webmcp.clearTools();
// 注册当前路由的工具
if (to.path === '/shop') {
const { registerShopTools } = await import('./webmcp/shop-tools.js');
registerShopTools();
} else if (to.path === '/orders') {
const { registerOrderTools } = await import('./webmcp/order-tools.js');
registerOrderTools();
}
});
7.2 上下文缓存
对于频繁读取的上下文,使用缓存减少计算开销:
navigator.webmcp.provideContext({
name: 'product-catalog',
description: '商品目录',
schema: { /* ... */ },
getter: (() => {
let cachedResult = null;
let cacheExpiry = 0;
return () => {
const now = Date.now();
if (cachedResult && now < cacheExpiry) {
return cachedResult;
}
cachedResult = computeCatalog(); // 耗时计算
cacheExpiry = now + 5000; // 5秒缓存
return cachedResult;
};
})()
});
7.3 工具调用的防抖与节流
navigator.webmcp.registerTool({
name: 'search-products',
description: '搜索商品',
inputSchema: { /* ... */ },
handler: debounce(async (input) => {
const results = await fetchSearchResults(input.keyword);
return results;
}, 300) // 300ms 防抖
});
function debounce(fn, delay) {
let timer;
let lastPromise;
return function(...args) {
clearTimeout(timer);
return new Promise((resolve, reject) => {
timer = setTimeout(() => {
lastPromise = fn.apply(this, args);
lastPromise.then(resolve).catch(reject);
}, delay);
});
};
}
7.4 监控与日志
在生产环境中,建议为所有工具调用添加监控:
const originalRegister = navigator.webmcp.registerTool.bind(navigator.webmcp);
navigator.webmcp.registerTool = function(toolDef) {
const originalHandler = toolDef.handler;
toolDef.handler = async function(input) {
const startTime = performance.now();
const toolName = toolDef.name;
try {
const result = await originalHandler(input);
const duration = performance.now() - startTime;
// 上报监控数据
monitor.report({
type: 'webmcp_tool_call',
tool: toolName,
duration,
success: result.success,
timestamp: Date.now()
});
return result;
} catch (error) {
const duration = performance.now() - startTime;
monitor.report({
type: 'webmcp_tool_error',
tool: toolName,
duration,
error: error.message,
timestamp: Date.now()
});
return {
success: false,
errorCode: 'INTERNAL_ERROR',
message: error.message,
retryable: true
};
}
};
return originalRegister(toolDef);
};
八、WebMCP 与竞争方案的对比
8.1 WebMCP vs 传统 RPA
| 维度 | 传统 RPA(UI Automation) | WebMCP |
|---|---|---|
| 交互方式 | 模拟点击、输入、截图 | 语义化工具调用 |
| 稳定性 | 低(UI 变更即失效) | 高(工具契约与 UI 解耦) |
| 开发成本 | 高(需要维护选择器) | 低(声明式或少量 JS) |
| Token 消耗 | 高(~16000/流程) | 低(~750/流程) |
| 延迟 | 2-5s/步 | <100ms/步 |
| 跨平台 | 需要浏览器驱动 | 浏览器原生支持 |
| 安全性 | 低(绕过安全机制) | 高(浏览器权限控制) |
8.2 WebMCP vs Computer Use / Browser Use
Computer Use(如 Anthropic 的 Claude Computer Use)通过截图和坐标操作电脑桌面,Browser Use 通过 DOM 操作浏览器。它们和 WebMCP 的区别:
Computer Use: 截图 → 识别 → 坐标点击 (最慢,最通用,最不稳定)
Browser Use: DOM → 选择器 → 执行操作 (中等速度,依赖 DOM 结构)
WebMCP: 工具列表 → 语义调用 (最快,最稳定,需网页支持)
三者不是互斥的,而是互补的层级:
- 首选 WebMCP:如果目标网页支持 WebMCP,直接语义调用
- 降级 Browser Use:如果不支持 WebMCP,用 DOM 操作
- 兜底 Computer Use:如果连 DOM 都不可用(如桌面应用),用截图+坐标
8.3 WebMCP vs Structured Data(Schema.org)
Schema.org 通过结构化数据标记(JSON-LD、Microdata)让搜索引擎理解页面内容。WebMCP 和它的区别:
- Schema.org:描述"页面是什么"(商品信息、价格、评分)——被动描述
- WebMCP:描述"页面能做什么"(搜索、加购、下单)——主动暴露能力
Schema.org 帮搜索引擎理解内容,WebMCP 帮 AI Agent 执行操作。
九、生态现状与未来展望
9.1 浏览器支持现状(2026 年 5 月)
| 浏览器 | WebMCP 支持 | 版本 |
|---|---|---|
| Chrome | ✅ 实验性支持 | 146+(需开启 flag) |
| Edge | ✅ 实验性支持 | 146+(与 Chrome 同步) |
| Firefox | 🔄 开发中 | 预计 2026 Q3 |
| Safari | 📋 评估中 | 暂无时间表 |
Chrome 中启用 WebMCP:
- 访问
chrome://flags - 搜索 "WebMCP" 或 "Web Model Context Protocol"
- 设置为 "Enabled"
- 重启浏览器
9.2 W3C 标准化进程
WebMCP 目前处于 W3C "First Public Working Draft" 阶段,预计标准化时间线:
- 2026 Q1:First Public Working Draft(已完成)
- 2026 Q3:Working Draft(多轮修改)
- 2027 Q1:Candidate Recommendation(候选推荐)
- 2027 Q3:Proposed Recommendation(建议推荐)
- 2027 Q4:W3C Recommendation(正式标准)
9.3 潜在的杀手级应用
- 无障碍访问的革命:视障用户可以通过 AI Agent 语义化操作网页,不再依赖屏幕阅读器猜测 UI
- 企业自动化的降本:内部系统的操作自动化从"RPA 开发"变成"工具注册"
- 跨应用工作流:Agent 可以在多个 WebMCP 增强的网页间无缝切换,完成跨应用流程
- 测试自动化:E2E 测试从"模拟用户操作"变成"直接调用工具",更稳定、更快速
9.4 挑战与风险
- 采用成本:需要网页主动适配,老系统的迁移需要时间和动力
- 安全边界:权限模型的细粒度控制还不够成熟,恶意注册工具的风险需要持续关注
- 标准化速度:W3C 标准化流程缓慢,Chrome 的早期预览和最终标准可能存在差异
- 竞争格局:Apple Safari 的态度尚不明朗,如果拒绝支持将形成碎片化
十、总结
WebMCP 代表了 AI 与 Web 交互的范式跃迁:从"Agent 猜 UI"到"网页主动暴露能力"。这个转变的深远影响不亚于从命令行到图形界面的变革。
核心要点回顾:
- 双轨 API 设计:声明式(HTML 属性)适合简单表单,命令式(JS 注册)适合复杂逻辑,渐进增强
- 工具契约机制:name + description + inputSchema + outputSchema + handler,与 UI 解耦,改版不影响 Agent
- 上下文提供:网页主动暴露状态,Agent 无需解析 DOM 或截图,Token 消耗降低 95%
- 安全模型:三级权限、浏览器原生确认、同源限制、速率限制,多层保护用户安全
- 生态互补:WebMCP 不是 MCP 的替代,是浏览器端的延伸;不是 RPA 的替代,是更高层的抽象
给开发者的建议:
- 现在就开始实验,Chrome 146 已支持早期预览
- 先为核心业务路径添加 WebMCP 工具,渐进式迁移
- 工具契约设计要像 API 设计一样严谨——一旦发布,尽量不改
- 关注 W3C 标准化进程,及时调整实现
WebMCP 不是未来,它正在发生。2026 年,让 AI Agent 操作你的网页不再是暴力破解,而是一次优雅的握手。
参考资源:
- W3C WebMCP Working Draft:https://www.w3.org/TR/webmcp/
- Chrome WebMCP Flag:chrome://flags/#enable-webmcp
- MCP 官方文档:https://modelcontextprotocol.io/
- Google Chrome 146 Release Notes:WebMCP Early Preview