Appearance
为 AI Agent 提供能力
utools.registerTool(name, handler)
注册一个工具方法,使其能够在运行时被 AI Agent 自动调用。
⚠️ 注册时机要求
registerTool必须在页面初始化阶段执行:
- ✅ 推荐:
preload.js- ✅ 或与
onPluginEnter同级作用域- ❌ 不可写在
onPluginEnter内部,AI Agent 调用时不会触发onPluginEnter
类型定义
ts
function registerTool(
name: string,
handler: (params: Record<string, any>, ctx: ToolContext) => any
): void;name工具名称,必须与plugin.json > tools中定义的 key 完全一致handlerAI Agent 调用工具时执行的函数params调用时传入的参数对象(由inputSchema自动构造)ctx工具执行上下文
ToolContext 类型定义
ts
interface ToolContext {
requestId: string | number;
sendProgress?: (progress: number, total?: number, message: string) => Promise<void>;
}字段说明
requestId- 当前调用请求标识(MCP 客户端生成)
sendProgress- 用于向 AI Agent 上报任务执行进度(适用于长时间任务)
- 参数:
progress当前进度值total总数(可选)message进度消息(可选)
注意:
sendProgress可能不存在(取决于 MCP 客户端能力),调用前必须做可选判断:ctx.sendProgress?.()
示例代码
js
// 简单工具
utools.registerTool('say_hi', async () => {
return 'hi'
})
// 带进度上报的长任务
utools.registerTool('video_convert', async ({ inputPath, format }, ctx) => {
const outputPath = `${window.utools.getPath('downloads')}/${Date.now()}.${format}`
const args =
format === 'webm'
? ['-i', inputPath, '-c:v', 'libvpx-vp9', '-crf', '30', '-b:v', '0', outputPath]
: ['-i', inputPath, outputPath]
await window.utools.runFFmpeg(args, (progress) => {
if (!ctx.sendProgress || progress.percent === undefined) return
ctx.sendProgress(
progress.percent,
100,
`视频处理中 ${progress.percent.toFixed(1)}%`
)
})
return { outputPath }
})最佳实践
1. 工具设计保持单一职责
- 一个工具只解决一个明确问题
- 避免“万能工具”,降低 AI 调用不确定性
2. 工具应避免动态注册
避免在运行时通过条件判断动态注册工具,例如:
js
if (xxx) {
utools.registerTool(...)
}3. 参数必须与 inputSchema 严格一致
- AI Agent 会基于
plugin.json中的inputSchema自动构造参数 handler内应直接消费参数,避免重复解析或结构推断
4. 长任务必须提供进度反馈
适用于执行时间 ≥ 5s 的任务:
- 提升用户感知(避免“卡死”)
- 降低 MCP 客户端超时风险
js
ctx.sendProgress?.(progress, total, message)5. 返回值保持结构化
统一返回对象,避免返回原始类型:
js
return {
outputPath,
duration,
size
}6. 错误处理要明确
直接抛出错误,由 AI Agent 感知并处理:
js
throw new Error('视频转换失败')