Skip to content

工具插件开发 ​

工具插件(type: "tool",transport: "in-process")的代码运行在客户端主进程内,可以访问宿主原生能力,是最灵活也最常用的一类插件。


适用场景 ​

场景是否推荐 in-process
纯计算/文本处理工具✅ 启动快、无依赖
需要读写本地文件、访问工作区✅ 可用 getWorkspacePath
需要截图、OCR、图像处理✅ 可用 sharp / ocr 宿主能力
需要模拟鼠标键盘、控制桌面✅ 可用 getDesktopControlManager
需要注册定时任务✅ 可用 cronScheduler
需要向客户端注入 UI✅ 必须 in-process
依赖大量第三方 npm 包⚠️ 需自行打包依赖,或改用 stdio
需要崩溃隔离❌ 用 stdio

目录结构 ​

plugins/my-tool/
├── manifest.json      # 必需
├── index.js           # 必需:MCP 工厂入口
├── package.json       # 声明 "type": "module"
├── ui-src/            # 可选:UI 扩展源码
├── ui.js              # 可选:UI 构建产物
└── icon/icon.png      # 可选:本地图标

manifest.json ​

json
{
  "id": "my-tool",
  "name": "My Tool",
  "nameZh": "我的工具",
  "description": "Do something useful",
  "descriptionZh": "做一些有用的事",
  "type": "tool",
  "version": "1.0.0",
  "main": "index.js",
  "icon": "Sparkles",
  "category": "utility",
  "capabilities": {
    "mcp": {
      "transport": "in-process",
      "autoConnect": true
    }
  },
  "configSchema": { "fields": [] },
  "permissions": [],
  "platforms": ["darwin", "win32", "linux"],
  "license": "MIT"
}

入口代码结构 ​

推荐固定三段式:获取 Host → 定义辅助函数 → 导出工厂。

javascript
/**
 * My Tool - 插件入口
 */

// ① 获取宿主注入的 MCP 运行时
function getHost() {
  const host = globalThis.__AWEECLAW_HOST__
  if (!host) throw new Error('Host services not available. globalThis.__AWEECLAW_HOST__ is not set.')
  return host
}

const { McpServer, InMemoryTransport, z } = getHost()

// ② 辅助函数
function text(t) {
  return { type: 'text', text: String(t) }
}
function ok(...items) {
  return { content: items }
}
function err(message) {
  return { content: [text(message)], isError: true }
}

// ③ 导出工厂,返回 { server, clientTransport }
export function createMcpServer() {
  const server = new McpServer(
    { name: 'my-tool', version: '1.0.0' },
    {
      capabilities: { tools: { listChanged: false } },
      instructions: 'One-line summary of what this plugin does.',
    },
  )

  server.tool(
    'hello',
    'Say hello to someone',
    {
      name: z.string().default('world').describe('Name to greet'),
      loud: z.boolean().default(false).describe('Uppercase the greeting'),
    },
    async (args) => {
      try {
        const greeting = `Hello, ${args.name}!`
        return ok(text(args.loud ? greeting.toUpperCase() : greeting))
      } catch (e) {
        return err(`Failed: ${e.message}`)
      }
    },
  )

  const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair()
  server.connect(serverTransport).catch(() => {})
  return { server, clientTransport }
}

工具注册签名 ​

javascript
server.tool(
  'tool_name',            // 工具名:小写下划线,智能体按此调用
  'One-line description', // 描述:LLM 据此判断何时调用,必须写清"做什么/何时用"
  { /* zod schema */ },   // 参数定义
  async (args, extra) => ({ content: [...] }),  // 处理器
)

参数用 zod 描述 ​

写法作用
z.string() / z.number() / z.boolean()基础类型
z.string().min(1).max(200)长度约束(同时约束 LLM 输出)
z.number().int().min(1).max(1000)整数与范围
z.array(z.string())数组
z.enum(['a', 'b'])枚举
.default(...)默认值,缺省时 LLM 可省略该参数
.optional()可选参数
.describe('...')参数级说明,强烈建议每个参数都写

返回值约定 ​

javascript
// 成功
return { content: [{ type: 'text', text: '结果文本' }] }

// 失败:不要抛异常,返回 isError
return { content: [{ type: 'text', text: '失败原因' }], isError: true }

抛出的异常会被客户端捕获并转成通用错误信息,智能体拿不到具体原因,因此业务错误一律用 isError 返回。


访问宿主能力 ​

in-process 插件可通过 globalThis.__AWEECLAW_HOST__ 访问客户端原生能力。

字段说明
McpServer / InMemoryTransport / zMCP 运行时与 zod
logger分区日志器(logger.system.info(...))
getWorkspacePath()当前工作区路径,返回 null 时应降级到临时目录
sharp图像处理(缩放、格式转换、裁剪)
ocr / macVisionOcrRouter文字识别
nativeImageElectron 原生图像对象
getDesktopControlManager桌面控制(鼠标、键盘、窗口、截图)
inputListener截图流输入监听(供录制类插件使用)
cronScheduler注册插件级定时任务
pptxgen / pptPreview / parsePptxFilePPT 生成、实时预览、解析
sharp见上
perceptionStore / localEmbedder / vlmModelManager感知数据存储、本地嵌入、视觉语言模型
behaviorPredictor / codeDependencyGraph / impactAnalyzer行为预测、代码依赖图、影响分析
monitoringService / iotBridge系统监控、IoT 设备

示例 —— 把结果写入工作区:

javascript
const { logger, getWorkspacePath } = getHost()

server.tool('export_report', 'Export report to workspace', {}, async () => {
  const root = getWorkspacePath()
  if (!root) return err('No workspace is open.')
  const dir = path.join(root, '.aweeclaw')
  fs.mkdirSync(dir, { recursive: true })
  fs.writeFileSync(path.join(dir, 'report.md'), '# Report\n', 'utf8')
  logger.system.info('[my-tool] report exported to', dir)
  return ok(text(`Exported to ${dir}/report.md`))
})

权限与 ctx.host ​

上面的 globalThis.__AWEECLAW_HOST__ 是全局共享对象,不做权限校验(为兼容早期插件保留)。若插件通过原生 PluginRuntime 上下文初始化,应优先使用 ctx.host —— 它会按 manifest.permissions 校验,未声明的能力访问会抛 PermissionDeniedError:

typescript
const host = ctx.host as HostServices   // 受限代理
host.ocr.recognize(buffer)               // 需声明 desktop.screen 或对应权限

规则:

  1. 用哪个能力,就在 manifest.permissions 里声明哪个权限;
  2. 声明了 contributes(UI 扩展)必须加 ui.render;
  3. 权限只做能力访问控制,不做网络域名白名单。

注册定时任务 ​

插件可向 cronScheduler 注册自己的定时回调,独立于平台的自动化任务系统(触发时不会进入 Agent 对话流):

javascript
const { cronScheduler } = getHost()

// initialize 阶段注册
cronScheduler.register({
  pluginId: 'my-tool',
  name: 'hourly-sync',
  pattern: '0 * * * *',
  handler: async () => {
    logger.system.info('[my-tool] hourly sync triggered')
  },
})

// 卸载时批量清理
cronScheduler.unregisterByPlugin('my-tool')

原生插件运行时(进阶) ​

若插件不通过 MCP 暴露工具,而是实现客户端原生插件接口(如渠道、Provider、Hook 类插件),则改用 PluginRuntime:

typescript
import type { PluginRuntime, PluginContext } from '@shared/plugin-sdk'

export const runtime: PluginRuntime = {
  async initialize(ctx: PluginContext) {
    ctx.logger.info('plugin initialized', ctx.getConfig())
    const token = await ctx.getSecret('API_KEY')
    ctx.registerHook('before-tool-call', (payload) => ({ proceed: true }))
  },
  async destroy() {
    /* 释放资源 */
  },
  async healthCheck() {
    return { healthy: true }
  },
}

PluginContext 提供的能力:

成员说明
pluginId插件 ID
dataDir插件独立数据目录
logger分区日志器
getConfig() / setConfig()读写配置
getSecret(key) / setSecret(key, value)读写安全存储中的密钥
registerHook(event, handler)注册生命周期钩子,返回取消函数
sendToRenderer(channel, ...args)向渲染进程发送 IPC 事件
host受限的原生能力代理(受 permissions 校验)

调试 ​

bash
node --check index.js          # 语法检查

运行时行为在客户端观察:「设置 → MCP 服务」查看连接状态与调用日志。完整方法见本地调试。


下一步 ​

AweeClaw AI 应用构建平台