AI 程式設計教學中文版
官方教學中文版工具與 MCP

建立自定義工具

在 OpenCode 裡給模型提供可控、可複用的專案動作。

📖 本篇術語速查表
英文 / 縮寫中文一句話解釋
Custom tools自定義工具給 agent 加專用能力。
介面interface工具的輸入輸出約定。
安全safe工具不暴露高危操作。

不想讀完?把下面這段提示詞丟給 AI 幫你跑完——幫你給 OpenCode 設計安全、職責清晰的自定義工具。

你是 OpenCode 自定義工具顧問。

【角色】
OpenCode 自定義工具顧問,按最小夠用、安全優先的原則給可落地方案,每條結論都落到能照做的步驟或示例,不停留在空泛建議。

【輸入】
- 工具要解決的能力缺口:___
- 輸入輸出:___
- 涉及的資源和許可權:___
- 使用場景:___
- 經驗水平:___

【工作流程】
1. 判斷是否真需要自定義
2. 設計工具介面
3. 限定能力和許可權
4. 說明安全處理
5. 給驗證

【輸出規範】
▌一、是否需要
▌二、介面設計
▌三、能力 / 許可權
▌四、安全 + 驗證

【硬約束】
- 現成夠用就不自建
- 只暴露必要能力
- 憑據安全處理
- 不要替我臆測情況或編造不存在的功能,資訊不全先問清
- 不確定的設定或介面一律以官方文件為準,禁止照搬過時寫法
- 給的每條結論都要落到具體可照做的步驟或示例,不停留在「建議」「考慮一下」這類沒法直接執行的空泛表述

Custom tools 是你寫給 LLM 呼叫的函式。它們和 OpenCode 內建的 readwritebashgrep 等工具並列存在,適合把專案專有、重複出現、輸入輸出穩定的動作封裝成可控入口。

這一篇用 12 分鐘換什麼:你會知道什麼時候值得寫 custom tool、工具檔案放哪裡、工具名怎麼生成、多個工具怎麼匯出、引數 schema 怎麼寫、context 能拿到什麼,以及哪些安全邊界不能交給模型猜。

先判斷是否真的需要

不要把 custom tool 當成“更高階的 bash”。先按這條路徑判斷:

flowchart LR
    Need["一個動作反覆出現"] --> Builtin{"內建工具能解決?"}
    Builtin -->|能| UseBuiltin["用 read / grep / bash / edit"]
    Builtin -->|不能| External{"是外部通用系統?"}
    External -->|是| MCP["優先 MCP"]
    External -->|不是| Stable{"輸入輸出穩定?"}
    Stable -->|否| Prompt["繼續用提示詞或指令碼"]
    Stable -->|是| Custom["封裝 custom tool"]

    style UseBuiltin fill:#dcfce7,stroke:#22c55e
    style MCP fill:#dbeafe,stroke:#3b82f6
    style Custom fill:#fef3c7,stroke:#f59e0b,stroke-width:2px

適合封裝的動作:

  • 查詢內部服務狀態。
  • 讀取專案專有設定格式。
  • 執行固定診斷指令碼並返回摘要。
  • 呼叫公司內部 API 的只讀介面。
  • 把一段穩定 shell / Python / Node 指令碼包裝成結構化工具。

不適合封裝的動作:一次性命令、危險刪除、生產釋出、資料庫寫入、萬能 shell wrapper、會返回大量記錄的指令碼。

工具一旦進入 OpenCode,就會成為模型可能呼叫的能力。能用 permission 控制內建工具,就不要靠覆蓋內建工具名來“限制”它。

1. 工具放在哪裡

工具定義必須是 TypeScript 或 JavaScript 檔案,但工具定義內部可以呼叫任何語言寫的指令碼。

位置適合什麼
.opencode/tools/專案級工具,只服務目前儲存庫,推薦從這裡開始
~/.config/opencode/tools/全域工具,影響所有專案,確認穩定後再放這裡

大多數專案先用 .opencode/tools/。這樣工具隨儲存庫一起演進,不會把一個專案的假設帶到其他專案。

2. 最小工具結構

最簡單的方式是使用 tool() helper,它提供型別安全和引數校驗。

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

檔名會變成工具名。上面這個檔案會建立 project-info 工具。新手先從只讀工具開始,確認呼叫鏈、輸出格式和許可權邊界都穩定,再考慮寫入或外部請求。

3. 一個檔案匯出多個工具

官方文件說明,一個檔案可以匯出多個工具。每個 export 會變成獨立工具,名字是 <filename>_<exportname>

例如 .opencode/tools/math.ts 裡匯出 addmultiply,OpenCode 會註冊成 math_addmath_multiply

如果工具共享同一組內部 helper,放在一個檔案裡更容易維護;如果職責不同,拆成多個檔案更清楚。不要為了少建檔案把無關工具塞到一起。

4. 引數 schema 要寫給模型看

tool.schema 就是 Zod。引數越少、描述越具體,模型越不容易誤用。

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

也可以直接匯入 Zod,返回普通物件:

import { z } from "zod";

args: {
  param: z.string().describe("Parameter description"),
}

好的引數描述會寫清邊界,例如“儲存庫內相對路徑”“只讀 SQL”“不包含金鑰”“只允許 staging 環境”。不要只寫 querypathname 這種模型無法判斷風險的描述。

5. context 能拿到什麼

工具會收到目前 session(會話)的上下文。官方示例裡包括 agentsessionIDmessageIDdirectoryworktree

  • context.directory 是 session 工作目錄(使用者啟動 OpenCode 時所在的那個資料夾)。
  • context.worktree 是 Git worktree(工作樹,簡單理解為"這個儲存庫目前 checkout 的檔案樹根目錄"——一個 Git 儲存庫可以同時有多個 worktree,每個對應一個分支)的根目錄。

路徑拼接優先基於 context.worktreecontext.directory,不要假設使用者總在專案根目錄啟動 OpenCode。

6. 呼叫 Python 或 Shell 指令碼

工具定義必須是 TypeScript / JavaScript,但真實邏輯可以放到 Python、Shell 或其他語言指令碼里。

核心模式是:工具定義負責 schema、路徑和輸出摘要,指令碼負責實際業務邏輯。例如用 context.worktree 找到專案內指令碼,再透過 Bun shell 呼叫:

const script = path.join(context.worktree, ".opencode/tools/add.py");
const result = await Bun.$`python3 ${script} ${args.a} ${args.b}`.text();
return result.trim();

這類結構適合複用已有指令碼。指令碼本身仍要能在終端獨立執行,不要只在 OpenCode 對話裡才“看起來能跑”。

7. 工具名衝突會覆蓋內建工具

Custom tools 按工具名註冊。如果自定義工具和內建工具同名,自定義工具會優先。

例如 .opencode/tools/bash.ts 會替換內建 bash。這不是普通命名問題,而是會改變模型可呼叫的基礎能力。

除非你明確要替換內建工具,否則不要使用 bashreadwriteedit 這類名字。如果只是想停用或收緊內建工具,應該用 permissions。

8. 安全邊界

設計 custom tool 時,預設按“會被模型頻繁呼叫,也可能被錯誤引數呼叫”處理:

  • 預設只讀。
  • 引數必須校驗,不把模型輸入直接拼 shell。
  • 輸出要短,只返回下一步判斷需要的資訊。
  • 金鑰從環境變數或憑據系統讀取,不寫進工具檔案。
  • 寫入、刪除、釋出、資料庫操作必須有 dry-run、確認和許可權邊界。
  • 錯誤返回清楚原因,不把完整堆疊和敏感環境打進上下文。

9. 驗收清單

一個可交付的 custom tool 至少滿足:

  • 模型能從 description 判斷什麼時候呼叫它。
  • 引數 schema 足夠具體,錯誤引數會被拒絕。
  • 工具名不和內建工具衝突,除非有明確替換意圖。
  • 同一輸入多次執行結果穩定。
  • 輸出短、可讀、無金鑰。
  • 專案級工具優先,確認穩定後再考慮全域化。

接下來去哪

官方資料

本頁目錄