Claude Code modのisDeferredでツールの載せ方を決める
modが登録したツールをツール検索の奥に置くか、最初からプロンプトに載せるか。isDeferredの値ごとの違い、書く場所が2つある理由、効かない条件を示します。
isDeferredは、modが用意したツールを「ツール検索の奥に置くか、最初からプロンプトに載せるか」を決める項目です。trueなら奥に置かれ、falseなら最初から載ります。書ける場所は$.tool.registerとtool.describeイベントの2つです。$.tool.registerのisDeferredはv2.1.293で追加されました。tool.describe側の導入バージョンは、changelogに載っていません。
isDeferredで何が変わるのか
Claude Codeのツール検索は、MCPのツール定義をセッション開始時に全部読み込まず、名前だけを先に見せる仕組みです。説明文とスキーマは、Claudeが探しに来たときに初めて届きます。modが登録したツールも、この仕組みの対象です。
その結果、modのツールは次の状態になりえます。
| isDeferred | Claudeに見えるもの | 呼ぶまでの流れ |
|---|---|---|
true | Claudeに見えるものツール名だけ | 呼ぶまでの流れToolSearchで探して、説明とスキーマを受け取ってから呼ぶ |
false | Claudeに見えるもの名前、説明、スキーマ | 呼ぶまでの流れ探す手順なしで、そのターンから呼べる |
Claudeはツールをいつ呼ぶかを、説明文を読んで決めます。名前しか見えない状態では、その判断材料がありません。毎ターンの候補に入れたいツールはfalse、たまにしか使わないツールはtrueのまま、というのが基本の使い分けです。
falseには代償があります。最初から載せたツールは、説明とスキーマの分だけ、会話に使えるコンテキストを毎回消費します。
$.tool.registerに渡す — 登録時に決める
ツールを登録する$.tool.registerに、isDeferredを足します。次は、チケットを引くツールを最初から載せる例です。登録の形はmodの作り方で扱うものと同じで、足したのは最後の1行だけです。
on('session.start', async ($, e, next) => {
await $.tool.register({
name: 'ticket',
description: 'Look up a ticket by its id and return its title and status',
inputSchema: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] },
// false: 説明とスキーマを最初からプロンプトに載せる
isDeferred: false,
})
return next(e)
})Claudeが見るツール名は、mcp__、プラグイン名、アンダースコア2つ、登録した名前をつないだ形です。プラグイン名がmy-modならmcp__my-mod__ticketになります。呼び出しの処理は、この完全な名前で絞ったtool.callフックに書きます。isDeferredはこの処理に影響しません。変わるのは、Claudeがツールを知るタイミングだけです。
注意点は、古いバージョンの挙動です。APIのページによると、登録時のisDeferredはv2.1.293以降のClaude Codeで必要な項目で、それより前のバージョンは無視します。エラーにはならず、ツールは奥に置かれたままです。複数人に配るmodでは、falseを渡したつもりで効いていない環境が混ざりえます。
tool.describeで返す — 説明文と一緒に決める
もう1か所がtool.describeイベントです。リファレンスでは、ツールごとに1回、説明が最初にClaudeへ送られるときに発火するイベントとして載っています。フックは{ description }を返し、必要ならisDeferredを添えます。
つまりtool.describeは、説明文を書き換える場所でもあります。奥に置くか上に出すかを、説明文とセットで決められます。骨格は次のとおりです。
on('tool.describe', { tool: 'mcp__my-mod__ticket' }, async ($, e, next) => {
return {
description: 'Look up a ticket by its id and return its title and status',
isDeferred: false,
}
})絞り込みの{ tool: ... }は、tool.callの例に倣った書き方です。tool.describeの絞り込みに使えるフィールドと、eの中身は、リファレンスの表に記載がありません。書く前に、手元のバージョン向けに書き出される型定義で確かめてください。型定義の取り方はmodの作り方にあります。
isDeferredは省略できます。省略したツールは、ツール検索の既定の扱いに従います。
登録時とdescribe、どちらに書くか
使い分けの目安は次のとおりです。
| 場面 | 向く書き方 |
|---|---|
| 自分のmodのツールを最初から載せたい | 向く書き方$.tool.registerにisDeferred: false |
| 説明文も状況に応じて直したい | 向く書き方tool.describeでdescriptionと一緒に返す |
表に載せていない使い方として、他のmodが登録したツールの載せ方をtool.describeで変える案があります。登録側のコードに触れずに済みますが、リファレンスが明言している使い方ではありません。tool.describeが「ツールごとに1回」発火することからの推測なので、手元で試してから採用してください。
効かないのはどんなときか
isDeferredは、ツール検索が働いている前提の項目です。ツール検索そのものが動かない構成では、全ツールが最初から載るため、そもそも奥に置かれる場面がありません。
MCPのドキュメントによると、ツール検索は既定で有効です。次の場合は、挙動が変わります。
ANTHROPIC_BASE_URLが自社以外のホストを指すと、ツール検索は無効になる。ENABLE_TOOL_SEARCHを明示すれば上書きできるENABLE_TOOL_SEARCH=falseでは、MCPツールが全部最初から載るENABLE_TOOL_SEARCH=autoまたはauto:Nでは、ツール定義の合計がコンテキストウィンドウの10%(またはN%)に達するまで、奥に置くはずのツールも最初から載る- Microsoft FoundryのAzure上のデプロイでは、サーバー側が拒否するため、
ENABLE_TOOL_SEARCHでも上書きできず最初から載る
しきい値を5%にして試す例です。
ENABLE_TOOL_SEARCH=auto:5 claude --plugin-dir ./my-mod--plugin-dirを付けると、インストールせずに、そのセッションだけmodを読み込めます。auto:5なら、定義の合計が小さいうちはtrueのツールも載ってしまいます。isDeferredの効果を確かめるときは、ENABLE_TOOL_SEARCHを設定せずに既定のまま試すほうが、結果を読み取りやすくなります。
ツール検索には、対応するモデルの条件もあります。Claude Sonnet 4.5、Claude Haiku 4.5、Claude Opus 4.5以降のモデルが対象です。
さらに、環境変数のCLAUDE_CODE_DISABLE_EXPERIMENTAL_BETASを設定している場合も、ツール検索は無効のままです。ENABLE_TOOL_SEARCHでは上書きできません。組織の管理設定でツール検索を有効に保つ手段が、v2.1.227以降にあります。permissions.denyにToolSearchを入れると、組み込みのToolSearchツールだけを無効にすることもできます。
奥に置いたツールは、セッション開始時にどう見えるか
MCPのドキュメントによると、ツール検索が有効なとき、セッション開始時に読み込まれるのはツール名とサーバーのinstructionsだけです。説明文とスキーマは、Claudeが探しに来るまで届きません。trueのmodツールが呼ばれにくいと感じたら、この前提に立ち返ると原因を切り分けやすくなります。
- 名前から用途が伝わらないと、Claudeは探しに来ない。名前は動詞と対象で付けておく
- 説明文は、探して届いたあとに「いま呼ぶか」を決める材料になる
- MCPサーバーのinstructionsは、いつ探すべきかをClaudeに伝える役を持つ。modのツールにこれに当たる欄があるかは、リファレンスにもAPIのページにも記載がない
ツールを探して見つけてもらえるかどうかを、検索の内部の仕組みから説明した記載は、ドキュメントにありません。見つけてもらえるかは名前と説明文の書き方に左右される、という程度にとどめ、実際の呼ばれ方で確かめるのが確実です。
tool系イベントの中での位置
tool.describeは、ツール系イベントの中で最も手前にあります。リファレンスの表では、3つが次の役割で並んでいます。
| イベント | 発火のタイミング | フックが返せるもの |
|---|---|---|
tool.describe | 発火のタイミングツールの説明が最初にClaudeへ送られるとき(ツールごとに1回) | フックが返せるもの{ description }と、任意でisDeferred |
tool.call | 発火のタイミングツールが実行される直前 | フックが返せるものnext(e)、{ deny: reason }、{ result } |
tool.check | 発火のタイミング実行してよいかをClaude Codeが決めるとき | フックが返せるもの{ decision }(allow、ask、deny) |
tool.describeが決めるのは、Claudeがツールを知る段階の話です。実行を止めたり、結果を差し替えたりする処理は、あとの2つが担います。載せ方の設定と、実行の可否は別のイベントが受け持ちます。
settings.jsonで書くフックの側は、PreToolUse hookで扱っています。
MCPサーバー側の設定とは別の層
同じ「最初から載せる」でも、MCPサーバーには専用の設定があります。modのisDeferredと並べると、層が分かれます。
| 設定 | 誰が書くか | 効く範囲 |
|---|---|---|
isDeferred($.tool.register、tool.describe) | 誰が書くかmodの作者 | 効く範囲modが登録したツール |
alwaysLoad(MCP設定のtrue / false) | 誰が書くかサーバーを追加した人 | 効く範囲そのサーバーのツール全部 |
_metaのanthropic/alwaysLoad | 誰が書くかMCPサーバーの作者 | 効く範囲サーバーが返すツール1本ごと |
MCPのalwaysLoadは、trueでそのサーバーのツールを最初から載せ、falseで作者が上に出したツールまで奥に戻します。後者はv2.1.287以降です。つまりMCPでは、サーバーを追加した人の設定が作者の指定より強くなります。
modのisDeferredが、これらの設定とぶつかったときの優先順位は、リファレンスにもAPIのページにも記載がありません。modのツールにalwaysLoad相当の設定が及ぶかどうかも書かれていません。優先順位に頼る設計は避け、手元で実際の載り方を確かめてから配布するのが安全です。
上に出すツールの選び方
falseにしたツールは、コンテキストを毎回使います。MCPのドキュメントも、最初から載せるのは「毎ターン必要な少数のツール」に限るよう説明しています。次の観点で絞ると決めやすくなります。
- 説明文が短く、スキーマも小さいか。
falseのツールは説明とスキーマが毎ターンのプロンプトに入るので、引数が多く説明が長いほど、会話に使える枠を毎回食う - ユーザーの発話に対して、毎回候補になるか。たまにしか呼ばないなら
trueのままで足りる - 名前だけで用途が伝わるか。伝わるなら、奥に置いても検索で見つけてもらえる
- 同じmodに登録するツールが何本あるか。全部を
falseにすると、ツール検索で節約した分が戻る
最後の点は、ツールが増えたときに効きます。10本のうち1本だけfalseにするのと、10本全部をfalseにするのとでは、コンテキストへの影響がまるで違います。
載り方を確かめる
trueのツールは、ClaudeがToolSearchで探してから呼びます。falseのツールは、探す手順なしで呼べます。確認はこの違いを見るだけです。観察の手順は次のとおりです。
ENABLE_TOOL_SEARCHを設定しないまま、--plugin-dirでmodを読み込んで起動する- ツールを使う依頼を出し、
ToolSearchの呼び出しが先に挟まるかを見る isDeferredの値を切り替えて、同じ依頼をもう一度出す
ToolSearchは、ツール検索が有効なときに、奥のツールを探して読み込む組み込みツールです。依頼文の言い方で挙動がぶれるので、同じ文面で比べてください。
観察の結果から、次のように切り分けられます。
| 観察された動き | 考えられる原因 |
|---|---|
trueでもToolSearchが挟まらず、すぐ呼ばれる | 考えられる原因ENABLE_TOOL_SEARCHがfalseかauto系、または自社以外のホストを指すANTHROPIC_BASE_URL |
falseにしてもToolSearchが挟まる | 考えられる原因v2.1.293より前のClaude Code。isDeferredが無視されている |
| どちらの値でも差が出ない | 考えられる原因Microsoft FoundryのAzure上のデプロイや、対応外のモデルで、最初から全部載っている |
値を変えてもToolSearchが出てこない | 考えられる原因ToolSearchがpermissions.denyで無効、またはCLAUDE_CODE_DISABLE_EXPERIMENTAL_BETASが設定されている |
先にclaude --versionで版を確かめ、環境変数を外して再実行すると、原因を絞り込めます。
まとめ
isDeferredは、modのツールを「名前だけ見せる」か「説明とスキーマまで見せる」かを選ぶスイッチです。書き場所は、登録時の$.tool.registerと、説明と同時に決めるtool.describeの2つ。登録時のisDeferredはv2.1.293より前のバージョンでは無視され、ツール検索が動いていない構成でも意味を持ちません。falseはコンテキストを毎回使うので、毎ターン候補にしたいツールだけに絞る。この運用が、ツールを増やしても重くならない最小の設計です。
バージョンごとの変更点はClaude Code v2.1.293のリリースノートにまとまっています。自前でツール検索を組む場合は、Tool Searchを自作する記事が参考になります。