For how zero data retention (ZDR) applies to this feature, see API and data retention.
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.
Each link leads to the full statement on the Thinking page:
auto or none: tool_choice options that force tool use return an error while thinking is on.redacted_thinking blocks 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.
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 thinking parameter, 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, and tool_use blocks 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.
To see thinking text like this output, add display: "summarized" to the request. On models where display defaults to omitted, including claude-opus-4-8, the thinking field otherwise comes back as an empty string with only the signature populated. Either way, echo the content array back unchanged; see Controlling thinking display.
{
"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_use block. The second is a user message carrying the tool_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:
{
"content": [
{
"type": "text",
"text": "Currently in Paris, the temperature is 88°F (31°C)"
}
]
}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:
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?