Skip to main content

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

Both calls happened in step 1: the model asked for them together and the runtime ran them as one batch. One tool is a plain function and the other is 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.
Pass 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:
A result too large for the context is saved to the workspace as an artifact, and the model gets a preview it can read further with 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

Raised when the agent prepares its tools (the first run, or list_all_available_tools) if one of yours is named like a workspace tool:
Rename your tool, or set agent_config={"enable_workspace_files": False}.
local_tools was given a list of plain functions, or register() was given one:
Decorate plain functions with @tools.register_tool(...) and pass the registry; a list must hold Tool objects or objects with get_tool().
The tool ran longer than 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:
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 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).
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.