Logo
热心市民王先生

关键代码验证

技术研究 WebMCP 代码示例

WebMCP协议的核心代码示例与集成配置

环境配置

Chrome Canary启用WebMCP

WebMCP目前在Chrome 146 Canary中以DevTrial形式提供,需要手动启用:

chrome://flags → "Experimental Web Platform Features" → Enable

或直接搜索webmcpmodel-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>

关键点说明

  1. toolnametooldescription是必需属性,代理通过这些理解工具功能。

  2. toolautosubmit="true"让浏览器在代理填充表单后自动提交。

  3. 每个输入字段可以有toolparamtitletoolparamdescription注解。

  4. 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' });