CertSafari
    Anthropic Claude Certified Developer – Foundations (CCDV-F)· Lessons

    Domain 8 · Lesson 23/25

    Handling Claude Tool Calls: The Agentic Loop, Errors, and Approvals

    Tool Implementation

    9 min read
    3.53% of exam
    5 sources
    Published 4 Oct 2026
    Docs as of 30 Sep 2026

    What you will be able to do

    • Implement the stop_reason-driven loop that dispatches client tool calls
    • Format tool_result blocks correctly, including multi-part content
    • Signal execution errors with is_error and tell them apart from invalid tool calls
    • Handle server tool responses, pause_turn, and turns that mix server and client tools
    • Gate tool execution with canUseTool, allow rules, and deny rules in the Agent SDK

    1.The harness loop: dispatching client tool calls

    Claude can't run your code, so every client tool call is a round trip. Claude asks, your harness runs the tool, and you report back. The standard harness is a while loop keyed on stop_reason. The loop exits on any other stop reason (end_turn, max_tokens, stop_sequence, refusal), and your application should handle each of those.

    Checkpoint 1 of 6· Put it in order

    Put the steps of the client-tool agentic loop in order

    1. 1.Send a new request with the original messages, the assistant's response, and a user message holding the tool_result blocks
    2. 2.Send a request with your tools array and the user message
    3. 3.Repeat from step 2 while stop_reason is tool_use
    4. 4.Claude responds with stop_reason tool_use and one or more tool_use blocks
    5. 5.Execute each tool and format the outputs as tool_result blocks

    For each tool_use block, dispatch on name, pass in input, and keep the id. Blocks from the computer use or browser use toolsets also carry a toolset_name, so dispatch those on both fields. If you'd rather not write the loop yourself, the SDK's Tool Runner runs your tools and sends the results back automatically.

    Pulling out the tool_use block and appending the assistant turn plus a tool_resultpython
    tool_use = next(block for block in response.content if block.type == "tool_use")
    print(f"Claude called {tool_use.name} with {json.dumps(tool_use.input)}")
    
    # Run the tool, then send the result back in a tool_result block.
    weather = "15 degrees Celsius, partly cloudy"  # your weather lookup goes here
    messages += [
        {"role": "assistant", "content": response.content},
        {
            "role": "user",
            "content": [
                {"type": "tool_result", "tool_use_id": tool_use.id, "content": weather}
            ],
        },
    ]

    Sources123

    2.Formatting tool_result blocks

    A tool result goes in a user message as a tool_result block. tool_use_id is required and matches the id of the request it answers. content is optional. It can be a plain string, or a list of text, image, document, or search_result blocks, so a tool can return a caption and a screenshot together. You may leave content out entirely. Results for toolset members are stricter: they must echo the same toolset_name, and the API rejects a member result without it.

    Checkpoint 2 of 6· Check yourself

    Claude made two tool calls in one turn. How does the API know which tool_result answers which call?

    Sources2

    3.Error handling: execution failures versus invalid calls

    There are two kinds of failure. In an execution error, the tool itself fails, for example the weather API returns HTTP 500. Return the error message as the content with is_error: true, and Claude will work the failure into its reply to the user. In an invalid call, Claude's request is malformed, for example a required parameter is missing. That usually means the definition didn't give Claude enough to go on. During development, the fix is a more detailed description. At runtime, you can return an is_error result that names the problem, and Claude will retry 2–3 times with corrections before apologizing. Strict tool use stops schema violations before they happen.

    Checkpoint 3 of 6· Fill the gap

    Which field tells Claude that this tool execution failed?

    {
      "role": "user",
      "content": [
        {
          "type": "tool_result",
          "tool_use_id": "toolu_01A09q90qw90lq917835lq9",
          "content": "ConnectionError: the weather service API is not available (HTTP 500)",
          " ? ": true
        }
      ]
    }

    Sources2

    4.Server tools at runtime: pause_turn and mixed turns

    Server tools run their own loop inside Anthropic's infrastructure. In the response, they appear as server_tool_use blocks whose ids start with srvtoolu_, each followed by its result block. You never send a tool_result for them, and server tool errors are handled transparently, so there's no is_error to manage. Two exceptions hand control back to you. First, the server loop has an iteration cap. When Claude hits it, you get pause_turn: re-send the paused response as-is with the same tools, and repeat with a cap on continuations. Second, Claude may call a server tool and a client tool in the same parallel group. Then you get tool_use with a server_tool_use block that has no matching result. Send only the client tool_result blocks and keep the same tools array. The API then runs the deferred server tool.

    Checkpoint 4 of 6· Match them up

    Match each response to how your harness should continue

    Tap a term, then the definition that fits it.

    Sources41

    5.Approval patterns: deciding which calls may run

    Claude only ever sends a request, and your code runs every client tool. That makes your harness the natural place to approve or block calls. The Agent SDK builds this in with permission modes, hooks, and declarative allow/deny rules. Hooks run custom code that can allow, deny, or modify a tool request. Allow rules such as allowed_tools=["Read", "Grep"] auto-approve the tools they list. Unlisted tools still exist, and their calls fall through to the permission mode and the canUseTool callback. A disallowed_tools entry is a different thing: it removes the tool definition from the request entirely. When no earlier step settles a call, canUseTool asks the user at runtime.

    A canUseTool callback returning allow or denytypescript
          // Return allow or deny based on user's response
          if (response.toLowerCase() === "y") {
            // Allow: tool executes with the original (or modified) input
            return { behavior: "allow", updatedInput: input };
          } else {
            // Deny: tool doesn't execute, Claude sees the message
            return { behavior: "deny", message: "User denied this action" };
          }

    Checkpoint 5 of 6· Exam question

    A security review requires that a specific destructive shell pattern, `rm -rf *`, always be blocked regardless of any other configuration, including future changes to permission mode. Which rule configuration satisfies this requirement, based on how the SDK's permission evaluation order treats deny rules?

    Checkpoint 6 of 6· Check yourself

    You set disallowed_tools=["Bash"]. What does Claude experience?

    Sources51

    Exam traps

    Each one states something that sounds right. Open it to see what is actually true.

    1. 1.Server tools need a tool_result from you, and is_error handling just like client tools.Why is that wrong?

      The API runs server tools itself and puts their result blocks in the same response. Server tool errors are handled transparently.

      Covered in Server tools at runtime: pause_turn and mixed turns

    2. 2.When a server tool hasn't run yet, the response is always pause_turn, so you re-send it as-is.Why is that wrong?

      If Claude also called a client tool, the API returns tool_use without running the server tool, and you continue by sending the client tool_result blocks.

      Covered in Server tools at runtime: pause_turn and mixed turns

    3. 3.Leaving a tool out of allowed_tools blocks Claude from using it.Why is that wrong?

      allowed_tools only auto-approves. Unlisted tools still exist and fall through to the permission mode and canUseTool. To remove a tool, use disallowed_tools.

      Covered in Approval patterns: deciding which calls may run

    Sources

    Every claim above is drawn from one of these pages, quoted as it was written on the date shown.

    1. 1.
      “Client-executed tools (both user-defined and Anthropic-schema) require your application to drive a loop.”
      ↩︎ The harness loop: dispatching client tool calls
      “A paused turn means the work isn't finished”
      ↩︎ Server tools at runtime: pause_turn and mixed turns
      “The model never executes anything on its own.”
      ↩︎ Approval patterns: deciding which calls may run
      “Send a new request containing the original messages, the assistant's response, and a user message with the tool_result blocks.”
      ↩︎ Checkpoint
    2. 2.
      “dispatch those blocks on both fields.”
      ↩︎ The harness loop: dispatching client tool calls
      “These content blocks can use the text, image, document, or search_result types.”
      ↩︎ Formatting tool_result blocks
      “a member result that omits it is rejected”
      ↩︎ Formatting tool_result blocks
      “Claude will retry 2-3 times with corrections before apologizing to the user.”
      ↩︎ Error handling: execution failures versus invalid calls
      “Your best bet during development is to try the request again with more-detailed description values in your tool definitions.”
      ↩︎ Error handling: execution failures versus invalid calls
      “Unlike client tools, you do not need to handle is_error results for server tools.”
      ↩︎ Exam trap 1
      “A unique identifier for this particular tool use block. This will be used to match up the tool results later.”
      ↩︎ Checkpoint
    3. 3.
      “To skip writing this round trip yourself, use Tool Runner: the SDK executes your tools and sends the results back automatically.”
      ↩︎ The harness loop: dispatching client tool calls
    4. 4.
      “Its id field uses the srvtoolu_ prefix to distinguish it from client tool calls”
      ↩︎ Server tools at runtime: pause_turn and mixed turns
      “capping the number of continuations as you would any retry loop.”
      ↩︎ Server tools at runtime: pause_turn and mixed turns
      “When that happens, the API does not run the server tool. It returns immediately so that you can run the client tool first”
      ↩︎ Exam trap 2
      “A response that leaves a client tool_use block waiting on you never has a stop_reason of pause_turn”
      ↩︎ Prediction
      “Detect the state by looking for a server_tool_use block whose id has no matching result block in the response.”
      ↩︎ Checkpoint
    5. 5.
      “Control how your agent uses tools with permission modes, hooks, and declarative allow/deny rules.”
      ↩︎ Approval patterns: deciding which calls may run
      “canUseTool callback: prompt users for approval at runtime, when no earlier step resolves the call.”
      ↩︎ Approval patterns: deciding which calls may run
      “Other tools not listed here still exist, and calls to them that need approval fall through to the permission mode and canUseTool.”
      ↩︎ Exam trap 3
      “The Bash tool definition is removed from the request. Claude does not see the tool and cannot attempt it.”
      ↩︎ Checkpoint

    Ready to test yourself?

    Practise the 20 questions on this subdomain.

    Spotted a mistake, or was something unclear? Tell us.