Claude Media
tool_useとpause_turnの違い — サーバーツール混在時の判定

tool_useとpause_turnの違い — サーバーツール混在時の判定

サーバーツールとクライアントツールが同一ターンで混在すると、stop_reasonはpause_turnではなくtool_useになります。判定方法と、tool_resultのみで応答しないと400エラーになる条件を扱います。

server_tool_useとクライアントのtool_useが同じターンに同居する場面

Claudeはweb_fetchのようなサーバーツールと、ユーザー定義のツールやBashツールのようなクライアントツールを、同じ並列ツール呼び出しのグループの中で同時に呼び出せます。このとき何が起きるかは、サーバーツール単独の長時間ループとは仕様が異なります(web_fetch単体の実行制約はurl_not_in_prior_contextエラーの原因で扱っています)。

server_tool_useブロックとは、Web検索やWeb FetchのようにAPI側が実行するツール呼び出しを表すコンテンツブロックです。srvtoolu_プレフィックスのidを持ち、通常は同じアシスタントターンの中で結果ブロックとペアになって返ります。サーバーツールとクライアントツールが同じ並列呼び出しグループに混在すると、APIはサーバーツールを実行せず、すぐに応答を返してクライアントツールを先に実行できるようにします。

混在時にstop_reasonがtool_useになる3つの見分け方

サーバーツールとクライアントツールが混在したとき、レスポンスは次の3点で識別できます。

  • stop_reason"pause_turn"ではなく"tool_use"になる
  • contentserver_tool_useブロックとクライアントのtool_useブロックの両方が含まれるが、サーバーツールの結果ブロックはまだ無い(その呼び出しは未完了)
  • 判定に使える専用のマーカーは無い。「server_tool_useブロックのうち、同じレスポンス内に対応する結果ブロックを持たないもの」を探すのが検出方法になる。MCP connector経由のmcp_tool_useブロックも同じ挙動をする
{
  "stop_reason": "tool_use",
  "content": [
    { "type": "text", "text": "I'll fetch the article and check your system at the same time." },
    {
      "type": "server_tool_use",
      "id": "srvtoolu_01HxbWnMRmbWyMfUtJKC45rA",
      "name": "Web Fetch",
      "input": { "url": "https://example.com/article" }
    },
    {
      "type": "tool_use",
      "id": "toolu_01PjgRJLbXrXEMZwDNYLnBqk",
      "name": "run_command",
      "input": { "command": "uname -a" }
    }
  ]
}

このレスポンスではweb_fetchserver_tool_useブロックに結果が付いていません。呼び出し自体は発行されているものの、まだ実行されていない状態です。

続きの送り方 — tool_resultだけを含む1通のメッセージ

ターンを続けるには、クライアントツールを実行し、レスポンス内のtool_useブロック1つにつき1つのtool_resultブロックだけを含むユーザーメッセージを送ります。tools配列は継続リクエストでも同じものを維持しなければなりません。待機中のサーバーツールがtoolsから消えていると、継続リクエストは400エラーになります。

{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_01PjgRJLbXrXEMZwDNYLnBqk",
      "content": "Linux demo-host 6.8.0-52-generic x86_64 GNU/Linux"
    }
  ]
}

APIはこのtool_resultを、開いたままのアシスタントターンに結び付け、保留していたサーバーツールを実行してからClaudeにターンを継続させます。次のレスポンスは、直前のserver_tool_useidに対応する結果ブロックから始まり、続けて新しく生成された内容と新しいstop_reasonが返る形です。server_tool_useブロックとその結果ブロックはtool_use_idでペアになりますが、位置ではなく値で対応します。この例では2つの異なるレスポンスにまたがって出現し、2回目のレスポンスでserver_tool_useブロック自体が繰り返されることはありません。

pause_turnとtool_useは継続の送り方が違う

tool_use(混在ターン)pause_turn(サーバーツール単独の長時間ループ)
発生条件tool_use(混在ターン)サーバーツールとクライアントツールが同じ並列呼び出しグループに混在pause_turn(サーバーツール単独の長時間ループ)サーバーツール単独のエージェントループが長時間続き、APIがループを一時停止
未完了のserver_tool_usetool_use(混在ターン)常にありうる(クライアントツールを待っている)pause_turn(サーバーツール単独の長時間ループ)ありうる(次のリクエストで続きが実行される)
継続の送り方tool_use(混在ターン)クライアントツールのtool_resultのみを含むメッセージを送るpause_turn(サーバーツール単独の長時間ループ)直前のアシスタントのcontentをそのまま次のリクエストのmessagesに追加して再送する
クライアントのtool_useブロックの有無tool_use(混在ターン)必ずある(混在の定義そのもの)pause_turn(サーバーツール単独の長時間ループ)無い。クライアントのtool_useブロックが応答に含まれるとき、stop_reasonpause_turnになることはない

両者はどちらも「未実行のサーバーツール呼び出しを残したまま応答が返る」という点で似ていますが、継続方法がまったく違いますpause_turnはレスポンスをそのまま再送するだけで済むのに対し、tool_useの混在ケースはtool_resultを組み立てて送る必要があります。バッチ処理でサーバーツールのエージェントループがターンあたりの反復上限に達したときも、pause_turnと同じ形で応答が返り、同じように継続リクエストで再開します。

pause_turnの継続には、公式ドキュメントが3つの注意点を挙げています。1つ目は、一時停止した応答をそのまま次のリクエストへ再送すること。2つ目は、継続リクエストでも同じtoolsを維持することです。一時停止したserver_tool_useブロックがまだ実行されていない状態なので、継続リクエストのtoolsからそのツールが消えていると、tool_use混在ケースと同じようにAPIはツール不足のエラーを返します。3つ目は、pause_turnは1回で終わるとは限らないという点です。継続したターンが再びpause_turnで止まることがあるため、レスポンスごとにstop_reasonを確認し、リトライループと同じ発想で継続回数の上限を設けておく必要があります。

継続リクエストの組み立てを誤ると400エラーになる2つのパターン

継続リクエストの組み立て方を誤ると、2種類のエラーが返ります。

1つ目は、tool_resultブロックだけで構成すべき続きのメッセージに、テキストなど他のブロックを混ぜてしまうケースです。サーバーツールを直接呼び出していた場合、そのサーバーツール呼び出しが未解決のまま残り、次のようなメッセージを含む400 invalid_request_errorになります。

`web_fetch` tool use with id `srvtoolu_01HxbWnMRmbWyMfUtJKC45rA` was found without a corresponding `web_fetch_tool_result` block

2つ目は、tool_resultの内容が先頭以外に来る、一部のtool_useのidにしか答えていない、あるいはtool_resultが1つも含まれていないケースです。この場合はより早い段階で、クライアントツールのエラーとして次のように弾かれます。

`tool_use` ids were found without `tool_result` blocks immediately after: toolu_01PjgRJLbXrXEMZwDNYLnBqk. Each `tool_use` block must have a corresponding `tool_result` block in the next message.

Claudeに追加の情報を渡したいだけなら、ターンが完了してから別のユーザーメッセージとして送る必要があります。継続リクエストの中に混ぜることはできません。

2つのエラーメッセージは、どちらも「何が足りないか」を具体的に名指しする作りになっています。1つ目はサーバーツールのidとツール名を、2つ目は答えの無いtool_useidを列挙する形式です。実装側でリトライやログ出力を組むときは、このエラーメッセージ本文をそのままパースして「どのブロックが未解決か」を特定できます。汎用的な400ハンドリングでひとまとめにキャッチしてしまうと、この情報を捨てることになるので注意が必要です。

ストリーミングでも識別方法は変わらない

ストリーミングでレスポンスを受け取っている場合も、server_tool_useブロックの見分け方は同じです。Claudeが直接呼び出すserver_tool_useブロックは、クライアントのtool_useブロックと同じようにcontent_block_startイベントに続けてinput_json_deltaイベントが流れます。違うのは結果ブロック側で、web_fetch_tool_resultのようなサーバーツールの結果は差分(delta)を経由せず、content_block_startイベント1回で丸ごと届きます。混在ターンでこのサーバーツールが未完了のまま応答が終わるときは、この結果ブロックのイベント自体がストリームに現れません。イベントの有無を見れば、非ストリーミングのレスポンスと同じ判定基準がそのまま使えます。

単独呼び出しのときは何も変わらない

この記事で扱った分岐は、サーバーツールとクライアントツールが同じ並列呼び出しグループに混在したときだけ発生する話です。クライアントのtool_useブロックしか無いターンは、これまで通りのtool_result往復で継続でき、サーバーツールしか無いターンは、結果ブロックが通常はすでに揃っているか、保留になった場合はpause_turnと同じ形で再送すれば済みます。既存の実装がクライアントツールのみ、あるいはサーバーツールのみを前提に書かれている場合、この2つのケースはそのままのコードで扱えます。分岐が必要になるのは、サーバーツールとクライアントツールを新たに1つのリクエストに同居させたときだけです。そのときだけ、両方の継続方法を切り替えるコードを追加する必要があります。

プログラマティックツールコーリングでは同じ形が別の意味になる

Programmatic Tool Callingを使っている場合、tool_useが保留になっているという同じレスポンス形状が、別の状態を意味します。クライアントのtool_useブロックはClaudeが直接呼んだものではなく、code_executionツールの中で実行中のコードから呼ばれたものです。このtool_useブロックのcallerフィールドには、呼び出し元のcode_executionブロックの名前が入ります。そのコードはすでに実行が始まっていて、tool_resultを待って一時停止している状態です。tool_resultを送るとコードの実行が再開されるのであって、新しいツールを起動するわけではありません。code_executionブロック自体の結果は、コードの実行が完了した時点で届く仕組みです。これは複数回のtool_result往復を経ることもあります。フォローアップのユーザーメッセージの形自体はどちらのケースでも同じですが、Programmatic Tool Callingの場合はレスポンスのcontainerフィールドのidもあわせて返す必要があります。

分岐判定を自分で書かずに済ませる方法はあるか

このtool_usetool_resultの往復判定を手で書きたくない場合、Tool RunnerのようなSDK側の自動化機能が候補になります。ただし、サーバーツールとクライアントツールの混在ケースをTool Runnerがどう扱うかは、本記事が参照したソースからは確認できません。往復の自動化そのものは謳われていますが、本記事で扱った「未完了のserver_tool_useを検出してから継続する」という分岐を、サーバーツール込みでどこまで肩代わりしてくれるのかは、実装を読むか実際に試して確認するのが安全です。コード実行とMCPの設計も、サーバー側の実行とクライアント側の実行が入り混じる場面の考え方として参考になります。

なぜAPIは未完了のターンにここまで厳格なのか

tool_resultだけを含む、全てのtool_useidに漏れなく答える、この2つを両方満たさない限り継続リクエストを受け付けない設計は、利便性よりも一貫性を優先した選択に見えます。並列ツール呼び出しグループの一部だけが解決した状態でClaudeに次の判断をさせると、どの呼び出しが完了済みでどれが未完了かをClaude自身が推測することになり、モデルの出力が不安定になりやすい構造です。エラーメッセージがどちらも「何が足りないか」を具体的に名指しする形になっているのも、この厳格さを実装側で吸収しやすくするための配慮と読めます。

まとめ

サーバーツールとクライアントツールが同じ並列呼び出しグループに混在すると、stop_reasonpause_turnではなくtool_useになり、クライアントツールのtool_resultだけを含む1通のメッセージで継続します。tools配列を維持すること、tool_result以外のブロックを混ぜないこと、すべてのtool_useidに答えることの3点を守れば、400エラーを避けられます。Programmatic Tool Callingを使っている場合は、同じレスポンス形状が別の意味を持つ点にも注意してください。

この記事を共有:XはてブLinkedIn