CloudCodeTree LogoCloudCodeTree
AI NewsTutorialsAbout
CloudCodeTree Logo
CloudCodeTree
  • AI News
  • Tutorials
  • About
← Back to AI News
Skip the While Loop: The Anthropic SDK's Tool Runner Runs Your Agent For You

Skip the While Loop: The Anthropic SDK's Tool Runner Runs Your Agent For You

Chris Harper

3 min read

Aug 30, 2026 · 12:05 UTC

AI
Workflow
Claude Code
Best Practices

TL;DR: The Anthropic SDK's beta @beta_tool decorator + client.beta.messages.tool_runner runs the tool-call loop automatically — the same four-step round trip, with no explicit tool_result handling code.

The four-step tool-use loop (define tool → get stop_reason: "tool_use" → execute → send tool_result back) is the right pattern when you need per-call logging, cost gating, or conditional execution. When you do not need those, the SDK ships a wrapper that handles the loop for you.

Two decorators, one loop:

import json
from anthropic import Anthropic, beta_tool

client = Anthropic()

@beta_tool
def get_weather(location: str, unit: str = "fahrenheit") -> str:
    """Get the current weather in a given location.

    Args:
        location: The city and state, e.g. San Francisco, CA
        unit: Temperature unit, either 'celsius' or 'fahrenheit'
    """
    # your real implementation here
    return json.dumps({"temperature": "18°C", "condition": "Cloudy"})

@beta_tool
def calculate_sum(a: int, b: int) -> str:
    """Add two numbers together.

    Args:
        a: First number
        b: Second number
    """
    return str(a + b)

runner = client.beta.messages.tool_runner(
    model="claude-sonnet-5-20260801",
    max_tokens=1024,
    tools=[get_weather, calculate_sum],
    messages=[{"role": "user", "content": "Weather in Berlin, and what is 15 + 27?"}],
)

for message in runner:
    # each iteration is a BetaMessage — the loop stops on end_turn
    if message.stop_reason == "end_turn":
        for block in message.content:
            if block.type == "text":
                print(block.text)

@beta_tool inspects the function's type hints and the Args: section of its docstring to generate the JSON schema Claude receives — the same schema you would write by hand in a manual loop. The runner handles the stop_reason check, calls your function, wraps the return value as a tool_result, and sends the next request. It stops when Claude returns stop_reason: "end_turn".

When to use which pattern:

PatternUse when
Tool RunnerQuick scripts, prototyping, simple single-agent tasks
Manual loopPer-call logging, cost gating, approval before execution, result transformation

The runner is iterable — each loop iteration yields a BetaMessage — so you still have access to intermediate messages when you need them. What you cannot do is intercept a call before it runs, which is what human-in-the-loop and conditional retry patterns need.

The real limit: Tool Runner is in beta (client.beta.messages.tool_runner). The API surface has changed between SDK minor versions; pin your anthropic version in requirements.txt and check the GitHub release notes when upgrading. An async variant (@beta_async_tool) is available for asyncio-based code.

Sources: Tool Runner — platform.claude.com · Anthropic SDK Python tools.md — GitHub · Claude Agent SDK complete guide — hidekazu-konishi.com · Anthropic Agent SDK: What It Ships vs. What It Leaves to You — Augment Code