Thinking in tool and multi-turn workflows
Walk through a complete two-turn tool-use round trip that preserves thinking blocks correctly, and see how interleaved thinking changes the flow.
This page walks through a complete two-turn tool-use round trip with thinking enabled: Claude thinks, requests a tool call, receives the result, and finishes its answer, with the thinking blocks handled correctly at every step. The full rules live on the Thinking page, in Thinking with tool use and Preserving thinking blocks; this page shows those rules applied in runnable code.
The rules this walkthrough applies
Each link leads to the full statement on the Thinking page:
- Limit tool choice to
autoornonein manual mode:tool_choiceoptions that force tool use return an error with manual extended thinking (thinking: {type: "enabled"}); adaptive thinking supports forced tool use. - Keep one thinking configuration per assistant turn: a tool-use loop is one assistant turn, so change the configuration only between turns.
- Pass thinking blocks back complete and unmodified: when you return a tool result, the thinking blocks from the assistant message must come back with it.
- Echo the assistant message exactly as received: rebuilding the message or filtering out
redacted_thinkingblocks triggers a 400 error.
The samples use adaptive thinking; on models that support only extended thinking, substitute thinking: {type: "enabled", budget_tokens: N}. The round-trip rules are identical.
Walk through a two-turn tool-use round trip
The example defines a get_weather tool, lets Claude think and request a tool call, then returns the tool result along with the assistant turn echoed exactly as received, thinking block included.
Make the first request with a tool available
Send a request with adaptive thinking enabled and the tool defined. Apart from the
thinkingparameter, this is a standard tool use request:client = anthropic.Anthropic() weather_tool = { "name": "get_weather", "description": "Get current weather for a location", "input_schema": { "type": "object", "properties": {"location": {"type": "string", "description": "City name"}}, "required": ["location"], }, } # First request - Claude responds with thinking and tool request response = client.messages.create( model="claude-opus-4-8", max_tokens=16000, thinking={"type": "adaptive"}, tools=[weather_tool], messages=[{"role": "user", "content": "What's the weather in Paris?"}], ) print(response)Capture the content array to echo back
You should see
thinking,text, andtool_useblocks in the response content on a run where Claude chose to think (on simpler requests, adaptive mode may skip the thinking block). Keep this content array intact: the next step sends it back verbatim.Output{ "content": [ { "type": "thinking", "thinking": "The user wants to know the current weather in Paris. I have access to a function `get_weather`...", "signature": "BDaL4VrbR2Oj0hO4XpJxT28J5T...." }, { "type": "text", "text": "I can help you get the current weather information for Paris. Let me check that for you" }, { "type": "tool_use", "id": "toolu_01CswdEQBMshySk6Y9DFKrfq", "name": "get_weather", "input": { "location": "Paris" } } ] }Return the tool result, echoing the assistant turn verbatim
Run the tool on your side, then send a second request that appends two messages to the conversation. The first is the assistant content echoed back exactly as received, so the thinking block stays unchanged alongside the
tool_useblock. The second is a user message carrying thetool_result.Each sample is a self-contained script: it repeats the first request, then immediately sends the follow-up using the response it just received.
client = anthropic.Anthropic() weather_tool = { "name": "get_weather", "description": "Get current weather for a location", "input_schema": { "type": "object", "properties": {"location": {"type": "string", "description": "City name"}}, "required": ["location"], }, } response = client.messages.create( model="claude-opus-4-8", max_tokens=16000, thinking={"type": "adaptive"}, tools=[weather_tool], messages=[{"role": "user", "content": "What's the weather in Paris?"}], ) # Extract the tool use block to get its ID for the tool result tool_use_block = next(block for block in response.content if block.type == "tool_use") # Call your actual weather API, here is where your actual API call would go # Let's pretend this is what we get back weather_data = {"temperature": 88} # Second request - Include the assistant turn and the tool result continuation = client.messages.create( model="claude-opus-4-8", max_tokens=16000, thinking={"type": "adaptive"}, tools=[weather_tool], messages=[ {"role": "user", "content": "What's the weather in Paris?"}, # Echo the assistant content exactly as received. When a thinking # block is present, it must accompany the tool_use block. {"role": "assistant", "content": response.content}, { "role": "user", "content": [ { "type": "tool_result", "tool_use_id": tool_use_block.id, "content": f"Current temperature: {weather_data['temperature']}°F", } ], }, ], ) print(continuation)Read the final response
You should see Claude complete the turn with text. Because interleaved thinking is automatic in adaptive mode, the continuation can also open with a new thinking block before the final text:
Output{ "content": [ { "type": "text", "text": "Currently in Paris, the temperature is 88°F (31°C)" } ] }
How interleaved thinking changes the flow
Interleaved thinking lets Claude think between tool calls, reasoning about each tool result before acting on it. The concept and per-model availability are covered in Interleaved thinking on the Thinking page; interleaving changes where thinking blocks appear, not whether tool calls can chain. The following comparison shows what interleaved thinking changes in a two-tool workflow:
Without interleaved thinking, Claude thinks once at the start of the assistant turn. Subsequent responses after tool results continue without new thinking blocks.
User: "What's the total revenue if we sold 150 units at $50 each,
and how does this compare to our average monthly revenue?"
Response 1: [thinking] "I need to calculate 150 * $50, then check the database..."
[tool_use: calculator] { "expression": "150 * 50" }
↓ tool result: "7500"
Response 2: [tool_use: database_query] { "query": "SELECT AVG(revenue)..." }
↑ no thinking block
↓ tool result: "5200"
Response 3: [text] "The total revenue is $7,500, which is 44% above your
average monthly revenue of $5,200."
↑ no thinking blockWith interleaved thinking enabled, Claude can think after receiving each tool result, allowing it to reason about intermediate results before continuing.
User: "What's the total revenue if we sold 150 units at $50 each,
and how does this compare to our average monthly revenue?"
Response 1: [thinking] "I need to calculate 150 * $50 first..."
[tool_use: calculator] { "expression": "150 * 50" }
↓ tool result: "7500"
Response 2: [thinking] "Got $7,500. Now I should query the database to compare..."
[tool_use: database_query] { "query": "SELECT AVG(revenue)..." }
↑ thinking after receiving calculator result
↓ tool result: "5200"
Response 3: [thinking] "$7,500 vs $5,200 average - that's a 44% increase..."
[text] "The total revenue is $7,500, which is 44% above your
average monthly revenue of $5,200."
↑ thinking before final answerNext steps
The overview: turn thinking on, read thinking output, and review the full rules for tool use, caching, and streaming.
Steer how often and how deeply Claude thinks with effort levels and prompt-based guidance.
Manual thinking budgets on older models: budget_tokens mechanics and migration to adaptive.
Was this page helpful?