Skip to main content

Handling tool use responses

📖 Lesson content

Summary

When Claude decides to use a tool, it returns a special response structure that requires careful handling. Understanding this response format and implementing proper conversation management is crucial for building robust tool-enabled applications.

Tool Choice Configuration

Before diving into responses, it's worth understanding how to control when Claude uses tools. The toolChoice parameter gives you three options:

  • auto - Claude decides whether to use a tool (default behavior)
  • any - Claude must use a tool but can choose which one
  • specific tool - Force Claude to use a particular tool by name

The third option is especially useful for testing when you want to ensure Claude calls a specific function.

Multi-Part Message Structure

When Claude wants to use a tool, it returns an assistant message with multiple content parts instead of just text:

The response contains two parts:

  • Text Part - Human-readable explanation like "I can help you find out the current time. Let me find that information for you"
  • ToolUse Part - Structured data telling you which tool to run and with what arguments

Understanding the ToolUse Part

The ToolUse part contains three key pieces of information:

  • toolUseId - A unique identifier you'll need when sending back the tool result
  • name - The exact tool name from your JSON schema that Claude wants to call
  • input - A dictionary of arguments Claude wants to pass to your tool function

Conversation Flow with Tools

Tool usage follows a specific conversation pattern that requires maintaining complete message history:

When you receive a tool use request, you need to:

  1. Extract the tool information from the ToolUse part
  2. Run your actual tool function
  3. Send back a ToolResult message along with the complete conversation history
  4. Include the original user message and the assistant's tool use message in your next request

User message + tools schema

Bedrock Converse API: text part + ToolUse part

1. Extract toolUseId, name, input from ToolUse part

2. Run actual tool function

3. Send ToolResult message + full history

4. Final response from Claude

Updating Helper Functions

To handle multi-part messages properly, you'll need to update your message handling functions. Here's how to make your functions flexible enough to handle both simple text and complex multi-part content:

def add_user_message(messages, content):
    if isinstance(content, str):
        user_message = {"role": "user", "content": [{"text": content}]}
    else:
        user_message = {"role": "user", "content": content}
    messages.append(user_message)

def add_assistant_message(messages, content):
    if isinstance(content, str):
        assistant_message = {"role": "assistant", "content": [{"text": content}]}
    else:
        assistant_message = {"role": "assistant", "content": content}
    messages.append(assistant_message)

You'll also want to update your chat function to return both the text and the full parts list:

def chat(messages, system=None, temperature=1.0, stop_sequences=[], tools=None):
    # ... existing setup code ...
    
    response = client.converse(**params)
    
    text = response["output"]["message"]["content"][0]["text"]
    parts = response["output"]["message"]["content"]
    
    return text, parts

Checking the Stop Reason

Always check the stopReason field in Claude's response. When it equals "tool_use", you know Claude wants to call a tool rather than just providing a text response. This is your signal to extract the tool information and execute the requested function.

With these patterns in place, you're ready to handle Claude's tool use requests and maintain proper conversation flow throughout multi-turn tool interactions.

🔁 Related lessons

📚 Source & attribution

Was this lesson helpful?

Feedback / ReportSpotted an issue or have an improvement idea?