> ## Documentation Index
> Fetch the complete documentation index at: https://docs.xpander.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# @register_tool

> Decorate a Python function to register it as a tool the agent can call.

`@register_tool` turns a Python function into a tool the agent can invoke. The SDK extracts type hints and the docstring to build a schema and description automatically: there's no manual schema writing.

```python theme={"dark"}
from xpander_sdk import register_tool

@register_tool
def calculate_sum(a: int, b: int) -> int:
    """Add two numbers."""
    return a + b
```

After this runs (typically at module import), `calculate_sum` is available to every `Agents().aget(...)` call as a local tool with id `"calculate_sum"`.

## Decorator forms

```python theme={"dark"}
@register_tool                                    # plain
def fn(...): ...

@register_tool(add_to_graph=True)                 # with options
def fn(...): ...
```

| Parameter      | Type   | Default | Description                                                                                                                                                      |
| -------------- | ------ | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `add_to_graph` | `bool` | `False` | When `True`, the tool is pushed into the agent's execution graph (so the platform routes calls through it). When `False`, the tool is only available in-process. |

## What it does

1. Inspects the function's parameters and type hints.
2. Builds a Pydantic model (`<FunctionName>Args`) from the parameters: required if no default, optional otherwise.
3. Constructs a `Tool` with:
   * `id` = function name
   * `name` = function name
   * `description` = function docstring
   * `parameters` = generated JSON schema
   * `fn` = the function
   * `is_local = True`
   * `should_add_to_graph = add_to_graph`
4. Registers the tool in `ToolsRepository`'s class-level `_local_tools` list (process-wide singleton).
5. Returns the original function unchanged: `calculate_sum(2, 3)` still works as plain Python.

## Examples

### Async functions are supported

```python theme={"dark"}
@register_tool
async def fetch_url(url: str) -> str:
    """Fetch the body of a URL."""
    async with httpx.AsyncClient() as client:
        response = await client.get(url)
    return response.text
```

When the agent calls this tool, the SDK awaits the coroutine.

### Optional parameters

```python theme={"dark"}
@register_tool
def search(query: str, limit: int = 10, region: str = "US") -> list[str]:
    """Search the catalog. Returns matching SKUs."""
    ...
```

Defaults become optional fields in the JSON schema.

### Push to the agent graph

```python theme={"dark"}
@register_tool(add_to_graph=True)
async def analyze_data(data: list, analysis_type: str) -> dict:
    """Analyze incoming data; returns aggregated metrics."""
    ...
```

`add_to_graph=True` makes the tool visible in the platform's graph view for the next `agents.aget(...)`: the SDK sync runs in the background after `aget` returns.

### Use the function directly

```python theme={"dark"}
@register_tool
def add(a: int, b: int) -> int:
    """Add two ints."""
    return a + b

# Still callable as a regular function
add(2, 3)   # 5
```

## Visibility rules

The decorator registers tools at module-import time on a process-wide registry. That means:

* Every agent loaded by `Agents().aget(...)` in this process inherits the local tools.
* Tools defined inside a function body register the **first** time the function runs, but stay registered for the rest of the process's lifetime.
* If you `del` or rename a function after decoration, the registered `Tool` keeps its captured reference (the `fn` attribute): it doesn't unregister itself.

For per-invocation tools, use `Backend.aget_args(tools=[fn])` instead: that adds callables to one specific `aget_args` call without polluting the global registry.

## Schema details

The generated schema uses Pydantic's `model_json_schema(mode="serialization")`. Parameter names, types, and defaults map directly:

```python theme={"dark"}
@register_tool
def book_flight(origin: str, destination: str, date: str = "tomorrow") -> dict:
    """Book a flight."""
    ...
```

generates roughly:

```json theme={"dark"}
{
  "properties": {
    "origin": {"type": "string", "title": "Origin"},
    "destination": {"type": "string", "title": "Destination"},
    "date": {"type": "string", "title": "Date", "default": "tomorrow"}
  },
  "required": ["origin", "destination"],
  "title": "BookFlightArgs",
  "type": "object"
}
```

The agent's LLM sees this schema; the docstring becomes the tool's description. Write docstrings the way you'd write a tool description: that's exactly what the LLM reads.

## Related

* [`Tool` class](/developers/sdk-reference/tools/tool): what the decorator creates.
* [`@on_tool_before` / `@on_tool_after` / `@on_tool_error`](/developers/sdk-reference/decorators/on-tool): lifecycle hooks around any tool invocation.
