可运行的 AI actions 与 MCP
无需 provider 即可注册和执行 WU actions;连接真实 MCP relay,检查 schema、权限、身份验证、取消和清理。
Action 不需要 LLM provider
wu.ai.execute 调用已注册的处理函数。可以从自己的界面、外部代理或测试中使用,无需配置 provider、编写 prompt 或输入 API key。只有与模型对话的操作需要 provider;action 注册和直接执行彼此独立。
examples/ai/local-actions.mjs 示例执行真实求和,并确认拒绝发生在处理函数开始计算之前。
下载示例项目,解压 ZIP 并运行 npm install 安装依赖。其中包含 Node 和 MCP 示例使用的相同 actions 及测试。
node examples/ai/local-actions.mjs
node scripts/verify-server-examples.mjs
注册、启用与执行
import wu from 'wu-framework';
const ai = await wu.aiReady();
ai.init({ permissions: {
executeActions: true,
readStore: false,
writeStore: false,
emitEvents: false,
readDOM: false,
modifyDOM: false,
readDiagnostics: false,
navigate: false,
network: false,
} });
ai.action('sum_local', {
description: 'Suma dos números sin efectos externos',
parameters: {
type: 'object',
properties: { a: { type: 'number' }, b: { type: 'number' } },
required: ['a', 'b'],
},
handler({ a, b }) {
if (!Number.isFinite(a) || !Number.isFinite(b) || !Number.isFinite(a + b)) {
throw new Error('Se requieren números y resultado finitos');
}
return { sum: a + b };
},
});
const outcome = await ai.execute('sum_local', { a: 2, b: 3 });
// { success: true, result: { sum: 5 } }
执行返回数据的操作前等待 aiReady()。action 返回 AI 对象,可链式注册。execute 返回包含 { success, result? , reason? } 的 Promise;权限或验证失败通常以 success: false 表示,不向调用方抛异常。
ai.tools() 向外部客户端暴露目录 schema。工具出现在列表中,不代表获得执行权限。
Schema、权限与领域规则
WU 的参数验证刻意保持简单:检查必填字段、第一层属性类型(包括 integer)和 enum,并非完整的 JSON Schema 验证器。数值边界、additionalProperties、格式及递归验证等规则,需要产品在产生副作用前自行验证。
示例测试缺少字段、数字位置传入字符串,以及非有限值。前两种由 WU 拒绝,第三种由处理函数的显式规则拒绝。计数器证明只有获准的情况进入计算。
ai.permissions.configure({ executeActions: false });
const result = await ai.execute('sum_local', { a: 2, b: 3 });
// { success: false, reason: 'Permission denied: executeActions' }
Action 可以声明 permissions: ['readStore'] 或 permissionsWhen(params)。所需标志在参数验证和处理函数前检查。第二个参数提供的 API 只包含已声明且已启用的操作,例如 getState、setState、emit 或 fetch。
请区分以下控制:
| 控制 | 决定内容 |
|---|---|
| WU 应用 capabilities | 通过自身 facade 对 store、事件和 RPC 的技术访问 |
| AI 标志 | 子系统能否执行 actions、读取 DOM、写状态或使用网络 |
| 领域授权 | 服务器允许已认证主体执行哪些业务操作 |
| 执行器能力 | 后端或 runner 是否实现取消、事务等操作 |
处理函数是可信宿主代码。闭包可以访问所提供 API 之外的全局对象,注册机制不是针对恶意 JavaScript 的沙箱。涉及后端副作用的 actions 应使用重新验证身份、schema、scope 和授权的服务。这些示例保持纯计算,不访问特权驱动。
确认与清理
const stop = wu.on('ai:tool:confirm', event => {
// La receta de rechazo no concede ninguna aprobación.
ai.rejectTool(event.data.callId);
});
try {
const result = await ai.execute('sum_with_confirmation', { a: 2, b: 3 });
// success: false; reason: 'User denied action'
} finally {
stop();
ai.destroy();
await wu.destroy();
}
sum_with_confirmation 在 examples/ai/register-actions.mjs 中以 confirm: true 注册。界面获得肯定决定后调用 confirmTool(callId);rejectTool 完成拒绝。如果没有决定,确认在 30 秒后超时并拒绝。
销毁子系统前,应完成或拒绝待处理确认,并移除集成安装的订阅。destroy 清理 AI 资源,不会任意取消自定义处理函数捕获的 Promise 或副作用,也不会把内存历史转换为持久审计。
MCP:从客户端到运行中的浏览器
软件包中的 MCP 服务器是 relay。客户端通过 stdio 使用 MCP/JSON-RPC,relay 经 WebSocket 向已加载 WU 的页面桥接发送命令。Action 在该页面中执行,使用其 AI 权限。
Cliente MCP → stdio → wu-mcp-server → WebSocket → wu.mcp → wu.ai.execute
运行完整示例:
node examples/mcp/local-relay.mjs
node scripts/verify-server-examples.mjs --mcp
示例通过 wu-framework/mcp-server export 启动打包服务器,从本地监听器提供已安装的 UMD bundle,并打开无头 Chromium。Windows 使用 Edge,其他系统使用 Playwright Chromium。缺少 Playwright 浏览器时,运行 npx playwright install chromium 安装二进制。可以通过 WU_BROWSER_CHANNEL 选择已安装的渠道。
无需安装外部 MCP 客户端:examples/mcp/stdio-client.mjs 只实现初始化、列出工具、调用和取消等待所需的 JSON-RPC 操作。通用集成请使用支持完整协议的 MCP 客户端。
连接自己的客户端
软件包安装 wu-mcp-server 可执行文件。请由启动进程的宿主配置环境。不要把 token 放在 URL 中,也不要发布到前端代码。
| 变量 | 行为 |
|---|---|
WU_MCP_TOKEN |
除非显式启用不安全模式,否则必填;16 至 4096 字节 |
WU_MCP_HOST |
默认 127.0.0.1 |
WU_MCP_PORT |
19100;支持 1–65535 |
WU_MCP_ORIGINS |
逗号分隔的精确 HTTP(S) origin,不含路径 |
WU_MCP_TIMEOUT |
桥接响应等待:默认 30000 ms,范围 50–300000 |
WU_MCP_AUTH_TIMEOUT |
身份验证等待:默认 5000 ms,范围 50–60000 |
WU_MCP_MAX_PAYLOAD |
默认 1048576 字节,范围 1024–16777216 |
WU_MCP_INSECURE |
仅 1 允许省略 token;示例保持强制身份验证 |
未设置 WU_MCP_ORIGINS 时,接受 HTTP(S) 回环页面;没有 Origin 的 WebSocket 客户端无法通过检查。示例将 relay 限制为刚创建服务器的精确 origin。
在页面中,可信集成将短期 token 交给桥接:
await wu.mcp.connect(relayUrl, { token: sessionToken });
Promise 加载模块并开始连接,不保证握手已完成。检查 wu.mcp.isConnected(),并从 MCP 客户端查询 wu_conexion,观察两端状态。relay 同时只允许一个已认证的活动桥接。
工具与结果读取
| 工具 | 主要参数 |
|---|---|
wu_conexion |
{};即使没有连接页面,也返回 relay 状态 |
wu_list_actions |
{};页面的 action 目录 |
wu_execute_action |
{ action, params? } |
wu_get_state |
{ path? };需要 readStore |
wu_set_state |
{ path, value };需要 writeStore |
wu_emit_event |
{ event, data? };拒绝 wu:、system: 和 app: 命名空间 |
relay 还注册 WU 状态、应用、事件、导航、DOM、诊断和挂载工具。tools/list 返回已安装版本的精确目录,权限仍在页面内生效。
结果有两层:MCP envelope 和 action 结果。被拒绝的 action 可能放在 isError: false 的 envelope 中,而 content[0].text 内的 JSON 包含 success: false。两层都要检查:
const response = await client.callTool('wu_execute_action', {
action: 'sum_local', params: { a: 2, b: 3 },
});
if (response.isError) throw new Error(response.content[0].text);
const outcome = JSON.parse(response.content[0].text);
if (!outcome.success) throw new Error(outcome.reason);
console.log(outcome.result);
此片段使用示例的最小客户端。其他 SDK 的 callTool 签名可能不同。
可观察的取消行为
取消 MCP request 等待时,示例发送 notifications/cancelled,随后验证已在浏览器启动的 bounded_sum 处理函数仍完成固定延迟和计算。relay 不会向 wu.ai.execute 传递 AbortSignal。
因此,取消等待不能证明工作已经停止。对于有副作用的长操作,应设计带操作标识符、status/cancel 授权及执行器协作取消的服务。WASM/WASI 可以承载可移植逻辑,但宿主仍需实现这些契约和网络传输。使用 WU 连接的服务展示另一种情况:把 RPC signal 连接到 Worker 的实际终止。
完成后调用 wu.mcp.disconnect(),停止重连并释放拦截器。测试确认 fetch 和 console.log 恢复原先引用、relay 不再看到连接应用,并关闭创建的进程。运行时边界请参阅扩展与 AI和安全。