agentkit.tools¶
The Tool Protocol, FunctionTool, the @tool decorator, the
ToolRegistry, and the as_tool adapter that turns an Agent into a
tool callable by another agent.
Tool Protocol stack — peer of agentkit.memory and agentkit.skills.
The Tool Protocol is the Command-pattern surface every action an agent can request must
satisfy. FunctionTool is the canonical impl wrapping a Python callable, with the @tool
decorator as the ergonomic front door. ToolRegistry is the Composite holding many tools
behind a single lookup-by-name surface.
Tool
¶
Bases: Protocol
An action an agent can request — Command pattern done as a Protocol.
Any object satisfying this shape IS a Tool: it carries a stable
name + description, a JSON schema describing its input
args, an optional output_schema for return-value validation,
flags for side_effecting and requires_approval so the
runtime can make dispatch decisions, and an async run(args, ctx)
that actually executes.
FunctionTool is the canonical impl wrapping a Python callable.
Others may implement directly — remote-procedure tools, MCP tools,
skill-as-tool adapters. The framework dispatches tools through the
middleware chain (audit / approval-gate / output-coerce / retry)
regardless of which concrete impl backs them.
schema may be None (loop-invisible tool advertising no
schema) or a ToolSchema; output_schema accepts the same
shapes FunctionTool.output_schema does — a JSON-Schema dict, a
Pydantic / dataclass / attrs class, or None (no check).
run.args is Mapping[str, Any] — a plain dict OR a
MappingProxyType handed down from a ToolCall both satisfy it.
ToolDefinitionError
¶
Bases: ValueError
Raised at decoration/registration time when a tool fails the framework's wiring contract:
missing/thin docstring, missing side_effecting= declaration, or other static defects the
framework can catch before the agent ever runs. Subclasses ValueError so existing
except ValueError paths still trip — the failure mode is genuinely a bad value.
ToolShapeError
¶
Bases: Exception
A tool's result didn't match its declared output_schema.
Raised by :meth:FunctionTool.run AFTER the function executed successfully but
the result failed schema validation. The retry middleware catches this and
reflects the error back to the model as a tool-call failure — the model sees a
structured "tool returned a value that doesn't match its declared output"
message and can re-issue the call (often with different args) or pivot.
Distinct from :class:OutputCoercionError (which is about MODEL response
coercion); tool shape mismatches have a different fire site (after the tool
function ran) and a different recovery shape on the model side (the model
didn't author the bad value — the tool did).
FileTool
¶
The memory(command=…) tool. Declares description and
output_schema so isinstance(FileTool(), Tool) holds under the
@runtime_checkable :class:Tool Protocol.
InMemoryFiles
¶
A minimal in-memory file tree (path -> text). The backend protocol is async so a real
durable/filesystem backend does its blocking I/O off the loop (via to_thread) without stalling it —
in-memory ops are instant but stay async def to keep the seam uniform (async-first).
create
async
¶
Create a file. Refuses to silently clobber an existing path — pass
overwrite=True to replace deliberately. An unconditional write
would actively lie about what happened when the path already held a
different note from a prior run. Pairs with rename's no-clobber
semantics so all destructive writes are explicit.
FunctionTool
dataclass
¶
FunctionTool(
name: str,
fn: Callable[[Any, Any], Any],
description: str,
side_effecting: bool,
schema: Any = None,
idempotent: bool = False,
requires_approval: bool = False,
caps: tuple[str, ...] = (),
url_arg: str | None = None,
output_schema: Any = None,
)
output_schema
class-attribute
instance-attribute
¶
Optional schema the tool's RESULT must match. Pydantic BaseModel /
dataclass / attrs class / raw JSON Schema dict — same accepted shapes
as Agent.output=. When set, the tool's return value is validated
through adapt(output_schema).validate(result) before the framework
hands it back to the model.
A mismatch drops a tool.shape_mismatch span event on the open
execute_tool span and raises :class:ToolShapeError — catchable by
the retry middleware so the model can see the structured error and
recover (re-issue the call with different args or pivot).
None (default) means NO check — fast path. Tools that already
return well-typed Python objects get zero overhead. from_callable
auto-sets this from the function's return-type annotation when the
annotation is a Pydantic / dataclass / attrs class; the caller can
override (or opt out via output_schema=None) on the @tool
decorator.
run
async
¶
Always-async run — bridges a sync fn off the event loop so a blocking tool never stalls it.
When an output_schema is wired on this tool, the result is run
through adapter.validate(result) AFTER the function returns.
A schema mismatch drops a tool.shape_mismatch event on the
currently-open span (the execute_tool span the tracing
middleware just opened) and raises :class:ToolShapeError so the
retry middleware can reflect the error back to the model.
Fast path: no adapter wired → one if branch, zero overhead.
from_callable
classmethod
¶
from_callable(
func: Callable[..., Any],
*,
name: str | None = None,
description: str | None = None,
side_effecting: bool = False,
idempotent: bool = False,
requires_approval: bool = False,
caps: tuple[str, ...] = (),
url_arg: str | None = None,
output_schema: Any = _OUTPUT_AUTO,
) -> FunctionTool
Turn a plain Python function into a tool: inspect its signature + type hints → a JSON-schema
ToolSchema, and wrap execution (sync → off the loop via to_thread, async → awaited). The model
calls it by name with JSON args; a parameter named ctx/context is injected with the RunContext
(and not advertised). The function's return value is the tool result; exceptions propagate to the
framework's per-tool isolation + typed Failure — no result is silently swallowed.
The function MUST carry a docstring (or be passed an explicit description=) of at least
_MIN_DESCRIPTION_LEN chars — otherwise raises ToolDefinitionError at registration time.
side_effecting defaults to False here for compatibility with the @tool decorator which
enforces explicit declaration; direct callers should always pass it knowingly.
output_schema controls tool-result validation:
- default (_OUTPUT_AUTO sentinel): auto-infer from the function's
return-type annotation. A Pydantic BaseModel / dataclass / attrs
class return type triggers validation on every call; everything
else (primitives, Any, generics) gets no check.
- explicit class / dict: use this as the output schema (overrides
auto-inference).
- explicit None: OPT OUT of validation even if the return type
looks enforceable.
ToolRegistry
¶
register
¶
register(tool: Any, *, replace: bool = False) -> Tool
Register a FunctionTool — or a plain callable, auto-converted via from_callable.
A plain callable goes through the same tool-writing contract as @tool (docstring floor,
explicit side_effecting if you want to deviate from the read-only default); a callable
whose docstring is too thin surfaces a ToolDefinitionError here, not at runtime.
Raises :class:ValueError on name collision unless replace=True.
The model advertises tools by name; a silent overwrite would change
the implementation under the agent without any signal.
from_tools
classmethod
¶
from_tools(items: Iterable[Any]) -> ToolRegistry
Build a registry from a list mixing FunctionTools and plain functions ([get_weather, …]).
Each plain callable is validated by FunctionTool.from_callable; a thin docstring or other
contract violation raises ToolDefinitionError here instead of silently wrapping a bad tool.
Name collisions across the list propagate the same
:class:ValueError as register.
schemas
¶
Advertised tools, stable order (so a cacheable system+tools prefix stays byte-identical).
as_tool
¶
as_tool(
runnable: Any,
*,
name: str,
description: str = "",
side_effecting: bool = False,
requires_approval: bool = False,
render: Callable[[Any], str] | None = None,
) -> FunctionTool
Wrap any runnable (leaf Agent / coordinator Agent / Workflow — anything with
async run(task, ctx) -> result) as a FunctionTool an Agent can call. The sub-run executes on
a ctx.child() (so depth/budget/cancellation/observation all flow), and its result is rendered to text
for the loop. This is the explicit/coordinator-inside-emergent composition.
render_result
¶
Render any run result to text for re-entry into a loop: AgentResult.output or a
WorkflowResult's terminal outputs.
tool
¶
tool(
func: Callable[..., Any] | None = None,
*,
side_effecting: Any = _REQUIRED,
idempotent: bool = False,
name: str | None = None,
description: str | None = None,
requires_approval: bool = False,
caps: tuple[str, ...] = (),
url_arg: str | None = None,
output_schema: Any = _OUTPUT_AUTO,
) -> Any
Decorator / converter: @tool(side_effecting=False), @tool(side_effecting=True, idempotent=False),
or tool(fn, side_effecting=...) → a FunctionTool. side_effecting= is REQUIRED — the framework's
gating + idempotency primitives rely on knowing whether a tool mutates the world. The decorated
callable MUST carry a docstring of >=30 chars (the model needs enough to understand the tool);
both failure modes raise ToolDefinitionError at decoration time (not at call time).
output_schema= is opt-in tool-result schema validation. By default (the
_OUTPUT_AUTO sentinel) it auto-infers from the function's return-type
annotation — a Pydantic / dataclass / attrs class triggers validation on
every tool result; primitives and generics are skipped. Pass an explicit
class to override; pass output_schema=None to opt out entirely even when
the return type looks enforceable.