Appearance
OpenCode 的自定义工具功能让你能够编写 TypeScript 或 JavaScript 函数,LLM 在对话中可以像使用内置工具一样调用它们。工具定义放在项目的 .opencode/tools/ 目录或全局 ~/.config/opencode/tools/ 目录中,文件名即为工具名。
自定义工具是你编写的函数,LLM 可以在对话中主动调用它们。它们与 OpenCode 的内置工具(如 read、write、bash)并列工作。
和 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_add 和 math_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 | 会话工作目录 |
worktree | Git 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、访问数据库等。