Claude tool_use and tool_result Blocks in Python: A Working Loop
Handle Claude tool_use and tool_result blocks in Python: a full multi-turn loop, tool_use_id matching, error handling, and a real trend-data tool.

If you are searching for how to handle tool_use and tool_result blocks in the Claude Python SDK, the short answer is: you make a client.messages.create call with a tools array, Claude replies with one or more tool_use blocks (and a stop_reason of tool_use), your code runs each function, and you send back a user message containing one tool_result block per tool_use, whose tool_use_id matches the original block's id. Repeat until Claude stops asking for tools and returns plain text.
That loop is small, but almost every failing implementation breaks in the same three places: the tool result is sent as an assistant message instead of a user message, the tool_use_id does not match, or the assistant's earlier response is dropped from the history. Before you write any code, note that Anthropic's documentation has moved to platform.claude.com; the older docs.anthropic.com URLs many tutorials cite may fail to load (readers have reported an "App unavailable in region" error), and whether that is temporary or a permanent redirect is not confirmed. Use platform.claude.com as your reference.
Client tools, server tools, and why the distinction matters
According to platform.claude.com, tool use lets Claude call functions you define or functions Anthropic provides. The difference is who executes:
- Client tools are yours. Claude returns a
tool_useblock; your Python process runs the function and returns atool_result. You control latency, errors and data access. - Server tools are executed by Anthropic inside the API call. A server tool such as web search is declared in the same
toolsarray onclient.messages.create(platform.claude.com shows a minimal example for theclaude-opus-5-5identifier), and you get results back without running anything yourself. - Code execution is a server tool that interleaves
server_tool_useblocks with their result blocks and returns a top-levelcontainerobject whoseidcan be reused across requests.
| Decision | Client tool | Server tool / code execution |
|---|---|---|
| Who runs it | Your application | Anthropic |
| Good for | Private APIs, auth, business logic | Web search, sandboxed computation |
| Context cost | Results enter context as tool_result blocks you can trim | Tool runs can stay outside Claude's context in advanced patterns |
| State | You manage it | Container id can be reused across requests |
| Failure handling | Fully yours | Partly Anthropic's |
Two engineering notes from the official material. First, Anthropic's advanced tool use writeup describes tools that execute without hitting Claude's context: your code receives a tool request with a caller field and the result is processed in the code execution environment. Second, JSON schemas define what is structurally valid but cannot express usage patterns — when to include an optional parameter, or which parameter combinations make sense. Write those rules in your tool descriptions and validate in Python.
The loop, step by step
- Define each tool as a name, description and JSON schema.
- Call
client.messages.createwithtoolsand your user message. - Check
stop_reason. If it is nottool_use, extract text and stop. - For each
tool_useblock, readblock.nameandblock.input, run the function, and capture either a result or an error. - Append the assistant's response, then a user message of
tool_resultblocks carrying the matchingtool_use_id. - Call again. platform.claude.com is explicit that the previous assistant response must be included for full history, and that every
tool_usegets exactly onetool_result— the computer-use page repeats the one-result-per-call rule.
A useful mental diagram: request → assistant tool_use(id="abc") → user tool_result(tool_use_id="abc") → request → assistant text. The ID is the thread that stitches the two turns together.
A trend-data client tool
Generic tutorials use a weather function. A trend-intelligence tool is more realistic for listening work: it returns measured topic velocity rather than a forecast. TrendsAGI exposes a REST API at https://api.trendsagi.com authenticated with X-API-Key, plus a Python SDK (pip install trendsagi, version 0.12.0+). Keep the key in an environment variable.
import json, os
import httpx
BASE = "https://api.trendsagi.com"
HEADERS = {"X-API-Key": os.environ["TRENDSAGI_API_KEY"]}
TOOLS = [{
"name": "list_trends",
"description": "List topics gaining attention. Velocity is a measured posts-per-hour snapshot rate, not a forecast or unique-user count.",
"input_schema": {
"type": "object",
"properties": {
"category": {"type": "string"},
"sort_by": {"type": "string", "enum": ["velocity", "growth", "volume", "timestamp", "name"]},
"limit": {"type": "integer"}
},
"required": ["category"]
}
}]
def list_trends(category: str, sort_by: str = "velocity", limit: int = 5) -> dict:
if limit < 1 or limit > 50:
raise ValueError("limit must be between 1 and 50")
r = httpx.get(f"{BASE}/api/trends", headers=HEADERS,
params={"category": category, "sort_by": sort_by, "limit": limit},
timeout=15.0)
r.raise_for_status()
return r.json()
The multi-turn handler with real execution and error handling
This is the part the ranking pages skip: result 2 hardcodes the tool result instead of calling a function. Here every call is executed, errors are converted into is_error results so Claude can recover, and final text is collected by filtering content blocks for type == "text" — the approach GitHub's Python tool-use example uses.
import anthropic
client = anthropic.Anthropic()
DISPATCH = {"list_trends": list_trends}
def run_agent(user_text: str, max_turns: int = 6) -> str:
messages = [{"role": "user", "content": user_text}]
for _ in range(max_turns):
resp = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
tools=TOOLS,
messages=messages,
)
if resp.stop_reason != "tool_use":
return "".join(b.text for b in resp.content if b.type == "text")
messages.append({"role": "assistant", "content": resp.content})
results = []
for block in resp.content:
if block.type != "tool_use":
continue
fn = DISPATCH.get(block.name)
try:
if fn is None:
raise LookupError(f"unknown tool {block.name}")
payload = fn(**block.input)
results.append({"type": "tool_result", "tool_use_id": block.id,
"content": json.dumps(payload)[:8000]})
except Exception as e:
results.append({"type": "tool_result", "tool_use_id": block.id,
"content": f"Error: {type(e).__name__}: {e}",
"is_error": True})
messages.append({"role": "user", "content": results})
return "Agent stopped before producing a final answer."
Composing the listening brief
Add two more tools that wrap documented endpoints: get_trend_ai_insights for summary, key themes, sentiment and audience, and get_trend_analytics with period=24h|7d|30d. Their schemas take trend_id. Ask the agent for a brief, and it will chain calls on its own:
brief = run_agent(
"Find the fastest-rising Technology trend. Pull its 7d analytics and AI insights, "
"then write a 150-word listening brief naming the risk if velocity reverses."
)
print(brief)
For a tighter, typed path you can skip hand-rolled HTTP and use the SDK directly inside your tool functions: TrendsAGIClient offers get_trends, get_ai_insights and get_trend_analytics. The SDK also ships TrendsAGITools, a ready-made read-only tool layer: tools = TrendsAGITools(client); tools.openai(), tools.anthropic() and tools.langchain() emit schemas for those ecosystems, and tools.call("list_trends", {...}) returns {"result": ...} rather than raising. Note that these TrendsAGI specifics come from the product's own documentation, not from Anthropic.
Safety: read-only by default, writes on request
- Principle of least privilege.
TrendsAGITools(client)is read-only; write tools appear only withTrendsAGITools(client, allow_writes=True). The MCP server mirrors this:trendsagi mcpis read-only unless started with--allow-writes. Keep writes off until the read path is proven. - Budget checks. Call
get_api_usage(orGET /api/user/api-usage) before long runs. Rate limits and quotas for Anthropic tool use itself are not stated here because no official pricing page was consulted; check current usage rather than assuming. - Preview before execution. TrendsAGI's ad-platform connectors preview every change for explicit approval. An agent should propose, not publish: have Claude emit the recommendation, route it through
GET /api/intelligence/recommendationsandPOST /api/intelligence/recommendations/{id}/actiononly after a human signs off. If you add an approval tool, gate it behindallow_writes=Trueand log every call. - Validate inputs. Schemas catch types, not intent. Bound numeric ranges in Python, as above, and return
is_error: trueinstead of crashing.
For editor tooling, the MCP server installs with pip install "trendsagi[mcp]" (Python 3.10+) and runs as trendsagi mcp with TRENDSAGI_API_KEY set; the hosted endpoint is https://api.trendsagi.com/mcp. Claude Code users can add it with claude mcp add --transport http trendsagi https://api.trendsagi.com/mcp. Each tool call counts as one API request.
Frequently asked questions
How do I define a tool for Claude in Python and what does the JSON schema need to include?
Provide a name, a description, and an input_schema written as JSON Schema with type: "object", a properties map and a required list. Use enum for fixed choices and description fields for anything a schema cannot express, such as valid parameter combinations.
How do I know Claude wants to call a tool and how do I extract the arguments?
Check response.stop_reason: when it equals tool_use, iterate response.content and read each block's type, name, input and id. The input is already a parsed dictionary, so call fn(**block.input) after validating it.
What exactly must the tool_result block contain and how is tool_use_id matched?
Each block has type: "tool_result", a tool_use_id equal to the id from the corresponding tool_use block, and a content field holding the result; set is_error: true on failure. platform.claude.com states the block must go in a user message, with the assistant's previous response included.
How do I keep the loop going until Claude returns a final text answer?
Append results as a user message, call the API again, and repeat while stop_reason is tool_use. On the first response where it is not, join the blocks whose type is text and return that string; cap the loop with a maximum turn count.
When should I use a client tool versus a server tool like web search or code execution?
Use a client tool for your own data or actions that need credentials and business rules. Use server tools for general web search, and code execution when you want sandboxed computation and a reusable container id across requests. Anthropic's advanced tool use material also covers running tools outside Claude's context.
Which SDK, model identifier and API surface should I use today?
Use the official anthropic Python package and read the docs at platform.claude.com rather than the older docs.anthropic.com URLs, which may not load for you. Model identifiers shown in official examples include claude-sonnet-4-5 and claude-opus-5-5; availability of other versions is not confirmed here, so check the current documentation.
How we researched this
Prepared by TrendsAGI Research. Facts checked on 2 November 2026 against the sources listed below.
Capability descriptions and code use TrendsAGI's documented REST API, Python SDK and MCP server. External facts come from the sources below.
| Claim | Applies to | Source |
|---|---|---|
| Tool use lets Claude call functions you define or functions Anthropic provides, returning a structured call that either your application executes (client tools) or Anthropic executes (server tools). | All plans | platform.claude.com |
| The tool_result block must go in a user message and its tool_use_id must match the ID from the earlier tool_use block, with the assistant's previous response included for full history. | All plans | platform.claude.com |
| When Claude requests a tool it emits a tool_use block and the application continues after returning a matching tool_result. | claude-sonnet-4-5 example | github.com |
| A Python example appends tool results as a user message and then extracts final text by filtering response content blocks for type text. | All plans | github.com |
| A minimal example of the Anthropic-provided web search server tool uses client.messages.create with a tools array. | claude-opus-5-5 example | platform.claude.com |
| With computer use, the response has a stop_reason of tool_use containing one or more tool_use blocks, and the application runs each call in order and returns one tool_result block per tool_use block before calling the API again. | All plans | platform.claude.com |
| The code-execution tool interleaves server_tool_use blocks with their tool result blocks and returns a top-level container object whose id can be reused across requests. | All plans | platform.claude.com |
| JSON schemas define what is structurally valid but cannot express usage patterns such as when to include optional parameters or which parameter combinations make sense. | All plans | anthropic.com |
| In advanced tool use, tools can execute without hitting Claude's context: the code calls a tool and you receive a tool request with a caller field, with the result processed in the code execution environment. | All plans | anthropic.com |
We re-research and rewrite this page at least every four months; the updated date only changes when the facts are re-checked.
Sources
- Claudeでのツール使用 (Tool use overview) (Claude Platform) — platform.claude.com
- Advanced tool use (Anthropic) — anthropic.com
- Python Tool Use (GitHub) — github.com