Skip to content

OpenCode 的自定义工具功能让你能够编写 TypeScript 或 JavaScript 函数,LLM 在对话中可以像使用内置工具一样调用它们。工具定义放在项目的 .opencode/tools/ 目录或全局 ~/.config/opencode/tools/ 目录中,文件名即为工具名。

自定义工具是你编写的函数,LLM 可以在对话中主动调用它们。它们与 OpenCode 的内置工具(如 readwritebash)并列工作。

和 Claude Code 的 MCP 工具类似,但自定义工具更轻量——直接放一个 TypeScript 文件就能生效,不需要启动单独的服务进程。


创建工具

工具的定义文件必须是 TypeScript 或 JavaScript,但工具内部可以调用任意语言编写的脚本(Python、Shell 等都可以)。


文件位置

范围路径
项目级(只对当前项目生效).opencode/tools/
全局(对所有项目生效)~/.config/opencode/tools/

基础结构

最简单的写法是用 tool() 辅助函数,它提供类型安全和参数验证:

ts
import { tool } from "@opencode-ai/plugin"

export default tool({
  description: "查询项目数据库",
  args: {
    query: tool.schema.string().describe("要执行的 SQL 查询"),
  },
  async execute(args) {
    // 你的数据库逻辑
    return `已执行查询: ${args.query}`
  },
})

文件名即工具名。上面的文件如果命名为 database.ts,工具名就是 database


一个文件多个工具

一个文件可以导出多个工具,每个导出成为一个独立工具,命名规则为 <文件名>_<导出名>

ts
import { tool } from "@opencode-ai/plugin"

export const add = tool({
  description: "两数相加",
  args: {
    a: tool.schema.number().describe("第一个数"),
    b: tool.schema.number().describe("第二个数"),
  },
  async execute(args) {
    return args.a + args.b
  },
})

export const multiply = tool({
  description: "两数相乘",
  args: {
    a: tool.schema.number().describe("第一个数"),
    b: tool.schema.number().describe("第二个数"),
  },
  async execute(args) {
    return args.a * args.b
  },
})

以上代码(文件名 math.ts)会创建两个工具:math_addmath_multiply


覆盖内置工具

如果自定义工具与内置工具同名,自定义工具优先。例如,创建一个 bash.ts 会替换内置的 bash 工具:

ts
import { tool } from "@opencode-ai/plugin"

export default tool({
  description: "受限的 bash 包装器",
  args: {
    command: tool.schema.string(),
  },
  async execute(args) {
    return `已拦截: ${args.command}`
  },
})

注意:除非你明确要替换内置工具,否则请使用唯一名称。如果只是想禁用某个内置工具,使用权限配置更合适。


参数定义

tool.schema 底层就是 Zod,你也可以直接使用 Zod:

ts
import { z } from "zod"

export default {
  description: "工具描述",
  args: {
    param: z.string().describe("参数说明"),
  },
  async execute(args, context) {
    return "结果"
  },
}

读取 Context

工具执行时可以访问当前会话的上下文信息:

ts
import { tool } from "@opencode-ai/plugin"

export default tool({
  description: "获取项目信息",
  args: {},
  async execute(args, context) {
    const { agent, sessionID, messageID, directory, worktree } = context
    return `Agent: ${agent}, Session: ${sessionID}, 工作目录: ${directory}`
  },
})
字段说明
directory会话工作目录
worktreeGit worktree 根目录
sessionID当前会话 ID
messageID当前消息 ID
agent当前 Agent 名称

调用其他语言的脚本

工具定义用 TypeScript,但内部逻辑可以是任何语言。比如用 Python 实现加法:

先写 Python 脚本:

python
# .opencode/tools/add.py
import sys
a = int(sys.argv[1])
b = int(sys.argv[2])
print(a + b)

再写工具定义来调用它:

ts
// .opencode/tools/python-add.ts
import { tool } from "@opencode-ai/plugin"
import path from "path"

export default tool({
  description: "用 Python 实现两数相加",
  args: {
    a: tool.schema.number().describe("第一个数"),
    b: tool.schema.number().describe("第二个数"),
  },
  async execute(args, context) {
    const script = path.join(context.worktree, ".opencode/tools/add.py")
    const result = await Bun.$`python3 ${script} ${args.a} ${args.b}`.text()
    return result.trim()
  },
})

这里用了 Bun Shell(Bun.$ 来运行 Python 脚本。


常见问题

Q: 自定义工具和 MCP 服务器有什么区别,我应该用哪个?

A: 自定义工具更轻量——直接放文件,无需启动独立进程。MCP 服务器更适合需要持久状态、跨会话共享或第三方集成的场景。如果只是封装一些项目内的命令或查询逻辑,用自定义工具就够了。

Q: 工具定义文件改了,OpenCode 会自动重新加载吗?

A: 需要重启 OpenCode 才能使修改生效。

Q: 可以在工具里发起网络请求吗?

A: 可以,工具里可以写任意 TypeScript/JavaScript 逻辑,包括调用外部 API、访问数据库等。