件名も中身も空の承認の申請が届いたのは、ヘッドレスで何本も起こした Claude Code に仕事を割り振る技術検証で、エージェントに申請用の MCP ツールを持たせたときでした。ツールが引数なし(input: {})で呼ばれていたのです。エラーにはならないので、空の申請がそのまま人の前に並び、何を承認すればよいのか分かりません。
結論
- Claude Code v2.1.69 以降、MCP ツールは既定で deferred になり、
ToolSearchで引いてから呼ばれます。この経路で、申請用の MCP ツールがinput: {}で呼ばれました。 - 毎回は起きませんでした。2回目は、同じ経路で入力が埋まっていました。
- 子プロセスの環境に
ENABLE_TOOL_SEARCH=falseを入れるとToolSearchが呼ばれなくなり、1回目から入力が埋まりました。 - あわせて、PreToolUse フックで必須の項目が空でないかを確かめ、空なら deny で差し戻すようにしました。deny の理由は一字一句そのままモデルに届くので、エージェントが理由を読んで書き直し、出し直すことを狙いました。
試した環境
- Mac、Claude Code 2.1.170
claude -p --output-format stream-json --verbose --include-hook-events …で起こしたエージェントに、承認の申請を出す MCP ツールを渡しました。その MCP ツールの呼び出しには PreToolUse フックを掛け、deferで承認待ちにしていました。
何が起きたか
エージェントが節目で申請用の MCP ツールを呼び、フックがそれを defer で止めて、人の承認を待つ作りでした。あるとき、届いた申請の件名も中身も空でした。stream-json を見ると、エージェントはまず ToolSearch で申請用のツールを引き、そのあとそのツールを input: {} で呼んでいました。
v2.1.69 以降の Claude Code では、MCP ツールは既定で deferred になっています。モデルは ToolSearch でツールを引いてから呼びます。空の入力で呼ばれたのは、この経路を通った回でした。
けれども、同じ経路を通った2回目は、入力がちゃんと埋まっていました。毎回起きるわけではないので、一度うまくいったのを見て、問題が無いと思い込みやすい所です。なぜ空になったのかまでは分かっていません。

どうしたか(1)ToolSearch を使わせない
子プロセスを起こすときの環境に ENABLE_TOOL_SEARCH=false を入れると、ToolSearch が呼ばれなくなり、1回目から入力が埋まりました。MCP ツールのスキーマが、最初から文脈に入るようになります。
子プロセスの環境は、次のように作っていました(抜き出し)。
function safeEnv(extra: Record<string, string> = {}): NodeJS.ProcessEnv {
const env: NodeJS.ProcessEnv = {
...process.env,
// MCP ツールのスキーマを最初から文脈に入れる
// (ToolSearch の経路で、申請用のツールが input: {} で呼ばれたため)
ENABLE_TOOL_SEARCH: 'false',
...extra,
};
// …(API キーなどを消す処理は省略)
return env;
}
どうしたか(2)フックで空を差し戻す
あわせて、フックの側にも確かめる処理を置きました。申請用のツールの呼び出しを受け取ったら、フックは必須の項目が空でないかを見ます。空なら deny を返し、理由の文に何が足りないかを書きます。deny の permissionDecisionReason は、ツールの結果として一字一句そのままモデルに届くので、エージェントが理由を読んで書き直し、出し直すことを狙いました。
こうしておけば、空の申請は人の前に並ぶ前に、エージェントの側へ差し戻されます。
どう確かめるか
stream-json を見て、次の2点を確かめます。
- 申請用のツールの前に
ToolSearchが呼ばれていないか(ENABLE_TOOL_SEARCH=falseが効いているか) - 申請用のツールの呼び出しの
inputが空でないか(フックが受け取るtool_inputでも見られます)
確かめていないこと
- 空の入力で呼ばれることは毎回は起きなかったので、
ENABLE_TOOL_SEARCH=falseだけで必ず防げるとまでは言えません。 - 2.1.170 より後の版でも同じかどうか。
おわりに
MCP ツールが、エラーも出さずに空の入力で呼ばれることがありました。私たちは、ToolSearch を使わせない環境変数と、フックで空の入力を差し戻す仕掛けの二重にして備えました。
※ この記事に書いた Claude Code の振る舞いは、執筆時点で私たちが測ったものです。いまは変わっている可能性があります。


