Understanding How Claude Uses Tools

Introduction & Overview

Welcome! In this lesson, you will learn how Claude responds when it decides to use your tools. In the previous lesson, you learned how to create tool schemas that describe your methods to Claude. Now, you will discover how to include these tools in your API requests and, more importantly, how to interpret Claude's responses when it seeks to use them.

In this lesson, you will learn how to configure your requests to enable tool use, understand the different types of responses Claude can provide, and extract the specific information needed to execute the tools Claude requests. By the end, you will be able to recognize when Claude wants to use a tool and gather all the details necessary to facilitate that process.

Adding Tool Guidance to Your System Prompt

While Claude automatically sees and can use any tools you provide in your request, mentioning tools in your system prompt can help ensure more consistent behavior and guide Claude toward using them appropriately.

Before we construct our prompt, we require the anthropic gem and initialize our API client. Then, we define the system_prompt:

require "anthropic"

# Initialize the Anthropic client
client = Anthropic::Client.new

# System prompt with optional tool usage guidance
system_prompt = (
  "You are a helpful math assistant. " \
  "When performing calculations, use the available tools for accuracy."
)

This guidance helps Claude understand your preferences for when and how to use tools, but it is not required for tool functionality. Claude can still recognize and use your tools based solely on their availability in the request.

Providing Tools to Claude

The tools parameter is the essential component that makes your methods available to Claude. This parameter accepts the JSON array of schemas you created in the previous lesson:

require "json"

# Load your tool schemas
tool_schemas = JSON.parse(File.read("schemas.json"))

# Create a message requesting a calculation
messages = [
  { role: "user", content: "Please calculate 15 + 27" }
]

# Send the request with tools enabled
response = client.messages.create(
  model: "claude-sonnet-4-6",
  max_tokens: 2000,
  messages: messages,
  system: system_prompt,
  tools: tool_schemas  # This makes your tools available to Claude
)

Claude recognizes the available tools from the tools parameter alone. While system prompt guidance can be helpful for encouraging consistent tool usage patterns, Claude can see and use your tools even without explicit instructions in the system prompt.

Understanding Claude's Tool Use Responses

When Claude decides to use a tool, the response structure changes significantly from a simple text response. Let's examine a complete tool use response by converting the response to a Hash and formatting it as JSON:

# Print the complete response structure
puts JSON.pretty_generate(response.to_h)

This will output a detailed JSON structure showing all the components of Claude's response:

{
  "id": "msg_01FDfzUf1P61sPEMJcZTE2T5",
  "content": [
    {
      "text": "Sure! Let me calculate that for you right away.",
      "type": "text"
    },
    {
      "id": "toolu_01G6cbK1EG7zkj8DCkWx97Ff",
      "caller": {
        "type": "direct"
      },
      "input": {
        "a": 15,
        "b": 27
      },
      "name": "sum_numbers",
      "type": "tool_use"
    }
  ],
  "model": "claude-sonnet-4-6",
  "role": "assistant",
  "stop_reason": "tool_use",
  "type": "message",
  "usage": {...}
}

Notice two key differences from regular text responses:

  • The stop_reason is "tool_use" instead of "end_turn".
  • The content array contains both text and tool_use blocks.

Additionally, you might notice the "caller": { "type": "direct" } field inside the tool_use block. This simply indicates that Claude itself made the direct decision to invoke the tool, which is standard for autonomous tool use.

This structure allows Claude to explain its actions while providing the structured information your system needs to execute the requested methods.

The Importance of stop_reason

The stop_reason field is your primary indicator for determining the next action in your agent logic. Let's extract and examine this field to understand its possible values:

# Check the stop reason to determine next steps
puts "Stop Reason: #{response.stop_reason}"

When you run this code, you will see the following output:

Stop Reason: tool_use

The stop_reason acts as a control flow signal for your agent. Here are the main values you will encounter:

  • "tool_use": Claude has requested tool execution and is waiting for results.
  • "end_turn": Claude has completed its response, and the conversation can continue normally.
  • "max_tokens": Claude reached the token limit and may have more to say.
  • "stop_sequence": Claude encountered a predefined stop sequence.

Understanding these values helps you build proper agent logic that responds appropriately to Claude's intentions.

Understanding the Content Array and Tool Use Blocks

When stop_reason is "tool_use", the content array contains a mix of text explanations and tool use requests. Claude includes text blocks to explain its reasoning and its intended actions, making the interaction more transparent and user-friendly. The content array can also contain multiple tool_use blocks when Claude wants to execute several tools in parallel. Each content item has a type field that indicates how it should be handled.

Let's iterate through the content array to examine each item:

# Process each content item
response.content.each_with_index do |content_item, i|
  puts "\nContent Item #{i + 1}:"
  puts "Type: #{content_item.type}"
  
  case content_item.type.to_s
  when "text"
    puts "Text: #{content_item.text}"
  when "tool_use"
    puts "Tool Name: #{content_item.name}"
    puts "Tool Input: #{content_item.input}"
    puts "Tool ID: #{content_item.id}"
  end
end

Running this code will show you the structure of each content item:

Content Item 1:
Type: text
Text: Sure! Let me calculate that for you right away.

Content Item 2:
Type: tool_use
Tool Name: sum_numbers
Tool Input: {:a=>15, :b=>27}
Tool ID: toolu_01G6cbK1EG7zkj8DCkWx97Ff

Notice that we use .to_s on content_item.type in our case statement. The Anthropic Ruby SDK returns API fields (like type and stop_reason) as symbol-like objects rather than plain strings. Converting them to strings with .to_s ensures a safe, robust comparison against our string literals.

Additionally, you might notice that the Tool Input prints as {:a=>15, :b=>27} with Ruby symbol keys (like :a) instead of the string keys defined in your JSON schema. The Anthropic Ruby SDK automatically parses the JSON returned by the API and converts the JSON object keys into Ruby symbols to make them easier to work with in your code.

Each tool_use block contains three essential pieces of information that your system needs to execute the requested method:

  • name: The method name that matches your tool schema (e.g., "sum_numbers").
  • input: A Hash of parameters to pass to your method, with keys mapped to symbols.
  • id: A unique identifier for this specific tool_use request, which you will need when sending results back to Claude.

These fields work together to provide everything you need to execute the tool and maintain a proper conversation flow.

Summary and Next Steps

You now understand how Claude communicates its tool use intentions through structured API responses. When Claude decides to use tools, it provides both human-readable explanations and machine-readable tool_use blocks within the content array; the stop_reason field then acts as a control signal for your system.

In the upcoming practice exercises, you will work with these response structures hands-on, learning to parse tool_use requests and preparing for the next step: executing the requested tools and sending results back to Claude to complete the conversation flow.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal