# ツール

1つのツールモジュールがすべてのエージェントハーネスで実行されます。beeuni.yaml には Beeuni のツールとプロジェクト独自のツールが記載され、各エージェントの tools リストで呼び出せるツールが決まります。

Canonical page: https://beeuni.ai/docs/ja/project/tools

ツールとは、モデルがターン中に呼び出す関数です。
[セッション](/docs/ja/work/sessions)には、どちらの
[ハーネス](/docs/ja/work/harnesses)（標準ハーネス と 実験的ハーネス）でも、次の3種類があります。

| 種類 | 名前 | 実装元 |
| --- | --- | --- |
| ハーネスツール | `bash`, `read`, `write`, `edit`, `glob`, `grep`, `task`, `question`, … | ハーネス。 |
| Beeuni ツール | `web_search`, `image_search`, `scrape_webpage`, `memory`, `show` | Beeuni。`beeuni.yaml`には、それぞれが`<name>: beeuni:<name>`として記載されています。`tools:`キーのないプロジェクトでは、5つすべてが使えます。 |
| プロジェクトツール | 宣言した任意の名前 | プロジェクト。`beeuni.yaml`の`tools:`の下に宣言する1つのモジュールです。 |

Beeuni ツールとプロジェクトツールはハーネスに依存しません。サンドボックスデーモンはツールごとに1つのモジュールを読み込み、どちらのハーネスでも同じ名前、引数、出力で実行します。実験的ハーネス はデーモンのプロセス内でモジュールを呼び出します。標準ハーネス はデーモンが書き出すプラグイン経由で呼び出し、プラグインは各呼び出しをデーモンに転送します。モジュールがハーネスのパッケージをインポートすることはありません。

<a id="beeuni-tools-in-beeuniyaml"></a>

## beeuni.yaml の Beeuni ツール

新しいプロジェクトでは、`tools:`の下に5つの Beeuni ツールを記載します。

```yaml
tools:
  web_search: beeuni:web_search      # maintained by Beeuni, updated automatically
  image_search: beeuni:image_search
  scrape_webpage: beeuni:scrape_webpage
  memory: beeuni:memory
  show: beeuni:show
  # lookup_order: tools/lookup_order.ts   # your own tool: a module in your repo
```

`beeuni:<name>`は、Beeuni が保守するツールを意味します。修正すると、そのツールを記載しているすべてのプロジェクトに反映され、プロジェクト側の変更は不要です。

<a id="which-beeuni-tools-a-session-gets"></a>

### セッションで使える Beeuni ツール

| `beeuni.yaml` | セッションで使えるツール |
| --- | --- |
| ルートファイルにも[インポート](/docs/ja/project/manifest#imports)したファイルにも`tools:`キーがない | Beeuni ツール5つすべて。リストが存在する前に作成されたプロジェクトでも、5つすべてが使えます。 |
| `tools:`キーがある（空の場合も含む。`tools: {}`、またはすべての行を削除した`tools:`など） | リストにある Beeuni ツールのみ。`beeuni:<name>`として指定するか、ツールを置き換えるモジュールとして指定します。 |

どちらの場合も、プロジェクト独自のツールは読み込まれます。

<a id="see-what-a-session-gets"></a>

### セッションで使えるツールを確認する

`beeuni tools ls`は`beeuni.yaml`とそのインポート先を読み込み、セッションで使える各ツールと、プロジェクトが除外した Beeuni ツールを表示します。

```text
$ beeuni tools ls
NAME            KIND                 SOURCE
web_search      beeuni (overridden)  tools/web_search.ts
scrape_webpage  beeuni               beeuni:scrape_webpage
memory          beeuni               beeuni:memory
show            beeuni               beeuni:show
lookup_order    project              tools/lookup_order.ts

Removed Beeuni tools: image_search
Add `<name>: beeuni:<name>` under `tools:` to get one back.
```

| 種類 | 意味 |
| --- | --- |
| `beeuni` | `beeuni:<name>`として記載された Beeuni ツール（または`tools:`キーがない場合はすべて）。 |
| `beeuni (overridden)` | モジュールパスが指定された Beeuni ツール名。プロジェクトのモジュールが代わりに実行されます。 |
| `project` | プロジェクト独自のツール。 |

`beeuni tools ls --json`は`{ manifest, tools_key, tools: [{ name, kind, source }], removed }`を表示します。

<a id="remove-a-beeuni-tool"></a>

### Beeuni ツールを削除する

`tools:`の下にある該当行を削除します。変更リクエストがマージされた後に開始するすべてのセッションで、両方のハーネスを使うプロジェクト内の全エージェントが、そのツールを使えなくなります。特定のエージェントだけからツールを取り除くには、代わりに[`agents.<name>.tools`](#who-may-use-which-tool)を使います。

ツールを削除したプロジェクトのセッションでは、次のようになります。

- **標準ハーネス** は、そのツールをモデルに提示しません。
- **実験的ハーネス** は、そのツールを提示しません。呼び出すと`Tool show not found`で失敗します（`show`の場合）。
- **デーモン**は、`POST /beeuni/tools/<name>`に`404`と`no tool named <name> is loaded`で応答します。
- **そのツールを記載したスキルは引き続き読み込まれます。** たとえば、`beeuni-memory`には`memory`が記載されています。モデルはスキルを読みますが該当するツールがないため、別のツールを使います。`memory`がなければ、ファイルツールで`memory/`以下のファイルを読み取り、編集します。

<a id="change-a-beeuni-tool-eject"></a>

### Beeuni ツールを変更する：コピーして切り離す

`beeuni tools eject <name>`を使うと、Beeuni ツールの独自コピーをプロジェクトに作成できます。

```text
$ beeuni tools eject web_search
  ✓  Ejected web_search
  wrote   tools/web_search.ts (the Beeuni source, now the project's to change)
  set     beeuni.yaml: tools.web_search: tools/web_search.ts

Next:
  1. Edit tools/web_search.ts. Keep the default export: { description, parameters, execute(args, context) }.
  2. beeuni validate
  3. git add tools/web_search.ts beeuni.yaml && git commit -m "Own the web_search tool"
  4. Open a change request: git push origin HEAD && beeuni cr open --title "Own the web_search tool"
Sessions load the copy after the change request merges.
```

- サンドボックスが実行するものと同じバイト列の Beeuni ソースを`tools/<name>.ts`に書き出します。このファイルは`node:*`モジュールのみをインポートし、`context.env`から`BEEUNI_API_URL`、`BEEUNI_TOKEN`、プロバイダーキーを読み込むため、そのまま実行できます。
- エントリを宣言しているファイル（ルートファイルまたはインポートしたファイル）の`beeuni.yaml`で`<name>: tools/<name>.ts`を設定します。それ以外の行やコメントはそのまま残ります。変更した行の末尾には`# ejected from beeuni:<name>`が付きます。
- `tools:`キーがない場合は作成し、残り4つの Beeuni ツールを`beeuni:<name>`として記載します。そのため、切り離しによってツールが削除されることはありません。
- `tools/<name>.ts`が存在する場合や、ツールがすでに別のモジュールを指している場合は実行を拒否します。`--force`は上書きします。
- Beeuni ツールではない名前は拒否します。

コピーの所有者はあなたです。Beeuni による更新は停止します。保守対象のツールに戻すには、該当行を`<name>: beeuni:<name>`に戻し、ファイルを削除します。

<a id="override-without-eject"></a>

### コピーせずに置き換える

モジュールパスを指定した Beeuni ツール名は、すべてのハーネスで Beeuni ツールを置き換えます。

```yaml
tools:
  web_search: integrations/search/web_search.ts
```

<a id="the-web-ui-views"></a>

### Web UI のビュー

Web UI には各 Beeuni ツールに対応するビューがあり、ツール名で選択されます。`show`と`memory`のビューは、呼び出しの引数（`type`、`path`、`url`、`content`、`items`、`command`、`path`）を読み取ります。`web_search`、`image_search`、`scrape_webpage`のビューは出力 JSON を読み取ります。名前を維持したままコピーを変更する場合は、引数と出力の形式も維持してください。そうしないと、UI に生の出力が表示されます。

<a id="validation"></a>

### 検証

`beeuni validate`はリストを検証します。

- `beeuni:<name>`内の`<name>`は Beeuni ツールで、そのキーも同じ名前でなければなりません。`web_search: beeuni:image_search`はエラーです。
  ``must be `beeuni:web_search` (the Beeuni tool) or a repo-relative path to a .ts or .js module (e.g. `tools/web_search.ts`) that replaces it.``
- Beeuni ツールを1つも記載していない`tools:`キーは警告です。
  ``Sessions of this project get no Beeuni tool (web_search, image_search, scrape_webpage, memory, show). Add `<name>: beeuni:<name>` to keep one.``
  プロジェクト独自の最初のツールを追加した際に、誤って5つのツールをすべて削除してしまうケースを検出します。
- `tools`に、プロジェクトで読み込まれない Beeuni ツールが指定されているエージェントは警告されます。
  ``"show" is a Beeuni tool this project does not load: add `show: beeuni:show` under the top-level `tools`, or remove it here.``

[マニフェストのリファレンス](/docs/ja/project/manifest#tools)に、すべてのルールが記載されています。

<a id="write-a-project-tool"></a>

## プロジェクトツールを作成する

```ts
// tools/lookup_order.ts
export default {
  description: 'Look up an order by id. Returns its status and carrier.',
  parameters: {
    type: 'object',
    properties: {
      id: { type: 'string', description: 'The order id, e.g. "A-1042"' },
      include_items: { type: 'boolean', description: 'Also list the items' },
    },
    required: ['id'],
  },
  async execute(args, context) {
    const response = await fetch(`https://orders.example.com/v1/orders/${args.id}`, {
      headers: { authorization: `Bearer ${context.env.ORDERS_API_KEY}` },
      signal: context.signal,
    })
    if (!response.ok) throw new Error(`orders API answered ${response.status}`)
    return await response.json()
  },
}
```

```yaml
# beeuni.yaml
tools:
  web_search: beeuni:web_search
  # … the other Beeuni tools you keep
  lookup_order: tools/lookup_order.ts
```

デフォルトエクスポートはプレーンオブジェクトです。

| フィールド | ルール |
| --- | --- |
| `description` | 空でない文字列。モデルはこれを読んで、ツールを呼び出すタイミングを判断します。 |
| `parameters` | 引数の[JSON Schema](https://json-schema.org)。`type: 'object'`。モデルにはそのまま渡されるため、必須引数は`required`に列挙してください。Beeuni は引数をこのスキーマに照らして検証しません。 |
| `execute(args, context)` | 文字列または任意の JSON 値を返します（インデント付き JSON として送信）。`async`の場合もあります。エラーがスローされるとツールのエラーとなり、モデルはそのメッセージを読み取ります。 |

`context`には次の情報が含まれます。

| フィールド | 内容 |
| --- | --- |
| `sessionId` | この呼び出しが属する Beeuni セッション。 |
| `agent` | 呼び出しを行ったエージェント。 |
| `directory` | プロジェクトのチェックアウト先。`/workspace`。相対パスはこの場所を基準に解決します。 |
| `env` | ボックス環境と、このエージェントに渡される[シークレット](/docs/ja/project/secrets)（現在の値）。シークレットは`context.env.NAME`として読み取ります。ボックスの起動後に変更されたシークレットは`process.env`ではなく`context.env`にあります。 |
| `signal` | ユーザーがターンを停止すると中断する`AbortSignal`。`fetch`に渡してください。 |

- **出力サイズ。** 出力が50 KBまたは2,000行を超える場合、サンドボックスの一時ディレクトリにファイル全体が保存されます。モデルには先頭部分、サイズ、ファイルパスが渡されます。生のダンプではなく、モデルに必要な内容を返してください。
- **エラー。** エラーがスローされると呼び出しは終了し、モデルはエラーメッセージを読み取ります。変更すべき内容が分かるメッセージをスローしてください。
- **インポート。** モジュールは `node:*` モジュール、Bun API、相対パスのファイルをインポートできます。パッケージは、モジュールの隣または上位にある `node_modules` から解決されます。サンドボックスはツール用のパッケージをインストールしません。また、[config release](/docs/ja/work/runtime#config-convergence) に含まれるのは、Gitが追跡するファイルだけです。HTTP APIには `fetch` を使ってください。TypeScriptはビルド手順なしで実行されます。
- **読み込み失敗。** モジュールを読み込めない場合（ファイルがない、default exportがない、`parameters` がオブジェクトスキーマではない、インポート時にエラーが起きる、パッケージを解決できないなど）、そのモジュールは除外され、ほかのツールは引き続き読み込まれます。デーモンはツールごとに、理由を添えて `[tools] project tools that did not load` をログに記録します。

<a id="parameters-with-zod"></a>

### zodを使った`parameters`

zod 4は同じJSON Schemaを生成します。

```ts
import { z } from 'zod'

export default {
  description: 'Look up an order by id.',
  parameters: z.toJSONSchema(z.object({ id: z.string().describe('The order id') })),
  async execute(args) {
    return `order ${args.id}: shipped`
  },
}
```

このモジュールが読み込まれるのは、セッションを実行するチェックアウト内の `node_modules` から `zod` を解決できる場合だけです。それ以外の場合は `Cannot find package 'zod'` で失敗します。
zodでスキーマを書き、プレーンなJSON Schemaとして提供するには、一度出力してから `parameters` に貼り付けます。

```bash
bun -e "import { z } from 'zod'; console.log(JSON.stringify(z.toJSONSchema(z.object({ id: z.string() })), null, 2))"
```

<a id="example-call-an-http-api-with-a-project-secret"></a>

### 例：プロジェクトシークレットを使ってHTTP APIを呼び出す

キーをプロジェクトシークレットとして保存します。コマンドは標準入力から値を読み取ります。

```bash
beeuni secrets set WEATHER_API_KEY=-
```

このツールを呼び出すエージェントにシークレットへのアクセスを許可します。スターターの `beeuni` エージェントには `secrets: all` があります。スコープ付きエージェントでの指定方法：`secrets: [WEATHER_API_KEY]`。

モジュールを作成します。

```ts
// tools/weather_now.ts
export default {
  description: 'Current weather for a city: temperature in °C and conditions.',
  parameters: {
    type: 'object',
    properties: { city: { type: 'string', description: 'City name, e.g. "Berlin"' } },
    required: ['city'],
  },
  async execute(args, context) {
    const key = context.env.WEATHER_API_KEY
    if (!key) throw new Error('WEATHER_API_KEY is not set: run `beeuni secrets set WEATHER_API_KEY=-` and grant it to this agent.')
    const url = `https://api.weather.example.com/v1/current?city=${encodeURIComponent(args.city)}`
    const response = await fetch(url, { headers: { authorization: `Bearer ${key}` }, signal: context.signal })
    if (!response.ok) throw new Error(`weather API answered ${response.status}: ${await response.text()}`)
    const data = await response.json()
    return { city: args.city, temperature_c: data.temp_c, conditions: data.conditions }
  },
}
```

宣言します。

```yaml
tools:
  # … the Beeuni tools you keep
  weather_now: tools/weather_now.ts
```

<a id="example-override-websearch"></a>

### 例：`web_search` をオーバーライドする

`beeuni tools eject web_search` は `tools/web_search.ts` を書き込み、`web_search: tools/web_search.ts` を設定します。その後、コピーを変更します。たとえば、1つのサイトの結果だけを残すには、リクエストの前に `execute` 内の各クエリにサイトを追加します。

```ts
// tools/web_search.ts, inside execute()
const queries = String(args.query ?? '').split('|||').map((query) => query.trim()).filter(Boolean)
  .map((query) => `site:docs.example.com ${query}`)
```

名前、引数、出力JSONはそのままにしてください。そうすれば、`web_search` を指定するスキルもWeb UIの表示も引き続き動作します。

<a id="test-a-tool"></a>

### ツールをテストする

`beeuni validate` は、名前、パス、ファイルがない場合、および `beeuni:` の値を確認します。`beeuni tools ls` は、セッションで利用できる内容を表示します。

次に、コミットしてプッシュし、変更リクエストを作成します。マージ後にセッションを開始し、実際の引数を使った呼び出しを1回依頼します。

```bash
beeuni sessions new --wait --prompt "Call the weather_now tool once with city Berlin and print its output."
```

読み込めないツールはモデルに提示されません。エラーをスローするツールは、そのエラーメッセージを返します。`beeuni sessions log <id>` は、各ツール呼び出しとそのステータスを含むセッションのメッセージを出力します。

<a id="declare-tools"></a>

## ツールを宣言する

`tools:` は、各ツール名をそのモジュールに割り当てるか、Beeuniツール名を `beeuni:<name>` に割り当てます。モジュールを置くフォルダーは自由に選べます。

```yaml
tools:
  web_search: beeuni:web_search
  memory: beeuni:memory
  lookup_order: tools/lookup_order.ts
  crm_note: integrations/crm/note.ts
```

- 名前はsnake_caseで、64文字以内です：`^[a-z][a-z0-9_]{0,63}$`。
- ハーネスツール名はエラーになります。Beeuniツール名には `beeuni:<name>` またはモジュールパスを指定します。モジュールは、すべてのハーネスでBeeuniツールを置き換えます。
- パスはリポジトリ相対で、`.ts`、`.js`、`.mts`、`.mjs`、`.cts`、または `.cjs` で終わる必要があります。
- [`imports:`](/docs/ja/project/manifest#imports) に記載されたファイルでは、ツールを宣言できます。同じ名前を2つのファイルで宣言するとエラーになります。インポートされたファイル内の `tools:` キーは、選択ルールにおいて `tools:` キーとして扱われます。

[マニフェストのリファレンス](/docs/ja/project/manifest#tools) にすべてのルールが記載されています。

<a id="who-may-use-which-tool"></a>

## ツールを使えるユーザー

`agents.<name>.tools` は、エージェントのモデルにどのツールを表示するかを決定します。ハーネス、Beeuni、プロジェクトの各ツールを指定できます。

```yaml
agents:
  beeuni:
    tools: all                                    # every tool (the default)
  researcher:
    tools: [read, grep, web_search, lookup_order] # only these
  support:
    tools:
      exclude: [bash, write, edit]                # every tool but these
  greeter:
    tools: none                                    # no tool
```

| 値 | エージェントが使えるもの |
| --- | --- |
| 省略、または `all` | すべてのツール。 |
| `none` | ツールなし。 |
| リスト | リストに記載されたツールのみ。`*` を含むリストは `all` です。 |
| `{ exclude: [...] }` | リストに記載されたものを除く、すべてのツール。 |
| `{ bash: false, … }` | 以前のマップ形式。`false` はツールを除外します。 |

- `tools` は表示可否を決めます。呼び出しを許可、確認、拒否するかは、エージェントの `permission` ルールで引き続き決まります。リストにないツールはモデルに表示されず、呼び出された場合は拒否されます。
- エージェントのリストで、プロジェクトが読み込んでいないBeeuniツールを追加することはできません。まず最上位の `tools:` に記載してください。
- `task` ツールで起動したサブエージェントの場合：実験的ハーネスでは、そのエージェント自身のリストとセッションエージェントのリストの両方で許可されたツールだけを使えます。標準ハーネスでは、そのエージェント自身のリストにあるツールを使えます。標準ハーネスでは、エージェントのルールはそのエージェントにのみ適用されます。エージェントによる委譲を禁止するには、そのリストから `task` を外してください。
- [Connector](/docs/ja/connect/connectors) ツールには、このリストではなく `agents.<name>.connectors` が適用されます。
- `beeuni validate` は、ハーネス、Beeuni、プロジェクトのいずれのツールでもない名前や、プロジェクトが読み込んでいないBeeuniツールについて警告します。`exclude` リストで名前を誤記しても、実際のツールは有効なままです。

標準ハーネスでは、`bash` はターミナルプラグインの `pty_*` ツールも対象とします。また、`write`、`edit`、`apply_patch` は1つの権限を共有します。そのうち1つをリストに含めると3つすべてが表示され、1つを除外すると3つすべてが非表示になります。実験的ハーネスでは、`write` ツールと `edit` ツールは別々です。

<a id="when-a-change-takes-effect"></a>

## 変更が反映されるタイミング

セッションでは、ランタイムの起動時にツールが読み込まれます。変更リクエストがマージされると変更が適用されます。新しいセッションでは変更が読み込まれ、[config releases](/docs/ja/work/runtime#config-convergence) を使っている場合、実行中のセッションでは次の収束時に読み込まれます。実験的ハーネスでは、プロジェクトツールまたはBeeuniツールのリストを変更すると、ターンが実行されていない間にランタイムがその場で再起動し、会話は続行されます。

<a id="tools-in-harnessesopencodetools"></a>

## `harnesses/opencode/tools/` 内のツール

標準ハーネスは引き続き、設定ディレクトリからカスタムツールを読み込みます。実験的ハーネスは読み込みません。ホスト型ツールの提供開始前に作成されたプロジェクトには、Beeuniツールのコピーが置かれています（`web_search.ts`、`memory.ts`、…）。

- 標準ハーネスでは、そのフォルダー内のツールが同名のホスト型ツールより優先されるため、削除するまでプロジェクトのコピーが使われます。Beeuniツールのコピーを削除してください。両方のハーネスで、デーモンが `beeuni.yaml` に記載されたツールをホストします。
- 標準ハーネスのツールを両方のハーネスで実行するには、ツールをそのフォルダーから移動し、`tool({ description, args, execute })` を `parameters` がJSON Schemaであるプレーンなオブジェクトに置き換え、`context.env` からシークレットを読み取り、`tools:` の下に宣言します。
