MusePi

Custom Tools

English 中文

自定义工具(custom tools)是可由模型调用的函数,它们接入与内置工具相同的工具执行流水线(tool execution pipeline)。

自定义工具是一个导出工厂函数(factory)的 TypeScript/JavaScript 模块。工厂函数接收宿主 API(CustomToolAPI),返回一个工具或一组工具。

这是什么(以及不是什么)

如果你需要模型直接调用代码,请使用自定义工具。

当前代码中的集成路径

当前有两种活跃的集成方式:

  1. SDK 提供的自定义工具options.customTools
    • 通过 CustomToolAdapter 或扩展包装器(extension wrappers)包装为 agent 工具。
    • 在 SDK 启动(bootstrap)时始终包含在初始活跃工具集中。
  2. 通过 loader API 从文件系统发现的模块discoverAndLoadCustomTools / loadCustomTools
    • 作为库 API 暴露在 src/extensibility/custom-tools/loader.ts 中。
    • 宿主代码可调用这些 API,从 config/provider/plugin 路径发现并加载工具模块。
模型工具调用流程

LLM tool call
   │
   ▼
Tool registry (内置工具 + 自定义工具适配器)
   │
   ▼
CustomTool.execute(toolCallId, params, onUpdate, ctx, signal)
   │
   ├─ onUpdate(...)  -> 流式传输的部分结果
   └─ return result  -> 最终工具内容/详情

发现位置(loader API)

discoverAndLoadCustomTools(configuredPaths, cwd, builtInToolNames) 合并以下来源:

  1. 能力提供者(toolCapability),包括:
    • 原生 OMP 配置(~/.musepi/agent/tools.musepi/tools
    • Claude 配置(~/.claude/tools.claude/tools
    • Codex 配置(~/.codex/tools.codex/tools
    • Claude marketplace 插件缓存提供者
  2. 已安装的插件清单(通过插件 loader 读取 ~/.musepi/plugins/node_modules/*
  3. 显式传入 loader 的已配置路径

重要行为

模块契约

自定义工具模块必须导出一个函数(推荐默认导出):

import type { CustomToolFactory } from "@musepi/pi-coding-agent";

const factory: CustomToolFactory = (pi) => ({
  name: "repo_stats",
  label: "Repo Stats",
  description: "Counts tracked TypeScript files",
  parameters: pi.zod.object({
    glob: pi.zod.string().optional(),
  }),

  async execute(toolCallId, params, onUpdate, ctx, signal) {
    onUpdate?.({
      content: [{ type: "text", text: "Scanning files..." }],
      details: { phase: "scan" },
    });

    const result = await pi.exec(
      "git",
      ["ls-files", params.glob ?? "**/*.ts"],
      { signal, cwd: pi.cwd },
    );
    if (result.killed) {
      throw new Error("Scan was cancelled");
    }
    if (result.code !== 0) {
      throw new Error(result.stderr || "git ls-files failed");
    }

    const files = result.stdout.split("\n").filter(Boolean);
    return {
      content: [{ type: "text", text: `Found ${files.length} files` }],
      details: { count: files.length, sample: files.slice(0, 10) },
    };
  },

  onSession(event) {
    if (event.reason === "shutdown") {
      // cleanup resources if needed
    }
  },
});

export default factory;

参数 schema 可使用与 Zod 兼容的 omptype 构建器(pi.zod)、原生 omptype 构建器(pi.arktype),或兼容旧版的 TypeBox shim(pi.typebox),它们都会流经共享的校验/传输流水线(validation/wire pipeline)。

工厂返回类型:

传给工厂的 API 表面(CustomToolAPI

来自 types.tsloader.ts

Loader 以 no-op 的 UI 上下文启动,并要求宿主代码在真实 UI 就绪时调用 setUIContext(...)

执行契约与类型

CustomTool.execute 签名:

execute(toolCallId, params, onUpdate, ctx, signal);

CustomToolAdapter 将其桥接到 agent 工具接口,并以正确的参数顺序转发调用。

工具定义还可以声明 stricthiddendeferrablemcpServerNamemcpToolNameapprovalformatApprovalDetails

工具如何暴露给模型

渲染钩子

可选的渲染钩子:

TUI 中的运行时行为:

会话/状态处理

可选的 onSession(event, ctx) 接收会话生命周期事件,包括:

当分支/会话上下文变化时,使用 ctx.sessionManager 从历史中重建状态。

失败与取消语义

同步/异步失败

取消

onSession 错误

设计时需要面对的真实约束