【Claude Code 連載 第85回】ツール検索で多くのツールを扱う(agent-sdk/tool-search)

スポンサーリンク
【Claude Code 連載 第85回】ツール検索で多くのツールを扱う(agent-sdk/tool-search) 用語解説
【Claude Code 連載 第85回】ツール検索で多くのツールを扱う(agent-sdk/tool-search)
この記事は約17分で読めます。
よっしー
よっしー

こんにちは。よっしーです(^^)

スポンサーリンク

背景

この連載では、Claude Codeの公式ドキュメントを1ページずつ読み解いていきます。公式ドキュメントは情報が網羅されている分、「結局どの機能を、どんな場面で使えばいいのか」は自分で考える必要があり、読むのに意外と時間がかかります。そこで、私が実務で使うために読み込んだ内容を「使う場面→実例コード」の順に整理して残していくことにしました。専門家の解説というより、一次情報を読んだ記録です。推測や動作を確認していない部分には、その都度そう書きます。

公式ドキュメント:https://code.claude.com/docs/ja/agent-sdk/tool-search

1. 一言でいうと

ツール検索は、エージェントに渡すツールが何百、何千と増えても困らないようにする仕組みです。全ツールの定義を最初からコンテキストに入れるのではなく、Claude が「こういうツールはないか」と検索し、必要なものだけを読み込みます。既定で有効になっています。

第83回の自作ツールでも、第84回の外部 MCP サーバーでも、「ツール検索が有効なのでツールは必要になるまで読み込まれない」という説明が何度も出てきました。今回はその仕組み自体の回です。


2. どういう場面で役立つか

シーン1:社内の大量の API をまとめて使わせる

社内の MCP サーバーが何百ものツールを公開しているような場合、それを全部コンテキストに入れると、作業に使える容量がほとんど残りません。原文によると、ツール50個分の定義だけで1万〜2万トークンを使うことがあります。ツール検索なら、Claude はツールの概要だけを持っておき、今の作業に必要なツールを探して読み込みます。

シーン2:複数の MCP サーバーをつないだエージェントで、ツールの選び間違いを減らす

Slack、GitHub、Jira など複数のサービスをつなぐと、似た名前のツールが並びます。原文は、30〜50個を超えるツールを一度に読み込むと、ツールを選ぶ精度が下がると書いています。ツール検索で候補を絞れば、Claude が関係のないツールを選ぶ余地が減ります。

シーン3:ツールの数に応じて自動で切り替える

ツールが少ないうちは全部読み込み、増えてきたら検索に切り替える、という運用を設定1つで実現できます(ENABLE_TOOL_SEARCH=auto)。開発中はツールが少なく、本番で増える、といった場合に向いています。

不要・向かないケース

  • ツールが10個程度より少ない:原文は、定義がコンテキストに無理なく収まる約10個未満のツールなら、全部を最初から読み込むほうが通常は速いと書いています。検索のたびに1往復増えるからです。
  • Azure でホストされた Microsoft Foundry のデプロイメント:ツール検索に対応していません。自動で全読み込みに切り替わり、設定でも変えられません。
  • tool_reference ブロックを転送しないプロキシを経由している:ツール検索を強制するとリクエストが失敗します(後述)。

3. コードと仕組みの解説

原文の fenced コードブロックは4本(Python と TypeScript の対訳2組)です。全数を引用します。コードは原文から機械的に抜き出したもので、入れ子に由来する先頭インデントを外した以外は変えていません。このページの本体は、コードよりも「いつツール検索が有効になり、いつ無効になるか」の規則です。

3-0. バージョン要件

挙動必要バージョン
Google Cloud の Agent Platform で、モデルの世代によってツール検索を有効にする(それ以前は、ENABLE_TOOL_SEARCH を設定しない限り全モデルで無効)Claude Code v2.1.221 以降
組織が管理設定でツール検索をオンに保つClaude Code v2.1.227 以降

3-1. ツール検索の仕組み

ツール検索が働いているときの流れは次のとおりです。

  1. ツールの定義はコンテキストに入れずに保留される。Claude は使えるツールの概要だけを受け取る。
  2. 作業にまだ読み込まれていない機能が必要になると、Claude は関係するツールを検索する。
  3. 関連性の高い上位5個まで(既定値)のツールがコンテキストに読み込まれ、以後のターンでも使える状態で残る。
  4. SDK が、ツールを見つけたときのメッセージをコンパクション(要約)で圧縮したあとは、Claude はそのツールが次に必要になったとき、もう一度検索する。

速さとの引き換え。 検索するたびに、やり取りが1往復増えます。ツールが多い場合は、毎ターンのコンテキストが小さくなるぶんで十分に元が取れます。一方、定義がコンテキストに無理なく収まる約10個未満なら、全部を最初から読み込むほうが通常は速い、と原文は書いています。

4つ目の点は長いセッションで効いてきます。コンパクションが起きるたびに、それまで使っていたツールも再検索が必要になり、1往復ずつ増えます。第70回で見たように、コンパクションでは「会話に差し込まれたもの」が要約されて消えます。検索で読み込んだツールの定義もその側に入る、と読めます(第70回との対応づけは筆者)。毎ターン使うツールが決まっているなら、alwaysLoad で常に読み込んでおくほうが無駄がありません(3-4)。

基盤となる API の仕組みは、Claude API の「ツール検索」のドキュメントに回されています。

3-2. 有効・無効を決める規則

ツール検索は既定でオンですが、環境によっては自動で「全読み込み」に切り替わります。そのうちいくつかは、設定で変えることもできません。原文の各所に分かれている条件を1つの表にまとめます(表の形は筆者の整理)。

条件ツール検索ENABLE_TOOL_SEARCH で変えられるか
通常(Anthropic の API に直接つなぐ、対応モデル)オン変えられる
SDK の「サポートされていないモデル」一覧にあるモデル全読み込み変えられない
Google Cloud の Agent Platform:Claude Opus 4.5・Sonnet 4.5・Haiku 4.5 以降オン変えられる
Google Cloud の Agent Platform:それより前のモデル全読み込み(その環境が必要なベータヘッダーを拒否するため)変えられない
Microsoft Foundry(Azure でホストされたデプロイメント)全読み込み(サーバー側がツール検索を拒否し、SDK がそれを検出して切り替える)変えられない
ANTHROPIC_BASE_URL がファーストパーティ以外のホスト(プロキシなど)を指すオフ(ほとんどのプロキシが tool_reference ブロックを転送しないため)変えられる
CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS を設定しているオフ変えられない(組織は管理設定でオンに保てる。v2.1.227 以降)

「変えられない」行は、どれもツール検索を受け付けない相手(モデルやサーバー)がいるか、実験的なベータ機能をまとめて止める設定が優先される場合です。プロキシの行だけは、プロキシが tool_reference ブロックに対応していると確認できれば、自分でオンにできます。CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS の影響範囲と、どこでオーバーライドが効くかは、「プレリリース機能を無効にする」の節(LLM ゲートウェイのプロトコルのページ)に回されています。

もう1つ、第84回で見た方法もあります。disallowedTools で ToolSearch ツール自体を外すと、そのセッションはツール検索なしで動きます。

3-3. ENABLE_TOOL_SEARCH の値

既定の振る舞いは、環境変数 ENABLE_TOOL_SEARCH で変えられます。第70回でもこの変数に触れました。

値動き
(未設定)オン。ツール定義は遅延され、必要なときに検索される。ただし 3-2 の表の条件(Agent Platform の古いモデル、ファーストパーティ以外の ANTHROPIC_BASE_URL、Azure の Foundry)では全読み込みに戻る
true常にオン。ただし Azure の Foundry ではサーバー側の拒否により全読み込みが強制され、Agent Platform の古いモデルでも全読み込みのまま。プロキシ経由でもベータヘッダーを送るので、tool_reference ブロックに対応していないプロキシではリクエストが失敗する
autoツール検索で遅延できる定義のトークン数を数え、モデルのコンテキストウィンドウと比べる。合計がウィンドウの10%に達したらツール検索を有効にし、それ未満なら全定義を最初から読み込む
auto:Nauto と同じで、割合を自分で決める。auto:5 なら5%で有効になる。値が小さいほど早く有効になる
falseオフ。全ツール定義が毎ターンコンテキストに入る

true の行の注意が実務では大事です。社内のプロキシや LLM ゲートウェイ経由で使っていて、既定でオフになっているのを true で強制すると、プロキシが対応していなければエラーで止まります。ゲートウェイの対応状況を確かめるまでは、既定のままにしておくのが安全です(筆者の指摘)。

auto で何を数えるか。 ツール検索は、リモートの MCP サーバーのツールにも、第83回のカスタム SDK MCP サーバーのツールにも、区別なく効きます。auto のときに閾値の計算に入るのは次のものです。

  • alwaysLoad を付けていない全ての MCP ツール(どのサーバーのものでも)
  • 必要なときに読み込まれる組み込みツール

Bash・Read・Edit などの中核の組み込みツールは常に最初から読み込まれ、閾値の計算には入りません。 つまり、ツール検索がどの設定でも、ファイルの読み書きやシェルのような基本の道具はいつでも使えます。

使い分けの目安は次のとおりです(筆者の整理。原文の「約10個未満なら全読み込みが速い」「30〜50個を超えると精度が落ちる」を当てはめたもの)。

状況向いている値
自作ツールが数個だけfalse(検索の往復が無いぶん速い)
ツールの数が増減する、開発と本番で違うauto か auto:N
大量のツールを常に抱えている未設定(既定のオン)

3-4. 設定のしかた

値は query() の env オプションで設定します。env の扱いは言語で違います(第79回と同じ)。

  • TypeScript:env は子プロセスの環境を置き換えます。引き継いだ環境変数を残すために ...process.env を展開して入れます。
  • Python:env は引き継いだ環境の上に重ねられます(マージ)。

次の例は、多くのツールを公開するリモートの MCP サーバーにつなぎ、ワイルドカードでその全ツールを事前承認し、auto:5 で「遅延できる定義がコンテキストウィンドウの5%に達したらツール検索を有効にする」設定にしています。

import { query } from "@anthropic-ai/claude-agent-sdk";

try {
  for await (const message of query({
    prompt: "Find and run the appropriate database query",
    options: {
      mcpServers: {
        "enterprise-tools": {
          // Connect to a remote MCP server
          type: "http",
          url: "https://tools.example.com/mcp"
        }
      },
      allowedTools: ["mcp__enterprise-tools__*"], // Wildcard pre-approves all tools from this server
      env: {
        ...process.env, // env replaces the subprocess environment, so keep inherited variables
        ENABLE_TOOL_SEARCH: "auto:5" // Activate tool search when deferrable definitions reach 5% of context
      }
    }
  })) {
    if (message.type === "result" && message.subtype === "success") {
      console.log(message.result);
    }
  }
} catch (error) {
  // A single-shot query() throws after yielding an error result
  console.log(`Session ended with an error: ${error}`);
}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage


async def main():
    options = ClaudeAgentOptions(
        mcp_servers={
            "enterprise-tools": {
                "type": "http",
                "url": "https://tools.example.com/mcp",
            }
        },
        allowed_tools=[
            "mcp__enterprise-tools__*"
        ],  # Wildcard pre-approves all tools from this server
        env={
            "ENABLE_TOOL_SEARCH": "auto:5"  # Activate tool search when deferrable definitions reach 5% of context
        },
    )

    try:
        async for message in query(
            prompt="Find and run the appropriate database query",
            options=options,
        ):
            if isinstance(message, ResultMessage) and message.subtype == "success":
                print(message.result)
    except Exception as error:
        # A single-shot query() raises after yielding an error result
        print(f"Session ended with an error: {error}")


asyncio.run(main())

動かすには、https://tools.example.com/mcp を自分の MCP サーバーの URL に置き換えます。成功すると結果の文章がコンソールに出ます。

この例は1回きりの query() 呼び出しなので、SDK はエラーの結果を出したあとで例外を投げます(第83・84回と同じ)。そのためループ全体を try で囲んでいます。失敗の理由を知りたいときは、ループの中で結果メッセージの subtype(error_during_execution など)を確認します。結果メッセージの扱いは第77回の範囲です。

TypeScript の env にある ...process.env を忘れると、PATH や API キーなど、親のプロセスが持っていた環境変数が子プロセスに渡らなくなります。ツール検索の設定だけのつもりが、エージェント自体が動かなくなる原因になるので注意が必要です(第79回の内容からの筆者の補足)。

この例の allowedTools は、サーバーの全ツールをワイルドカードで事前承認しています。ツール検索を使うほどツールが多いサーバーで全部を承認すると、書き込みや削除のツールまで確認なしで動くことになります。実際の運用では、第84回で見たように必要なツールに絞るほうが安全です(筆者の指摘)。

毎ターン使うツールは常に読み込む。 第83・84回で見た alwaysLoad: true を付けたツール(またはサーバー)は、遅延の対象から外れ、最初から完全なスキーマで読み込まれます。auto の閾値の計算にも入りません。頻繁に使う少数のツールだけを alwaysLoad にし、残りを検索に任せる、という組み合わせができます。

3-5. 見つけてもらいやすくする

ツール検索は、検索の言葉をツールの名前と説明に照らし合わせて候補を探します。したがって、名前と説明の書き方で、見つかりやすさが変わります。

  • 名前:search_slack_messages のような名前は、query_slack より広い範囲の依頼で候補に出ます。何をするツールかが名前から読み取れるほうが有利です。
  • 説明:「キーワード、チャンネル、日付範囲で Slack のメッセージを検索する」のように具体的な言葉を含む説明は、「Slack に問い合わせる」のような漠然とした説明より、多くの検索に一致します。

第83回で「説明は Claude がいつツールを呼ぶかを決める材料」だと書きました。ツール検索が有効な環境では、説明はそもそも候補に挙がるかどうかも左右します。説明が曖昧なツールは、存在していても Claude に見つけてもらえない可能性があります(筆者の整理)。

システムプロンプトで道具の種類を知らせる。 使えるツールの分類を並べた一節をシステムプロンプトに加えると、Claude は「どんな種類のツールを探せばよいか」の手がかりを得られます。TypeScript では systemPrompt、Python では system_prompt オプションで渡します。claude_code プリセットに append を使うと、プリセットのプロンプトを置き換えずに、文章を後ろに付け足せます。

options: {
  systemPrompt: {
    type: "preset",
    preset: "claude_code",
    append: "You can search for tools to interact with Slack, GitHub, and Jira."
  }
}
options = ClaudeAgentOptions(
    system_prompt={
        "type": "preset",
        "preset": "claude_code",
        "append": "You can search for tools to interact with Slack, GitHub, and Jira.",
    }
)

例では「Slack、GitHub、Jira とやり取りするツールを検索できます」という一文を足しています。システムプロンプトのオプションの全体は、「システムプロンプトの変更」のページに回されています。

3-6. 制限

項目内容
最大ツール数カタログ内に10,000個まで
検索結果既定では1回の検索につき関連性の高い上位5個
対応モデルClaude Sonnet 4.5、Claude Haiku 4.5、Claude Opus 4.5 以降のモデル。最新の一覧は API ドキュメントのモデル互換性の節を参照。Google Cloud の Agent Platform でも同じ最低条件

1回の検索で読み込まれるのは既定で5個までなので、1つの作業で多くの種類のツールを同時に使う場合は、Claude が複数回検索することになります。そのぶん往復が増えるので、作業ごとに使うツールが大きく入れ替わるエージェントほど、名前と説明の書き方(3-5)が効いてきます(筆者の指摘)。


4. まとめ

  • ツール検索は、ツールの定義をコンテキストに入れずに保留し、Claude が必要なものを検索して読み込む仕組み。既定で有効で、リモートの MCP ツールにも自作の SDK MCP ツールにも効く。
  • 解決するのは2つの問題:コンテキストの圧迫(50ツールで1万〜2万トークン)と、選択精度の低下(30〜50個超で落ちる)。
  • 1回の検索で上位5個が読み込まれ、以後のターンも残る。コンパクション後は再検索が必要。検索ごとに1往復増えるので、約10個未満なら全読み込みのほうが速い。
  • 自動で全読み込みになる環境がある。サポート外のモデル、Agent Platform の古いモデル、Azure の Foundry は設定でも変えられない。ファーストパーティ以外の ANTHROPIC_BASE_URL は既定でオフだが変えられる。CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS は優先される。
  • ENABLE_TOOL_SEARCH は未設定・true・auto(10%)・auto:N・false の5通り。true は非対応のプロキシでリクエストを失敗させる。
  • Bash・Read・Edit などの中核の組み込みツールは常に読み込まれ、閾値に数えない。alwaysLoad のツールも数えない。
  • 設定は env オプションで。TypeScript は ...process.env を忘れない。
  • 見つけてもらうには、具体的な名前と説明、システムプロンプトでの分類の案内(claude_code プリセット+append)。
  • 上限はカタログ10,000個、検索1回あたり5個、対応は 4.5 世代以降。

次回予告

次回は 「システムプロンプトの変更」(agent-sdk/modifying-system-prompts) を取り上げる予定です。

今回、使えるツールの分類をシステムプロンプトに書き足すために、claude_code プリセットに append で一文を加えました。次回は、このプリセットを使う・使わない・書き足す・丸ごと置き換える、といったシステムプロンプトの選択肢全体を見ていきます。エージェントの性格や振る舞いを決める一番上の指示を、どこまで自分で決められるのかが分かります。

※テーマは変更になる場合があります。


よっしー
よっしー

何か質問や相談があれば、コメントをお願いします。また、エンジニア案件の相談にも随時対応していますので、お気軽にお問い合わせください。

それでは、また明日お会いしましょう(^^)

コメント

タイトルとURLをコピーしました