关键代码验证
技术研究 WebMCP 代码示例
WebMCP协议的核心代码示例与集成配置
环境配置
Chrome Canary启用WebMCP
WebMCP目前在Chrome 146 Canary中以DevTrial形式提供,需要手动启用:
chrome://flags → "Experimental Web Platform Features" → Enable
或直接搜索webmcp或model-context标志。
检测API可用性
// 检测WebMCP是否可用
if ('modelContext' in navigator) {
console.log('WebMCP is available');
// 可以开始注册工具
} else {
console.log('WebMCP not supported, falling back to DOM automation');
}
声明式API示例
基础表单注解
以下是一个电商网站搜索表单的完整示例:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>WebMCP示例 - 产品搜索</title>
</head>
<body>
<!-- 声明式API:仅需添加HTML属性 -->
<form
id="product-search-form"
action="/search"
method="GET"
toolname="searchProducts"
tooldescription="搜索产品目录,支持关键词、分类和价格范围筛选"
toolautosubmit="true"
>
<input
type="text"
name="query"
placeholder="搜索关键词"
required
toolparamtitle="搜索关键词"
toolparamdescription="产品名称或描述中的关键词"
>
<select
name="category"
toolparamtitle="产品分类"
toolparamdescription="选择产品所属分类"
>
<option value="">全部分类</option>
<option value="electronics">电子产品</option>
<option value="clothing">服装</option>
<option value="home">家居用品</option>
</select>
<input
type="number"
name="maxPrice"
placeholder="最高价格"
min="0"
toolparamtitle="最高价格"
toolparamdescription="筛选价格不超过此数值的产品"
>
<button type="submit">搜索</button>
</form>
<script>
// 区分人工提交和代理提交
document.getElementById('product-search-form').addEventListener('submit', (e) => {
if (e.agentInvoked) {
console.log('此提交由AI代理触发');
// 可以添加额外的审计日志或验证逻辑
} else {
console.log('此提交由人工触发');
}
});
</script>
</body>
</html>
关键点说明:
-
toolname和tooldescription是必需属性,代理通过这些理解工具功能。 -
toolautosubmit="true"让浏览器在代理填充表单后自动提交。 -
每个输入字段可以有
toolparamtitle和toolparamdescription注解。 -
SubmitEvent.agentInvoked标志区分来源,便于服务端审计。
命令式API示例
注册单个工具
// 页面加载后注册工具
document.addEventListener('DOMContentLoaded', () => {
// 检查API可用性
if (!navigator.modelContext) {
console.warn('WebMCP not available');
return;
}
// 注册产品筛选工具
navigator.modelContext.registerTool({
name: "filterProducts",
description: "应用产品筛选条件:品牌、评分和库存状态",
inputSchema: {
type: "object",
properties: {
brands: {
type: "array",
items: { type: "string" },
description: "品牌名称列表"
},
minRating: {
type: "number",
minimum: 0,
maximum: 5,
description: "最低评分(0-5)"
},
inStockOnly: {
type: "boolean",
description: "仅显示有库存商品"
}
}
},
execute: async (inputs, client) => {
// 调用应用的业务逻辑层(而非直接操作DOM)
const { brands, minRating, inStockOnly } = inputs;
// 假设这是应用已有的筛选函数
await window.app.applyFilters({
brands,
minRating,
inStockOnly
});
// 返回结构化结果
const count = document.querySelectorAll('.product-card').length;
return {
content: [{
type: "text",
text: JSON.stringify({
success: true,
message: `筛选已应用,共找到 ${count} 个匹配产品`,
filterCount: count
})
}]
};
},
annotations: {
readOnlyHint: false // 此工具会修改页面状态
}
});
});
带用户确认的敏感操作
// 注册需要用户确认的工具
navigator.modelContext.registerTool({
name: "addToCart",
description: "将指定产品添加到购物车",
inputSchema: {
type: "object",
properties: {
productId: { type: "string", description: "产品ID" },
quantity: { type: "number", minimum: 1, default: 1 },
variant: { type: "string", description: "产品变体(尺寸、颜色等)" }
},
required: ["productId"]
},
execute: async (inputs, client) => {
// 请求用户确认
await client.requestUserInteraction(async () => {
// 用户确认后执行
const result = await window.app.cart.add({
productId: inputs.productId,
quantity: inputs.quantity || 1,
variant: inputs.variant
});
// 更新UI
window.app.ui.updateCartBadge(result.totalItems);
});
return {
content: [{
type: "text",
text: JSON.stringify({
success: true,
message: `已添加 ${inputs.quantity || 1} 件商品到购物车`
})
}]
};
},
annotations: {
readOnlyHint: false
}
});
批量注册工具
// 使用provideContext一次性注册多个工具
navigator.modelContext.provideContext({
tools: [
{
name: "getProducts",
description: "获取当前页面显示的产品列表",
inputSchema: { type: "object", properties: {} },
execute: async (_, __) => {
const products = Array.from(document.querySelectorAll('.product-card'))
.map(card => ({
id: card.dataset.productId,
name: card.querySelector('.product-name')?.textContent,
price: card.querySelector('.price')?.textContent
}));
return {
content: [{ type: "text", text: JSON.stringify(products) }]
};
},
annotations: { readOnlyHint: true }
},
{
name: "getPageInfo",
description: "获取当前页面的基本信息",
inputSchema: { type: "object", properties: {} },
execute: async (_, __) => {
return {
content: [{
type: "text",
text: JSON.stringify({
title: document.title,
url: window.location.href,
breadcrumb: Array.from(document.querySelectorAll('.breadcrumb a'))
.map(a => a.textContent)
})
}]
};
},
annotations: { readOnlyHint: true }
}
]
});
集成最佳实践
避免直接DOM操作
// ❌ 错误:直接操作DOM可能与框架状态不同步
execute: async (inputs, _) => {
document.getElementById('quantity').value = inputs.qty;
// React/Vue可能不会感知到这个变化
}
// ✅ 正确:调用应用的业务逻辑层
execute: async (inputs, _) => {
// 调用与UI事件处理器相同的函数
window.app.setQuantity(inputs.qty);
}
优雅降级策略
// 提供fallback方案
async function registerTools() {
const tools = [
{ name: "searchProducts", /* ... */ },
{ name: "addToCart", /* ... */ },
];
if (navigator.modelContext) {
// WebMCP可用
navigator.modelContext.provideContext({ tools });
} else {
// Fallback:通过其他方式暴露工具信息
window.__agentTools = tools;
console.info('WebMCP not available. Tools exposed via window.__agentTools');
}
}
清理上下文
// 页面卸载或用户登出时清理工具
window.addEventListener('beforeunload', () => {
if (navigator.modelContext) {
navigator.modelContext.clearContext();
}
});
// 用户登出时
function onUserLogout() {
if (navigator.modelContext) {
navigator.modelContext.clearContext();
}
// ... 其他登出逻辑
}
调试技巧
查看已注册工具
// 目前规范中没有getTools()方法,但可以通过以下方式调试
// 注意:这是调试代码,不应在生产环境使用
// 方案1:维护本地注册表
const registeredTools = new Map();
const originalRegister = navigator.modelContext.registerTool;
navigator.modelContext.registerTool = function(tool) {
registeredTools.set(tool.name, tool);
return originalRegister.call(this, tool);
};
// 查看所有已注册工具
console.log('Registered tools:', Array.from(registeredTools.keys()));
测试工具执行
// 手动测试工具执行(开发调试用)
async function testToolExecution(toolName, inputs) {
const tool = registeredTools.get(toolName);
if (!tool) {
console.error(`Tool "${toolName}" not found`);
return;
}
// 模拟客户端对象
const mockClient = {
requestUserInteraction: async (callback) => {
console.log('User confirmation requested');
await callback();
}
};
const result = await tool.execute(inputs, mockClient);
console.log('Execution result:', result);
return result;
}
// 测试调用
testToolExecution('searchProducts', { query: 'laptop' });