WebMCP 学习指南

WebMCP (Web Model Context Protocol) 是一个新兴的 W3C 提案,旨在让网站能够向 AI 代理暴露结构化工具,使 AI 能够可靠地与网页应用交互。

目录

  1. 什么是 WebMCP?
  2. 为什么需要 WebMCP?
  3. WebMCP 与 MCP 的区别
  4. 核心概念
  5. 环境准备与安装
  6. 如何使用 WebMCP(详细指南)
  7. Vue 框架集成(详细步骤)
  8. 声明式 API
  9. 安全最佳实践
  10. 开发与调试
  11. 参考 Demo 实战演练(小白教程)
  12. 未来展望

什么是 WebMCP?

WebMCP 是 W3C Web Machine Learning 社区组正在孵化的一个新规范,它允许 Web 开发者将 JavaScript 函数暴露为 AI 代理可以发现和调用的"工具"。

核心特点:

  • 浏览器原生 API:通过 navigator.modelContext 访问
  • 类型安全:支持 TypeScript 和 Zod 验证
  • 开发者友好:简单的 API,配合 Vue/React Hooks 使用
  • 框架无关:支持 React、Vue、Vanilla JS 或任何框架
  • 继承认证:工具继承用户的会话和权限
  • 零后端:完全在浏览器中运行,无需服务器更改

为什么需要 WebMCP?

传统方式的痛点

目前,AI 代理与网站交互通常需要:

  1. 屏幕截图:AI 通过视觉模型解析 UI
  2. 模拟点击:使用 Playwright 等工具模拟用户操作
  3. DOM 解析:通过 CSS 选择器抓取数据

这些方法存在以下问题:

  • 脆弱:UI 变化会导致代理失效
  • 昂贵:视觉模型计算成本高
  • 不准确:复杂交互容易出错

WebMCP 的解决方案

WebMCP 用可靠的函数调用替代了脆弱的屏幕抓取和模拟点击:

  • 工具在浏览器内执行,自然继承用户认证状态
  • AI 代理可以直接调用网站的业务逻辑
  • 提供结构化的输入输出,而非依赖视觉解析

WebMCP 与 MCP 的区别

特性

WebMCP

MCP

环境

浏览器

后端服务器

用途

人机协作

服务器到代理

认证

继承用户会话

OAuth 2.1

API

navigator.modelContext

MCP SDK

选择建议:

  • 使用 WebMCP:如果你想让网站直接暴露工具给浏览器中的 AI 代理
  • 使用 MCP:如果你在构建需要向 AI 模型暴露企业数据库或本地文件的后端系统

核心概念

Model Context

模型上下文是一个包含以下内容的结构:

  • 工具映射 (tool map):可调用函数的集合
  • 描述性元数据:帮助 AI 理解工具用途

Tool Definition (工具定义)

每个工具定义包含:

  • name:工具名称(必须唯一)
  • description:自然语言描述(告诉 AI 这个工具是做什么的)
  • inputSchema:输入参数的 JSON Schema
  • readOnlyHint:是否为只读操作(默认 false)
  • execute:执行函数

Declarative API vs Imperative API

  1. 声明式 API:通过 HTML 属性让表单等标准元素立即可被 AI 识别(最简单)
  2. 命令式 API:使用 JavaScript 手动注册和管理工具(更灵活)

环境准备与安装

方式一:使用 Chrome 146+(原生支持)

  1. 下载安装 Chrome 146 (Canary 或更高版本)
  2. 打开 chrome://flags
  3. 搜索并启用 "Experimental Web Platform features"
  4. 重启浏览器

方式二:使用 MCP-B 扩展(推荐,更稳定)

  1. 在 Chrome 浏览器中打开 Chrome Web Store
  2. 搜索 "MCP-B" 或 "Model Context Tool Inspector"
  3. 点击"添加至 Chrome"完成安装

安装 Polyfill 包

# 在你的项目中安装 pnpm add @mcp-b/global # 或使用 npm npm install @mcp-b/global

如何使用 WebMCP(详细指南)

方式一:命令式 API(手动注册工具)

这是最灵活的方式,适合需要精细控制工具的场景。

第一步:引入 Polyfill

在项目的入口文件(如 main.jsmain.ts)中添加:

import '@mcp-b/global';

或者在 HTML 中使用 script 标签:

<script src="https://unpkg.com/@mcp-b/global@latest/dist/index.iife.js"></script>

第二步:注册你的第一个工具

在你需要暴露功能的组件或文件中:

// 注册一个获取页面信息的工具 navigator.modelContext.registerTool({ name: 'get_page_info', description: '获取当前页面的标题和描述信息', inputSchema: { type: 'object', properties: {}, required: [] }, async execute() { return { title: document.title, url: window.location.href, description: document.querySelector('meta[name="description"]')?.content || '' }; } });

第三步:注册带参数的工具

// 注册一个搜索产品的工具 navigator.modelContext.registerTool({ name: 'search_products', description: '根据关键词搜索产品', inputSchema: { type: 'object', properties: { keyword: { type: 'string', description: '搜索关键词' }, limit: { type: 'number', description: '返回结果数量限制', default: 10 } }, required: ['keyword'] }, async execute({ keyword, limit = 10 }) { // 这里调用你的实际 API const response = await fetch(`/api/products?search=${keyword}&limit=${limit}`); const data = await response.json(); return data; } });

第四步:注册会修改数据的工具

// 注册一个添加到购物车的工具 navigator.modelContext.registerTool({ name: 'add_to_cart', description: '将商品添加到购物车', inputSchema: { type: 'object', properties: { productId: { type: 'string', description: '商品ID' }, quantity: { type: 'number', description: '购买数量', default: 1 } }, required: ['productId'] }, async execute({ productId, quantity }) { // 你的业务逻辑 const result = await cartApi.add(productId, quantity); return { success: true, message: '添加成功', cartCount: result.cartCount }; } });

工具生命周期管理

// 注销单个工具 navigator.modelContext.unregisterTool('tool_name'); // 清除所有工具 navigator.modelContext.clearContext(); // 批量注册工具 navigator.modelContext.provideContext({ tools: [ { name: 'tool1', description: '工具1', // ... }, { name: 'tool2', description: '工具2', // ... } ] });

方式二:声明式 API(最简单)

只需在 HTML 标签上添加两个属性:

<!-- 声明一个可提交的表单工具 --> <form action="/api/submit" method="POST" webmcp:tool="submit_contact_form" webmcp:read-only="false"> <input type="text" name="name" placeholder="姓名"> <input type="email" name="email" placeholder="邮箱"> <textarea name="message" placeholder="留言内容"></textarea> <button type="submit">提交</button> </form> <!-- 声明一个搜索框工具 --> <input type="search" webmcp:tool="site_search" webmcp:read-only="true" placeholder="搜索站内内容...">

这样 AI 代理就能自动识别这些表单元素的功能!


Vue 框架集成(详细步骤)

安装依赖

# 创建 Vue 项目(如果还没有) npm create vue@latest my-webmcp-app cd my-webmcp-app npm install # 安装 WebMCP 相关包 npm install @mcp-b/global @mcp-b/vue-webmcp zod

步骤一:配置入口文件

src/main.jssrc/main.ts 中引入 polyfill:

import { createApp } from 'vue' import App from './App.vue' import '@mcp-b/global' // 添加这一行 const app = createApp(App) app.mount('#app')

步骤二:创建 WebMCP 组合式函数(推荐)

src/composables/useWebMCP.js 中封装工具注册逻辑:

import { ref, onMounted, onUnmounted } from 'vue' export function useWebMCP(tools) { const registeredTools = ref([]) onMounted(() => { if (!navigator.modelContext) { console.warn('WebMCP is not supported in this browser') return } // 注册传入的工具 const toolList = Array.isArray(tools) ? tools : [tools] toolList.forEach(tool => { navigator.modelContext.registerTool({ ...tool, inputSchema: tool.inputSchema || { type: 'object', properties: {}, required: [] } }) registeredTools.value.push(tool.name) }) }) onUnmounted(() => { // 组件卸载时清理工具 registeredTools.value.forEach(name => { navigator.modelContext.unregisterTool(name) }) }) return { registeredTools } }

步骤三:在组件中使用

示例:产品搜索组件

<script setup> import { ref } from 'vue' import { useWebMCP } from '@/composables/useWebMCP' import { z } from 'zod' // 定义搜索状态 const searchResults = ref([]) const loading = ref(false) // 注册 WebMCP 工具 useWebMCP({ name: 'search_products', description: '根据关键词搜索产品,返回产品列表', schema: z.object({ keyword: z.string().min(1, '关键词不能为空'), maxResults: z.number().min(1).max(50).default(10) }), async execute({ keyword, maxResults = 10 }) { loading.value = true try { const response = await fetch(`/api/products?keyword=${encodeURIComponent(keyword)}&limit=${maxResults}`) const data = await response.json() searchResults.value = data.products || [] return { success: true, count: searchResults.value.length, products: searchResults.value } } catch (error) { return { success: false, error: error.message } } finally { loading.value = false } } }) // 本地搜索方法(用于普通用户) async function handleSearch(keyword, maxResults = 10) { const result = await fetch(`/api/products?keyword=${encodeURIComponent(keyword)}&limit=${maxResults}`) const data = await result.json() searchResults.value = data.products || [] } </script> <template> <div> <h2>产品搜索</h2> <div> <input v-model="keyword" @keyup.enter="handleSearch(keyword)" placeholder="输入产品名称搜索..." /> <button @click="handleSearch(keyword)">搜索</button> </div> <div v-if="loading">加载中...</div> <ul v-else> <li v-for="product in searchResults" :key="product.id"> {{ product.name }} - ¥{{ product.price }} </li> </ul> </div> </template>

示例:购物车组件

<script setup> import { ref, computed } from 'vue' import { useWebMCP } from '@/composables/useWebMCP' import { z } from 'zod' const cartItems = ref([]) const cartTotal = computed(() => cartItems.value.reduce((sum, item) => sum + item.price * item.quantity, 0) ) // 注册添加到购物车的工具 useWebMCP({ name: 'add_to_cart', description: '将产品添加到购物车', schema: z.object({ productId: z.string(), quantity: z.number().min(1).default(1) }), async execute({ productId, quantity }) { // 检查产品是否存在 const product = await fetch(`/api/products/${productId}`).then(r => r.json()) if (!product) { return { success: false, error: '产品不存在' } } // 添加到购物车 const existingItem = cartItems.value.find(item => item.productId === productId) if (existingItem) { existingItem.quantity += quantity } else { cartItems.value.push({ productId, name: product.name, price: product.price, quantity }) } return { success: true, message: `已添加 ${product.name} x${quantity} 到购物车`, cartCount: cartItems.value.length, total: cartTotal.value } } }) // 注册查看购物车的工具 useWebMCP({ name: 'get_cart', description: '获取当前购物车内容', schema: z.object({}), async execute() { return { items: cartItems.value, count: cartItems.value.length, total: cartTotal.value } } }) </script> <template> <div> <h2>购物车</h2> <div v-if="cartItems.length === 0">购物车是空的</div> <ul v-else> <li v-for="item in cartItems" :key="item.productId"> {{ item.name }} x{{ item.quantity }} - ¥{{ item.price * item.quantity }} </li> </ul> <div>总计: ¥{{ cartTotal }}</div> </div> </template>

步骤四:测试你的工具

  1. 运行项目:npm run dev
  2. 打开 Chrome 浏览器(带 MCP-B 扩展)
  3. 点击浏览器工具栏的 MCP-B 扩展图标
  4. 切换到 "Tools" 标签页
  5. 你应该能看到已注册的工具列表
  6. 点击工具可以测试调用

声明式 API

声明式 API 是最简单的方式,只需在 HTML 元素上添加属性即可。

基本语法

<!-- 表单工具 --> <form webmcp:tool="form_name" webmcp:read-only="false"> <!-- 表单内容 --> </form> <!-- 输入框工具 --> <input webmcp:tool="input_name" webmcp:read-only="true">

完整示例

<!DOCTYPE html> <html> <head> <title>WebMCP 声明式示例</title> <script src="https://unpkg.com/@mcp-b/global@latest/dist/index.iife.js"></script> </head> <body> <h1>WebMCP 声明式 API 示例</h1> <!-- 搜索框工具 --> <div> <label>搜索产品:</label> <input type="search" webmcp:tool="product_search" webmcp:read-only="true" placeholder="输入产品名称..."> </div> <!-- 订阅表单工具 --> <form webmcp:tool="email_subscribe" webmcp:read-only="false" action="/api/subscribe" method="POST"> <input type="email" name="email" placeholder="输入邮箱..." required> <button type="submit">订阅</button> </form> <!-- 联系表单工具 --> <form webmcp:tool="contact_form" webmcp:read-only="false" action="/api/contact" method="POST"> <input type="text" name="name" placeholder="姓名" required> <textarea name="message" placeholder="留言内容" required></textarea> <button type="submit">提交</button> </form> </body> </html>

安全最佳实践

关键原则

  1. 最小权限:只暴露用户已授权的功能
  2. 输入验证:使用 Zod 或 JSON Schema 验证输入
  3. 只读优先:优先使用 readOnlyHint: true
  4. 用户感知:使用 requestUserInteraction() 处理敏感操作

安全建议

  • 限制每页工具数量(建议 < 50 个)
  • 定期审计暴露的工具
  • 实施速率限制
  • 监控异常调用模式
  • 不要暴露管理员功能

开发与调试

调试工具

  1. Model Context Tool Inspector:Chrome 扩展
    • 查看页面上所有注册的工具
    • 手动测试工具调用
    • 验证 schema
    • 监控调用日志
  2. 浏览器开发者工具
    • 打开控制台查看注册日志
    • 检查 navigator.modelContext 对象

测试步骤

  1. 启用开发工具(F12)
  2. 打开 MCP-B 扩展
  3. 导航到 Tools 标签页
  4. 点击工具进行测试
  5. 查看返回值

参考 Demo 实战演练(小白教程)

在线 Demo 地址

访问演示网站:https://travel-demo.bandarra.me

第一次打开 Demo(图文步骤)

第一步:打开网站

  1. 打开 Chrome 浏览器
  2. 在地址栏输入:https://travel-demo.bandarra.me
  3. 按回车,等待页面加载

第二步:安装 MCP-B 扩展(如未安装)

  1. 如果你没有安装 MCP-B 扩展,页面可能会提示你安装
  2. 点击提示中的链接,前往 Chrome Web Store
  3. 点击"添加至 Chrome"
  4. 安装完成后,刷新 Demo 页面

第三步:探索 Demo 功能

你会看到一个旅行预订网站,包含:

  • 航班搜索
  • 酒店搜索
  • 行程规划

第四步:使用 MCP-B 扩展查看工具

  1. 点击浏览器工具栏的 MCP-B 扩展图标
  2. 在弹出的面板中,点击 "Tools" 标签
  3. 你会看到页面上注册的所有 WebMCP 工具列表,例如:
    • search_flights - 搜索航班
    • search_hotels - 搜索酒店
    • add_to_itinerary - 添加到行程

第五步:测试工具调用

  1. 在 MCP-B 面板的工具列表中,点击 search_flights
  2. 点击 "Execute" 按钮
  3. 查看返回的搜索结果

在参数输入框中填写:

{ "from": "北京", "to": "上海", "date": "2026-04-01" }

第六步:理解工具定义

点击任意工具,查看其详细信息:

{ "name": "search_flights", "description": "Search for available flights based on origin, destination, and date", "inputSchema": { "type": "object", "properties": { "from": { "type": "string", "description": "Origin city or airport code" }, "to": { "type": "string", "description": "Destination city or airport code" }, "date": { "type": "string", "description": "Departure date in YYYY-MM-DD format" } }, "required": ["from", "to", "date"] } }

这展示了 WebMCP 工具的完整结构:

  • name:工具唯一标识
  • description:让 AI 理解工具用途
  • inputSchema:定义输入参数格式

你自己动手尝试

  1. 尝试搜索酒店
    • 找到 search_hotels 工具
    • 填写参数:{ "city": "上海", "checkIn": "2026-04-01", "checkOut": "2026-04-03" }
    • 执行并查看结果
  2. 添加行程
    • 找到 add_to_itinerary 工具
    • 填写参数:{ "flightId": "FL123", "date": "2026-04-01" }
    • 执行并查看返回的行程信息
  3. 查看自己的 WebMCP 页面
    • 如果你按照上面的 Vue 教程创建了项目
    • 运行 npm run dev 打开你的页面
    • 用 MCP-B 扩展查看你注册的工具
    • 尝试调用它们

Demo 展示了什么

这个 Demo 让你体验到:

  • WebMCP 如何让网站功能被 AI 发现
  • 工具注册后的结构化调用
  • 返回的 JSON 数据格式
  • 声明式表单和命令式工具的区别

未来展望

发展状态

  • WebMCP 仍处于 DevTrial 阶段
  • API 可能会变化
  • Chrome 146 稳定版预计近期发布

生态系统

  • @mcp-b/global - Polyfill
  • @mcp-b/vue-webmcp - Vue 集成
  • @mcp-b/react-webmcp - React 集成
  • Chrome 调试扩展
  • CLI 测试工具
  • 活跃的社区建设

W3C 参与

  • Google 和 Microsoft 工程师共同参与
  • 微软和 Google 的 W3C 工作组

参考资源

官方文档

社区资源

技术文章


总结

WebMCP 代表了 Web 开发的范式转变——从只为人类用户设计,到同时为人类和 AI 代理设计。随着 AI 代理能力的成熟,掌握 WebMCP 将成为前端开发者的重要技能。

行动建议:

  1. 在本地环境中设置 WebMCP
  2. 从简单的只读工具开始
  3. 尝试声明式 API
  4. 按照 Vue 教程动手实践
  5. 访问 Demo 反复练习
  6. 关注规范进展
⚠️ 注意:WebMCP 目前处于早期预览阶段,生产环境使用需谨慎。请密切关注官方更新。

Read more

try/catch/Promise:前端错误处理实战|JS 基础语法与数据操作篇

try/catch/Promise:前端错误处理实战|JS 基础语法与数据操作篇

【try/catch/Promise】+【前端错误处理】:从【异常捕获逻辑】到【落地实操】,彻底搞懂前端错误处理的最佳写法,避开异步捕获、HTTP状态码判断高频坑! 📑 文章目录 * 开篇 * 一、先搞清楚:try/catch 到底能抓到啥 * 1.1 能抓到的:同步代码里的异常 * 1.2 抓不到的:异步里的错误 * 二、Ajax 错误:别只盯着 try/catch * 2.1 fetch 是什么?小白必读 * 2.2 常见误解 * 2.3 正确做法 * 三、JSON 解析错误:最容易漏掉的一类 * 3.1 常见场景

【保姆级教程】手把手教你安装OpenClaw并接入飞书,让AI在聊天软件里帮你干活

【保姆级教程】手把手教你安装OpenClaw并接入飞书,让AI在聊天软件里帮你干活

这里先做一下简单的科普: OpenClaw 的名字经历了三次变更,第一次叫做 ClawdBot,后来因为名字跟 Claude 太过相似,被 CLaude 告侵权,遂改名 MoltBot 。 但是后来在改名过程中遭遇域名和社交账号被抢注,甚至出坑同名加密货币割韭菜的情况,导致名称传播受阻。 最终定名为:OpenClaw。 所以,名字经历先后顺序为:ClawdBot -> MoltBot -> OpenClaw 大家不要因为名字困惑了,怀疑是不是自己下错软件了,他们都是同一个。 一、什么是 OpenClaw? OpenClaw(曾用名 Clawdbot)是一款 2026 年爆火的开源个人 AI 助手,GitHub 星标已超过 10 万颗。与传统 AI 聊天机器人的根本区别在于: * 真正的执行能力:不仅能回答问题,

前端部署:别让你的应用在上线后掉链子

前端部署:别让你的应用在上线后掉链子 毒舌时刻 这部署流程写得跟绕口令似的,谁能记得住? 各位前端同行,咱们今天聊聊前端部署。别告诉我你还在手动上传文件到服务器,那感觉就像在石器时代用石头砸坚果——能用,但效率低得可怜。 为什么你需要自动化部署 最近看到一个项目,部署时需要手动复制文件到服务器,每次部署都要花上几个小时。我就想问:你是在做部署还是在做体力活? 反面教材 # 反面教材:手动部署 # 1. 构建项目 npm run build # 2. 压缩文件 zip -r build.zip build # 3. 上传到服务器 scp build.zip user@server:/var/www/html # 4. 登录服务器 ssh user@server # 5. 解压文件 unzip

【硬核排查】挂了代里还是“裸奔”?深度解析 WebRTC 泄露与 Google 账号风控机制

【硬核排查】挂了代里还是“裸奔”?深度解析 WebRTC 泄露与 Google 账号风控机制

本文仅用于技术研究,禁止用于非法用途。 Author:枷锁 前言:一个“玄学”的网络故障 最近在进行网络环境配置时遇到了一个非常反直觉的现象: 我在本地开启了 戴笠,状态栏显示连接正常,访问Gemini毫无压力。但是,当我打开 ip138 或百度搜索 “IP” 时,显示的却依然是我本地的 ISP 真实 IP。更糟糕的是,我的 Google 账号开始频繁触发安全风控——要么是登录时无限弹出验证码,要么是刚登上去就被踢下线。 这不仅仅是“连不上”的问题,而是一个典型的网络协议泄露与安全风控案例。本着“知其然更要知其所以然”的精神,我深扒了其背后的技术原理,发现罪魁祸首主要有两个:路由分流策略与WebRTC 协议漏洞。 第一部分:为什么 ip138 “出卖”了你?—— 聊聊路由分流 (Split Tunneling) 很多新手判断 是否生效的标准是: