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

Anthropic Messages — Three Tool Categories

~22 min · anthropic, claude, tool-categories, computer-use

Level 0Curious Reader
0 XP0/48 lessons0/14 achievements
0/100 XP to next level100 XP to go0% complete

Anthropic's tool-use story has a small twist that pays back later: it splits tools into three categories, all defined in the same tools array but with different shapes.

  1. Custom tools — your tools, defined by name, description, and input_schema (JSON Schema). This is what 95% of developers reach for first and what looks identical to other providers.
  2. Anthropic-defined tools — pre-built tools that Anthropic ships and the model knows natively, like computer_20250124, text_editor_20250124, and bash_20250124. You declare them by type; Anthropic owns the schema. Their existence is why Claude can drive a real computer in Claude Code without you teaching it the action verbs.
  3. MCP server tools — Anthropic's API can talk to MCP servers directly, advertising those tools alongside your custom ones in the same array. We will see this again in the MCP track.

Mechanically, Anthropic's loop is clean: send messages with tools, read back content blocks. A response's content is a list — text blocks have type: "text", tool calls have type: "tool_use" with name and input (a real dict, not a JSON string). Tool results go back as a user message containing a tool_result block referencing the original tool_use.id.

The stop_reason field is the loop's authoritative signal: "end_turn" means the model is done, "tool_use" means it called a tool, "max_tokens" means you ran out of budget. Branch on it; never branch on text.

Code

Anthropic round-trip with a custom tool·python
import anthropic

client = anthropic.Anthropic()
tools = [{
    "name": "get_weather",
    "description": "Get current weather for a city.",
    "input_schema": {
        "type": "object",
        "properties": {"location": {"type": "string"}},
        "required": ["location"],
    },
}]

def get_weather(location: str) -> str:
    return f"68°F and clear in {location}"

messages = [{"role": "user", "content": "Weather in Seoul?"}]
while True:
    resp = client.messages.create(
        model="claude-opus-4-7",
        max_tokens=1024,
        tools=tools,
        messages=messages,
    )
    messages.append({"role": "assistant", "content": resp.content})

    if resp.stop_reason == "end_turn":
        break

    results = []
    for block in resp.content:
        if block.type == "tool_use":
            output = get_weather(**block.input)
            results.append({"type": "tool_result", "tool_use_id": block.id, "content": output})
    messages.append({"role": "user", "content": results})

print(messages[-1]["content"])
Mixing custom tools with an Anthropic-defined tool·python
tools = [
    {"type": "computer_20250124", "name": "computer", "display_width_px": 1024, "display_height_px": 768},
    {  # your own tool, side by side
        "name": "lookup_user",
        "description": "Find user by id.",
        "input_schema": {"type": "object", "properties": {"id": {"type": "string"}}, "required": ["id"]},
    },
]

External links

Exercise

Build a 30-line Anthropic agent loop that uses a single custom tool. Then add an Anthropic-defined tool (text_editor or computer, whichever your account has access to) to the same tools array. Verify the model can choose between them based on the prompt. Notice that your loop did not have to change to support the new category.

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.