news 2026/10/18 14:07:41

WebMCP 学习指南:用 Vue + TypeScript + Zod 打通 W3C 标准下的前端工具调用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
WebMCP 学习指南:用 Vue + TypeScript + Zod 打通 W3C 标准下的前端工具调用

1. WebMCP 到底是什么,为什么 Vue 项目要现在开始学

WebMCP(Web Model Context Protocol)是 W3C Web Machine Learning 社区组正在推进的一份提案规范,它做的事情可以用一句话概括:让网页把自己内部的 JavaScript 函数,以结构化「工具」的形式暴露给浏览器里的 AI 代理,让 AI 直接调用你的业务逻辑,而不是靠截图识别和模拟点击去猜你的界面。它适合谁?适合正在做 Vue、React 或者任意前端框架,并且希望自己的网站能被 AI 代理「看懂并操作」的开发者。核心检索词就是 WebMCP、W3C 标准、Vue 集成、TypeScript 类型安全、Zod 入参校验,这几个词基本覆盖了本文要讲的全部内容。

我先把传统方案的问题摆出来,你就能理解为什么需要它。现在 AI 代理要操作一个网页,常见做法有三种:截图后交给视觉模型解析、用 Playwright 之类的工具模拟点击、直接解析 DOM 抓数据。这三种方式都有一个共同的软肋——它们依赖的是「界面长什么样」,而不是「功能是什么」。你改一个按钮的 class,或者把搜索框从顶部挪到侧边栏,代理就可能失效。视觉模型还贵,复杂交互还容易点错。

WebMCP 换了个思路。它不关心你的 UI 怎么排布,而是让你主动声明:我这个页面有一个叫search_products的工具,它接收keyword和limit两个参数,返回产品列表。AI 代理拿到这份结构化描述后,直接调用这个函数,拿到 JSON 结果。整个过程在浏览器内执行,天然继承当前用户的登录态和权限,不需要后端做任何改造,也不需要把接口密钥暴露出去。

这里要区分一个容易混淆的点:WebMCP 和 MCP 不是一回事。MCP 是后端服务器向 AI 暴露能力的协议,通常涉及 OAuth 2.1 认证,跑在服务端;WebMCP 是浏览器原生 API,通过navigator.modelContext访问,跑在页面里,继承用户会话。简单说,你要暴露企业数据库或本地文件系统,用 MCP;你要让网站本身被浏览器里的 AI 代理操作,用 WebMCP。两者定位不同,不冲突。

对 Vue + TypeScript 项目来说,WebMCP 的吸引力在于类型安全。工具定义里的inputSchema如果手写 JSON Schema,很容易和实际执行函数的参数类型对不上。而 Zod 可以在 TypeScript 里定义一次 schema,既做运行时校验,又能推导出静态类型,还能转成 JSON Schema 给 AI 看。这条链路打通之后,你写工具就像写普通的组合式函数一样自然。

下面我会按「环境准备 → 可复制配置 → 验证调用 → 排错」的顺序,把整条链路走一遍。你跟着做,最后能在自己的 Vue 项目里注册一个工具,并用浏览器扩展手动调用它,看到真实返回结果。

2. 环境准备与依赖安装:Vue + TypeScript + Zod 项目初始化

这一节的目标是让你有一个能跑起来的 Vue + TypeScript 项目,并且装好 WebMCP 相关的 polyfill 和 Zod。如果你已经有项目,可以跳过创建步骤,直接看依赖安装部分。

先创建项目。用 Vite 的 Vue + TypeScript 模板,这是目前最省事的方式:

npm create vite@latest my-webmcp-app -- --template vue-ts cd my-webmcp-app npm install

装完之后,项目结构里会有src/main.ts、src/App.vue这些文件。TypeScript 配置默认已经开好,tsconfig.json里strict通常是 true,这对我们后面用 Zod 推导类型很友好。

接下来装三个依赖:

npm install @mcp-b/global @mcp-b/vue-webmcp zod

这三个包各自的作用要分清楚。@mcp-b/global是 polyfill,它会在浏览器里挂载navigator.modelContext对象,让还不支持原生 WebMCP 的浏览器也能用。@mcp-b/vue-webmcp是 Vue 的集成层,提供组合式函数,帮你在组件挂载和卸载时自动注册、注销工具。zod负责入参校验和类型推导。

关于浏览器环境,有两条路。第一条是用 Chrome 146 以上的 Canary 版本,打开chrome://flags,搜索并启用 "Experimental Web Platform features",重启浏览器,这样能用原生 API。第二条是装 MCP-B 扩展,在 Chrome 应用商店搜索 "MCP-B" 或 "Model Context Tool Inspector",添加到 Chrome。我建议两条都准备:polyfill 保证代码在任何环境都能跑,扩展用来手动测试工具调用。扩展这条路更稳定,因为原生 API 还在 DevTrial 阶段,行为可能变。

装完依赖后,在src/main.ts里引入 polyfill,注意要放在创建应用之前:

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

这一步很关键。polyfill 必须在任何组件调用navigator.modelContext之前完成挂载,否则你会遇到Cannot read properties of undefined (reading 'registerTool')这类报错。放在main.ts顶部是最稳妥的位置。

如果你不想用 npm 包,也可以在index.html里用 script 标签引入 IIFE 版本:

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

但这种方式没有类型提示,在 TypeScript 项目里不推荐。npm 包自带类型声明,写代码时能自动补全registerTool的参数结构,体验好很多。

环境准备好之后,先跑一下npm run dev,确认页面能正常打开,控制台没有报错。打开开发者工具,在 Console 里输入navigator.modelContext,如果返回一个对象而不是 undefined,说明 polyfill 生效了。这一步是后面所有操作的前提,别跳过。

3. 可复制配置:用 Zod 定义工具并用组合式函数注册

这一节是全文的核心,我会给你一份可以直接复制到项目里的配置,包含 Zod schema 定义、TypeScript 类型推导、Vue 组合式函数封装,以及一个完整的工具注册示例。路径和文件名都写清楚,你照着建文件就行。

先建一个目录src/composables,在里面新建useWebMCP.ts。这个文件封装工具注册逻辑,处理挂载时注册、卸载时注销:

import { onMounted, onUnmounted, ref } from 'vue' import type { z } from 'zod' export interface WebMCPTool<T extends z.ZodTypeAny> { name: string description: string schema: T readOnlyHint?: boolean execute: (input: z.infer<T>) => Promise<unknown> } export function useWebMCP<T extends z.ZodTypeAny>(tool: WebMCPTool<T>) { const registered = ref(false) onMounted(() => { if (!navigator.modelContext) { console.warn('WebMCP 不可用,请检查 polyfill 或浏览器扩展') return } navigator.modelContext.registerTool({ name: tool.name, description: tool.description, inputSchema: zodToJsonSchema(tool.schema), readOnlyHint: tool.readOnlyHint ?? false, execute: tool.execute, }) registered.value = true }) onUnmounted(() => { if (registered.value && navigator.modelContext) { navigator.modelContext.unregisterTool(tool.name) } }) return { registered } }

这里有个细节要说明:navigator.modelContext.registerTool接收的inputSchema是 JSON Schema 格式,而 Zod 的 schema 对象不能直接传进去。所以需要一个转换函数zodToJsonSchema。Zod 官方有zod-to-json-schema这个包,但为了减少依赖,我这里给一个简化版的手写转换,覆盖常见的 string、number、boolean、object 类型:

import type { z } from 'zod' function zodToJsonSchema(schema: z.ZodTypeAny): Record<string, unknown> { const def = (schema as any)._def const typeName = def.typeName if (typeName === 'ZodObject') { const shape = def.shape() const properties: Record<string, unknown> = {} const required: string[] = [] for (const [key, value] of Object.entries(shape)) { properties[key] = zodToJsonSchema(value as z.ZodTypeAny) if (!(value as any).isOptional()) { required.push(key) } } return { type: 'object', properties, required } } if (typeName === 'ZodString') { return { type: 'string', description: def.description } } if (typeName === 'ZodNumber') { return { type: 'number', description: def.description } } if (typeName === 'ZodBoolean') { return { type: 'boolean', description: def.description } } if (typeName === 'ZodOptional') { return zodToJsonSchema(def.innerType) } if (typeName === 'ZodDefault') { const inner = zodToJsonSchema(def.innerType) return { ...inner, default: def.defaultValue() } } return { type: 'object', properties: {}, required: [] } }

如果你不想手写这个转换,直接npm install zod-to-json-schema,然后import { zodToJsonSchema } from 'zod-to-json-schema'也行,功能更全。我上面这个版本是为了让你看清转换逻辑,实际项目建议用成熟库。

接下来在组件里用。新建src/components/ProductSearch.vue,写一个产品搜索工具:

<script setup lang="ts"> import { ref } from 'vue' import { z } from 'zod' import { useWebMCP } from '@/composables/useWebMCP' const keyword = ref('') const results = ref<Array<{ id: string; name: string; price: number }>>([]) const loading = ref(false) const searchSchema = z.object({ keyword: z.string().min(1).describe('搜索关键词'), limit: z.number().min(1).max(50).default(10).describe('返回结果数量上限'), }) useWebMCP({ name: 'search_products', description: '根据关键词搜索产品,返回产品列表', schema: searchSchema, readOnlyHint: true, async execute(input) { loading.value = true try { const res = await fetch( `/api/products?keyword=${encodeURIComponent(input.keyword)}&limit=${input.limit}` ) const data = await res.json() results.value = data.products ?? [] return { success: true, count: results.value.length, products: results.value } } catch (error) { return { success: false, error: (error as Error).message } } finally { loading.value = false } }, }) async function handleSearch() { if (!keyword.value) return const res = await fetch(`/api/products?keyword=${encodeURIComponent(keyword.value)}&limit=10`) const data = await res.json() results.value = data.products ?? [] } </script> <template> <div class="product-search"> <h2>产品搜索</h2> <input v-model="keyword" placeholder="输入产品名称..." @keyup.enter="handleSearch" /> <button @click="handleSearch">搜索</button> <div v-if="loading">加载中...</div> <ul v-else> <li v-for="item in results" :key="item.id">{{ item.name }} - ¥{{ item.price }}</li> </ul> </div> </template>

这份配置里,searchSchema用 Zod 定义了keyword和limit两个字段,limit有默认值 10。useWebMCP接收这个 schema,在组件挂载时把工具注册到navigator.modelContext,卸载时自动注销。readOnlyHint: true告诉 AI 这是一个只读操作,不会修改数据,代理可以放心调用。

如果你要注册会修改数据的工具,比如加入购物车,把readOnlyHint设为 false,并在execute里做权限检查。工具定义的三件套是 Base URL、Key、Model ID 吗?不是,那是模型接入的配置。WebMCP 工具的三件套是name、description、inputSchema,这三个字段缺一不可,AI 靠它们理解工具用途和参数格式。

4. 验证请求:用 MCP-B 扩展手动调用工具看结果

配置写完了,怎么确认工具真的注册成功、调用真的生效?这一节给你一套可操作的验证流程,从浏览器控制台到扩展面板,一步步来。

第一步,启动项目:

npm run dev

打开浏览器访问本地地址,通常是http://localhost:5173。按 F12 打开开发者工具,切到 Console 标签。

第二步,在 Console 里输入:

navigator.modelContext

如果返回一个对象,里面有registerTool、unregisterTool、provideContext这些方法,说明 polyfill 正常。如果返回 undefined,回到main.ts检查import '@mcp-b/global'是不是在createApp之前。

第三步,确认工具已注册。在 Console 里输入:

navigator.modelContext.getTools?.()

不同版本的 polyfill 方法名可能不同,有的用listTools。如果这个方法不存在,直接看 MCP-B 扩展面板更直观。

第四步,打开 MCP-B 扩展。点击浏览器工具栏的扩展图标,在弹出的面板里切到 "Tools" 标签页。你应该能看到一个工具列表,里面有一项叫search_products,描述是「根据关键词搜索产品,返回产品列表」。点开它,能看到inputSchema的结构:

{ "type": "object", "properties": { "keyword": { "type": "string", "description": "搜索关键词" }, "limit": { "type": "number", "description": "返回结果数量上限", "default": 10 } }, "required": ["keyword"] }

这份 schema 就是 Zod 转换出来的结果。AI 代理看到它,就知道调用这个工具需要传keyword,可选传limit。

第五步,手动执行工具。在扩展面板里点击search_products,参数输入框里填:

{ "keyword": "笔记本", "limit": 5 }

点 "Execute" 按钮。如果一切正常,你会看到返回的 JSON:

{ "success": true, "count": 5, "products": [ { "id": "p1", "name": "轻薄笔记本", "price": 4999 }, { "id": "p2", "name": "游戏笔记本", "price": 8999 } ] }

这个返回结果就是你的execute函数实际跑出来的。注意看,limit传了 5,返回的count就是 5,说明 Zod 的默认值和校验都生效了。

第六步,测试校验失败的情况。把参数改成:

{ "keyword": "", "limit": 100 }

keyword是空字符串,limit超过 50。如果 Zod 校验生效,你应该看到错误提示,而不是执行函数。这一步验证的是入参校验链路,确保 AI 传了非法参数时不会直接打到你的业务逻辑。

第七步,测试只读标记。在扩展面板里看search_products的详情,readOnlyHint应该显示为 true。这个标记会影响 AI 代理的调用策略,只读工具通常不需要用户二次确认。

如果你在扩展面板里看不到工具,先刷新页面,再检查 Console 有没有报错。常见的是navigator.modelContext is undefined,回到第二步排查。还有一种情况是组件没挂载,工具注册在onMounted里,如果组件被v-if条件渲染且初始为 false,工具就不会注册。

验证通过之后,你可以试着注册第二个工具,比如get_cart,然后刷新扩展面板,确认两个工具都在列表里。这一步做完,整条链路就算打通了。

5. 常见报错排查:401、local proxy failed、reading choices、OAuth

这一节把你在接入过程中可能遇到的真实报错列出来,对照着排查。这些报错不一定都来自 WebMCP 本身,有些是模型接入或代理调用环节的,但都会影响你验证工具是否生效。

报错一:navigator.modelContext is undefined

这是最常见的。原因通常是 polyfill 没加载,或者加载顺序不对。检查main.ts里import '@mcp-b/global'是否在createApp之前。如果你用的是 script 标签,确认它在所有业务脚本之前。还有一种可能是浏览器扩展和 polyfill 冲突,先禁用扩展试试。

报错二:401 Unauthorized

这个报错通常出现在你的execute函数里调用了后端 API,而后端要求认证。WebMCP 工具继承用户会话,但如果你的 fetch 请求没带 cookie 或 token,就会 401。检查 fetch 是否设置了credentials: 'include',或者手动带上 Authorization 头。如果你是通过 TaoToken 这类平台接入模型来驱动工具调用,401 也可能是 API Key 配置问题,去控制台确认 Key 是否有效、是否过期。

报错三:local proxy failed

这个报错一般出现在你本地起了代理服务,但代理没启动或端口不对。如果你在开发环境用代理转发请求,检查代理进程是否在跑,端口是否和配置一致。WebMCP 本身不需要代理,这个报错多半来自你的网络请求层。

报错四:reading 'choices'

Cannot read properties of undefined (reading 'choices')是模型返回格式不符合预期时的典型报错。通常是你调用的模型接口返回了错误信息,而不是标准的choices数组。检查请求体里的model字段是否写对,Model ID 是否和平台文档一致。如果你用 TaoToken 接入,确认 Base URL 是https://taotoken.net/api,Model ID 从文档里复制,别手写。

报错五:OAuth相关错误

WebMCP 本身继承用户会话,不涉及 OAuth。但如果你在工具里调用了需要 OAuth 的第三方 API,就会遇到 token 过期或 scope 不足的问题。检查 OAuth token 是否还在有效期,scope 是否包含你要调用的接口。如果是 MCP 后端的 OAuth 2.1 流程,那是另一套体系,别和 WebMCP 混在一起。

报错六:工具注册了但扩展面板看不到

先刷新页面,再检查组件是否真的挂载了。如果工具注册在onMounted里,而组件被v-if控制且初始为 false,工具不会注册。把v-if改成v-show,或者把注册逻辑提到父组件。还有一种可能是工具名重复,registerTool对同名工具的处理策略因版本而异,换个名字试试。

报错七:Zod 校验不生效

检查zodToJsonSchema转换后的 schema 是否正确。在 Console 里打印navigator.modelContext里注册的工具,看inputSchema是不是你期望的结构。如果required数组为空,说明 Zod 的.optional()判断有问题,检查字段是否真的必填。

排查的时候有个通用思路:先在 Console 里确认navigator.modelContext存在,再确认工具注册成功,最后确认execute函数能独立跑通。把这三层分开验证,比一次性调试整个链路快得多。

6. 从工具注册到代理调用:把链路接到真实模型上

工具能在扩展面板里手动调用,只完成了一半。真正的场景是 AI 代理自动发现并调用你的工具。这一节讲怎么把链路接到真实模型上,以及接入时需要注意的配置。

WebMCP 的设计是浏览器原生 API,AI 代理通过navigator.modelContext发现工具。目前支持这个能力的代理还在早期,Chrome 146 的 DevTrial 和 MCP-B 扩展是主要入口。如果你要在自己的应用里驱动工具调用,通常的做法是:前端注册工具,后端或代理层通过模型决定调用哪个工具,再把调用结果返回给模型继续推理。

这里就涉及模型接入的配置。以 TaoToken 为例,你需要三样东西:Base URL、API Key、Model ID。Base URL 是https://taotoken.net/api,API Key 在控制台的 API Keys 页面创建,Model ID 从文档里选一个支持工具调用的模型。配置写进你的请求体:

{ "model": "claude-sonnet-4-5", "messages": [ { "role": "user", "content": "帮我搜索一下笔记本" } ], "tools": [ { "name": "search_products", "description": "根据关键词搜索产品,返回产品列表", "input_schema": { "type": "object", "properties": { "keyword": { "type": "string" }, "limit": { "type": "number", "default": 10 } }, "required": ["keyword"] } } ] }

模型返回tool_use块后,你在前端执行对应的execute函数,把结果作为tool_result回传,模型继续生成最终回答。这条链路里,WebMCP 负责工具发现和注册,模型负责决策调用哪个工具,你的execute函数负责实际执行。

如果你用 Claude Code 做开发,可以在项目里配置settings.json,把工具调用相关的环境变量写进去。Cline 的 MCP 配置也是类似思路,在配置文件里声明工具来源。Codex 的auth.json则用来存认证信息。这三件套的核心都是:Base URL 指向服务地址,Key 做认证,Model ID 指定用哪个模型。

长期做编码和 Agent 场景的话,可以考虑用 Coding Plan 这类方案,把模型调用和工具编排统一管理。验证模型是否支持工具调用,可以在模型对话页面直接测试,发一条带 tools 定义的请求,看返回里有没有tool_use。

接入文档里有完整的参数说明和示例,遇到配置问题先查文档,比在代码里猜快。API Keys 页面可以创建和管理密钥,注意别把 Key 提交到代码仓库。

整条链路跑通之后,你的 Vue 项目就不只是给人看的页面了,它同时是一个 AI 代理可以调用的工具集。从注册工具到浏览器端调用,再到模型决策,每一层都有可验证的动作。先把search_products这个只读工具跑通,再逐步加带副作用的工具,配合readOnlyHint和权限检查,这样上线风险可控。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/18 14:06:43

COMP9021 Python课程测验资料:练习与高频题型拆解

简介&#xff1a;COMP9021课程Python编程练习与测验资料合集是悉尼新南威尔士大学Principles of Programming课程的配套练习包&#xff0c;覆盖Python基础语法、控制流、函数定义与简单数据结构应用&#xff0c;适合课堂同步巩固、课后自主复习与考前冲刺。压缩包共7个文件&…

作者头像 李华
网站建设 2026/10/18 14:06:41

charinfo-droiyanOnline:Unicode字符元数据解析工具链

简介&#xff1a;本资源是Droiyan Online游戏后端中「角色信息与聊天服务」的核心源码实现&#xff0c;面向C游戏服务器开发者、网络编程学习者及MMORPG架构研究者&#xff0c;聚焦于实时通信、用户状态管理与跨平台文本处理等关键后端能力。压缩包共75个文件&#xff0c;以19个…

作者头像 李华
网站建设 2026/10/18 14:03:06

OpenClaw开源AI助手:本地优先部署、插件开发与自动化任务实战

2025年下半年开始&#xff0c;我明显感觉到一个趋势&#xff1a;身边的个人开发者和小团队&#xff0c;渐渐对“云端AI助手”产生了某种倦怠感。订阅费越叠越高&#xff0c;数据全部要上传到别人的服务器上&#xff0c;想改底层的提示词逻辑又改不了&#xff0c;更不用说把AI跟…

作者头像 李华
网站建设 2026/10/18 14:02:13

泛微OA明细数据写入全指南:从表结构到SQL与接口实战

泛微OA系统里写入明细数据这件事&#xff0c;看着就是个常规操作&#xff0c;但真正动手做过的都知道&#xff0c;这里面的坑一点不少。主表、明细表、requestid、mainid、detail_mxid&#xff0c;这几个词听起来是同一回事&#xff0c;实际在库里查一圈你会发现它们的关系微妙…

作者头像 李华
网站建设 2026/10/18 14:01:56

PDF 文档处理实战:从结构解析到生成校验的完整链路

简介&#xff1a;这是一份围绕PDF便携式文件格式整理的资源包&#xff0c;面向需要了解PDF格式原理、研究PDF阅读器组件结构或进行文档兼容性开发的用户。PDF由Adobe Systems于1993年推出&#xff0c;以跨平台、保留原始版式、开放标准且可自由授权开发兼容软件著称&#xff0c…

作者头像 李华
网站建设 2026/10/18 14:00:40

Postman批量执行接口测试:从Collection Runner到Newman自动化实践

先说明一下&#xff0c;这篇内容我从一个很实际的角度切入&#xff1a;你手上已经有一套接口&#xff0c;数量不少&#xff0c;参数组合也多&#xff0c;手动点测试点得想骂人&#xff0c;这时候Postman批量执行就是那个能救命的工具。文章会按照“先搞懂什么时候用、再做好准备…

作者头像 李华