What you will be able to do
- Describe the five steps of a client-side agentic loop, from the first request to the final answer
- Write loop control flow that keeps going on stop_reason "tool_use" and stops on "end_turn" and the other exit values
- Add tool_use and tool_result blocks to the conversation history correctly so Claude can use each result on the next iteration
- Explain why Claude, not a pre-configured sequence of tools, chooses the next action in an agentic loop, and tell a workflow from an agent
- Avoid the three termination anti-patterns: parsing natural language, treating an iteration cap as the primary stop, and treating assistant text as a completion signal
Key concept
stop_reason-driven loop — An agentic loop is a while loop controlled by one field in each API response. When stop_reason is "tool_use", you run the requested tools, add the results to the history and send another request. Any other value ends the loop.
1.The request, execute, return cycle
Tool use is a contract between your application and Claude. You declare which operations exist and what shape their inputs take, and Claude decides when to call them. Claude never runs anything itself. It sends back a structured request, your code performs the operation, and the result goes back into the conversation. Claude never sees your implementation. It sees only the schema you defined and the result you return.
Claude can't run your code, so every call to a client-executed tool is a round trip, and your application has to drive it. The agentic loop is that round trip, repeated until the task is done. The documentation gives it a standard five-step shape:
1. Send a request with your tools array and the user message. 2. Claude responds with stop_reason "tool_use" and one or more tool_use blocks. 3. Execute each tool and format the outputs as tool_result blocks. 4. Send a new request containing the original messages, the assistant's response, and a user message with the tool_result blocks. 5. Repeat from step 2 for as long as stop_reason is "tool_use".
The Agent SDK describes the same cycle from the inside. Claude receives the prompt along with the system prompt, tool definitions and conversation history. It evaluates the current state and decides what to do next. The SDK runs the tools Claude requested, and the cycle starts again. The SDK counts each full cycle as one turn.
Checkpoint 1 of 7· Put it in order
Put the steps of one client-side agentic loop iteration in order
- 1.Send a new request with the original messages, the assistant's response, and a user message of tool_result blocks
- 2.Repeat while stop_reason is still "tool_use"
- 3.Send a request with the tools array and the user message
- 4.Receive a response with stop_reason "tool_use" and one or more tool_use blocks
- 5.Execute each tool and format the outputs as tool_result blocks
The documentation lists these steps in this order. The new request in step four has to include the assistant's response as well as the results, so Claude can see both its own request and what came back.
“Send a new request containing the original messages, the assistant's response, and a user message with the tool_result blocks.”Source: platform.claude.com
2.Branching on stop_reason
A single field decides which way the loop goes. Every Messages API response includes a stop_reason that says why Claude stopped generating. The loop's control flow depends on that field and nothing else in the response.
The rule is short. If stop_reason is "tool_use", run the tools and continue the conversation. On any other value, leave the loop: either Claude has given its final answer, or it stopped for a reason your application has to deal with separately. "end_turn" is the normal exit and means Claude finished its response naturally. The other values are exits too, but they don't mean the work succeeded.
| stop_reason | When it occurs | What to do |
|---|---|---|
| tool_use | Claude is calling a tool | Run the tool, return the result, iterate again |
| end_turn | Claude finished its response naturally | Use the response; the loop is done |
| max_tokens | The response reached your max_tokens limit | Raise max_tokens or continue the response |
| pause_turn | A server-tool loop reached its iteration limit | Send the assistant content back to continue |
| refusal | Claude declined to respond | Read stop_details and retry on a fallback model |
A simple request shows what this means in practice. The loop doesn't need a tool call before it can end. The Agent SDK says Claude keeps calling tools and processing the results until it produces a response with no tool calls. If a question can be answered directly, that happens on the very first request. A first-iteration "end_turn" with only text means the task is finished. It isn't a bug.
The documentation recommends checking stop_reason explicitly in your response-handling code and giving each value its own branch:
def handle_response(response):
match response.stop_reason:
case "tool_use":
return handle_tool_use(response)
case "max_tokens":
return handle_truncation(response)
case "model_context_window_exceeded":
return handle_context_limit(response)
case "pause_turn":
return handle_pause(response)
case "refusal":
return handle_refusal(response)
case _:
# Handle end_turn and other cases
return next(
(block.text for block in response.content if block.type == "text"),
"",
)Checkpoint 2 of 7· Fill the gap
Which stop_reason value belongs in the blank so that this code runs the requested tools?
if response.stop_reason == " ? ":
# Extract and execute the tool
for block in response.content:
if block.type == "tool_use":
result = execute_tool(block.name, block.input)
# Return result to Claude for final responseClaude sets stop_reason to "tool_use" when it is calling a client tool and expects you to run it. "end_turn" means the response is finished, and "pause_turn" belongs to server-tool loops.
Source: platform.claude.comCheckpoint 3 of 7· Exam question
An architect is building a client-side agent loop against the Messages API for a data-cleanup task. The first response has stop_reason set to "tool_use" and contains a tool_use block requesting a file-listing tool. What should the loop do next to correctly continue the agentic execution?
Correct answer: A — Execute the requested tool, append a tool_result block tagged with the tool_use_id, and send the full updated conversation back to Claude
- A. Correct. The client executes the tool and returns a tool_result block tied to the tool_use_id in the next request; this is how the agentic loop lifecycle continues.
- B. Incorrect. Dropping later context and resending only the original prompt discards the pending tool call and any prior reasoning, breaking the model's view of what it already asked for.
- C. Incorrect. stop_reason "tool_use" is itself the signal to execute immediately; Claude does not need to narrate expected output before the tool runs.
- D. Incorrect. If the tool output never reaches the conversation as a tool_result, Claude has no way to see it and cannot reason about the next action.
3.Adding tool results to the conversation history
Running the tool is only half of each iteration. On every request, Claude sees the prompt, system prompt, tool definitions and the conversation history you send. If the result isn't in that history, Claude can't use it to decide what to do next.
Each tool_use block has three fields: an id, a name, and an input that matches the tool's input_schema. The id lets you pair each result with its request. Your tool_result block repeats that id in its tool_use_id field, and it goes inside a message with the user role:
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "15 degrees"
}
]
}The next request must contain three things: the original messages, the assistant's complete response including its tool_use blocks, and this user message. The continuation message needs one tool_result for every tool_use block in the response. If one is missing, the API rejects the request with an error saying tool_use ids were found without tool_result blocks immediately after them. Once the result is in the history, Claude uses it to continue answering the original prompt.
Failures go back to Claude in the same way. If the tool throws an error, for example a network failure, return the error message as the content and set "is_error": true. Claude can then explain the failure to the user, or try the call again with the missing information filled in.
One detail of message structure matters a lot. Don't put a text block after the tool_result blocks. The documentation warns that this teaches Claude to expect user input after every tool use. Claude may then end its turn early with an empty "end_turn" response.
Checkpoint 4 of 7· Check yourself
After Claude returns stop_reason "tool_use", which history update lets it use the result on the next request?
Claude needs to see its own tool_use request and the matching tool_result, linked by id. If you drop the tool_use blocks, Claude has no record of the request. A text block after the results can make Claude end its turn early.
“id: A unique identifier for this particular tool use block. This will be used to match up the tool results later.”Source: platform.claude.com
Checkpoint 5 of 7· Exam question
A junior engineer proposes capping an autonomous research agent at exactly 5 tool-call iterations and treating that cap as the loop's primary stopping mechanism, regardless of stop_reason. What is the main architectural problem with relying on a fixed iteration cap this way?
Correct answer: A — It can cut the loop off while stop_reason is still "tool_use", forcing the agent to abandon work Claude has not finished reasoning through
- A. Correct. An iteration cap is arbitrary and unrelated to task completion, so using it as the primary stop condition can truncate the loop mid-task; termination should instead follow stop_reason == "end_turn".
- B. Incorrect. The Messages API has no built-in limit on tool calls per conversation; any cap is an application-level choice.
- C. Incorrect. Appending tool_result blocks is a client-side responsibility that has nothing to do with any iteration counter the loop maintains.
- D. Incorrect. Reaching an iteration cap only affects whether the loop sends another request; it does not alter the tools parameter of any request.
4.Model-driven decisions versus pre-configured sequences
Nothing in the loop you have just seen says which tool runs next. Your code defines the available operations and their schemas, and Claude determines when and how to call them. On each iteration Claude evaluates the current state, which includes every tool_result already in the history, and decides how to proceed: call another tool, call several, or give a final answer. The result of one call can change what it does next.
This is what separates an agent from a workflow. Anthropic draws the line this way: in a workflow, LLMs and tools are orchestrated through predefined code paths. In an agent, the LLM dynamically directs its own process and tool usage. A hard-coded sequence such as "always call search, then fetch, then summarise", or a decision tree your code walks regardless of what the results say, is a workflow. The loop only counts as agentic when the choice of the next action comes from Claude's reasoning over the history.
The two are not ranked. Workflows offer predictability and consistency for well-defined tasks. Agents are the better option when flexibility and model-driven decision-making are needed. Your loop's job in the agentic case is to stay mechanical: run what Claude asked for, return the results, and let stop_reason say when it is finished.
Checkpoint 6 of 7· Match them up
Match each term to its description
Tap a term, then the definition that fits it.
A workflow fixes the path in code, while an agent lets the model choose each next step from the context it has accumulated.
“systems where LLMs dynamically direct their own processes and tool usage”Source: www.anthropic.com
5.Termination anti-patterns
Because stop_reason is the loop's control signal, three tempting shortcuts are wrong.
1. Parsing natural language to decide when to stop. Scanning Claude's prose for phrases such as "I'm done" or "task complete" is fragile. The tool-use documentation gives the general rule: if you're writing a regex to extract a decision from model output, that decision should have been a tool call. For termination, the structured signal already exists, and it is stop_reason.
2. Making an iteration cap the primary stopping mechanism. The SDK loop runs until Claude produces a response with no tool calls. A limit such as max_turns or max_iterations is a safeguard, and the documentation's own example uses it to prevent runaway sessions. Hitting the cap is not a normal finish. The result subtype becomes error_max_turns, which means the turn limit was hit before the task finished, and no result field is available. If your loop routinely ends this way, the cap is doing the job that stop_reason should do.
3. Treating assistant text as a completion indicator. A response can carry text, tool calls, or both. Claude often writes a sentence such as "I'll check the weather" in the same response as a tool_use block, and that response has stop_reason "tool_use". If you stop because text is present, you abandon the task mid-step. In the other direction, a response with no text can still be a legitimate end_turn. Branch on stop_reason, not on whether text exists.
Checkpoint 7 of 7· Check yourself
A response contains a text block followed by a tool_use block, and its stop_reason is "tool_use". What should the loop do?
A response may contain text and tool calls together. The presence of text says nothing about completion, so the loop continues while stop_reason is "tool_use".
“It may respond with text, request one or more tool calls, or both.”Source: code.claude.com
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.A task must go through at least one tool call before an "end_turn" response counts as a valid finish.Why is that wrong?
The loop ends at the first response with no tool calls, and that can be the very first response. A text-only "end_turn" on iteration one means the task is complete.
Covered in Branching on stop_reason
2.Adding the assistant's text to the history is enough to keep the loop going, and the tool_use and tool_result blocks can be left out.Why is that wrong?
The next request must include the assistant's full response and a user message with the tool_result blocks. Without them Claude has no record of the result and may ask for the same call again.
3.A loop that always calls the same tools in a fixed order is an agent, because it uses tool_use and tool_result blocks.Why is that wrong?
Using the tool API does not make a loop agentic. When code fixes the path, it is a workflow; an agent is one where the LLM dynamically directs its own process and tool usage.
Covered in Model-driven decisions versus pre-configured sequences
4.Setting max_turns is the correct way to decide when an agentic loop is finished.Why is that wrong?
The loop finishes when Claude returns a response with no tool calls. A turn cap prevents runaway sessions, and reaching it is reported as the error_max_turns result, not as success.
Covered in Termination anti-patterns
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.
“The model never executes anything on its own.”
↩︎ The request, execute, return cycle“Claude never sees your implementation; it only sees the schema you provided and the result you returned.”
↩︎ The request, execute, return cycle“Client-executed tools (both user-defined and Anthropic-schema) require your application to drive a loop.”
↩︎ The request, execute, return cycle“In practice this reads as: while stop_reason == "tool_use", execute the tools and continue the conversation.”
↩︎ Branching on stop_reason“Claude determines when and how to call them.”
↩︎ Model-driven decisions versus pre-configured sequences“if you're writing a regex to extract a decision from model output, that decision should have been a tool call”
↩︎ Termination anti-patterns“In practice this reads as: while stop_reason == "tool_use", execute the tools and continue the conversation.”
↩︎ Key concept“Send a new request containing the original messages, the assistant's response, and a user message with the tool_result blocks.”
↩︎ Exam trap 2“Send a new request containing the original messages, the assistant's response, and a user message with the tool_result blocks.”
↩︎ Checkpoint“The loop exits on any other stop reason ("end_turn", "max_tokens", "stop_sequence", or "refusal")”
↩︎ Prediction - 2.https://code.claude.com/docs/en/agent-sdk/agent-loopOfficial docs
“Steps 2 and 3 repeat as a cycle. Each full cycle is one turn.”
↩︎ The request, execute, return cycle“Claude continues calling tools and processing results until it produces a response with no tool calls.”
↩︎ Branching on stop_reason“Claude receives your prompt, along with the system prompt, tool definitions, and conversation history.”
↩︎ Adding tool results to the conversation history“Claude evaluates the current state and determines how to proceed.”
↩︎ Model-driven decisions versus pre-configured sequences“Prevent runaway sessions”
↩︎ Termination anti-patterns“Hit the maxTurns limit before finishing”
↩︎ Termination anti-patterns“It may respond with text, request one or more tool calls, or both.”
↩︎ Termination anti-patterns“Claude continues calling tools and processing results until it produces a response with no tool calls.”
↩︎ Exam trap 1“Hit the maxTurns limit before finishing”
↩︎ Exam trap 4 - 3.
“Every Messages API response includes a stop_reason field that tells you why Claude stopped generating.”
↩︎ Branching on stop_reason“The continuation is a user message of tool_result blocks, one for every tool_use block in the response”
↩︎ Adding tool results to the conversation history“tool_use ids were found without tool_result blocks immediately after”
↩︎ Adding tool results to the conversation history“Never add text blocks immediately after tool results: This teaches Claude to expect user input after every tool use.”
↩︎ Adding tool results to the conversation history - 4.
“After receiving the tool result, Claude will use that information to continue generating a response to the original user prompt.”
↩︎ Adding tool results to the conversation history“you can return the error message in the content along with "is_error": true”
↩︎ Adding tool results to the conversation history“id: A unique identifier for this particular tool use block. This will be used to match up the tool results later.”
↩︎ Checkpoint - 5.https://www.anthropic.com/engineering/building-effective-agentsSecondary source
“Workflows are systems where LLMs and tools are orchestrated through predefined code paths.”
↩︎ Model-driven decisions versus pre-configured sequences“systems where LLMs dynamically direct their own processes and tool usage”
↩︎ Model-driven decisions versus pre-configured sequences“Workflows are systems where LLMs and tools are orchestrated through predefined code paths.”
↩︎ Exam trap 3