# 自建 AI 工具

Agenvoy 支援三種在內建集合之外新增工具的方式：從 capability gap 自動生成、script tool 與 MCP tool。使用者自訂的 API tool 已移除於 v1.3.2（見下文）。由 extension 安裝的工具落在 `~/.config/agenvoy/tools/.extension/`，並以 `ext_` 前綴註冊；v1.3.2 起只來自 script。

## 自動生成（Capability Gap）

當 user request 需要即時外部資料（天氣、匯率、股票、geocoding、翻譯等）且無現有 tool 涵蓋時，agent 先查找免金鑰的公開 API 並直接呼叫，只有在目錄中沒有合適的 API 時才當場建立 script tool，隨即執行以回答。無需寫程式。自 v1.3.1 起，設定 decision model 時每輪會先問它：這個需求是否需要現有工具名稱都沒涵蓋的能力 —— 對週期性即時資料而言，`http_request`、`run_command`、`fetch_page`、`search_web` 這類通用工具不算涵蓋；以 0.7 以上信心判為缺少時，turn context 會帶一行 `Tool gap`，要求 agent 在任何其他工具前先呼叫 `reasoning_guide(topics=["tool_generate"])`。`reasoning_guide` 或 `edit_tool` 被排除時不做此檢查。

`reasoning_guide(topics=[tool_generate])` 承載建置契約（v1.0.26 起此工具改收 `topics` 陣列，一次呼叫可載入多份 guide；之前為單一 `topic`）；同一份文字也透過 MCP server 以 `tool_generate_guide` 對外部 agent 曝露。序列如下：

| 步驟 | 動作 |
|---|---|
| 1. 找 API | `public_api_list()` 取得分類名稱 → 挑選最多 3 個相關分類 → 逐一呼叫 `public_api_list(category)`；列出的 API 皆免金鑰，偏好 HTTPS |
| 2. 有合適的 API | 以 `fetch_page` 讀取候選的文件 URL，確認 base URL、端點、參數與回應格式 → `http_request`（依答案需要呼叫多次）→ 回答。不建工具；下次同樣走這套流程 |
| 3. 建 script tool | 只在公開目錄之外：使用者提供、需要金鑰的 API（沙箱內的 `get_key()` 讓金鑰不進入對話，放在 `http_request` headers 則會進入），或 Python（stdlib、公開資料檔、套件）能產出答案。`edit_tool(mode=write)` 搭配 `tag=json` + `tag=script` |
| 4. 驗證 | `test_tool` 於沙箱執行 `script.py`；失敗以 `edit_tool(mode=patch)` 修正後重測（最多重試 3 次） |
| 5. 回答 | 呼叫新工具，並以其輸出回答；既無 API 也無 Python 途徑時，退回 `search_web` / `fetch_page`，並說明資料來源 |

v1.3.2 之前，步驟 1 呼叫 API tool `api_public_api_list(type=category)`，步驟 2 可為單一 REST 端點選擇宣告式 API tool（`tag=api`）；兩者皆已移除於 v1.3.2。

建立後，工具持久化於 `~/.config/agenvoy/tools/script/<name>/`，在所有未來 session 及所有連線的 MCP agent 中皆可用。憑證絕不硬編碼：命名慣例為 `{BRAND}_API_KEY`，以 `store_secret` 取得。本機金鑰查詢端點 `GET /v1/key?key=<KEY_NAME>` 已於 v1.0.23 移除：script tool 改為自行讀取（guide 提供的 `get_key()` helper），順序為 OS keychain（service `agenvoy`）→ Linux 上的 `~/.config/agenvoy/.secrets` → 同名環境變數。

關鍵限制：

- 自 v1.3.2 起，免金鑰的公開 API 直接以 `http_request` 回答，不存成工具；原本「每個資料請求都必須以寫入磁碟的可重用工具回答」的規則已移除於 v1.3.2。只有走 script tool 路徑（步驟 3）時才寫入工具。一次性的驗證或除錯（探測端點、參數或旗標）透過 `run_command` inline 執行，不存成工具
- `fetch_page` 用於讀取 API 文件；只有在既無 API 也無 Python 途徑的退路中，才與 `search_web` 一起抓取回答資料，並註明來源
- 名稱採 `snake_case` 動詞 + 名詞（`fetch_weather`、`calculate_rsi`）且不帶前綴——runtime 會自動加上 `script_`

## Script tool（`script_*`）

在 `~/.config/agenvoy/tools/script/<name>/`（或專案本地的 `<cwd>/.config/agenvoy/tools/script/<name>/`）放入 `tool.json` descriptor 與 `script.py`（Python）或 `script.js`（JavaScript）。Agenvoy 自動註冊為 `script_<name>`。原本的 `extensions/scripts/<name>/` + `run.py` 佈局不會被掃描；由 extension 安裝在 `tools/.extension/script/` 的 script 以 `ext_<name>` 註冊。

```
~/.config/agenvoy/tools/script/my_tool/
├── tool.json     # name, description, parameter schema
└── script.py     # actual script (or script.js)
```

## API tool（`api_*`）

使用者自訂的 API tool 已移除於 v1.3.2；請改以 `http_request` 呼叫端點，或改寫成 script tool。`~/.config/agenvoy/tools/api/`、專案本地的 `<cwd>/.config/agenvoy/tools/api/` 與 `tools/.extension/api/` 下的 JSON descriptor 不再載入：殘留的 `.json` 檔會逐一略過，並在 log 留下 `[Invalid] api tool` 警告。內建的 `extensions/apis/` 也已不存在；其中的 `api_public_api_list` 改為內建工具 `public_api_list` —— 不帶 `category` 時回傳分類名稱，帶 `category`（不分大小寫）時回傳該分類下每個免金鑰 API 的名稱、說明、HTTPS 旗標與文件 URL。`http_request` 的 `GET` 不需確認即執行；其他 method 一律經過確認 gate。

## MCP tool（`mcp__*`）

由 MCP server 曝露的 tool 自動註冊為 `mcp__<server>__<tool>`。v1.3.7 起每輪的 `Tools` 清單依 server 分組寫成 `mcp__<server>__{a, b}`，`find_tools` 與 `run_tool` 仍使用完整名稱；server 提供的 instructions 則渲染在主 system prompt 內，不再作為第二則 system message。MCP tool output 每次呼叫上限 **1 MiB**，以將 tool result 保持在 provider 限制內。
