7、OpenCode自定义工具完整指南
OpenCode 自定义工具完整指南
第一步:什么是自定义工具
自定义工具是用户创建的函数,AI 可以在对话过程中调用这些工具。
核心特点:
- 与内置工具(read、write、bash)配合使用
- 用 TypeScript/JavaScript 定义
- 可调用任何语言的脚本
- 灵活的参数和返回值处理
与 MCP 的对比:
| 特性 | 自定义工具 | MCP 服务器 |
|------|-----------|-----------|
| 安全性 | 本地执行,更安全 | 需要网络通信 |
| 复杂性 | 简单直接 | 配置较复杂 |
| 控制粒度 | 完全控制代码 | 依赖第三方服务 |
| 上下文占用 | 较小 | 可能很大 |
第二步:工具存放位置
项目本地:
.opencode/tools/
全局配置:
~/.config/opencode/tools/
第三步:创建单个工具
使用 tool() helper 创建工具:
import { tool } from "@opencode-ai/plugin"
export default tool({
description: "Query the database",
args: {
query: tool.schema.string().describe("SQL query to execute")
},
async execute(args) {
// 你的数据库逻辑
return `Executed query: ${args.query}`
}
})
关键点:
- 文件名 = 工具名
- description 描述工具用途
- args 定义参数(使用 Zod schema)
第四步:创建多工具文件
单个 TypeScript 文件可导出多个工具:
import { tool } from "@opencode-ai/plugin"
export const add = tool({
description: "Add two numbers",
args: {
a: tool.schema.number().describe("First number"),
b: tool.schema.number().describe("Second number")
},
async execute(args) {
return args.a + args.b
}
})
export const multiply = tool({
description: "Multiply two numbers",
args: {
a: tool.schema.number().describe("First number"),
b: tool.schema.number().describe("Second number")
},
async execute(args) {
return args.a * args.b
}
})
工具命名规则:
- 导出名 = 工具名的一部分
- 完整工具名:
<filename>_<exportname> - 示例:
math_add、math_multiply
第五步:参数类型定义
使用 tool.schema(基于 Zod)定义参数:
args: {
name: tool.schema.string().describe("User name"),
age: tool.schema.number().describe("User age"),
active: tool.schema.boolean().describe("Is active"),
tags: tool.schema.array(tool.schema.string()).describe("Tags"),
config: tool.schema.object({
key: tool.schema.string()
}).describe("Config object")
}
支持的类型:
- string、number、boolean
- array、object
- enum、optional
第六步:集成任意语言脚本
工具定义可以用 TypeScript,实际执行任意语言脚本:
1. 创建 Python 脚本 .opencode/tools/add.py:
import sys
a = int(sys.argv[1])
b = int(sys.argv[2])
print(a + b)
2. 创建工具定义 .opencode/tools/python-add.ts:
import { tool } from "@opencode-ai/plugin"
import path from "path"
export default tool({
description: "Add two numbers using Python",
args: {
a: tool.schema.number().describe("First number"),
b: tool.schema.number().describe("Second number")
},
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.$命令执行child_process模块- 直接调用系统命令
第七步:访问会话上下文
工具可以接收会话上下文信息:
export default tool({
description: "Get project information",
args: {},
async execute(args, context) {
const { agent, sessionID, messageID, directory, worktree } = context
return `Agent: ${agent}, Session: ${sessionID}`
}
})
上下文属性:
| 属性 | 描述 |
|------|------|
| agent | 当前使用的代理名称 |
| sessionID | 会话唯一标识 |
| messageID | 消息唯一标识 |
| directory | 会话工作目录 |
| worktree | Git worktree 根目录 |
第八步:使用工具
在对话中直接调用:
Use the database tool to query users
Calculate 5 + 3 using python-add
AI 会自动识别并调用相应工具。
第九步:工具管理配置
通过权限控制工具行为:
{
"permission": {
"database": "allow",
"python-add": "ask",
"bash": "deny"
}
}
权限选项:
allow:允许执行deny:拒绝执行ask:每次询问
通配符配置:
{
"permission": {
"math_*": "allow"
}
}
第十步:实用示例
示例1:文件搜索工具
import { tool } from "@opencode-ai/plugin"
import { glob } from "glob"
export default tool({
description: "Search for files matching pattern",
args: {
pattern: tool.schema.string().describe("Glob pattern")
},
async execute(args) {
const files = await glob(args.pattern)
return files.join("\n")
}
})
示例2:API 调用工具
import { tool } from "@opencode-ai/plugin"
export default tool({
description: "Call external API",
args: {
url: tool.schema.string().describe("API URL"),
method: tool.schema.string().describe("HTTP method")
},
async execute(args) {
const response = await fetch(args.url, { method: args.method })
return await response.json()
}
})
示例3:代码分析工具
import { tool } from "@opencode-ai/plugin"
import fs from "fs"
export default tool({
description: "Analyze code complexity",
args: {
file: tool.schema.string().describe("File path")
},
async execute(args) {
const content = fs.readFileSync(args.file, "utf-8")
const lines = content.split("\n").length
const functions = content.match(/function\s+\w+/g) || []
return `Lines: ${lines}, Functions: ${functions.length}`
}
})
常见问题
问题1:工具不被识别
- 确认文件在正确目录
- 检查文件名和导出名
- 重启 OpenCode
问题2:参数验证失败
- 检查 Zod schema 定义
- 确认参数类型匹配
- 查看错误信息调整
问题3:脚本执行失败
- 确认脚本有执行权限
- 检查路径是否正确
- 验证依赖已安装
问题4:上下文访问错误
- 确认使用了正确的上下文属性
- 检查异步操作是否正确 await
- 查看 OpenCode 日志
最佳实践
- 工具命名:使用清晰、描述性的名称
- 参数设计:必填参数放在前面
- 错误处理:返回有意义的错误信息
- 文档完善:为每个工具写清晰的 description
- 安全性:避免执行未验证的用户输入
- 性能:长时间操作考虑异步处理
快速参考
# 工具目录结构
.opencode/tools/
├── database.ts # 工具:database
├── math.ts # 工具:math_add, math_multiply
├── python-add.ts # 工具:python-add
└── api.ts # 工具:api
# 调用示例
Use database tool to query users
Add 5 and 3 using python-add
与其他扩展方式的对比
| 方式 | 适用场景 | 复杂度 | 安全性 |
|------|---------|--------|--------|
| 自定义工具 | 本地操作、简单集成 | 低 | 高 |
| MCP 服务器 | 外部服务集成 | 中 | 中 |
| 插件 | 深度定制 | 高 | 高 |
| 命令 | 简单任务 | 低 | 高 |
推荐选择:
- 简单任务 → 自定义工具
- 外部 API → MCP 服务器
- 深度定制 → 插件