Skip to content

Function Tools

Build tools from plain Python functions using the @tool decorator. The SDK extracts the tool name, description, and parameter schema automatically from the function signature and docstring.

Minimal example

python
from elsai import tool

@tool
def greet(name: str) -> str:
    """Greet a person by name.

    Args:
        name: The person's name.
    """
    return f"Hello, {name}!"

Docstring format

The model uses the docstring to understand when and how to call the tool. Write clear, concise descriptions:

python
@tool
def send_email(to: str, subject: str, body: str) -> dict:
    """Send an email to a recipient.

    Args:
        to: The recipient's email address.
        subject: The email subject line.
        body: The full email body text.

    Returns:
        A dict with 'success' bool and 'message_id' string.
    """
    # implementation
    return {"success": True, "message_id": "msg-123"}

Type hints

Always add type hints — they become the JSON schema the model sees:

Python typeJSON Schema
strstring
intinteger
floatnumber
boolboolean
list[str]array of string
dictobject
Optional[str]string (nullable)

Optional parameters with defaults

python
@tool
def search_web(query: str, max_results: int = 5, safe_search: bool = True) -> list[str]:
    """Search the web and return URLs.

    Args:
        query: The search query.
        max_results: Maximum results to return (default 5).
        safe_search: Enable safe search filtering.
    """
    ...

Returning rich content

Return a dict with a "content" key for structured results the model can read:

python
@tool
def get_chart_data(symbol: str) -> dict:
    """Retrieve stock chart data for a ticker symbol.

    Args:
        symbol: The stock ticker (e.g. AAPL).
    """
    data = fetch_from_api(symbol)
    return {
        "content": [
            {"text": f"Price: ${data['price']}, Change: {data['change']}%"},
        ]
    }

Async tools

Tools can be async:

python
import httpx
from elsai import tool

@tool
async def fetch_url(url: str) -> str:
    """Fetch the text content of a URL.

    Args:
        url: The URL to fetch.
    """
    async with httpx.AsyncClient() as client:
        response = await client.get(url)
        return response.text[:2000]  # truncate

Accessing agent state inside tools

Add an agent parameter to receive the calling agent:

python
from elsai import tool

@tool
def save_note(content: str, agent) -> str:
    """Save a note to the agent's memory.

    Args:
        content: The note content to save.
    """
    notes = agent.state.get("notes", [])
    notes.append(content)
    agent.state["notes"] = notes
    return f"Saved note #{len(notes)}"

:::caution Do not include agent in the Args docstring section — it's injected automatically and the model should not try to fill it. :::

Context injection with ToolContext

For dependency injection without modifying the function signature:

python
from elsai import tool
from elsai.tools.tool_provider import ToolProvider

@tool
def query_db(sql: str) -> list:
    """Execute a read-only SQL query.

    Args:
        sql: The SQL query to run.
    """
    # db_connection injected at runtime
    ...

Tool result format

Tools can return:

  • A str — sent as text to the model
  • A dict with a "content" list — rich content blocks
  • A list — converted to JSON string
  • Any Python object — converted to string via str()

Error handling

Raise an exception to signal a tool error back to the model:

python
@tool
def divide(a: float, b: float) -> float:
    """Divide a by b.

    Args:
        a: The dividend.
        b: The divisor.
    """
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b

The agent will catch the exception, format it as a tool error, and let the model decide how to proceed.

Hot-reloading from a directory

Create a ./tools/ directory and place tool files there:

my_project/
├── main.py
└── tools/
    ├── database.py    # contains @tool decorated functions
    ├── email.py
    └── search.py
python
# main.py
from elsai import Agent

agent = Agent(load_tools_from_directory=True)
# Tools are loaded from ./tools/ and reloaded automatically on file changes

Copyright © 2026 Elsai Foundry.