Skip to content

Latest commit

 

History

History
102 lines (84 loc) · 3.4 KB

File metadata and controls

102 lines (84 loc) · 3.4 KB

Tools helpers

To define a tool, you can use the @beta_tool decorator on any python function like so:

from anthropic import beta_tool

@beta_tool
def sum(left: int, right: int) -> str:
    """Adds two integers together.
    Args:
        left (int): The first integer to add.
        right (int): The second integer to add.
    Returns:
        int: The sum of left and right integers.
    """

    return str(left + right)

Tip

If you're using the async client, replace @beta_tool with @beta_async_tool and define the function with async def.

The @beta_tool decorator will inspect the function arguments and the docstring to extract a json schema representation of the given function, in this case it'll be turned into:

{
  "name": "sum",
  "description": "Adds two integers together.",
  "input_schema": {
    "additionalProperties": false,
    "properties": {
      "left": {
        "description": "The first integer to add.",
        "title": "Left",
        "type": "integer"
      },
      "right": {
        "description": "The second integer to add.",
        "title": "Right",
        "type": "integer"
      }
    },
    "required": ["left", "right"],
    "type": "object"
  }
}

If you want to implement calling the tool yourself, you can then pass the to the API like so:

message = client.beta.messages.create(
    tools=[get_weather.to_dict()],
    # ...
    max_tokens=1024,
    model="claude-sonnet-4-5-20250929",
    messages=[{"role": "user", "content": "What is 2 + 2?"}],
)

or you can use our tool runner!

Tool runner

We provide a client.beta.messages.tool_runner() method that can automatically call tools defined with @beta_tool(). This method returns a BetaToolRunner class that is an iterator where each iteration yields a new BetaMessage instance from an API call. Iteration is driven by each message's stop_reason: on tool_use the runner executes the requested tools and sends the results back, on pause_turn or compaction it sends the turn back unchanged so the server can resume it, and on any other stop reason it stops after yielding that final message without running tools.

runner = client.beta.messages.tool_runner(
    max_tokens=1024,
    model="claude-sonnet-4-5-20250929",
    tools=[sum],
    messages=[{"role": "user", "content": "What is 9 + 10?"}],
)
for message in runner:
    rich.print(message)

ToolError

To report an error from a tool back to the model, raise a ToolError. Unlike a plain exception, ToolError accepts content blocks, allowing you to include images or other structured content in the error response:

from anthropic import beta_tool
from anthropic.lib.tools import ToolError

@beta_tool
def take_screenshot(url: str) -> str:
    """Take a screenshot of a URL."""
    if not is_valid_url(url):
        raise ToolError(f"Invalid URL: {url}")
    result = capture(url)
    if result.error:
        # Include the error screenshot so the model can see what went wrong
        raise ToolError([
            {"type": "text", "text": f"Failed to load page: {result.error}"},
            {"type": "image", "source": {"type": "base64", "data": result.screenshot, "media_type": "image/png"}},
        ])
    return result.data

If a plain exception is raised, its repr() will be sent to the model as a text error and logged. ToolError is not logged since it represents an intentional error response.