📚AI 编程官方教程中文版
官方教程中文版工具与 MCP

创建自定义工具

在 OpenCode 里给模型提供可控、可复用的项目动作。

自定义工具不是让新手一上来写插件系统,而是把“模型总是要靠猜的动作”变成一个清晰、可验证的函数。

官方教程说明了工具文件、tool() 辅助函数、参数 schema、上下文对象和多语言脚本调用。这里重点讲:什么时候你真的需要自定义工具,以及怎么把它做成新手也能维护的形态。

先理解它解决什么问题

模型擅长读代码、写代码、解释问题,但不擅长稳定执行你项目里的专有动作。例如:

  • 查询内部数据库。
  • 调用公司自己的 API。
  • 读取一个只有本项目懂的配置格式。
  • 跑一段固定脚本并把结果整理给模型。

这些动作如果每次都让模型临时写 bash 或临时猜接口,结果会不稳定。自定义工具的作用就是把这类动作封装成一个“模型可以调用,但边界由你控制”的入口。

先别急着写工具

新手可以按这个顺序判断:

  1. 内置工具能解决,就用内置工具:readgrepglobbash 已经覆盖大量日常场景。
  2. 外部服务已有 MCP,就优先接 MCP:数据库、GitHub、浏览器、搜索这类通用能力通常不值得自己写。
  3. 只有当动作是你项目专有、且会重复使用时,再写自定义工具。

不要把一次性脚本包装成工具。工具一旦进入 OpenCode,就会成为模型可见能力,维护成本和安全边界都要考虑。

工具放哪里

  • 项目级:.opencode/tools/,适合只服务当前仓库的工具。
  • 全局级:~/.config/opencode/tools/,适合你所有项目都会用的个人工具。

大多数新手从项目级开始更安全。项目级工具随仓库一起演进,不会影响其他项目。

最小工具长什么样

工具文件是 TypeScript 或 JavaScript。文件名就是工具名。

例如 .opencode/tools/project-info.ts

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

export default tool({
  description: "Return current project directory information",
  args: {},
  async execute(args, context) {
    return `directory=${context.directory}\nworktree=${context.worktree}`;
  },
});

这个工具不做危险动作,只把当前工作目录和 worktree 返回给模型。新手可以先用这种只读工具验证机制,再逐步接入真实脚本。

参数要少而明确

工具参数使用 tool.schema 定义。参数越少,模型越不容易误用。

args: {
  query: tool.schema.string().describe("Read-only SQL query to execute"),
}

描述要写给模型看,不是写给人类炫技。好的描述会说明输入边界,例如“只读 SQL”“仓库内相对路径”“不包含密钥”。

调用其他语言脚本

官方支持用 TypeScript / JavaScript 定义工具,再在工具里调用 Python、Shell 或其他语言脚本。这个模式适合已有稳定脚本的项目。

建议结构:

  • 工具定义负责接收参数、校验参数、返回文本。
  • Python / Shell 脚本负责实际业务逻辑。
  • 不要在工具定义里硬编码密钥。

如果脚本会修改文件、访问网络或查询数据库,先给它加权限边界,再暴露给模型。

安全边界

自定义工具是模型可调用的能力,默认要按“可能被频繁调用、可能被错误参数调用”来设计:

  • 工具名不要和内置工具冲突,除非你明确要替换内置行为。
  • 默认只读,确认稳定后再允许写入或外部请求。
  • 参数必须校验,不要把模型输入直接拼进 shell 命令。
  • 返回结果要短,避免把无关日志塞进上下文。
  • 需要密钥时,从环境变量或安全凭据读取,不写进工具文件。

如果只是想限制 bashedit 这类内置能力,不要写一个同名工具去覆盖,直接用 permission

怎么判断做对了

一个可用的自定义工具应该满足:

  • 模型能从 description 判断什么时候调用它。
  • 参数少且含义明确。
  • 错误时返回清楚的原因,而不是一大段堆栈。
  • 输出能直接帮助下一步决策。
  • 多次调用结果稳定,不依赖当前对话里的隐含假设。

如果模型经常不该调用却调用,通常是 description 太泛。如果模型调用时经常传错参数,通常是 schema 描述不够具体。

新手常见坑

  • 把工具写成“万能执行器”。这会让模型失去边界,风险比直接用 bash 更高。
  • 一次返回几千行日志。工具输出会占上下文,应该只返回判断所需信息。
  • 忘记项目级和全局级的区别。全局工具会影响所有项目,先在项目级验证。
  • 直接覆盖内置工具名。除非你非常清楚后果,否则不要用 bashreadwrite 这类名字。

© Anomaly

最近更新: 2026年5月1日

On this page

官方教程说明了工具文件、tool() 辅助函数、参数 schema、上下文对象和多语言脚本调用。这里重点讲:什么时候你真的需要自定义工具,以及怎么把它做成新手也能维护的形态。先理解它解决什么问题这些动作如果每次都让模型临时写 bash 或临时猜接口,结果会不稳定。自定义工具的作用就是把这类动作封装成一个“模型可以调用,但边界由你控制”的入口。先别急着写工具不要把一次性脚本包装成工具。工具一旦进入 OpenCode,就会成为模型可见能力,维护成本和安全边界都要考虑。工具放哪里大多数新手从项目级开始更安全。项目级工具随仓库一起演进,不会影响其他项目。最小工具长什么样这个工具不做危险动作,只把当前工作目录和 worktree 返回给模型。新手可以先用这种只读工具验证机制,再逐步接入真实脚本。参数要少而明确描述要写给模型看,不是写给人类炫技。好的描述会说明输入边界,例如“只读 SQL”“仓库内相对路径”“不包含密钥”。调用其他语言脚本如果脚本会修改文件、访问网络或查询数据库,先给它加权限边界,再暴露给模型。安全边界如果只是想限制 bashedit 这类内置能力,不要写一个同名工具去覆盖,直接用 permission怎么判断做对了如果模型经常不该调用却调用,通常是 description 太泛。如果模型调用时经常传错参数,通常是 schema 描述不够具体。新手常见坑