第一部分:项目概览与核心功能
一、项目简介:什么是 WebMCP Nexus?
WebMCP Nexus 是阿里巴巴开源的一套面向 WebMCP 标准的零侵入前端集成方案。简单来说,它能让任何 React 应用在几分钟内变成 AI Agent(如 Claude、Cursor)可以直接操控的对象。
打个比方,如果把你的 Web 应用比作一个"遥控玩具",那么 WebMCP Nexus 就是让这个玩具能够接收 AI Agent 指令的"接收器"。AI Agent 可以像操作遥控器一样,调用你应用里的各种功能------查询数据、创建任务、修改状态、跳转页面等等。
WebMCP 是什么?
WebMCP(Web Model Context Protocol)是 W3C 浏览器标准提案,由 Google 与 Microsoft 联合推动。它定义了一个标准 API:navigator.modelContext.registerTool(),允许网页将自身能力暴露为 MCP(Model Context Protocol)工具,供 AI 模型调用。
核心理念:零侵入
WebMCP Nexus 最大的特点是"零侵入"------你不需要修改现有业务代码,不需要包装函数,不需要写复杂的配置。只需要:
- 写一个普通的 TypeScript 函数
- 加上 JSDoc 注释
- 一行代码注册
就这样,这个函数就能被 AI Agent 调用了!
二、为什么需要 WebMCP Nexus?
在了解 WebMCP Nexus 之前,我们先看看当前 AI Agent 操作 Web 应用的痛点:
1. 没有标准接口
不同的 AI Agent 有不同的集成方式,缺乏统一标准。每个应用都需要单独适配,成本高昂。
2. 需要大量包装代码
传统方式需要:
- 手写 JSON Schema 描述函数参数
- 用装饰器或包装函数包装业务方法
- 维护类型定义和 Schema 的双重一致性
- 手动管理工具的生命周期
3. 类型信息容易丢失
TypeScript 的类型信息在编译后会丢失,需要手动维护运行时的类型描述,容易出现不一致。
4. 生命周期管理复杂
工具注册后需要手动注销,否则会造成"幽灵工具"污染上下文,导致 Agent 在错误的页面调用错误的工具。
5. 浏览器兼容性问题
WebMCP 标准还在推进中,不同浏览器支持程度不同,需要自行处理兼容性。
6. 桌面 Agent 接入困难
本地运行的 AI Agent(如 Claude Desktop、Cursor)如何连接到浏览器中的 Web 应用?需要自己实现桥接机制。
WebMCP Nexus 正是为了解决这些问题而设计的。
三、核心功能特性
3.1 极简 API:两个函数搞定一切
WebMCP Nexus 的运行时 SDK 只导出两个 API:
registerGlobalTools():全局注册
- 在应用启动时调用一次
- 注册全局可用的工具(如查询、认证、通用 CRUD)
- 永不注销
useWebMcpTools():组件级注册
- 在 React 组件内作为 Hook 调用
- 注册当前页面/组件专用的工具
- 组件卸载时自动注销
就这么简单!30 秒看懂,5 分钟接入。
3.2 构建时类型反推:零配置生成 Schema
这是 WebMCP Nexus 最核心的黑科技!
传统方式:
// 需要手写 Schema
const schema = {
name: 'searchUsers',
description: '搜索用户',
inputSchema: {
type: 'object',
properties: {
keyword: { type: 'string', description: '搜索关键词' },
pageSize: { type: 'number', description: '每页数量' }
},
required: ['keyword']
}
};
WebMCP Nexus 方式:
// 只需要写 TS 类型和 JSDoc
export interface SearchUsersParams {
/** 搜索关键词 */
keyword: string;
/** 每页数量 */
pageSize?: number;
}
/**
* 搜索用户列表
* @readonly
*/
export async function searchUsers(params: SearchUsersParams) {
// 你的业务实现
}
构建插件会自动:
- 分析 TypeScript 类型定义
- 提取 JSDoc 注释
- 生成符合 JSON Schema 的描述
- 注入到函数对象上
完全零配置,零运行时开销!
3.3 三级注册策略:精细化生命周期管理
WebMCP Nexus 提供了三级作用域,让工具的生命周期与 React 组件完美绑定:
| 级别 | API | 生命周期 | 适用场景 |
|---|---|---|---|
| 全局 | registerGlobalTools() |
应用启动注册,永不注销 | 通用 API(查询、认证、CRUD) |
| 路由 | useWebMcpTools() |
页面 mount/unmount | 当前路由独占的操作 |
| 组件 | useWebMcpTools() |
组件 mount/unmount | 弹窗、面板等局部交互 |
示例:页面级工具
export default function TasksPage() {
const { createTask, updateTask, deleteTask } = useTodoStore();
// 只在这个页面可用
useWebMcpTools({ createTask, updateTask, deleteTask });
return <div>...</div>;
}
当用户离开这个页面,这三个工具会自动注销,避免 Agent 在其他页面错误调用。
3.4 跨浏览器透明兼容
WebMCP Nexus 内置了智能的兼容策略:
Chrome 146+:
- 使用原生
navigator.modelContextAPI - 性能最优
其他浏览器(Chrome <146 / Firefox / Safari / Edge legacy):
- SDK 入口自动加载内置 polyfill
- 对业务代码完全透明
- 无需开发者判断环境
支持的浏览器:
- Chrome / Chromium(全版本)
- Firefox
- Safari
- Edge
3.5 桌面 Agent 直连:让本地 AI 操作浏览器
这是 WebMCP Nexus 的杀手级功能!
通过官方的 @mcp-b/webmcp-local-relay 包,本地运行的 AI Agent 可以直接调用浏览器中正在运行的 Web 应用。
工作原理:
Claude Desktop/Cursor/VS Code
↓ (stdio MCP)
webmcp-local-relay (本地中继)
↓ (WebSocket)
浏览器中的 Web 应用
接入步骤:
-
在 HTML 中引入一行脚本:
<script src="https://cdn.jsdelivr.net/npm/@mcp-b/webmcp-local-relay@latest/dist/browser/embed.js"></script> -
在 Claude Desktop 配置文件中添加:
{ "mcpServers": { "webmcp-local-relay": { "command": "npx", "args": ["-y", "@mcp-b/webmcp-local-relay@latest"] } } } -
启动应用,重启 Claude Desktop
就这样!Claude 现在可以直接操作你的 Web 应用了。
示例对话:
用户:把所有 todo 状态的任务按截止日期升序展示,然后把第一条标记为已完成。
Claude:好的,我来操作。
[调用 listTasks 工具]
[调用 setTaskSort 工具]
[调用 setTaskStatus 工具]
完成!已在浏览器中可视化展示。
3.6 AI 编码 Skill:让 AI 帮你写工具
WebMCP Nexus 内置了一份面向 AI 编码 Agent 的 Skill 文档,让"为函数生成工具"变成一句话指令。
安装到 Claude Code:
mkdir -p .claude/skills
cp skill/SKILL.md .claude/skills/webmcp-nexus.md
安装到 Cursor:
mkdir -p .cursor/rules
cp skill/SKILL.md .cursor/rules/webmcp-nexus.mdc
使用示例:
用户:把 TodoStore.tsx 里的 createTask 改造成 WebMCP 工具。
AI Agent:好的,我来改造。
1. 补充 JSDoc 注释
2. 调整参数为对象形态
3. 在对应组件添加 useWebMcpTools
完成!业务逻辑完全未修改。
3.7 双构建工具支持:Vite & Webpack
WebMCP Nexus 同时提供 Vite 和 Webpack 插件,适配主流构建工具:
Vite 插件:
import { vitePluginWebMcp } from 'vite-plugin-webmcp-nexus';
export default defineConfig({
plugins: [
vitePluginWebMcp({ include: ['src/**/*.ts', 'src/**/*.tsx'] }),
react(),
],
});
Webpack 插件:
import { WebMcpPlugin } from 'webpack-plugin-webmcp-nexus';
module.exports = {
plugins: [
new WebMcpPlugin({ include: ['src'] }),
],
};
3.8 HMR 友好:开发体验极佳
在开发阶段,修改函数签名后:
- 工具 schema 自动重新注册
- 无需手动刷新
- 无需重启开发服务器
3.9 冲突感知:安全的多人协作
SDK 内部维护 scope ownership registry:
- 多个 scope 注册同名工具时:控制台警告,但仍允许注册
- 注销时只清理自己 scope 的注册
- 严格隔离,不会相互影响
第二部分:技术原理与体系架构
四、WebMCP 标准基础
4.1 什么是 WebMCP?
WebMCP(Web Model Context Protocol)是 W3C 浏览器标准提案,它定义了网页如何向 AI 模型暴露工具能力。
核心 API:
navigator.modelContext.registerTool({
name: 'searchUsers',
description: '搜索用户',
inputSchema: { /* JSON Schema */ },
handler: async (params) => { /* 实现 */ }
});
标准推进情况:
- 由 Google 与 Microsoft 联合推动
- 目前处于提案阶段
- Chrome 146+ 已支持原生 API
- 其他浏览器通过 polyfill 支持
4.2 MCP 协议简介
MCP(Model Context Protocol)是 Anthropic 提出的协议,定义了 AI 模型如何与外部工具交互。
核心概念:
Tools(工具):
- AI 可以调用的函数
- 有明确的输入参数和输出格式
- 通过 JSON Schema 描述
Resources(资源):
- AI 可以访问的数据源
- 如文件、数据库、API
Prompts(提示):
- 预定义的提示模板
- 可以参数化
WebMCP 与 MCP 的关系:
WebMCP 是 MCP 在浏览器环境的实现,让网页可以成为 MCP 工具提供者。
五、系统架构设计
5.1 整体架构
WebMCP Nexus 采用 Monorepo 结构,包含多个协作的包:
webmcp-nexus/
├── apps/
│ └── demo/ # 示例应用
├── packages/
│ ├── webmcp-core/ # 构建时核心
│ ├── webmcp-sdk/ # 运行时 SDK
│ ├── vite-plugin-webmcp/ # Vite 插件
│ └── webpack-plugin-webmcp/ # Webpack 插件
└── skill/
└── SKILL.md # AI 编码 Skill
5.2 核心包详解
1. webmcp-core:构建时核心
这是整个方案的"大脑",负责:
- TypeScript 类型抽取
- JSDoc 解析
- JSON Schema 生成
- Schema 注入
技术栈:
ts-morph:TypeScript AST 操作库- 静态分析,无运行时开销
工作流程:
源代码 (TS + JSDoc)
↓
ts-morph 解析 AST
↓
提取函数签名、类型、注释
↓
生成 JSON Schema
↓
注入 __webmcpSchema 字段
↓
编译后的代码
2. webmcp-sdk:运行时 SDK
这是开发者的"接口",提供:
registerGlobalTools()APIuseWebMcpTools()Hook- Polyfill 自动加载
- Scope 管理
- 工具注册/注销
核心逻辑:
// 简化的 registerGlobalTools 实现
export function registerGlobalTools(...toolMaps: Record<string, Function>[]) {
for (const map of toolMaps) {
for (const [name, fn] of Object.entries(map)) {
// 读取构建时注入的 schema
const schema = fn.__webmcpSchema;
// 向浏览器注册
navigator.modelContext.registerTool({
name,
...schema,
handler: fn
});
}
}
}
3. vite-plugin-webmcp / webpack-plugin-webmcp
构建插件,负责:
- 扫描源文件
- 调用 webmcp-core 进行类型抽取
- 注入 schema 到函数对象
- HMR 支持
5.3 类型抽取原理
示例:从 TypeScript 类型到 JSON Schema
源代码:
export interface SearchParams {
/** 搜索关键词 */
keyword: string;
/** 每页数量(默认 20) */
pageSize?: number;
/** 排序方式 */
sort?: 'asc' | 'desc';
}
/**
* 搜索用户
* @readonly
*/
export async function searchUsers(params: SearchParams) {
// 实现
}
构建时处理:
-
解析函数签名:
- 参数类型:
SearchParams - 参数名:
params
- 参数类型:
-
解析 interface:
keyword: string→{ type: 'string', description: '搜索关键词' }pageSize?: number→{ type: 'number', description: '每页数量(默认 20) }(可选)sort?: 'asc' | 'desc'→{ type: 'string', enum: ['asc', 'desc'], description: '排序方式' }
-
提取 JSDoc:
- 函数描述:
'搜索用户' @readonly标记
- 函数描述:
-
生成 Schema:
{ "description": "搜索用户", "inputSchema": { "type": "object", "properties": { "keyword": { "type": "string", "description": "搜索关键词" }, "pageSize": { "type": "number", "description": "每页数量(默认 20)" }, "sort": { "type": "string", "enum": ["asc", "desc"], "description": "排序方式" } }, "required": ["keyword"] }, "annotations": { "readOnlyHint": true } } -
注入到函数:
searchUsers.__webmcpSchema = { /* 上面的 schema */ };
5.4 三级作用域实现
Scope Ownership Registry:
SDK 内部维护一个注册表:
interface ScopeRegistry {
[toolName: string]: {
scope: 'global' | 'route' | 'component';
scopeId: string; // 唯一标识
unregister: () => void;
}[];
}
注册流程:
function registerTool(name: string, fn: Function, scope: Scope, scopeId: string) {
// 检查冲突
if (registry[name]) {
console.warn(`Tool "${name}" already registered by other scope`);
}
// 注册到浏览器
const unregister = navigator.modelContext.registerTool(...);
// 记录到注册表
registry[name] = registry[name] || [];
registry[name].push({ scope, scopeId, unregister });
}
注销流程:
function unregisterScope(scope: Scope, scopeId: string) {
for (const [name, entries] of Object.entries(registry)) {
// 只注销当前 scope 的注册
const toRemove = entries.filter(e => e.scope === scope && e.scopeId === scopeId);
for (const entry of toRemove) {
entry.unregister();
}
// 从注册表移除
registry[name] = entries.filter(e => !(e.scope === scope && e.scopeId === scopeId));
}
}
React Hook 集成:
export function useWebMcpTools(toolMap: Record<string, Function>) {
const scopeId = useMemo(() => Math.random().toString(), []);
useEffect(() => {
// mount 时注册
for (const [name, fn] of Object.entries(toolMap)) {
registerTool(name, fn, 'component', scopeId);
}
// unmount 时注销
return () => unregisterScope('component', scopeId);
}, [toolMap, scopeId]);
}
5.5 Polyfill 机制
检测与加载:
// SDK 入口
export async function registerGlobalTools(...args: any[]) {
// 检测原生 API
if (!navigator.modelContext) {
// 动态加载 polyfill
await import('@mcp-b/webmcp-polyfill');
}
// 现在可以安全使用 navigator.modelContext
// ...
}
Polyfill 实现:
@mcp-b/webmcp-polyfill 提供:
navigator.modelContext的完整实现- 内存中的工具注册表
- 工具调用处理
六、桌面 Agent 连接原理
6.1 架构图
┌─────────────────────────────┐
│ Claude Desktop / Cursor │
│ (本地 MCP Client) │
└─────────────────────────────┘
↓ stdio (MCP/JSON-RPC)
┌─────────────────────────────┐
│ webmcp-local-relay │
│ (本地中继服务) │
│ - stdio MCP server │
│ - WebSocket server │
│ (ws://127.0.0.1:9333) │
└─────────────────────────────┘
↓ WebSocket
┌─────────────────────────────┐
│ 浏览器中的 Web 应用 │
│ ┌───────────────────────┐ │
│ │ Hidden Iframe │ │
│ │ (embed.js 注入) │ │
│ │ - 连接 WebSocket │ │
│ │ - 扫描工具注册表 │ │
│ └───────────────────────┘ │
│ ┌───────────────────────┐ │
│ │ Your App │ │
│ │ (webmcp-nexus-sdk) │ │
│ │ - 注册工具 │ │
│ │ - 处理调用 │ │
│ └───────────────────────┘ │
└─────────────────────────────┘
6.2 工作流程
1. 启动阶段:
Claude Desktop 启动
↓
读取配置,发现 webmcp-local-relay
↓
启动 relay 进程(stdio MCP server)
↓
relay 在 localhost:9333 启动 WebSocket server
2. 连接阶段:
浏览器加载 Web 应用
↓
加载 embed.js
↓
创建隐藏 iframe
↓
iframe 连接到 ws://127.0.0.1:9333
↓
扫描 navigator.modelContext
↓
上报所有已注册的工具
3. 调用阶段:
Claude 决定调用工具
↓
通过 stdio 发送请求给 relay
↓
relay 通过 WebSocket 转发给 iframe
↓
iframe 调用 navigator.modelContext
↓
执行工具函数
↓
返回结果
↓
沿原路返回给 Claude
6.3 embed.js 的作用
embed.js 是连接浏览器和本地 relay 的桥梁:
功能:
- 创建隐藏 iframe
- 建立 WebSocket 连接
- 监听
navigator.modelContext变化 - 实时上报工具列表
- 接收调用请求并执行
使用方式:
<script
src="https://cdn.jsdelivr.net/npm/@mcp-b/webmcp-local-relay@latest/dist/browser/embed.js"
data-relay-port="9333"
data-request-timeout="60000"
></script>
第三部分:使用教程与部署指南
七、快速开始
7.1 环境要求
- Node.js 18+
- React 18+ 或 React 19
- 构建工具:Vite 或 Webpack
- 包管理器:推荐 pnpm
7.2 安装依赖
安装 SDK:
pnpm add webmcp-nexus-sdk
# 或
npm install webmcp-nexus-sdk
安装构建插件:
Vite 项目:
pnpm add -D vite-plugin-webmcp-nexus
Webpack 项目:
pnpm add -D webpack-plugin-webmcp-nexus
7.3 配置构建插件
Vite 配置:
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { vitePluginWebMcp } from 'vite-plugin-webmcp-nexus';
export default defineConfig({
plugins: [
vitePluginWebMcp({
include: ['src/**/*.ts', 'src/**/*.tsx']
}),
react(),
],
});
Webpack 配置:
// webpack.config.ts
import { WebMcpPlugin } from 'webpack-plugin-webmcp-nexus';
export default {
// ... 其他配置
plugins: [
new WebMcpPlugin({ include: ['src'] }),
],
};
7.4 编写第一个工具函数
步骤 1:定义参数类型
// src/tools/queries.ts
export interface SearchTasksParams {
/** 搜索关键词 */
query: string;
/** 返回数量上限(默认 50) */
limit?: number;
/** 任务状态筛选 */
status?: 'todo' | 'in_progress' | 'done';
}
步骤 2:编写函数
/**
* 根据关键词搜索任务
* @readonly
*/
export async function searchTasks(params: SearchTasksParams): Promise<{
count: number;
tasks: Task[];
}> {
const { query, limit = 50, status } = params;
// 你的业务实现
const response = await fetch('/api/tasks/search', {
method: 'POST',
body: JSON.stringify({ query, limit, status }),
});
return response.json();
}
关键点:
- 参数必须是单一对象类型
- 每个字段都要有 JSDoc 注释
- 函数要有 JSDoc 描述
- 只读操作加
@readonly
7.5 注册工具
全局注册:
// src/main.tsx
import { registerGlobalTools } from 'webmcp-nexus-sdk';
import * as queries from './tools/queries';
import * as navigation from './tools/navigation';
// 应用启动时注册
registerGlobalTools(queries, navigation);
// 然后启动 React 应用
ReactDOM.createRoot(document.getElementById('root')!).render(
<App />
);
组件级注册:
// src/pages/TasksPage.tsx
import { useWebMcpTools } from 'webmcp-nexus-sdk';
export default function TasksPage() {
const { createTask, updateTask, deleteTask } = useTodoStore();
// 只在这个页面可用
useWebMcpTools({ createTask, updateTask, deleteTask });
return (
<div>
{/* 页面内容 */}
</div>
);
}
7.6 验证工具注册
方法 1:Debug Panel
示例应用内置了 Debug Panel,按 ⌘ + \(Mac)或 Ctrl + \(Windows)唤起,可以:
- 查看已注册的工具
- 查看参数 schema
- 测试工具调用
方法 2:浏览器控制台
// 查看已注册的工具
console.log(navigator.modelContext.getTools());
方法 3:构建产物检查
# 搜索注入的 schema
grep -r "__webmcpSchema" dist/
八、完整示例:Todo 应用
让我们通过一个完整的 Todo 应用示例,演示 WebMCP Nexus 的使用。
8.1 项目结构
src/
├── main.tsx # 应用入口
├── App.tsx # 根组件
├── tools/
│ ├── queries.ts # 查询工具
│ ├── mutations.ts # 修改工具
│ └── navigation.ts # 导航工具
├── pages/
│ ├── TasksPage.tsx # 任务列表页
│ └── TaskDetailPage.tsx # 任务详情页
└── store/
└── TodoStore.tsx # 状态管理
8.2 工具函数定义
查询工具(src/tools/queries.ts):
export interface ListTasksParams {
/** 任务状态筛选 */
status?: 'todo' | 'in_progress' | 'done';
/** 排序字段 */
sortBy?: 'dueDate' | 'priority' | 'createdAt';
/** 排序方向 */
sortOrder?: 'asc' | 'desc';
}
/**
* 列出所有任务
* @readonly
*/
export async function listTasks(params: ListTasksParams = {}) {
const store = useTodoStore.getState();
let tasks = [...store.tasks];
// 筛选
if (params.status) {
tasks = tasks.filter(t => t.status === params.status);
}
// 排序
if (params.sortBy) {
tasks.sort((a, b) => {
const aVal = a[params.sortBy!];
const bVal = b[params.sortBy!];
return params.sortOrder === 'desc'
? bVal > aVal ? 1 : -1
: aVal > bVal ? 1 : -1;
});
}
return { count: tasks.length, tasks };
}
export interface GetTaskParams {
/** 任务 ID */
id: string;
}
/**
* 获取任务详情
* @readonly
*/
export async function getTask(params: GetTaskParams) {
const store = useTodoStore.getState();
const task = store.tasks.find(t => t.id === params.id);
if (!task) {
throw new Error(`Task ${params.id} not found`);
}
return task;
}
修改工具(src/tools/mutations.ts):
export interface CreateTaskParams {
/** 任务标题 */
title: string;
/** 任务描述 */
description?: string;
/** 截止日期 */
dueDate?: string;
/** 优先级 */
priority?: 'low' | 'medium' | 'high';
}
/**
* 创建新任务
*/
export async function createTask(params: CreateTaskParams) {
const store = useTodoStore.getState();
const newTask = {
id: generateId(),
...params,
status: 'todo',
createdAt: new Date().toISOString(),
};
store.setTasks([...store.tasks, newTask]);
return newTask;
}
export interface UpdateTaskParams {
/** 任务 ID */
id: string;
/** 新标题 */
title?: string;
/** 新状态 */
status?: 'todo' | 'in_progress' | 'done';
/** 新优先级 */
priority?: 'low' | 'medium' | 'high';
}
/**
* 更新任务
*/
export async function updateTask(params: UpdateTaskParams) {
const store = useTodoStore.getState();
const { id, ...updates } = params;
const tasks = store.tasks.map(t =>
t.id === id ? { ...t, ...updates } : t
);
store.setTasks(tasks);
return tasks.find(t => t.id === id);
}
export interface DeleteTaskParams {
/** 任务 ID */
id: string;
}
/**
* 删除任务
*/
export async function deleteTask(params: DeleteTaskParams) {
const store = useTodoStore.getState();
const tasks = store.tasks.filter(t => t.id !== params.id);
store.setTasks(tasks);
return { success: true };
}
导航工具(src/tools/navigation.ts):
export interface NavigateToTaskParams {
/** 任务 ID */
taskId: string;
}
/**
* 导航到任务详情页
*/
export async function navigateToTask(params: NavigateToTaskParams) {
window.location.href = `/tasks/${params.taskId}`;
return { success: true };
}
export interface NavigateToPageParams {
/** 页面路径 */
path: string;
}
/**
* 导航到指定页面
*/
export async function navigateToPage(params: NavigateToPageParams) {
window.location.href = params.path;
return { success: true };
}
8.3 注册入口
src/main.tsx:
import React from 'react';
import ReactDOM from 'react-dom/client';
import { registerGlobalTools } from 'webmcp-nexus-sdk';
import App from './App';
// 导入工具模块
import * as queries from './tools/queries';
import * as mutations from './tools/mutations';
import * as navigation from './tools/navigation';
// 全局注册工具
registerGlobalTools(queries, mutations, navigation);
// 启动应用
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
8.4 页面级工具
src/pages/TasksPage.tsx:
import { useWebMcpTools } from 'webmcp-nexus-sdk';
import { useTodoStore } from '../store/TodoStore';
export default function TasksPage() {
const {
setTaskSort,
setTaskFilter,
batchUpdateStatus
} = useTodoStore();
// 页面级工具
useWebMcpTools({
setTaskSort,
setTaskFilter,
batchUpdateStatus
});
return (
<div>
<h1>任务列表</h1>
{/* 任务列表 UI */}
</div>
);
}
九、类型支持与约束
9.1 支持的类型
| TypeScript 类型 | JSON Schema 类型 | 说明 |
|---|---|---|
string |
string |
✅ 完全支持 |
number |
number |
✅ 完全支持 |
boolean |
boolean |
✅ 完全支持 |
| `'a' \ | 'b' \ | 'c'` |
T[] |
array + items |
✅ 数组支持 |
| 嵌套对象 | object + properties |
✅ 支持最多 3 层嵌套 |
Date |
string |
⚠️ 建议用 ISO 字符串 |
可选字段 field?: T |
不在 required 中 |
✅ 可选字段 |
9.2 不支持的类型
| TypeScript 类型 | 原因 |
|---|---|
泛型 <T>(p: T) |
无法静态推断具体类型 |
any / unknown |
类型信息丢失 |
enum |
建议用字面量联合替代 |
交叉类型 A & B |
可能导致 schema 复杂 |
| 超过 3 层嵌套 | 性能和可读性考虑 |
9.3 MUST 约束(必须遵守)
M1:函数必须可追踪
两种方式:
-
作为对象字面量属性传入:
registerGlobalTools({ myTool }); -
作为具名导出:
// ✅ 正确 export async function myTool() {}
// ❌ 错误(工具名会变成 "default") export default async function myTool() {}
**M2:参数必须是单一对象类型**
```typescript
// ✅ 正确
export async function search(params: { query: string }) {}
// ❌ 错误(原始类型会污染 schema)
export async function search(query: string) {}
// ❌ 错误(多个参数)
export async function search(query: string, limit: number) {}
9.4 SHOULD 约束(强烈建议)
S1:函数要有 JSDoc
// ✅ 好
/**
* 搜索用户
*/
export async function searchUsers() {}
// ❌ 差(description 为空)
export async function searchUsers() {}
S2:每个字段要有 JSDoc
// ✅ 好
export interface Params {
/** 搜索关键词 */
query: string;
/** 每页数量 */
limit?: number;
}
// ❌ 差(字段没有描述)
export interface Params {
query: string;
limit?: number;
}
S3:只读操作加 @readonly
// ✅ 好
/**
* 搜索用户
* @readonly
*/
export async function searchUsers() {}
// ❌ 差(写操作不应该加)
/**
* 创建用户
* @readonly // 错误!
*/
export async function createUser() {}
十、连接桌面 Agent
10.1 安装 relay
relay 不需要安装到项目中,通过 npx 直接运行:
npx @mcp-b/webmcp-local-relay@latest
10.2 配置 Claude Desktop
编辑配置文件:
MacOS :~/Library/Application Support/Claude/claude_desktop_config.json
Windows :%APPDATA%\Claude\claude_desktop_config.json
添加配置:
{
"mcpServers": {
"webmcp-local-relay": {
"command": "npx",
"args": ["-y", "@mcp-b/webmcp-local-relay@latest"]
}
}
}
10.3 在 Web 应用中引入 embed.js
在 HTML 入口文件添加:
<!DOCTYPE html>
<html>
<head>
<!-- 其他 head 内容 -->
</head>
<body>
<div id="root"></div>
<!-- 添加这一行 -->
<script src="https://cdn.jsdelivr.net/npm/@mcp-b/webmcp-local-relay@latest/dist/browser/embed.js"></script>
<!-- 应用脚本 -->
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
可选配置:
<script
src="..."
data-relay-port="9444" <!-- 自定义端口 -->
data-request-timeout="120000" <!-- 自定义超时 -->
></script>
10.4 测试连接
-
启动 Web 应用:
pnpm dev -
重启 Claude Desktop
-
在 Claude 中尝试:
列出所有任务
Claude 应该能调用 listTasks 工具并返回结果。
第四部分:应用场景与最佳实践
十一、典型应用场景
11.1 AI 驱动的管理后台
场景:企业管理后台,需要 AI 助手帮助操作
实现:
- 将 CRUD 操作暴露为工具
- AI 可以查询数据、创建记录、修改状态
- 用户通过自然语言指挥 AI 操作
示例对话:
用户:帮我创建一个新的项目,名称是"新产品开发",截止日期是下月底。
AI:好的,我来创建项目。
[调用 createProject 工具]
项目已创建,ID 是 proj-123。
11.2 智能数据查询
场景:复杂数据查询系统,用户不会写查询语句
实现:
- 将查询构建器暴露为工具
- AI 理解用户意图,构建查询
- 返回可视化结果
示例:
用户:找出所有逾期的高优先级任务。
AI:[调用 listTasks,参数:{ status: 'overdue', priority: 'high' }]
找到 5 个任务:
1. 任务 A - 逾期 3 天
2. 任务 B - 逾期 1 天
...
11.3 自动化工作流
场景:需要执行一系列操作的工作流
实现:
- 将每个步骤暴露为工具
- AI 可以组合调用多个工具
- 完成复杂工作流
示例:
用户:把所有已完成的任务归档,然后发送总结邮件给团队。
AI:好的,我来执行。
[调用 listTasks,筛选已完成]
[调用 archiveTasks]
[调用 generateSummary]
[调用 sendEmail]
完成!已归档 12 个任务,邮件已发送。
11.4 辅助开发调试
场景:开发过程中需要快速测试功能
实现:
- 将测试工具暴露为 WebMCP 工具
- 开发者通过 AI 快速测试
- 无需手动操作 UI
示例:
开发者:测试一下创建订单流程,用测试数据。
AI:[调用 createOrder,使用测试数据]
订单创建成功,订单号是 ORD-456。
十二、最佳实践
12.1 工具设计原则
原则 1:单一职责
每个工具只做一件事:
// ✅ 好:职责单一
export async function searchTasks(params: SearchParams) {}
export async function createTask(params: CreateParams) {}
export async function updateTask(params: UpdateParams) {}
// ❌ 差:职责混杂
export async function manageTask(params: { action: string; ... }) {}
原则 2:语义化命名
工具名要清晰表达意图:
// ✅ 好
export async function listTasks() {}
export async function archiveCompletedTasks() {}
// ❌ 差
export async function get() {}
export async function doAction() {}
原则 3:完整的 JSDoc
描述要准确、完整:
// ✅ 好
/**
* 根据关键词搜索任务,支持状态筛选和排序
* @readonly
*/
export async function searchTasks(params: SearchParams) {}
// ❌ 差
/** 搜索 */
export async function searchTasks() {}
原则 4:合理的参数设计
参数要平衡灵活性和简洁性:
// ✅ 好:提供合理默认值
export interface SearchParams {
query: string;
limit?: number; // 默认 50
offset?: number; // 默认 0
}
// ❌ 差:必填参数太多
export interface SearchParams {
query: string;
limit: number;
offset: number;
sortBy: string;
sortOrder: string;
// ...
}
12.2 作用域划分
全局工具:
- 查询类操作(list、get、search)
- 通用功能(认证、配置)
- 不依赖特定页面状态的操作
页面级工具:
- 当前页面的专有操作
- 依赖页面状态的操作
- 需要页面上下文的操作
组件级工具:
- 弹窗、面板内的操作
- 临时性的交互
- 高度依赖组件状态的操作
12.3 错误处理
提供清晰的错误信息:
export async function getTask(params: { id: string }) {
const task = await findTask(params.id);
if (!task) {
// ✅ 好:清晰的错误信息
throw new Error(`Task not found: ${params.id}. Use listTasks to see available tasks.`);
}
return task;
}
区分业务错误和系统错误:
export async function createTask(params: CreateParams) {
try {
const task = await api.createTask(params);
return task;
} catch (error) {
if (error.code === 'VALIDATION_ERROR') {
// 业务错误,提供友好提示
throw new Error(`Invalid task data: ${error.message}`);
} else {
// 系统错误,记录日志
console.error('System error:', error);
throw new Error('Failed to create task. Please try again.');
}
}
}
12.4 性能优化
避免重复查询:
// ✅ 好:使用缓存
const taskCache = new Map();
export async function getTask(params: { id: string }) {
if (taskCache.has(params.id)) {
return taskCache.get(params.id);
}
const task = await fetchTask(params.id);
taskCache.set(params.id, task);
return task;
}
批量操作:
// ✅ 好:支持批量
export async function batchUpdateStatus(params: {
taskIds: string[];
status: string;
}) {
// 一次请求更新多个
return api.batchUpdate(params);
}
// ❌ 差:单个操作
export async function updateStatus(params: {
taskId: string;
status: string;
}) {}
12.5 安全考虑
输入验证:
export async function searchTasks(params: SearchParams) {
// 验证输入
if (params.query.length > 1000) {
throw new Error('Query too long');
}
if (params.limit && params.limit > 1000) {
throw new Error('Limit too large');
}
// 执行查询
return doSearch(params);
}
权限检查:
export async function deleteTask(params: { id: string }) {
const task = await getTask(params.id);
// 检查权限
if (!canDelete(currentUser, task)) {
throw new Error('Permission denied: You cannot delete this task');
}
return doDelete(params.id);
}
十三、常见问题与解决方案
13.1 工具没有注册成功
症状:AI Agent 看不到工具
排查步骤:
-
检查构建插件是否配置:
# 搜索配置文件 grep -r "webmcp" vite.config.ts webpack.config.ts -
检查 schema 是否注入:
# 搜索构建产物 grep -r "__webmcpSchema" dist/ -
检查注册调用:
// 确保在应用启动时调用 registerGlobalTools(...); -
检查函数签名:
// 确保参数是对象类型 export async function myTool(params: { ... }) {}
13.2 Schema 不正确
症状:工具参数描述不对,或包含多余字段
原因:
- 参数不是对象类型
- 使用了不支持的类型(泛型、any)
- JSDoc 格式不对
解决方案:
// ❌ 错误:原始类型参数
export async function search(query: string) {}
// ✅ 正确:对象参数
export async function search(params: { query: string }) {}
// ❌ 错误:any 类型
export async function create(data: any) {}
// ✅ 正确:具体类型
export async function create(data: CreateParams) {}
13.3 桌面 Agent 连接失败
症状:Claude Desktop 无法调用工具
排查步骤:
-
检查 relay 是否运行:
# 查看进程 ps aux | grep webmcp-local-relay -
检查 embed.js 是否加载:
// 浏览器控制台 console.log(window.__webmcpRelay); -
检查 WebSocket 连接:
// 浏览器控制台 // 应该看到 WebSocket 连接 -
检查配置文件:
// claude_desktop_config.json { "mcpServers": { "webmcp-local-relay": { "command": "npx", "args": ["-y", "@mcp-b/webmcp-local-relay@latest"] } } }
13.4 工具调用超时
症状:工具调用长时间无响应
原因:
- 工具执行时间过长
- 网络请求超时
- 死锁或无限循环
解决方案:
// 添加超时控制
export async function longRunningTask(params: Params) {
const timeout = setTimeout(() => {
throw new Error('Task timeout');
}, 30000);
try {
const result = await doWork(params);
clearTimeout(timeout);
return result;
} catch (error) {
clearTimeout(timeout);
throw error;
}
}
结语
WebMCP Nexus 是一个革命性的前端集成方案,它让 Web 应用能够轻松成为 AI Agent 可驱动的对象。通过零侵入的设计、构建时类型反推、三级作用域管理等创新特性,它极大地降低了 AI 集成的门槛。
核心价值:
- 零侵入:不修改业务代码,只加注释和一行注册
- 类型安全:从 TypeScript 类型自动生成 Schema,单一事实源
- 生命周期管理:工具与 React 组件生命周期绑定,自动注销
- 跨浏览器兼容:自动处理 polyfill,对业务透明
- 桌面 Agent 支持:通过 relay 连接本地 AI,直接操作浏览器
适用场景:
- AI 驱动的管理后台
- 智能数据查询系统
- 自动化工作流
- 辅助开发调试
- 任何需要 AI 操作的 Web 应用
未来展望:
随着 WebMCP 标准的推进和 AI Agent 的普及,WebMCP Nexus 将成为连接 AI 和 Web 应用的重要桥梁。它代表了前端开发的新范式:不仅是为人设计的界面,也是为 AI 设计的工具。
希望这篇文章能帮助你全面理解 WebMCP Nexus,并在实际项目中发挥其价值。
参考资源
- GitHub 仓库 :https://github.com/alibaba/webmcp-nexus
- 在线 Demo :https://alibaba.github.io/webmcp-nexus/
- WebMCP 标准 :https://webmcp.org/
- MCP 协议 :https://modelcontextprotocol.io/
- npm 包 :
- webmcp-nexus-sdk
- vite-plugin-webmcp-nexus
- webpack-plugin-webmcp-nexus
- @mcp-b/webmcp-local-relay
- @mcp-b/webmcp-polyfill
- 作者:GYF