---
title: Tools
description: Define and use tools in model and agent workflows.
type: guide
summary: Define function tools, design schemas, handle tool errors, use schema-only tools, provider tools, and MCP tools.
---

# Tools



AI SDK for Python supports multiple types of tools, including function tools
defined in code using `@ai.tool`, as well as built-in provider-side tools.

## Declare function tools

Decorate an async function with `@ai.tool`:

```python
import ai


@ai.tool
async def contact_mothership(query: str) -> str:
    """Contact the mothership for important decisions."""
    return "Soon."
```

The tool name comes from the function name. The model receives the function
parameters as a JSON schema and the docstring as the tool description.

Function tools will only be automatically executed by the SDK when used
in the context of the agent. Provider-side tools will always get executed
by the provider, even when passed to `ai.stream`.

## Handle tool errors

Tool exceptions become `ToolCallResult` events with `is_error=True`. The model
sees the error text on the next turn:

```python
async with agent.run(model, messages) as stream:
    async for event in stream:
        if isinstance(event, ai.events.ToolCallResult):
            for result in event.results:
                if result.is_error:
                    print(f"{result.tool_name} failed: {result.result}")
```

The original exception is available on `event.exception` for logging.

## Declare tools that stream output

AI SDK for Python supports tools that are async-generators. You might want to
use those when your tools need to return partial output, e.g. when wrapping
subagents.

Streaming tools use aggregators. An aggregator solves two problems: the tool can
yield many values over time, but the agent still needs one final tool result;
and your app may want a rich stored result while the model needs a simpler value
on the next turn.

The core interface is:

```python
class Aggregator[Item, Result, ModelInput]:
    def feed(self, item: Item) -> None: ...
    def snapshot(self) -> Result: ...

    @classmethod
    def to_model_input(cls, snapshot: Result) -> ModelInput: ...
```

Use `ai.StreamingTextTool` when yielded strings should concatenate into the
tool result:

```python
@ai.tool
async def draft_reply(topic: str) -> ai.StreamingTextTool:
    """Draft a reply."""
    yield "Checking "
    yield "records for "
    yield topic
```

Use `ai.StreamingStatusTool[T]` when intermediate yields are progress updates
and the last yielded value is the final result.

Use `ai.SubAgentTool` when a tool streams events from a nested agent. The stored
result is a message bundle, and the model sees the final assistant text.

The agent emits `PartialToolCallResult` events for those values, then sends
the aggregated result back to the model on the next turn.

## Understand tool anatomy

`ai.Tool` represents tools of all kinds in the SDK.

Each tool carries an optional `spec`, which is a description consumed by the
model, as well as a `tool_config` that contains tool-specific execution config
(e.g. retry policy).

`AgentTool` wraps `ai.Tool` together with its corresponding Python function,
which allows `agent.run` to find and call that function when the model requests
to do so.

Pass `ai.Tool` objects directly to `ai.stream` when you want the model to emit
tool calls but you do not want the SDK to execute them:

```python
tool = ai.Tool(
    kind="function",
    name="contact_mothership",
    spec=ai.tools.ToolSpec(
        description="Contact the mothership.",
        params={
            "type": "object",
            "properties": {"query": {"type": "string"}},
            "required": ["query"],
        },
    ),
)

async with ai.stream(model, messages, tools=[tool]) as stream:
    async for event in stream:
        if isinstance(event, ai.events.ToolEnd):
            print(event.tool_call.tool_args)
```

## Use provider-executed tools

Provider-executed tools run on the provider side. Pass them to `ai.stream` in
the `tools` list:

```python
messages = [
    ai.user_message("Check the latest mothership telemetry reports."),
]

async with ai.stream(
    model,
    messages,
    tools=[ai.providers.anthropic.tools.web_search(max_uses=3)],
) as stream:
    async for event in stream:
        if isinstance(event, ai.events.TextDelta):
            print(event.chunk, end="", flush=True)
```

When you route through AI Gateway, you can use provider-specific tool factories
and AI Gateway tool factories:

```python
tools = [
    ai.providers.anthropic.tools.web_search(max_uses=3),
    ai.providers.ai_gateway.tools.perplexity_search(max_results=5),
]
```

## Use MCP tools

The Model Context Protocol (MCP) adapter converts server tools into agent tools:

```bash
uv add "ai[mcp]"
```

```python
tools = await ai.mcp.get_http_tools(
    "http://localhost:3000/mcp",
    headers={"Authorization": "Bearer your_access_token_here"},
)

agent = ai.Agent(tools=tools)
```

Use `ai.mcp.get_stdio_tools` for subprocess-based MCP servers.


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)