Local Tools
A local tool is a Python function in your application that the model may call. By the end of this page you will have an agent that calls two of your functions in one step, and you will know what the model sees of them, what it gets back, and how a failure reaches it.Every output on this page is what the code printed when it was run. The model’s
wording, and sometimes the arguments it chooses, will differ on your run.
Two tools, one agent
async; both work. A plain function runs in a worker thread, so a slow one
does not block the agent.
How it works
1
Register
@tools.register_tool("name") adds the function to the registry and returns
it unchanged, so you can still call it yourself. Without a name, the
function’s own name is used.2
The schema is inferred
Type hints become the JSON Schema the model is sent. A parameter with no
default is required; unknown arguments are rejected.
3
The docstring is the description
The model reads it to decide when to call the tool. A docstring line
name: text also becomes that parameter’s description.4
Each call is checked, then run
The runtime validates the model’s arguments against the schema, asks the
policy if governance is on, runs the function
under
tool_call_timeout, passes the result through the guardrail, and
records the call in the run’s trajectory.What the model sees
This registry is never given to an agent; it only prints what the model would be sent:str, int, float, bool, list[...], dict[...], Literal[...],
unions and Optional map to their JSON Schema types. Any other annotation, or
none, is sent as string. When inference is not enough, pass your own schema:
register_tool("name", inputSchema={...}, description="...").
Tools that hold state: Tool and get_tool()
When a tool needs a connection, a client or configuration, build it as a
Tool from an object. registry.register(obj) accepts a Tool, or any object
with a get_tool() method that returns one.
local_tools=registry to the agent as before. execute_tool is how you
test a tool without a model. local_tools also accepts a list of Tool
objects (or objects with get_tool()), and registry.merge(other) copies
another registry’s tools in.
What a tool returns
Return any value: a string, a number, a list, a dictionary. The model receives it as the call’s data. A dictionary is read as a result envelope only when it has exactly that shape:
So
{"error": f"No invoice {number}"} above is an error the model sees as one.
To return a business dictionary that happens to have an envelope’s shape, wrap
it: {"status": "success", "data": your_dict}.
Raising is the other way to fail. The exception’s message goes to the
model, and the call is recorded as an error:
read_artifact
(tool_offload in the agent settings). Your tool
does not write to the conversation history itself: the runtime stores one
result per call.
Options
Every agent setting is in the agent settings reference.
Names the runtime reserves
Workspace files are on by default, and they bring their own tools:ls,
read_file, write_file, edit_file, insert_file, delete_file,
move_file, clear_files, glob and grep. Result offloading adds
read_artifact, tail_artifact, search_artifact and list_artifacts. Give
your tools names from your domain, such as fetch_invoice or
query_knowledge_base.
When things go wrong
ValueError: Tool name conflict: built-in workspace tools reserve these names
ValueError: Tool name conflict: built-in workspace tools reserve these names
Raised when the agent prepares its tools (the first Rename your tool, or set
run, or
list_all_available_tools) if one of yours is named like a workspace tool:agent_config={"enable_workspace_files": False}.TypeError: Expected Tool object or object with get_tool() method
TypeError: Expected Tool object or object with get_tool() method
local_tools was given a list of plain functions, or register() was
given one:@tools.register_tool(...) and pass the
registry; a list must hold Tool objects or objects with get_tool().The call failed with 'Tool execution exceeded its time limit'
The call failed with 'Tool execution exceeded its time limit'
The tool ran longer than A timed-out call may still finish and take effect: a plain (synchronous)
function runs in a thread that cannot be stopped, so it carries on after
the limit. The model is told so (“it may still take effect… Check before
calling it again”), not that it failed; the trace records it as
tool_call_timeout. With
agent_config={"tool_call_timeout": 2} and a tool that sleeps for ten
seconds, the run carried on and the trajectory showed:timeout.
Give a tool with side effects a way to check (a list or lookup tool), and
raise the timeout or make the tool return sooner (start the work and
return a handle the model can check later).The model's arguments were rejected
The model's arguments were rejected
Arguments that do not match the schema never reach your function; the
model is told why and usually corrects itself. Called directly, a missing
argument reads:If the model keeps getting it wrong, make the docstring say what each
parameter is (
name: description lines), or use Literal for fixed choices.Next
MCP tools
Tools from MCP servers, next to your own.
Code mode
Let the model call your tools from a short Python program.
Many tools
Dozens or hundreds of tools: the model searches for the ones it needs.
Ask before a tool runs
A policy that asks a person before a refund.