本文档说明其他网站项目如何调用当前 AX 文档编辑器中的 WebMCP 工具。
当前方案支持其他网站通过 HTTP 调用当前项目页面中的 WebMCP 工具。
调用链如下:
其他网站
-> POST /api/v1/webmcp/invoke
-> 当前项目后端 WebMCP Bridge
-> 当前项目页面 WebSocket Connector
-> 当前项目 WebMCP Runtime
-> 执行工具
-> 返回执行结果
当前项目页面必须保持打开,因为以下工具需要操作浏览器页面状态:
open_documentget_documentsearch_documentget_blockget_document_tocget_document_statslist_documentslist_export_records启动后端:
cd "D:\work space\ax-shell\ax-backend-v1"
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
启动前端:
cd "D:\work space\ax-shell\ax-frontend-app"
npm run dev
然后在浏览器打开当前项目:
http://localhost:5173/
当前项目页面启动时会自动连接:
ws://localhost:8000/api/v1/webmcp/bridge/ax-editor-local
后端使用前端环境中的实际 API 地址。例如当前配置是:
http://192.168.0.195:8000
因此其他网站如果不在同一台机器上,需要使用当前项目后端所在机器的局域网地址或公网地址,不能使用远程机器上的 localhost。
当前 WebMCP 实现参考 WebMCP Community Group Draft 和 MCP Tools 安全建议,已启用以下防护:
重要:VITE_WEBMCP_BRIDGE_TOKEN 会进入浏览器构建产物,不能当作长期机密凭证。生产环境不要把高权限 token 放入 VITE_* 变量;建议使用用户登录态、短期签名凭证或服务端代理,并在服务端增加用户身份、租户和文档权限校验。当前 Bridge 适合本机或受控内网调用,不建议直接暴露到公网。
WebMCP 仍是 Community Group Draft,不是正式 W3C 标准。工具描述和工具返回内容都可能影响 AI 判断,不能把模型生成的指令或文档内容当作可信控制信号。
生产部署至少需要配置:
WEBMCP_AUTH_REQUIRED=true
WEBMCP_JWT_SECRET=使用独立的高熵服务端密钥
WEBMCP_SESSION_COOKIE_NAME=ax_session
WEBMCP_REDIS_URL=redis://用户名:密码@redis-host:6379/0
WEBMCP_REQUIRE_SHARED_STORE=true
WEBMCP_TICKET_TTL_SECONDS=60
WEBMCP_CONFIRMATION_TTL_SECONDS=120
生产环境不要配置 VITE_WEBMCP_BRIDGE_TOKEN。页面应通过登录会话的安全 cookie 获取短期 Bridge ticket;cookie 的值必须是由服务端签发、包含 sub 和 sid 的 HS256 JWT,并使用 Secure、HttpOnly、SameSite 属性。服务端必须验证 JWT 的用户和会话声明。Redis 用于一次性凭证、共享限流和多实例协调,未配置 Redis 时生产 WebMCP 请求会被拒绝。WEBMCP_JWT_SECRET 不能与普通 API 密钥或前端变量共用。
接口地址:
POST http://当前后端地址:8000/api/v1/webmcp/invoke
请求头:
Content-Type: application/json
X-WebMCP-Token: 当前后端配置的 Bridge Token
请求结构:
{
"clientId": "ax-editor-local",
"tool": "工具名称",
"arguments": {}
}
字段说明:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
clientId |
string |
是 | 当前项目页面的客户端 ID,默认是 ax-editor-local |
tool |
string |
是 | WebMCP 工具名称 |
arguments |
object |
否 | 工具参数 |
const response = await fetch(
'http://192.168.0.195:8000/api/v1/webmcp/invoke',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-WebMCP-Token': 'REPLACE_WITH_WEBMCP_BRIDGE_TOKEN',
},
body: JSON.stringify({
clientId: 'ax-editor-local',
tool: 'open_document',
arguments: {
documentId: 'doc-prod-001',
},
}),
}
);
const result = await response.json();
console.log(result);
成功响应:
{
"code": 0,
"message": "Success",
"data": {
"requestId": "request-uuid",
"tool": "open_document",
"result": {
"ok": true,
"data": {
"documentId": "doc-prod-001",
"message": "文档已打开"
}
}
}
}
调用成功后,当前项目右侧编辑器会打开 doc-prod-001。
const result = await callWebMcpTool('get_document', {
documentId: 'doc-prod-001',
});
async function callWebMcpTool(tool, argumentsValue = {}) {
const response = await fetch(
'http://192.168.0.195:8000/api/v1/webmcp/invoke',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-WebMCP-Token': 'REPLACE_WITH_WEBMCP_BRIDGE_TOKEN',
},
body: JSON.stringify({
clientId: 'ax-editor-local',
tool,
arguments: argumentsValue,
}),
}
);
const payload = await response.json();
if (!response.ok || payload.code !== 0) {
throw new Error(payload.detail || payload.message || 'WebMCP 调用失败');
}
const toolResult = payload.data.result;
if (!toolResult.ok) {
throw new Error(toolResult.error || 'WebMCP 工具执行失败');
}
return toolResult.data;
}
const result = await callWebMcpTool('search_document', {
documentId: 'doc-prod-001',
query: 'WebMCP',
});
const result = await callWebMcpTool('get_block', {
documentId: 'doc-prod-001',
blockId: 'block-p-50',
});
const result = await callWebMcpTool('get_document_toc', {
documentId: 'doc-prod-001',
});
open_document打开当前项目右侧编辑器中的文档。
{
"tool": "open_document",
"arguments": {
"documentId": "doc-prod-001"
}
}
list_documents获取当前用户的文档列表。
{
"tool": "list_documents",
"arguments": {
"page": 1,
"pageSize": 20,
"sessionId": "optional-session-id"
}
}
get_document获取文档详情和内容块。
{
"tool": "get_document",
"arguments": {
"documentId": "doc-prod-001"
}
}
search_document在文档中搜索内容块。
{
"tool": "search_document",
"arguments": {
"documentId": "doc-prod-001",
"query": "关键词",
"type": "paragraph"
}
}
get_block获取指定文档块。
{
"tool": "get_block",
"arguments": {
"documentId": "doc-prod-001",
"blockId": "block-p-50"
}
}
get_document_toc获取文档目录。
{
"tool": "get_document_toc",
"arguments": {
"documentId": "doc-prod-001"
}
}
get_document_stats获取文档统计信息。
{
"tool": "get_document_stats",
"arguments": {
"documentId": "doc-prod-001"
}
}
list_export_records获取当前用户的导出记录。
{
"tool": "list_export_records",
"arguments": {
"page": 1,
"pageSize": 20
}
}
以下工具在当前 Bridge 中不会被其他网站直接执行:
insert_blockupdate_blockdelete_blockexport_documentdownload_export_record原因是这些工具的 requiresConfirmation 为 true。跨网站调用会得到失败结果:
{
"ok": false,
"error": "该 WebMCP 工具需要当前页面用户确认"
}
这可以防止其他网站绕过当前项目的用户确认机制,直接修改或删除文档内容。
503 Service Unavailable
响应:
{
"detail": "WebMCP Bridge 未配置访问令牌"
}
401 Unauthorized
响应:
{
"detail": "WebMCP Bridge 访问令牌无效"
}
404 Not Found
响应:
{
"detail": "当前 WebMCP 页面未连接"
}
504 Gateway Timeout
响应:
{
"detail": "WebMCP 工具执行超时"
}
HTTP 请求可能成功返回,但 data.result.ok 为 false:
{
"code": 0,
"data": {
"result": {
"ok": false,
"error": "文档不存在"
}
}
}
调用方必须同时检查:
if (!response.ok || payload.code !== 0 || !payload.data.result.ok) {
// 按失败处理
}
建议其他项目封装一个统一函数,不要在多个页面重复拼接请求:
const WEBMCP_API = 'http://192.168.0.195:8000/api/v1/webmcp/invoke';
const WEBMCP_TOKEN = 'REPLACE_WITH_WEBMCP_BRIDGE_TOKEN';
const WEBMCP_CLIENT_ID = 'ax-editor-local';
export async function invokeAxWebMcp(tool, argumentsValue = {}) {
const response = await fetch(WEBMCP_API, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-WebMCP-Token': WEBMCP_TOKEN,
},
body: JSON.stringify({
clientId: WEBMCP_CLIENT_ID,
tool,
arguments: argumentsValue,
}),
});
const payload = await response.json();
if (!response.ok) {
throw new Error(payload.detail || `HTTP ${response.status}`);
}
if (payload.code !== 0) {
throw new Error(payload.message || 'WebMCP 调用失败');
}
const result = payload.data?.result;
if (!result?.ok) {
throw new Error(result?.error || 'WebMCP 工具执行失败');
}
return result.data;
}
// 打开当前项目中的文档
await invokeAxWebMcp('open_document', {
documentId: 'doc-prod-001',
});
可以调用:
http://localhost:8000/api/v1/webmcp/invoke
需要使用后端电脑的局域网地址:
http://192.168.0.195:8000/api/v1/webmcp/invoke
并确保:
--host 0.0.0.0 启动。8000 端口。不能直接使用用户电脑上的 localhost。需要将当前后端或 WebMCP Gateway 部署到公网 HTTPS 地址,或者通过安全隧道暴露接口。
不要在其他网站中直接使用:
useUIStore.getState().openDocumentPreview(...);
executeWebMcpTool(...);
这些属于当前项目内部代码,其他网站无法直接访问,也不应依赖。
不要把 XAgent API Key 放在其他网站前端代码中。其他网站只需要使用 WebMCP Bridge Token,不需要知道 XAgent API Key。
不要让其他网站直接连接:
ws://localhost:8000/api/v1/webmcp/bridge/ax-editor-local
这个 WebSocket 是当前项目页面的内部连接。其他网站应使用 HTTP POST /invoke 接口。
http://localhost:5173/。await invokeAxWebMcp('open_document', {
documentId: 'doc-prod-001',
});
如果返回“当前 WebMCP 页面未连接”,说明当前项目页面没有成功连接后端 Bridge,需要检查前端 .env 中的:
VITE_API_BASE_URL
VITE_WEBMCP_BRIDGE_TOKEN
VITE_WEBMCP_CLIENT_ID
修改环境变量后必须重启 Vite 和 Uvicorn。