C.W.K.
Stream
Lesson 03 of 05 · published

Claude Tool Use: tool_use and tool_result

~34 min · anthropic, claude, tool-use

Level 0Observer
0 XP0/40 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

Claude integrates tools into messages

Claude's Messages API represents tool calls as content blocks. The assistant returns a tool_use block, then your next user message contains a tool_result block with the matching tool_use_id. Same loop, different wire format.

This structure is elegant once you see it, but it is unforgiving about ordering. Tool results must appear where the protocol expects them. If you separate the result from the immediately preceding tool use, the API will complain, and rightly so.

Client tools versus server tools

Client tools execute on your system. You define the schema and implement the action. Server tools execute on Anthropic's side, such as hosted search-style capabilities. The safety model is different, so do not treat them as equivalent.

Tool choice is a steering wheel

auto lets Claude decide, any forces some tool, a named tool forces one tool, and none disables tool use. Use forcing sparingly. If you force a tool too often, you teach the system to obey architecture instead of evidence.

Code

Claude tool_use / tool_result loop·python
import anthropic, json

client = anthropic.Anthropic()

tools = [{
    "name": "lookup_order",
    "description": "Look up one order by id. Use only for order status questions.",
    "input_schema": {
        "type": "object",
        "properties": {
            "order_id": {"type": "string", "description": "Public order id, e.g. ORD-1042"}
        },
        "required": ["order_id"],
    },
}]

messages = [{"role": "user", "content": "Where is ORD-1042?"}]

response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,
    tools=tools,
    messages=messages,
)

messages.append({"role": "assistant", "content": response.content})
tool_results = []
for block in response.content:
    if block.type == "tool_use":
        result = {"order_id": block.input["order_id"], "status": "shipped"}
        tool_results.append({
            "type": "tool_result",
            "tool_use_id": block.id,
            "content": json.dumps(result),
        })

messages.append({"role": "user", "content": tool_results})
final = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,
    tools=tools,
    messages=messages,
)

External links

Exercise

Rewrite your search_notes tool from the previous lesson in Claude's input_schema format. Then write the shape of the tool_result message you would send back.
Hint
The result needs the tool_use_id from Claude's returned block.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.