OpenCode 自定义工具(Custom Tools)完全指南:为 AI 编程助手打造专属功能扩展

引言

OpenCode 作为一款开源的 AI 编程助手,内置了丰富的工具集——文件读写、终端命令执行、代码搜索等——覆盖了日常开发的大部分需求。然而,在实际项目中,我们往往需要一些特定领域的操作:查询内部数据库、调用团队 API、执行自定义的构建脚本等。针对这类需求,OpenCode 提供了 Custom Tools(自定义工具)机制,允许开发者用少量代码创建 AI 可调用的功能扩展。

本文将深入讲解 OpenCode 自定义工具的创建、配置与实战应用,帮助你为 AI 编程助手赋予更贴合项目需求的能力。

什么是 Custom Tools

Custom Tools 本质上是定义在 TypeScript(或 JavaScript)文件中的函数,AI 模型可以在对话过程中自动调用它们。每个工具包含三个核心部分:

  • description:向 AI 描述工具的用途,让模型理解何时该调用它
  • args:定义工具接收的参数及其类型,支持 Zod 校验
  • execute:工具被调用时执行的逻辑,返回值会回传给 AI

工具定义文件放置在项目的 .opencode/tools/ 目录下,文件名即工具名。OpenCode 启动时会自动加载该目录中的所有工具。

环境准备

编写自定义工具需要安装 @opencode-ai/plugin 包:

# 在项目目录下安装
npm install -D @opencode-ai/plugin

# 或使用 bun
bun add -d @opencode-ai/plugin

该包提供了 tool() 辅助函数和类型定义,让工具编写更简洁、类型安全。

快速上手:第一个自定义工具

让我们从一个简单的示例开始——创建一个天气查询工具。在项目根目录创建 .opencode/tools/weather.ts

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

export default tool({
  description: "查询指定城市的当前天气",
  args: {
    city: tool.schema.string().describe("城市名称,如 北京、上海"),
  },
  async execute(args) {
    const res = await fetch(
      `https://wttr.in/${encodeURIComponent(args.city)}?format=%C+%t`
    );
    const text = await res.text();
    return `${args.city} 天气:${text}`;
  },
});

保存后在 OpenCode 对话中输入"北京今天天气怎么样",AI 会自动识别并调用 weather 工具,获取实时天气数据返回给你。

工具 filename(不含 .ts 后缀)就是工具名称,AI 根据 description 判断何时调用。因此,description 的精确性直接影响工具的可用频率

参数定义与校验

OpenCode 使用 Zod 进行参数校验,支持丰富的类型系统:

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

export default tool({
  description: "查询数据库中的用户信息",
  args: {
    userId: tool.schema.number().describe("用户 ID"),
    includeDeleted: tool.schema
      .boolean()
      .optional()
      .default(false)
      .describe("是否包含已删除用户"),
    fields: tool.schema
      .array(tool.schema.string())
      .optional()
      .describe("需要返回的字段列表"),
  },
  async execute(args) {
    // args.userId 为 number 类型
    // args.includeDeleted 默认为 false
    // args.fields 可选
    return `查询用户 ${args.userId} 的信息`;
  },
});

Zod 支持的类型包括 stringnumberbooleanarrayenum 等,配合 .describe() 方法提供语义描述,AI 就能准确生成参数。如果 AI 传入的参数不符合定义,OpenCode 会自动校验并提示修正。

访问会话上下文

工具的 execute 函数第二个参数 context 提供了当前会话的上下文信息:

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 名称
      sessionID,  // 当前会话 ID
      directory,  // 会话工作目录
      worktree,   // Git 工作根目录
    };
  },
});

context.directorycontext.worktree 在需要操作文件的工具中尤其有用,比如创建文件、读取配置等。

单文件多工具导出

在一个文件中可以导出多个工具,每个导出项都是一个独立的工具,命名格式为 <文件名>_<导出名>

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).toString();
  },
});

export const multiply = tool({
  description: "计算两个数字之积",
  args: {
    a: tool.schema.number().describe("第一个数字"),
    b: tool.schema.number().describe("第二个数字"),
  },
  async execute(args) {
    return (args.a * args.b).toString();
  },
});

这样会生成 math_addmath_multiply 两个工具。这种方式适合将相关功能的工具归类到同一文件。

实战案例:数据库查询工具

在实际项目中,让 AI 直接查询数据库是常见需求。下面是一个安全的只读数据库查询工具:

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

export default tool({
  description: "对项目 MySQL 数据库执行只读查询(SELECT 语句)",
  args: {
    sql: tool.schema
      .string()
      .describe("SQL SELECT 查询语句,仅支持 SELECT"),
  },
  async execute(args) {
    const sql = args.sql.trim().toUpperCase();
    if (!sql.startsWith("SELECT")) {
      return "错误:仅允许执行 SELECT 查询";
    }
    try {
      const result = execSync(
        `mysql -u root -p${process.env.DB_PASS} -e "${args.sql}"`,
        { encoding: "utf-8", timeout: 10000 }
      );
      return result;
    } catch (err) {
      return `查询失败:${err.message}`;
    }
  },
});

AI 就可以根据你的需求生成并执行 SQL 查询,快速获取数据。

实战案例:调用外部 API

团队内部常常有各种微服务 API,可以让 AI 直接调用:

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

export default tool({
  description: "调用项目部署状态 API,获取当前线上版本信息",
  args: {
    environment: tool.schema
      .enum(["staging", "production"])
      .describe("部署环境"),
  },
  async execute(args) {
    const baseUrl = process.env.DEPLOY_API_URL;
    const res = await fetch(`${baseUrl}/status?env=${args.environment}`, {
      headers: { Authorization: `Bearer ${process.env.DEPLOY_API_KEY}` },
    });
    return res.json();
  },
});

注意将 API Key 等敏感信息放在环境变量或 .env 文件中,不要硬编码在工具代码里。

覆盖内置工具

如果 OpenCode 内置的某个工具不符合你的需求,可以用同名文件覆盖它。例如限制 bash 命令的执行范围:

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

export default tool({
  description: "受限的 shell 命令执行",
  args: {
    command: tool.schema.string().describe("要执行的命令"),
  },
  async execute(args) {
    const allowed = ["npm run", "ls", "cat", "pwd", "git status"];
    if (!allowed.some((cmd) => args.command.startsWith(cmd))) {
      return `不允许执行命令:${args.command}`;
    }
    // 执行逻辑...
    return `已执行:${args.command}`;
  },
});

如果你只想禁用而非覆盖某个内置工具,使用权限系统(Permissions)是更推荐的方式。

全局工具与项目工具

工具定义可以放在两个位置:

  • 项目级.opencode/tools/(推荐,随项目一起版本控制)
  • 全局级~/.config/opencode/tools/(对所有项目生效)

全局工具适合放一些通用性的功能,比如天气预报、时间转换、通用代码搜索等。项目级工具则适合放和当前项目强关联的逻辑,比如数据库查询、项目 API 调用、自定义构建命令等。

在任意语言中实现工具逻辑

虽然工具定义必须是 TypeScript 文件,但具体的执行逻辑可以用任何语言实现。工具定义文件负责参数校验和调用,实际工作交给外部脚本:

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

export default tool({
  description: "使用 Python 脚本分析代码复杂度",
  args: {
    filePath: tool.schema.string().describe("要分析的文件路径"),
  },
  async execute(args, context) {
    const script = path.join(context.worktree, ".opencode/tools/analyze.py");
    const result = execSync(
      `python3 ${script} ${args.filePath}`,
      { encoding: "utf-8" }
    );
    return result.trim();
  },
});

这样,你团队中已有的 Python、Go、Rust 等脚本都可以封装成 Custom Tools,让 AI 调用。

调试与最佳实践

开发 Custom Tools 时,有几个实用建议:

description 要精确:AI 依靠描述判断何时调用工具,描述越精准,调用越准确

参数加 .describe():每个参数都添加语义描述,帮助 AI 生成正确的参数值

错误处理要完善:工具执行可能失败,返回清晰的错误信息有助于 AI 自我修正

避免耗时操作:工具执行时间过长会影响交互体验,建议设置超时

日志输出:在工具内添加日志,方便排查问题

安全第一:涉及敏感操作时做好权限校验,不要在工具中硬编码密钥

总结

OpenCode 的 Custom Tools 机制为 AI 编程助手提供了强大的可扩展性。你只需要编写一个简单的 TypeScript 文件,就能让 AI 具备查询数据库、调用 API、运行自定义脚本等能力。结合项目级和全局级工具的划分,以及多语言脚本的支持,Custom Tools 可以灵活适应各种开发场景。

现在就为你的项目编写第一个自定义工具吧——从简单的信息查询开始,逐步扩展,让 OpenCode 成为真正懂你项目的 AI 编程伙伴。