Introduction & Overview

In the previous lessons, you learned how to create tool schemas and understand Claude's responses when it wants to use tools. You can now recognize when Claude requests tool execution through the stop_reason: "tool_use" signal and extract the necessary details from tool use blocks. However, knowing what Claude wants to do is only half the story — you still need to actually execute those tools and complete the conversation cycle.

In this lesson, you'll learn how to bridge that gap by executing the functions Claude requests, capturing their results, and sending those results back to Claude in the proper format. By the end of this lesson, you'll have a complete tool execution pipeline that can handle Claude's tool requests from start to finish, maintaining proper conversation flow throughout the entire process.

The Complete Tool Execution Flow

Before we dive into the implementation, let's understand the complete workflow we'll be building in this lesson. Here's the step-by-step process that transforms Claude from a simple chatbot into a capable agent:

  1. Set up the foundation - Create function mappings, tool schemas, and initial conversation messages
  2. Send the initial request - Make the first API call to Claude with the user's question and available tools
  3. Detect tool use requests - Check Claude's response for the "tool_use" stop reason
  4. Extract tool information - Pull out the function name, parameters, and unique ID from each tool use block
  5. Execute the requested functions - Use our function mapping to call the actual Python functions with Claude's parameters
  6. Collect and format tool results - Structure all function outputs in the specific format Claude expects
  7. Send results back to Claude - Make a second API call with the complete conversation including tool results
  8. Display the final response - Show Claude's natural language answer that incorporates the tool outputs

This complete cycle enables Claude to seamlessly use tools as part of its reasoning process, transforming raw function outputs into conversational responses that directly answer user questions.

Setting Up the Foundation

Before diving into tool execution, we need to establish the foundation that connects Claude's tool requests to our actual Python functions. As we covered in previous lessons, this involves creating a mapping dictionary and preparing our tool schemas and initial messages. The critical component here is the function mapping dictionary — this serves as the bridge between the tool names Claude uses and our actual Python functions.

This setup creates everything we need for tool execution: the tools dictionary enables dynamic function lookup, the system_prompt guides Claude's behavior, the tool_schemas provide technical specifications, and the messages array starts the conversation. The function mapping is particularly important because it allows our code to execute the correct function based on Claude's string-based tool requests.

Sending the Initial Request

With our foundation in place, we can now send the initial request to Claude and immediately prepare for processing its response. This approach ensures we maintain proper conversation history from the start.

This API call provides Claude with the complete context: the user's question through messages, behavioral guidance through system, and available capabilities through tools. By immediately adding Claude's response to the messages array, we ensure proper conversation flow whether Claude needs tools or can answer directly.

Detecting Tool Use Requests

After receiving Claude's response and adding it to our conversation history, we examine the stop_reason field to determine if tool execution is needed. When Claude determines that tools are required, it signals this through the "tool_use" stop reason.

The stop_reason == "tool_use" condition indicates that Claude has identified tools it wants to execute and is waiting for their results before continuing. We initialize a tool_results list to collect all the results from this tool execution cycle. The extraction process identifies each tool use block within Claude's response and pulls out the three critical pieces of information: the function name, the input parameters, and the unique identifier. The tool_id is particularly important because we'll need it to match results back to their corresponding tool requests.

Executing the Requested Functions

With the tool information extracted, we can now execute the actual Python functions using our mapping dictionary. This is where the tool names from Claude's requests get translated into actual function calls.

Using a try-except block provides comprehensive error handling that catches not only missing tools but also any runtime errors that might occur during function execution. For example, if a function receives invalid parameters (like trying to divide by zero), the exception will be caught and converted into an error message that can be sent back to Claude. The tools[tool_name] lookup retrieves the actual function object from our mapping dictionary, while **tool_input unpacks the input dictionary as keyword arguments. This approach allows dynamic function execution based on Claude's string-based requests while gracefully handling both missing functions and execution failures.

Collecting and Formatting Tool Results

After executing each tool (or encountering an error), we must format the results in the specific structure that Claude expects and collect them all before sending them back. This ensures that all tool results are sent together as a single message.

The tool result structure has specific requirements: each result needs a "tool_result" type, the tool_use_id must exactly match the id from the original tool use block, and the content must be converted to a string. By collecting all results in a list and sending them as a single message, we maintain proper conversation structure while ensuring Claude receives results for all requested tools.

It's critical that you provide a tool result for every tool use request that Claude makes. If you skip providing a result for any tool_use_id, your next API call to Claude will fail with an error because Claude expects to receive results for all the tools it requested. This is why we handle both successful executions and errors in the same way - Claude needs to know what happened with each tool request, whether it succeeded or failed.

Getting Claude's Final Response

Once we've executed all requested tools and added their results to the messages array as a single message, we can send the updated conversation back to Claude to get the final response that incorporates the tool results.

This second API call uses the same parameters as the first, but now the messages array contains the complete conversation history including all tool results in a single message. Claude can now provide a comprehensive answer that incorporates the tool execution results, transforming raw calculation outputs into natural, conversational responses.

The complete execution flow produces the following output, showing both the tool execution and Claude's final response:

This output demonstrates the complete tool execution cycle: our code executes the requested function with the provided parameters, captures the numerical result, and then Claude transforms that raw output into a natural, conversational response that directly answers the user's original question.

Handling Non-Tool Responses

Not every user request will require tool usage. When Claude can answer directly without needing to execute functions, it will respond with a different stop_reason. Since we've already added Claude's response to our messages array, we just need to handle the display of non-tool responses.

This else block catches any stop_reason that isn't "tool_use" and handles it appropriately. The most common alternative stop_reason is "end_turn", which indicates Claude has finished its response and doesn't need any tools to answer the user's question.

If you prefer to be more explicit about which stop reasons you're handling, you can replace the general else with a specific condition:

Both approaches accomplish the same goal, but the explicit elif makes it clear that you're specifically handling the "end_turn" case. For example, if a user asks "What is the capital of France?", Claude would respond directly with stop_reason: "end_turn" since no mathematical calculations are needed. This ensures your application handles both tool-requiring and non-tool scenarios gracefully, maintaining proper conversation flow in all cases.

Visualizing the Complete Conversation History

Throughout the tool execution cycle, maintaining proper conversation history enables Claude to understand context and provide coherent responses. Let's examine how the complete conversation flow develops through each step of the process.

We check for strings vs lists because message content can be either format: simple user messages are often strings (like our initial "Please calculate 15 + 27"), while SDK responses and tool results are always lists of structured content blocks. Without this check, iterating over a string would print each character separately instead of the full message.

This debugging output reveals the complete conversation structure that enables Claude's tool-using capabilities:

This conversation history shows the complete tool execution cycle: the user's initial request, Claude's response containing both explanation and tool request, our tool result with the matching ID, and Claude's final response incorporating the tool output. This structure demonstrates how proper message management creates seamless conversation flow that transforms Claude into a capable agent.

Summary & Practice Preparation

You now understand the complete tool execution workflow that transforms Claude from a text-only assistant into an agent capable of performing actions and calculations. The process involves detecting tool use requests through stop reasons, executing functions through mapping dictionaries, collecting and formatting all results together, and maintaining conversation history throughout the entire cycle.

The key components work together to create a robust system: function mapping enables dynamic execution, proper result collection ensures all tools are handled, correct message formatting allows Claude to understand results, and conversation history maintains context across the entire interaction. This foundation allows Claude to act as an intelligent agent that can reason about when to use tools and incorporate their results into natural responses.

In the upcoming practice exercises, you'll implement this complete workflow yourself, working with different types of tools and handling various scenarios, including multiple tool uses and error handling. Happy coding!

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