A quick reference for defining tools, running the agentic loop, and using the SDK tool runner with Claude.
name, description, and JSON Schema input_schema.stop_reason is tool_use, run the tool and send results back.input_schema Defines a tool's inputs as JSON Schema with a name and description.tools = [{
"name": "get_weather",
"description": "Returns current weather for a given city.",
"input_schema": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"]
}
}]tools parameter Passes the tools array to messages.create() alongside the prompt.message = client.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
tools=tools,
messages=[{"role": "user", "content": "Weather in Tokyo?"}]
)description field Tells Claude exactly when to call the tool, not just what it does."description": "Call this when the user asks about current weather."required array Marks which input fields Claude must always provide."required": ["city"]tool_use stop_reason Signals Claude wants to call a tool instead of finishing the reply.if message.stop_reason == "tool_use":
# extract tool calls, run them, send results backextract tool calls Filters the response content for tool_use blocks to find what Claude called.tool_calls = [b for b in message.content if b.type == "tool_use"]tool_result block Packages a tool's output with the matching tool_use_id to send back.tool_results.append({
"type": "tool_result",
"tool_use_id": call.id,
"content": str(result)
})loop until end_turn Repeats the call-execute-respond cycle until Claude stops calling tools.while message.stop_reason == "tool_use":
message = client.messages.create(...)@beta_tool Turns a plain Python function into a tool with an auto-generated schema.from anthropic.lib.beta import beta_tool
@beta_tool
def get_weather(city: str) -> str:
"""Returns current weather for a given city."""
return f"Sunny, 22C in {city}"tool_runner() Runs the full call-execute-loop cycle automatically until Claude is done.runner = client.beta.messages.tool_runner(
model="claude-opus-4-8",
max_tokens=1024,
tools=[get_weather],
messages=[{"role": "user", "content": "Weather in Tokyo?"}]
)until_done() Blocks until the loop finishes and returns Claude's final response.final_message = runner.until_done()
print(final_message.content[0].text)tool_choice: auto Lets Claude decide whether to call a tool; this is the default.tool_choice={"type": "auto"}tool_choice: tool Forces Claude to call one specific named tool on this turn.tool_choice={"type": "tool", "name": "get_weather"}tool_choice: none Disables tool calls mid-conversation once you have the data you need.tool_choice={"type": "none"}disable_parallel_tool_use Forces exactly one tool call per turn when call order matters.tool_choice={"type": "auto", "disable_parallel_tool_use": True}tool_choice: {type: "tool"} to force a specific tool call instead of asking Claude for structured JSON.content block, including tool_use blocks, back in the next turn or you'll get a validation error.tool_result for a turn in a single message — splitting them across messages trains Claude to stop making parallel calls.Fast, clear reference sheets for technology, finance, health, and everyday adulting.