tool & Tool

Reference for the tool decorator and the Tool dataclass — how AgentRoute turns a Python function into a model-callable tool.


A tool is a Python function the agent can call mid-run. The tool decorator wraps a function into a Tool, auto-extracting the model-visible parameter schema from the function's type hints and docstring. You register tools by passing them to Agent(tools=[...]) or with the @agent.tool decorator.

For the conceptual overview — when to reach for a tool, approval gates, and the agentic loop — see Tools.

tool

tool — wraps a plain function into a Tool.

from agentroute import tool
def tool(
    fn: Callable[..., Any] | None = None,
    /,
    *,
    name: str | None = None,
    description: str | None = None,
    needs_approval: bool | Callable[[Context, dict[str, Any]], bool] = False,
    timeout: float | None = None,
) -> Tool | Callable[[Callable[..., Any]], Tool]:
    ...

It is a dual-call decorator: bare @tool and parameterized @tool(name="...") both work. Bare usage returns a Tool directly; parameterized usage returns a decorator that returns a Tool.

ParameterTypeDefaultDescription
fnCallable[..., Any] | NoneNoneThe function to wrap. Passed positionally by the bare @tool form; leave it unset when calling @tool(...) with keyword options.
namestr | NoneNoneOverride the tool name the model sees. Defaults to the function's name.
descriptionstr | NoneNoneOverride the tool description. Defaults to the function's docstring.
needs_approvalbool | Callable[[Context, dict], bool]FalseGate execution behind human approval. Pass True to always require approval, or a callable (ctx, args) -> bool to decide per call.
timeoutfloat | NoneNoneMaximum seconds the tool may run before it is cancelled. None means no timeout.
ReturnsTool
A Tool (bare form) or a decorator returning a Tool (parameterized form).

Dual-call forms

Both forms produce an equivalent Tool.

forms.py
from agentroute import tool
 
 
@tool
def get_weather(city: str) -> str:
    """Get the current weather for a city."""
    return f"It's sunny in {city}."
 
 
@tool(name="lookup_weather", timeout=5.0)
def get_weather_2(city: str) -> str:
    """Get the current weather for a city."""
    return f"It's sunny in {city}."

Schema extraction

The parameter schema is built from the function's type hints and docstring — you do not write JSON Schema by hand. A parameter annotated Context is auto-injected at call time and is omitted from the model-visible schema.

schema.py
from agentroute import Context, tool
 
 
@tool
def add_note(ctx: Context, text: str) -> str:
    """Save a note to memory.
 
    Args:
        text: The note body to store.
    """
    # `ctx` is injected by the runtime, not chosen by the model.
    # The model only sees the `text` parameter.
    return "saved"
One docstring, two jobs

The docstring becomes the tool description the model reads, and per-argument descriptions (when present) flow into the parameter schema. Write it for the model.

Registering tools on an agent

Pass Tool objects or plain callables to the agent, or decorate with @agent.tool to register in place.

register.py
from agentroute import Agent, tool
 
 
@tool
def get_weather(city: str) -> str:
    """Get the current weather for a city."""
    return f"It's sunny in {city}."
 
 
agent = Agent(
    name="assistant",
    model="claude-sonnet-4",
    tools=[get_weather],
)
 
 
@agent.tool(needs_approval=True)
def delete_file(path: str) -> str:
    """Delete a file from disk."""
    return f"deleted {path}"
 
 
result = agent.run("What's the weather in Berlin?")
print(result)  # It's sunny in Berlin.

Tool

Tool — a callable tool the agent can invoke.

from agentroute import Tool
@dataclass
class Tool:
    name: str
    description: str
    params_schema: dict[str, Any]
    fn: Callable[..., Any]
    takes_context: bool = False
    needs_approval: bool | Callable[[Context, dict[str, Any]], bool] = False
    timeout: float | None = None

You rarely construct a Tool by hand — the tool decorator builds one for you. The fields are useful for inspection (for example via agent.tools_map).

ParameterTypeDefaultDescription
namerequiredstrThe tool name exposed to the model.
descriptionrequiredstrHuman- and model-readable description, sourced from the docstring.
params_schemarequireddict[str, Any]The JSON-Schema parameters object the model sees, auto-extracted from the function signature.
fnrequiredCallable[..., Any]The underlying function executed when the tool is called.
takes_contextboolFalseWhether the first positional parameter is annotated Context and should receive the active Context at call time. Set automatically during extraction.
needs_approvalbool | Callable[[Context, dict], bool]FalseApproval gate — True to always require approval, or a predicate (ctx, args) -> bool.
timeoutfloat | NoneNoneMaximum seconds the tool may run, or None for no limit.

invoke

Run the tool with model-supplied arguments and the active context.

async def invoke(self, args: dict[str, Any], *, ctx: Context) -> Any:
    ...
ParameterTypeDefaultDescription
argsrequireddict[str, Any]The arguments parsed from the model's tool call, keyed by parameter name.
ctxrequiredContextThe active run context, injected as the first positional argument when takes_context is True.
ReturnsAny
Whatever the wrapped function returns, fed back to the model as the tool result.

Execution behavior:

  • Async functions are awaited directly.
  • Sync functions are offloaded with asyncio.to_thread, so a blocking call does not stall the event loop.
  • When takes_context is True, ctx is passed as the first positional argument; the rest of args is splatted as keyword arguments.
The agent calls invoke for you

During a run the agent matches the model's tool call to a registered Tool and awaits invoke internally. You only call it directly when testing a tool in isolation.

Calling a tool directly

Useful in tests. Build a Context and await invoke.

invoke_directly.py
import asyncio
 
from agentroute import Context, tool
 
 
@tool
def get_weather(city: str) -> str:
    """Get the current weather for a city."""
    return f"It's sunny in {city}."
 
 
async def main() -> None:
    ctx = Context()
    out = await get_weather.invoke({"city": "Berlin"}, ctx=ctx)
    print(out)  # It's sunny in Berlin.
 
 
asyncio.run(main())

A context-aware tool reads from ctx — note the model still only supplies text:

invoke_with_ctx.py
import asyncio
 
from agentroute import Context, Memory, tool
 
 
@tool
async def remember_fact(ctx: Context, key: str, value: str) -> str:
    """Store a fact for later recall."""
    await ctx.memory.remember(key, value)
    return "ok"
 
 
async def main() -> None:
    ctx = Context(memory=Memory())
    out = await remember_fact.invoke({"key": "city", "value": "Berlin"}, ctx=ctx)
    print(out)  # ok
 
 
asyncio.run(main())

Next steps

Source

tool.py on GitHub