SPB Git

Repository Public

parallel-tool-use.md 53.5 KB 1,595 lines markdown

1---2title: Parallel tool use3url: https://platform.claude.com/docs/en/agents-and-tools/tool-use/parallel-tool-use4description: Enable, format, and disable parallel tool calls, with message-history guidance and troubleshooting.5---67By default, Claude may call multiple tools in a single response. This page covers how to run those calls, how to format the message history so parallelism keeps working, and how to disable parallel tool use when you need to. For the single-call flow, see [Handle tool calls](https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls).89## Execution semantics1011When Claude calls tools, the response has a `stop_reason` of `tool_use` and can contain several `tool_use` blocks in a single assistant turn. How you run those calls is your decision. The API doesn't prescribe an execution order: you can run the calls concurrently (`Promise.all`, `asyncio.gather`), sequentially in the order they appear, or in any combination that suits your tools.1213Choose the strategy based on what your tools do. Independent, read-only operations are usually safe to run in parallel for lower latency. Tools with side effects, shared state, or ordering requirements might be better run sequentially.1415Whichever strategy you use, return one `tool_result` for each `tool_use` block, all together in the next user message. Match each result to its call with `tool_use_id`, and put every `tool_result` block before any text content in that message. See [Handle tool calls](https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls) for the full formatting rules. If you choose not to run a particular call (for example, because you ran the batch sequentially and an earlier call failed), still return a `tool_result` for it with `is_error: true` and a brief explanation.1617```json18{19  "type": "tool_result",20  "tool_use_id": "toolu_02",21  "is_error": true,22  "content": "Not executed: the preceding write_file call failed."23}24```2526The [computer use tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool#batch-actions) and the [browser use tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#batch-actions) are stricter. When Claude returns several of their member tool calls in one turn (a batch action), run them sequentially in the order they appear and stop at the first failure; each tool defines the exact text to return for the calls you skip.2728## Test parallel tool calls2930<Note>31  **Use the Tool Runner for most applications:** the SDK [Tool Runner](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-runner) handles responses with multiple tool calls and formats the results for you, so you don't write this handling yourself. Use the manual pattern on this page when you need direct control over how the calls run, such as custom batching, ordering, or error handling.32</Note>3334The following script sends a request that should trigger parallel tool calls, verifies the response contains them, and formats the tool results so parallelism keeps working. Run it with `ANTHROPIC_API_KEY` set in your environment:3536<CodeGroup>37  ```bash cURL38  # This end-to-end test flow doesn't translate well to a one-off shell command.39  # See the SDK tabs for the full flow. The underlying HTTP request is a standard40  # tool use request with multiple tools defined.41  ```4243  ```bash CLI44  # This end-to-end test flow doesn't translate well to a one-off shell command.45  # See the SDK tabs for the full flow.46  ```4748  ```python Python49  client = Anthropic()5051  # Define tools52  tools = [53      {54          "name": "get_weather",55          "description": "Get the current weather in a given location",56          "input_schema": {57              "type": "object",58              "properties": {59                  "location": {60                      "type": "string",61                      "description": "The city and state, e.g. San Francisco, CA",62                  }63              },64              "required": ["location"],65          },66      },67      {68          "name": "get_time",69          "description": "Get the current time in a given timezone",70          "input_schema": {71              "type": "object",72              "properties": {73                  "timezone": {74                      "type": "string",75                      "description": "The timezone, e.g. America/New_York",76                  }77              },78              "required": ["timezone"],79          },80      },81  ]8283  # Test conversation with parallel tool calls84  messages = [85      {86          "role": "user",87          "content": "What's the weather in SF and NYC, and what time is it there?",88      }89  ]9091  # Make initial request92  print("Requesting parallel tool calls...")93  response = client.messages.create(94      model="claude-opus-5", max_tokens=1024, messages=messages, tools=tools95  )9697  # Check for parallel tool calls98  tool_uses = [block for block in response.content if block.type == "tool_use"]99  print(f"\n✓ Claude made {len(tool_uses)} tool calls")100101  if len(tool_uses) > 1:102      print("✓ Parallel tool calls detected!")103      for tool in tool_uses:104          print(f"  - {tool.name}: {tool.input}")105  else:106      print("✗ No parallel tool calls detected")107108  # Simulate tool execution and format results correctly109  tool_results = []110  for tool_use in tool_uses:111      if tool_use.name == "get_weather":112          if "San Francisco" in str(tool_use.input):113              result = "San Francisco: 68°F, partly cloudy"114          else:115              result = "New York: 45°F, clear skies"116      else:  # get_time117          if "Los_Angeles" in str(tool_use.input):118              result = "2:30 PM PST"119          else:120              result = "5:30 PM EST"121122      tool_results.append(123          {"type": "tool_result", "tool_use_id": tool_use.id, "content": result}124      )125126  # Continue conversation with tool results127  messages.extend(128      [129          {"role": "assistant", "content": response.content},130          {"role": "user", "content": tool_results},  # All results in one message!131      ]132  )133134  # Get final response135  print("\nGetting final response...")136  final_response = client.messages.create(137      model="claude-opus-5", max_tokens=1024, messages=messages, tools=tools138  )139140  final_text = next(141      block.text for block in final_response.content if block.type == "text"142  )143  print(f"\nClaude's response:\n{final_text}")144145  # Verify formatting146  print("\n--- Verification ---")147  print(f"✓ Tool results sent in single user message: {len(tool_results)} results")148  print("✓ No text before tool results in content array")149  print("✓ Conversation formatted correctly for future parallel tool use")150  ```151152  ```typescript TypeScript153  const client = new Anthropic();154155  // Define tools156  const tools: Anthropic.Tool[] = [157    {158      name: "get_weather",159      description: "Get the current weather in a given location",160      input_schema: {161        type: "object" as const,162        properties: {163          location: {164            type: "string",165            description: "The city and state, e.g. San Francisco, CA"166          }167        },168        required: ["location"]169      }170    },171    {172      name: "get_time",173      description: "Get the current time in a given timezone",174      input_schema: {175        type: "object" as const,176        properties: {177          timezone: {178            type: "string",179            description: "The timezone, e.g. America/New_York"180          }181        },182        required: ["timezone"]183      }184    }185  ];186187  // Make initial request188  console.log("Requesting parallel tool calls...");189  const response = await client.messages.create({190    model: "claude-opus-5",191    max_tokens: 1024,192    messages: [193      {194        role: "user",195        content: "What's the weather in SF and NYC, and what time is it there?"196      }197    ],198    tools: tools199  });200201  // Check for parallel tool calls202  const toolUses = response.content.filter((block) => block.type === "tool_use");203  console.log(`\n✓ Claude made ${toolUses.length} tool calls`);204205  if (toolUses.length > 1) {206    console.log("✓ Parallel tool calls detected!");207    for (const tool of toolUses) {208      if (tool.type === "tool_use") {209        console.log(`  - ${tool.name}: ${JSON.stringify(tool.input)}`);210      }211    }212  } else {213    console.log("✗ No parallel tool calls detected");214  }215216  // Simulate tool execution and format results correctly217  const toolResults: Anthropic.ToolResultBlockParam[] = toolUses218    .filter((block): block is Anthropic.ToolUseBlock => block.type === "tool_use")219    .map((toolUse) => {220      const input = toolUse.input as Record<string, string>;221      let result: string;222      if (toolUse.name === "get_weather") {223        result = input.location?.includes("San Francisco")224          ? "San Francisco: 68F, partly cloudy"225          : "New York: 45F, clear skies";226      } else {227        result = input.timezone?.includes("Los_Angeles") ? "2:30 PM PST" : "5:30 PM EST";228      }229230      return {231        type: "tool_result" as const,232        tool_use_id: toolUse.id,233        content: result234      };235    });236237  // Get final response with correct formatting238  console.log("\nGetting final response...");239  const finalResponse = await client.messages.create({240    model: "claude-opus-5",241    max_tokens: 1024,242    messages: [243      {244        role: "user",245        content: "What's the weather in SF and NYC, and what time is it there?"246      },247      { role: "assistant", content: response.content },248      { role: "user", content: toolResults }249    ],250    tools: tools251  });252253  for (const block of finalResponse.content) {254    if (block.type === "text") {255      console.log(`\nClaude's response:\n${block.text}`);256    }257  }258259  // Verify formatting260  console.log("\n--- Verification ---");261  console.log(`✓ Tool results sent in single user message: ${toolResults.length} results`);262  console.log("✓ No text before tool results in content array");263  console.log("✓ Conversation formatted correctly for future parallel tool use");264  ```265266  ```csharp C#267  AnthropicClient client = new();268269  var tools = new List<ToolUnion>270  {271      new ToolUnion(new Tool()272      {273          Name = "get_weather",274          Description = "Get the current weather in a given location",275          InputSchema = new InputSchema()276          {277              Properties = new Dictionary<string, JsonElement>278              {279                  ["location"] = JsonSerializer.SerializeToElement(new { type = "string", description = "The city and state, e.g. San Francisco, CA" }),280              },281              Required = ["location"],282          },283      }),284      new ToolUnion(new Tool()285      {286          Name = "get_time",287          Description = "Get the current time in a given timezone",288          InputSchema = new InputSchema()289          {290              Properties = new Dictionary<string, JsonElement>291              {292                  ["timezone"] = JsonSerializer.SerializeToElement(new { type = "string", description = "The timezone, e.g. America/New_York" }),293              },294              Required = ["timezone"],295          },296      }),297  };298299  Console.WriteLine("Requesting parallel tool calls...");300  var parameters = new MessageCreateParams301  {302      Model = Model.ClaudeOpus5,303      MaxTokens = 1024,304      Messages = [new() { Role = Role.User, Content = "What's the weather in SF and NYC, and what time is it there?" }],305      Tools = tools306  };307308  var response = await client.Messages.Create(parameters);309310  var toolUses = new List<ToolUseBlock>();311  foreach (var block in response.Content)312  {313      if (block.TryPickToolUse(out var toolUse))314      {315          toolUses.Add(toolUse);316      }317  }318  Console.WriteLine($"\n✓ Claude made {toolUses.Count} tool calls");319320  if (toolUses.Count > 1)321  {322      Console.WriteLine("✓ Parallel tool calls detected!");323      foreach (var tool in toolUses)324      {325          Console.WriteLine($"  - {tool.Name}: {JsonSerializer.Serialize(tool.Input)}");326      }327  }328  else329  {330      Console.WriteLine("✗ No parallel tool calls detected");331  }332333  var toolResults = new List<ContentBlockParam>();334  foreach (var toolUse in toolUses)335  {336      string result;337      if (toolUse.Name == "get_weather")338      {339          result = JsonSerializer.Serialize(toolUse.Input).Contains("San Francisco")340              ? "San Francisco: 68°F, partly cloudy"341              : "New York: 45°F, clear skies";342      }343      else344      {345          result = JsonSerializer.Serialize(toolUse.Input).Contains("Los_Angeles")346              ? "2:30 PM PST"347              : "5:30 PM EST";348      }349350      toolResults.Add(new ContentBlockParam(new ToolResultBlockParam()351      {352          ToolUseID = toolUse.ID,353          Content = result,354      }));355  }356357  Console.WriteLine("\nGetting final response...");358  var finalParameters = new MessageCreateParams359  {360      Model = Model.ClaudeOpus5,361      MaxTokens = 1024,362      Messages = [363          new() { Role = Role.User, Content = "What's the weather in SF and NYC, and what time is it there?" },364          new() { Role = Role.Assistant, Content = response.Content.Select(block => new ContentBlockParam(block.Json)).ToList() },365          new() { Role = Role.User, Content = new MessageParamContent(toolResults) }366      ],367      Tools = tools368  };369370  var finalResponse = await client.Messages.Create(finalParameters);371  var text = finalResponse.Content.Select(b => b.Value).OfType<TextBlock>().FirstOrDefault();372  Console.WriteLine($"\nClaude's response:\n{text?.Text}");373374  Console.WriteLine("\n--- Verification ---");375  Console.WriteLine($"✓ Tool results sent in single user message: {toolResults.Count} results");376  Console.WriteLine("✓ No text before tool results in content array");377  Console.WriteLine("✓ Conversation formatted correctly for future parallel tool use");378  ```379380  ```go Go381  client := anthropic.NewClient()382383  tools := []anthropic.ToolUnionParam{384  	{OfTool: &anthropic.ToolParam{385  		Name:        "get_weather",386  		Description: anthropic.String("Get the current weather in a given location"),387  		InputSchema: anthropic.ToolInputSchemaParam{388  			Properties: map[string]any{389  				"location": map[string]any{390  					"type":        "string",391  					"description": "The city and state, e.g. San Francisco, CA",392  				},393  			},394  			Required: []string{"location"},395  		},396  	}},397  	{OfTool: &anthropic.ToolParam{398  		Name:        "get_time",399  		Description: anthropic.String("Get the current time in a given timezone"),400  		InputSchema: anthropic.ToolInputSchemaParam{401  			Properties: map[string]any{402  				"timezone": map[string]any{403  					"type":        "string",404  					"description": "The timezone, e.g. America/New_York",405  				},406  			},407  			Required: []string{"timezone"},408  		},409  	}},410  }411412  fmt.Println("Requesting parallel tool calls...")413  response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{414  	Model:     anthropic.ModelClaudeOpus5,415  	MaxTokens: 1024,416  	Messages: []anthropic.MessageParam{417  		anthropic.NewUserMessage(anthropic.NewTextBlock("What's the weather in SF and NYC, and what time is it there?")),418  	},419  	Tools: tools,420  })421  if err != nil {422  	log.Fatal(err)423  }424425  // Find tool use blocks using type switch426  type toolUseInfo struct {427  	ID    string428  	Name  string429  	Input json.RawMessage430  }431  var toolUses []toolUseInfo432  for _, block := range response.Content {433  	switch variant := block.AsAny().(type) {434  	case anthropic.ToolUseBlock:435  		toolUses = append(toolUses, toolUseInfo{436  			ID:    variant.ID,437  			Name:  variant.Name,438  			Input: variant.Input,439  		})440  	}441  }442443  fmt.Printf("\n✓ Claude made %d tool calls\n", len(toolUses))444445  if len(toolUses) > 1 {446  	fmt.Println("✓ Parallel tool calls detected!")447  	for _, tool := range toolUses {448  		fmt.Printf("  - %s: %s\n", tool.Name, string(tool.Input))449  	}450  } else {451  	fmt.Println("✗ No parallel tool calls detected")452  }453454  // Build tool results455  var toolResults []anthropic.ContentBlockParamUnion456  for _, toolUse := range toolUses {457  	var result string458  	inputStr := string(toolUse.Input)459460  	if toolUse.Name == "get_weather" {461  		if strings.Contains(inputStr, "San Francisco") {462  			result = "San Francisco: 68°F, partly cloudy"463  		} else {464  			result = "New York: 45°F, clear skies"465  		}466  	} else {467  		if strings.Contains(inputStr, "Los_Angeles") {468  			result = "2:30 PM PST"469  		} else {470  			result = "5:30 PM EST"471  		}472  	}473474  	toolResults = append(toolResults, anthropic.NewToolResultBlock(toolUse.ID, result, false))475  }476477  fmt.Println("\nGetting final response...")478  finalResponse, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{479  	Model:     anthropic.ModelClaudeOpus5,480  	MaxTokens: 1024,481  	Messages: []anthropic.MessageParam{482  		anthropic.NewUserMessage(anthropic.NewTextBlock("What's the weather in SF and NYC, and what time is it there?")),483  		response.ToParam(),484  		anthropic.NewUserMessage(toolResults...),485  	},486  	Tools: tools,487  })488  if err != nil {489  	log.Fatal(err)490  }491492  var finalText string493  for _, block := range finalResponse.Content {494  	if textBlock, ok := block.AsAny().(anthropic.TextBlock); ok {495  		finalText = textBlock.Text496  		break497  	}498  }499500  fmt.Printf("\nClaude's response:\n%s\n", finalText)501502  fmt.Println("\n--- Verification ---")503  fmt.Printf("✓ Tool results sent in single user message: %d results\n", len(toolResults))504  fmt.Println("✓ No text before tool results in content array")505  fmt.Println("✓ Conversation formatted correctly for future parallel tool use")506  ```507508  ```java Java509  AnthropicClient client = AnthropicOkHttpClient.fromEnv();510511  Tool weatherTool = Tool.builder()512      .name("get_weather")513      .description("Get the current weather in a given location")514      .inputSchema(InputSchema.builder()515          .properties(JsonValue.from(Map.of(516              "location", Map.of(517                  "type", "string",518                  "description", "The city and state, e.g. San Francisco, CA"519              )520          )))521          .putAdditionalProperty("required", JsonValue.from(List.of("location")))522          .build())523      .build();524525  Tool timeTool = Tool.builder()526      .name("get_time")527      .description("Get the current time in a given timezone")528      .inputSchema(InputSchema.builder()529          .properties(JsonValue.from(Map.of(530              "timezone", Map.of(531                  "type", "string",532                  "description", "The timezone, e.g. America/New_York"533              )534          )))535          .putAdditionalProperty("required", JsonValue.from(List.of("timezone")))536          .build())537      .build();538539  MessageCreateParams params = MessageCreateParams.builder()540      .model(Model.CLAUDE_OPUS_5)541      .maxTokens(1024L)542      .addTool(weatherTool)543      .addTool(timeTool)544      .addUserMessage("What's the weather in SF and NYC, and what time is it there?")545      .build();546547  IO.println("Requesting parallel tool calls...");548  Message response = client.messages().create(params);549550  List<ToolUseBlock> toolUses = new ArrayList<>();551  for (ContentBlock block : response.content()) {552      if (block.toolUse().isPresent()) {553          toolUses.add(block.toolUse().get());554      }555  }556557  IO.println("\n✓ Claude made " + toolUses.size() + " tool calls");558559  if (toolUses.size() > 1) {560      IO.println("✓ Parallel tool calls detected!");561      for (ToolUseBlock tool : toolUses) {562          IO.println("  - " + tool.name() + ": " + tool._input());563      }564  } else {565      IO.println("✗ No parallel tool calls detected");566  }567568  List<ContentBlockParam> toolResults = new ArrayList<>();569  for (ToolUseBlock toolUse : toolUses) {570      String result;571      if (toolUse.name().equals("get_weather")) {572          String location = toolUse._input().toString();573          result = location.contains("San Francisco")574              ? "San Francisco: 68°F, partly cloudy"575              : "New York: 45°F, clear skies";576      } else {577          String timezone = toolUse._input().toString();578          result = timezone.contains("Los_Angeles")579              ? "2:30 PM PST"580              : "5:30 PM EST";581      }582      toolResults.add(ContentBlockParam.ofToolResult(583          ToolResultBlockParam.builder()584              .toolUseId(toolUse.id())585              .content(result)586              .build()587      ));588  }589590  IO.println("\nGetting final response...");591  MessageCreateParams finalParams = MessageCreateParams.builder()592      .model(Model.CLAUDE_OPUS_5)593      .maxTokens(1024L)594      .addTool(weatherTool)595      .addTool(timeTool)596      .addUserMessage("What's the weather in SF and NYC, and what time is it there?")597      .addMessage(response)598      .addUserMessageOfBlockParams(toolResults)599      .build();600601  Message finalResponse = client.messages().create(finalParams);602  finalResponse.content().stream()603      .flatMap(block -> block.text().stream())604      .forEach(textBlock -> IO.println("\nClaude's response:\n" + textBlock.text()));605606  IO.println("\n--- Verification ---");607  IO.println("✓ Tool results sent in single user message: " + toolResults.size() + " results");608  IO.println("✓ No text before tool results in content array");609  IO.println("✓ Conversation formatted correctly for future parallel tool use");610  ```611612  ```php PHP613  $client = new Client();614615  $tools = [616      [617          'name' => 'get_weather',618          'description' => 'Get the current weather in a given location',619          'input_schema' => [620              'type' => 'object',621              'properties' => [622                  'location' => [623                      'type' => 'string',624                      'description' => 'The city and state, e.g. San Francisco, CA'625                  ]626              ],627              'required' => ['location']628          ]629      ],630      [631          'name' => 'get_time',632          'description' => 'Get the current time in a given timezone',633          'input_schema' => [634              'type' => 'object',635              'properties' => [636                  'timezone' => [637                      'type' => 'string',638                      'description' => 'The timezone, e.g. America/New_York'639                  ]640              ],641              'required' => ['timezone']642          ]643      ]644  ];645646  echo "Requesting parallel tool calls...\n";647  $response = $client->messages->create(648      maxTokens: 1024,649      messages: [650          ['role' => 'user', 'content' => "What's the weather in SF and NYC, and what time is it there?"]651      ],652      model: 'claude-opus-5',653      tools: $tools,654  );655656  $toolUses = array_filter($response->content, fn($block) => $block->type === 'tool_use');657  echo "\n✓ Claude made " . count($toolUses) . " tool calls\n";658659  if (count($toolUses) > 1) {660      echo "✓ Parallel tool calls detected!\n";661      foreach ($toolUses as $tool) {662          echo "  - {$tool->name}: " . json_encode($tool->input) . "\n";663      }664  } else {665      echo "✗ No parallel tool calls detected\n";666  }667668  $toolResults = [];669  foreach ($toolUses as $toolUse) {670      if ($toolUse->name === 'get_weather') {671          $result = str_contains(json_encode($toolUse->input), 'San Francisco')672              ? 'San Francisco: 68°F, partly cloudy'673              : 'New York: 45°F, clear skies';674      } else {675          $result = str_contains(json_encode($toolUse->input), 'Los_Angeles')676              ? '2:30 PM PST'677              : '5:30 PM EST';678      }679680      $toolResults[] = [681          'type' => 'tool_result',682          'tool_use_id' => $toolUse->id,683          'content' => $result684      ];685  }686687  echo "\nGetting final response...\n";688  $finalResponse = $client->messages->create(689      maxTokens: 1024,690      messages: [691          ['role' => 'user', 'content' => "What's the weather in SF and NYC, and what time is it there?"],692          ['role' => 'assistant', 'content' => $response->content],693          ['role' => 'user', 'content' => $toolResults]694      ],695      model: 'claude-opus-5',696      tools: $tools,697  );698699  $textBlock = array_find($finalResponse->content, static fn ($block): bool => $block->type === 'text');700  echo "\nClaude's response:\n{$textBlock->text}\n";701702  echo "\n--- Verification ---\n";703  echo "✓ Tool results sent in single user message: " . count($toolResults) . " results\n";704  echo "✓ No text before tool results in content array\n";705  echo "✓ Conversation formatted correctly for future parallel tool use\n";706  ```707708  ```ruby Ruby709  client = Anthropic::Client.new710711  tools = [712    {713      name: "get_weather",714      description: "Get the current weather in a given location",715      input_schema: {716        type: "object",717        properties: {718          location: {719            type: "string",720            description: "The city and state, e.g. San Francisco, CA"721          }722        },723        required: ["location"]724      }725    },726    {727      name: "get_time",728      description: "Get the current time in a given timezone",729      input_schema: {730        type: "object",731        properties: {732          timezone: {733            type: "string",734            description: "The timezone, e.g. America/New_York"735          }736        },737        required: ["timezone"]738      }739    }740  ]741742  puts "Requesting parallel tool calls..."743  response = client.messages.create(744    model: "claude-opus-5",745    max_tokens: 1024,746    messages: [747      { role: "user", content: "What's the weather in SF and NYC, and what time is it there?" }748    ],749    tools: tools750  )751752  tool_uses = response.content.select { |block| block.type == :tool_use }753  puts "\n✓ Claude made #{tool_uses.length} tool calls"754755  if tool_uses.length > 1756    puts "✓ Parallel tool calls detected!"757    tool_uses.each do |tool|758      puts "  - #{tool.name}: #{tool.input}"759    end760  else761    puts "✗ No parallel tool calls detected"762  end763764  tool_results = tool_uses.map do |tool_use|765    result = if tool_use.name == "get_weather"766      location = tool_use.input[:location].to_s767      location.include?("San Francisco") ? "San Francisco: 68°F, partly cloudy" : "New York: 45°F, clear skies"768    else769      timezone = tool_use.input[:timezone].to_s770      timezone.include?("Los_Angeles") ? "2:30 PM PST" : "5:30 PM EST"771    end772773    {774      type: "tool_result",775      tool_use_id: tool_use.id,776      content: result777    }778  end779780  puts "\nGetting final response..."781  final_response = client.messages.create(782    model: "claude-opus-5",783    max_tokens: 1024,784    messages: [785      { role: "user", content: "What's the weather in SF and NYC, and what time is it there?" },786      { role: "assistant", content: response.content },787      { role: "user", content: tool_results }788    ],789    tools: tools790  )791792  final_text = final_response.content.find { |block| block.type == :text }793  puts "\nClaude's response:\n#{final_text.text}"794795  puts "\n--- Verification ---"796  puts "✓ Tool results sent in single user message: #{tool_results.length} results"797  puts "✓ No text before tool results in content array"798  puts "✓ Conversation formatted correctly for future parallel tool use"799  ```800</CodeGroup>801802The summary lines at the end restate the two formatting rules that keep parallelism working: every tool result returns in a single user message, and no text content appears before the tool results in that message.803804## Maximizing parallel tool use805806Claude 4 and later models make parallel tool calls by default when a request benefits from multiple tools. For all models, you can increase the likelihood of parallel tool calls with targeted prompting:807808<AccordionGroup>809  <Accordion title="System prompts for parallel tool use">810    For Claude 4 and later models, add this to your system prompt:811812    ```text wrap813    For maximum efficiency, whenever you need to perform multiple independent operations, invoke all relevant tools simultaneously rather than sequentially.814    ```815816    For even stronger parallel tool use (recommended if the default isn't sufficient), use:817818    ```text wrap819    <use_parallel_tool_calls>820    For maximum efficiency, whenever you perform multiple independent operations, invoke all relevant tools simultaneously rather than sequentially. Prioritize calling tools in parallel whenever possible. For example, when reading 3 files, run 3 tool calls in parallel to read all 3 files into context at the same time. When running multiple read-only commands like `ls` or `list_dir`, always run all of the commands in parallel. Err on the side of maximizing parallel tool calls rather than running too many tools sequentially.821    </use_parallel_tool_calls>822    ```823  </Accordion>824825  <Accordion title="User message prompting">826    You can also encourage parallel tool use within specific user messages:827828    ```text wrap829    Instead of:830    "What's the weather in Paris? Also check London."831832    Use:833    "Check the weather in Paris and London simultaneously."834835    Or be explicit:836    "Please use parallel tool calls to get the weather for Paris, London, and Tokyo at the same time."837    ```838  </Accordion>839</AccordionGroup>840841<Note>842  **Claude Fable 5.1 in long agent loops**843844  Claude Fable 5.1 may issue fewer parallel tool calls than earlier models, most noticeably in long agent loops where the next reads are only implied (custom coding agents, bash and text editor harnesses, computer use). Standard function calling is unaffected. For the batching instruction to add and where to put it, see [Batch independent tool calls in agent loops](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/prompting-claude-fable-5-1#batch-independent-tool-calls-in-agent-loops).845</Note>846847## Disable parallel tool use848849Parallel tool use is on by default. To turn it off, set `disable_parallel_tool_use: true` inside the [`tool_choice`](https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools#forcing-tool-use) object. It is not a top-level request parameter. The effect depends on the `tool_choice` type.850851### At most one tool call852853When `tool_choice` type is `auto` (the default), setting `disable_parallel_tool_use: true` means Claude calls at most one tool per response. Claude can still answer in plain text without calling any tool. The highlighted lines are the only change from a standard tool use request:854855<CodeGroup>856  ```bash cURL857  curl https://api.anthropic.com/v1/messages \858    -H "content-type: application/json" \859    -H "x-api-key: $ANTHROPIC_API_KEY" \860    -H "anthropic-version: 2023-06-01" \861    -d '{862      "model": "claude-opus-5",863      "max_tokens": 1024,864      "tools": [{865        "name": "get_weather",866        "description": "Get the current weather in a given location",867        "input_schema": {868          "type": "object",869          "properties": {870            "location": {871              "type": "string",872              "description": "The city and state, e.g. San Francisco, CA"873            }874          },875          "required": ["location"]876        }877      }],878      "tool_choice": {"type": "auto", "disable_parallel_tool_use": true},879      "messages": [880        {"role": "user", "content": "What is the weather in San Francisco and New York?"}881      ]882    }'883  ```884885  ```bash CLI886  ant messages create <<'YAML'887  model: claude-opus-5888  max_tokens: 1024889  tools:890    - name: get_weather891      description: Get the current weather in a given location892      input_schema:893        type: object894        properties:895          location:896            type: string897            description: The city and state, e.g. San Francisco, CA898        required: [location]899  tool_choice:900    type: auto901    disable_parallel_tool_use: true902  messages:903    - role: user904      content: What is the weather in San Francisco and New York?905  YAML906  ```907908  ```python Python909  client = Anthropic()910911  response = client.messages.create(912      model="claude-opus-5",913      max_tokens=1024,914      tools=[915          {916              "name": "get_weather",917              "description": "Get the current weather in a given location",918              "input_schema": {919                  "type": "object",920                  "properties": {921                      "location": {922                          "type": "string",923                          "description": "The city and state, e.g. San Francisco, CA",924                      }925                  },926                  "required": ["location"],927              },928          }929      ],930      tool_choice={"type": "auto", "disable_parallel_tool_use": True},931      messages=[932          {933              "role": "user",934              "content": "What is the weather in San Francisco and New York?",935          }936      ],937  )938  print(response.content)939  ```940941  ```typescript TypeScript942  const client = new Anthropic();943944  const response = await client.messages.create({945    model: "claude-opus-5",946    max_tokens: 1024,947    tools: [948      {949        name: "get_weather",950        description: "Get the current weather in a given location",951        input_schema: {952          type: "object",953          properties: {954            location: {955              type: "string",956              description: "The city and state, e.g. San Francisco, CA"957            }958          },959          required: ["location"]960        }961      }962    ],963    tool_choice: { type: "auto", disable_parallel_tool_use: true },964    messages: [{ role: "user", content: "What is the weather in San Francisco and New York?" }]965  });966  console.log(response.content);967  ```968969  ```csharp C#970  AnthropicClient client = new();971972  var parameters = new MessageCreateParams973  {974      Model = Model.ClaudeOpus5,975      MaxTokens = 1024,976      Tools = [977          new ToolUnion(new Tool()978          {979              Name = "get_weather",980              Description = "Get the current weather in a given location",981              InputSchema = new InputSchema()982              {983                  Properties = new Dictionary<string, JsonElement>984                  {985                      ["location"] = JsonSerializer.SerializeToElement(new { type = "string", description = "The city and state, e.g. San Francisco, CA" }),986                  },987                  Required = ["location"],988              },989          }),990      ],991      ToolChoice = new ToolChoiceAuto { DisableParallelToolUse = true },992      Messages = [new() { Role = Role.User, Content = "What is the weather in San Francisco and New York?" }]993  };994995  var response = await client.Messages.Create(parameters);996  Console.WriteLine(response);997  ```998999  ```go Go1000  client := anthropic.NewClient()10011002  response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{1003  	Model:     anthropic.ModelClaudeOpus5,1004  	MaxTokens: 1024,1005  	Tools: []anthropic.ToolUnionParam{1006  		{OfTool: &anthropic.ToolParam{1007  			Name:        "get_weather",1008  			Description: anthropic.String("Get the current weather in a given location"),1009  			InputSchema: anthropic.ToolInputSchemaParam{1010  				Properties: map[string]any{1011  					"location": map[string]any{1012  						"type":        "string",1013  						"description": "The city and state, e.g. San Francisco, CA",1014  					},1015  				},1016  				Required: []string{"location"},1017  			},1018  		}},1019  	},1020  	ToolChoice: anthropic.ToolChoiceUnionParam{1021  		OfAuto: &anthropic.ToolChoiceAutoParam{1022  			DisableParallelToolUse: anthropic.Bool(true),1023  		},1024  	},1025  	Messages: []anthropic.MessageParam{1026  		anthropic.NewUserMessage(anthropic.NewTextBlock("What is the weather in San Francisco and New York?")),1027  	},1028  })1029  if err != nil {1030  	log.Fatal(err)1031  }1032  fmt.Println(response.Content)1033  ```10341035  ```java Java1036  AnthropicClient client = AnthropicOkHttpClient.fromEnv();10371038  InputSchema schema = InputSchema.builder()1039      .properties(1040          JsonValue.from(1041              Map.of(1042                  "location", Map.of(1043                      "type", "string",1044                      "description", "The city and state, e.g. San Francisco, CA"1045                  )1046              )1047          )1048      )1049      .putAdditionalProperty("required", JsonValue.from(List.of("location")))1050      .build();10511052  MessageCreateParams params = MessageCreateParams.builder()1053      .model(Model.CLAUDE_OPUS_5)1054      .maxTokens(1024L)1055      .addTool(1056          Tool.builder()1057              .name("get_weather")1058              .description("Get the current weather in a given location")1059              .inputSchema(schema)1060              .build()1061      )1062      .toolChoice(ToolChoiceAuto.builder().disableParallelToolUse(true).build())1063      .addUserMessage("What is the weather in San Francisco and New York?")1064      .build();10651066  Message response = client.messages().create(params);1067  IO.println(response.content());1068  ```10691070  ```php PHP1071  $client = new Client();10721073  $response = $client->messages->create(1074      maxTokens: 1024,1075      messages: [1076          ['role' => 'user', 'content' => 'What is the weather in San Francisco and New York?']1077      ],1078      model: 'claude-opus-5',1079      toolChoice: ['type' => 'auto', 'disableParallelToolUse' => true],1080      tools: [1081          [1082              'name' => 'get_weather',1083              'description' => 'Get the current weather in a given location',1084              'input_schema' => [1085                  'type' => 'object',1086                  'properties' => [1087                      'location' => [1088                          'type' => 'string',1089                          'description' => 'The city and state, e.g. San Francisco, CA'1090                      ]1091                  ],1092                  'required' => ['location']1093              ]1094          ]1095      ],1096  );10971098  echo $response;1099  ```11001101  ```ruby Ruby1102  client = Anthropic::Client.new11031104  response = client.messages.create(1105    model: "claude-opus-5",1106    max_tokens: 1024,1107    tools: [1108      {1109        name: "get_weather",1110        description: "Get the current weather in a given location",1111        input_schema: {1112          type: "object",1113          properties: {1114            location: {1115              type: "string",1116              description: "The city and state, e.g. San Francisco, CA"1117            }1118          },1119          required: ["location"]1120        }1121      }1122    ],1123    tool_choice: { type: "auto", disable_parallel_tool_use: true },1124    messages: [1125      { role: "user", content: "What is the weather in San Francisco and New York?" }1126    ]1127  )1128  puts response.content1129  ```1130</CodeGroup>11311132### Exactly one tool call11331134When `tool_choice` type is `any` or `tool`, setting `disable_parallel_tool_use: true` means Claude calls exactly one tool. Claude Fable 5.1 and Claude Mythos 5.1 don't support these `tool_choice` types (see [Forcing tool use](https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools#forcing-tool-use)). The following example uses `any`. The same field works with `tool`:11351136<CodeGroup>1137  ```bash cURL1138  curl https://api.anthropic.com/v1/messages \1139    -H "content-type: application/json" \1140    -H "x-api-key: $ANTHROPIC_API_KEY" \1141    -H "anthropic-version: 2023-06-01" \1142    -d '{1143      "model": "claude-opus-5",1144      "max_tokens": 1024,1145      "tools": [{1146        "name": "get_weather",1147        "description": "Get the current weather in a given location",1148        "input_schema": {1149          "type": "object",1150          "properties": {1151            "location": {1152              "type": "string",1153              "description": "The city and state, e.g. San Francisco, CA"1154            }1155          },1156          "required": ["location"]1157        }1158      }],1159      "tool_choice": {"type": "any", "disable_parallel_tool_use": true},1160      "messages": [1161        {"role": "user", "content": "What is the weather in San Francisco and New York?"}1162      ]1163    }'1164  ```11651166  ```bash CLI1167  ant messages create <<'YAML'1168  model: claude-opus-51169  max_tokens: 10241170  tools:1171    - name: get_weather1172      description: Get the current weather in a given location1173      input_schema:1174        type: object1175        properties:1176          location:1177            type: string1178            description: The city and state, e.g. San Francisco, CA1179        required: [location]1180  tool_choice:1181    type: any1182    disable_parallel_tool_use: true1183  messages:1184    - role: user1185      content: What is the weather in San Francisco and New York?1186  YAML1187  ```11881189  ```python Python1190  client = Anthropic()11911192  response = client.messages.create(1193      model="claude-opus-5",1194      max_tokens=1024,1195      tools=[1196          {1197              "name": "get_weather",1198              "description": "Get the current weather in a given location",1199              "input_schema": {1200                  "type": "object",1201                  "properties": {1202                      "location": {1203                          "type": "string",1204                          "description": "The city and state, e.g. San Francisco, CA",1205                      }1206                  },1207                  "required": ["location"],1208              },1209          }1210      ],1211      tool_choice={"type": "any", "disable_parallel_tool_use": True},1212      messages=[1213          {1214              "role": "user",1215              "content": "What is the weather in San Francisco and New York?",1216          }1217      ],1218  )1219  print(response.content)1220  ```12211222  ```typescript TypeScript1223  const client = new Anthropic();12241225  const response = await client.messages.create({1226    model: "claude-opus-5",1227    max_tokens: 1024,1228    tools: [1229      {1230        name: "get_weather",1231        description: "Get the current weather in a given location",1232        input_schema: {1233          type: "object",1234          properties: {1235            location: {1236              type: "string",1237              description: "The city and state, e.g. San Francisco, CA"1238            }1239          },1240          required: ["location"]1241        }1242      }1243    ],1244    tool_choice: { type: "any", disable_parallel_tool_use: true },1245    messages: [{ role: "user", content: "What is the weather in San Francisco and New York?" }]1246  });1247  console.log(response.content);1248  ```12491250  ```csharp C#1251  AnthropicClient client = new();12521253  var parameters = new MessageCreateParams1254  {1255      Model = Model.ClaudeOpus5,1256      MaxTokens = 1024,1257      Tools = [1258          new ToolUnion(new Tool()1259          {1260              Name = "get_weather",1261              Description = "Get the current weather in a given location",1262              InputSchema = new InputSchema()1263              {1264                  Properties = new Dictionary<string, JsonElement>1265                  {1266                      ["location"] = JsonSerializer.SerializeToElement(new { type = "string", description = "The city and state, e.g. San Francisco, CA" }),1267                  },1268                  Required = ["location"],1269              },1270          }),1271      ],1272      ToolChoice = new ToolChoiceAny { DisableParallelToolUse = true },1273      Messages = [new() { Role = Role.User, Content = "What is the weather in San Francisco and New York?" }]1274  };12751276  var response = await client.Messages.Create(parameters);1277  Console.WriteLine(response);1278  ```12791280  ```go Go1281  client := anthropic.NewClient()12821283  response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{1284  	Model:     anthropic.ModelClaudeOpus5,1285  	MaxTokens: 1024,1286  	Tools: []anthropic.ToolUnionParam{1287  		{OfTool: &anthropic.ToolParam{1288  			Name:        "get_weather",1289  			Description: anthropic.String("Get the current weather in a given location"),1290  			InputSchema: anthropic.ToolInputSchemaParam{1291  				Properties: map[string]any{1292  					"location": map[string]any{1293  						"type":        "string",1294  						"description": "The city and state, e.g. San Francisco, CA",1295  					},1296  				},1297  				Required: []string{"location"},1298  			},1299  		}},1300  	},1301  	ToolChoice: anthropic.ToolChoiceUnionParam{1302  		OfAny: &anthropic.ToolChoiceAnyParam{1303  			DisableParallelToolUse: anthropic.Bool(true),1304  		},1305  	},1306  	Messages: []anthropic.MessageParam{1307  		anthropic.NewUserMessage(anthropic.NewTextBlock("What is the weather in San Francisco and New York?")),1308  	},1309  })1310  if err != nil {1311  	log.Fatal(err)1312  }1313  fmt.Println(response.Content)1314  ```13151316  ```java Java1317  AnthropicClient client = AnthropicOkHttpClient.fromEnv();13181319  InputSchema schema = InputSchema.builder()1320      .properties(1321          JsonValue.from(1322              Map.of(1323                  "location", Map.of(1324                      "type", "string",1325                      "description", "The city and state, e.g. San Francisco, CA"1326                  )1327              )1328          )1329      )1330      .putAdditionalProperty("required", JsonValue.from(List.of("location")))1331      .build();13321333  MessageCreateParams params = MessageCreateParams.builder()1334      .model(Model.CLAUDE_OPUS_5)1335      .maxTokens(1024L)1336      .addTool(1337          Tool.builder()1338              .name("get_weather")1339              .description("Get the current weather in a given location")1340              .inputSchema(schema)1341              .build()1342      )1343      .toolChoice(ToolChoiceAny.builder().disableParallelToolUse(true).build())1344      .addUserMessage("What is the weather in San Francisco and New York?")1345      .build();13461347  Message response = client.messages().create(params);1348  IO.println(response.content());1349  ```13501351  ```php PHP1352  $client = new Client();13531354  $response = $client->messages->create(1355      maxTokens: 1024,1356      messages: [1357          ['role' => 'user', 'content' => 'What is the weather in San Francisco and New York?']1358      ],1359      model: 'claude-opus-5',1360      toolChoice: ['type' => 'any', 'disableParallelToolUse' => true],1361      tools: [1362          [1363              'name' => 'get_weather',1364              'description' => 'Get the current weather in a given location',1365              'input_schema' => [1366                  'type' => 'object',1367                  'properties' => [1368                      'location' => [1369                          'type' => 'string',1370                          'description' => 'The city and state, e.g. San Francisco, CA'1371                      ]1372                  ],1373                  'required' => ['location']1374              ]1375          ]1376      ],1377  );13781379  echo $response;1380  ```13811382  ```ruby Ruby1383  client = Anthropic::Client.new13841385  response = client.messages.create(1386    model: "claude-opus-5",1387    max_tokens: 1024,1388    tools: [1389      {1390        name: "get_weather",1391        description: "Get the current weather in a given location",1392        input_schema: {1393          type: "object",1394          properties: {1395            location: {1396              type: "string",1397              description: "The city and state, e.g. San Francisco, CA"1398            }1399          },1400          required: ["location"]1401        }1402      }1403    ],1404    tool_choice: { type: "any", disable_parallel_tool_use: true },1405    messages: [1406      { role: "user", content: "What is the weather in San Francisco and New York?" }1407    ]1408  )1409  puts response.content1410  ```1411</CodeGroup>14121413## Troubleshooting14141415If Claude isn't making parallel tool calls when expected, check these common issues:14161417**1. Incorrect tool result formatting**14181419The most common issue is formatting tool results incorrectly in the conversation history. This "teaches" Claude to avoid parallel calls.14201421Specifically for parallel tool use:14221423* **Wrong:** a separate user message for each tool result1424* **Correct:** all tool results together in a single user message14251426```json1427// Wrong: separate user messages reduce parallel tool use1428[1429  {"role": "assistant", "content": [tool_use_1, tool_use_2]},1430  {"role": "user", "content": [tool_result_1]},1431  {"role": "user", "content": [tool_result_2]}  // Separate message1432]14331434// Correct: one user message with all results maintains parallel tool use1435[1436  {"role": "assistant", "content": [tool_use_1, tool_use_2]},1437  {"role": "user", "content": [tool_result_1, tool_result_2]}  // Single message1438]1439```14401441See [Handle tool calls](https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls) for other formatting rules.14421443**2. Weak prompting**14441445Default prompting might not be sufficient. Use the stronger system prompt from [Maximizing parallel tool use](https://platform.claude.com/docs/en/agents-and-tools/tool-use/parallel-tool-use#maximizing-parallel-tool-use).14461447**3. Measuring parallel tool usage**14481449To verify parallel tool calls are working:14501451<CodeGroup>1452  ```bash cURL1453  # Measuring parallel tool use is client-side analysis of responses you've already1454  # collected, so it doesn't translate to a one-off shell command. See the SDK tabs.1455  ```14561457  ```bash CLI1458  # Measuring parallel tool use is client-side analysis of responses you've already1459  # collected, so it doesn't translate to a one-off shell command. See the SDK tabs.1460  ```14611462  ```python Python1463  messages = []  # Message objects returned by client.messages.create across your run14641465  tool_call_messages = [1466      msg for msg in messages if any(block.type == "tool_use" for block in msg.content)1467  ]1468  total_tool_calls = sum(1469      len([block for block in msg.content if block.type == "tool_use"])1470      for msg in tool_call_messages1471  )1472  avg_tools_per_message = (1473      total_tool_calls / len(tool_call_messages) if tool_call_messages else 0.01474  )1475  print(f"Average tools per message: {avg_tools_per_message}")1476  # Should be > 1.0 if parallel calls are working1477  ```14781479  ```typescript TypeScript1480  const messages: Anthropic.Message[] = []; // Message objects returned by client.messages.create across your run14811482  const toolCallMessages = messages.filter((message) =>1483    message.content.some((block) => block.type === "tool_use")1484  );1485  const totalToolCalls = toolCallMessages.reduce(1486    (sum, message) => sum + message.content.filter((block) => block.type === "tool_use").length,1487    01488  );1489  const avgToolsPerMessage =1490    toolCallMessages.length > 0 ? totalToolCalls / toolCallMessages.length : 0;1491  console.log(`Average tools per message: ${avgToolsPerMessage}`);1492  // Should be > 1.0 if parallel calls are working1493  ```14941495  ```csharp C#1496  List<Message> messages = []; // Message objects returned by client.Messages.Create across your run14971498  var toolCallMessages = messages1499      .Where(message => message.Content.Any(block => block.TryPickToolUse(out _)))1500      .ToList();1501  var totalToolCalls = toolCallMessages1502      .Sum(message => message.Content.Count(block => block.TryPickToolUse(out _)));1503  var avgToolsPerMessage = toolCallMessages.Count > 0 ? (double)totalToolCalls / toolCallMessages.Count : 0.0;1504  Console.WriteLine($"Average tools per message: {avgToolsPerMessage}");1505  // Should be > 1.0 if parallel calls are working1506  ```15071508  ```go Go1509  var messages []anthropic.Message // Message values returned by client.Messages.New across your run15101511  toolCallMessageCount := 01512  totalToolCalls := 01513  for _, message := range messages {1514  	callsInMessage := 01515  	for _, block := range message.Content {1516  		if block.Type == "tool_use" {1517  			callsInMessage++1518  		}1519  	}1520  	if callsInMessage > 0 {1521  		toolCallMessageCount++1522  		totalToolCalls += callsInMessage1523  	}1524  }15251526  avgToolsPerMessage := 0.01527  if toolCallMessageCount > 0 {1528  	avgToolsPerMessage = float64(totalToolCalls) / float64(toolCallMessageCount)1529  }1530  fmt.Println("Average tools per message:", avgToolsPerMessage)1531  // Should be > 1.0 if parallel calls are working1532  ```15331534  ```java Java1535  List<Message> messages = List.of(); // Message objects returned by client.messages().create() across your run15361537  List<Message> toolCallMessages = messages.stream()1538      .filter(message -> message.content().stream().anyMatch(ContentBlock::isToolUse))1539      .toList();1540  long totalToolCalls = toolCallMessages.stream()1541      .mapToLong(message -> message.content().stream().filter(ContentBlock::isToolUse).count())1542      .sum();1543  double avgToolsPerMessage = toolCallMessages.isEmpty() ? 0.0 : (double) totalToolCalls / toolCallMessages.size();1544  IO.println("Average tools per message: " + avgToolsPerMessage);1545  // Should be > 1.0 if parallel calls are working1546  ```15471548  ```php PHP1549  // $messages: Message objects returned by $client->messages->create() across your run1550  $messages = [];15511552  $toolCallMessages = array_values(array_filter(1553      $messages,1554      fn ($message) => count(array_filter($message->content, fn ($block) => $block->type === 'tool_use')) > 01555  ));1556  $totalToolCalls = array_sum(array_map(1557      fn ($message) => count(array_filter($message->content, fn ($block) => $block->type === 'tool_use')),1558      $toolCallMessages1559  ));1560  $avgToolsPerMessage = count($toolCallMessages) > 0 ? $totalToolCalls / count($toolCallMessages) : 0.0;1561  echo "Average tools per message: {$avgToolsPerMessage}\n";1562  // Should be > 1.0 if parallel calls are working1563  ```15641565  ```ruby Ruby1566  messages = [] # Message objects returned by client.messages.create across your run15671568  tool_call_messages = messages.select { |message| message.content.any? { |block| block.type == :tool_use } }1569  total_tool_calls = tool_call_messages.sum { |message| message.content.count { |block| block.type == :tool_use } }1570  avg_tools_per_message = tool_call_messages.empty? ? 0.0 : total_tool_calls.to_f / tool_call_messages.size1571  puts "Average tools per message: #{avg_tools_per_message}"1572  # Should be > 1.0 if parallel calls are working1573  ```1574</CodeGroup>15751576**4. Calls in a batch appear to depend on each other**15771578Execution order is your choice. If your tools have ordering dependencies, running the batch sequentially and stopping on the first failure is a valid strategy (and the required one for the [computer use](https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool#batch-actions) and [browser use](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#batch-actions) tools): return `is_error: true` for any call you didn't run. If you run in parallel and a call fails because its prerequisite hadn't completed, return `is_error: true` with the natural error message. Claude will reissue the call on the next turn. To reduce dependent calls appearing together, add this to your system prompt: "Only batch tool calls that are independent of each other."15791580## Next steps15811582<CardGroup cols={3}>1583  <Card title="Tool Runner (SDK)" icon="wrench" href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-runner">1584    Use the SDK's Tool Runner abstraction to handle the agentic loop, error wrapping, and type safety automatically.1585  </Card>15861587  <Card title="Handle tool calls" icon="arrows-left-right" href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls">1588    Parse tool\_use blocks, format tool\_result responses, and handle errors with is\_error.1589  </Card>15901591  <Card title="Define tools" icon="hammer" href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools">1592    Specify tool schemas, write effective descriptions, and control when Claude calls your tools.1593  </Card>1594</CardGroup>1595
Files main
Menu

Theme