Skip to main content
A tool is an action your agent takes. Wrapping a function as a tool routes every call through the kernel, where it’s recorded as a tool_call step. Policy applies to it, it replays on resume, and it shows up in the audit trail.

@tool

Decorate an async function:
The wrapper keeps the original signature (via functools.wraps), so frameworks that introspect your function bind it unchanged. Hand tools to a framework as a plain list:
Forms of the decorator:

Idempotency

idempotency controls whether a tool may run again when a dispatch replays.
  • safe_to_retry (default) is for reads and anything else fine to execute again. On resume the recorded result replays, and a step that never completed runs again.
  • at_most_once is for destructive operations such as sending an email or charging a card. If a resumed dispatch finds the step already executing, the kernel fails it with reason indeterminate and the SDK raises ToolError, so your handler decides how to reconcile. A step recorded but never started is still safe, so it runs.

Resources

resources names the external resources a tool may change. Defaults to none.

Denied calls

A tool denied by policy does not raise. @tool and wrap_tool catch the PolicyError and return a string instead:
The LLM sees that as the tool’s result and can pick a different path, rather than the denial crashing the run. A denied rebuno.step() raises PolicyError normally, since nothing reads its result as a tool response.

Subagents

subagent() runs another agent as a subagent of the calling tool. Return its result from the tool body:
The tool’s result is the subagent’s output, and a failed or cancelled subagent raises ToolError. Other tool calls keep running while it waits. Once every call in flight waits, the execution suspends and releases its worker. The handler reruns after they settle and replays their outcomes, so concurrent tool calls can each start a subagent. Pass session= to continue a conversation with a subagent. Subagents started with the same session run one at a time, and each continues from the last one that completed. The session must differ from the parent’s. Derive it from the parent’s session or ID so other parents cannot reuse it:
The subagent is created with a Client, which needs a key with the executions:write scope. Pass client=, or set REBUNO_URL and REBUNO_API_KEY. Calling subagent() outside a tool body raises RuntimeError.

Blocking work

The event loop has to stay responsive. The kernel lease is renewed by a background heartbeat that only fires while the handler yields (see How it works).
Offload blocking or CPU-bound work to a thread:

Calling context

A tool records against the current execution. Calling one outside an active dispatch raises RuntimeError. Active means inside a handler running under agent.run(), or inside a test context.

wrap_tool

@tool fits plain functions. wrap_tool builds a Rebuno-routed callable from a name plus an invoke(args) seam. Use it for tools that aren’t plain callables, like framework tool objects or schema-only tools:
  • name is the tool id the LLM sees (via __name__) and the kernel sees, so put any namespace prefix directly in it.
  • args_schema’s properties and required build a keyword-only signature that frameworks introspect. The schema itself is exposed on __input_schema__. The wrapper still accepts **kwargs, so an argument outside the schema is passed through.
  • to_result maps the raw return before it’s recorded. Defaults to identity.
  • transform_args maps the argument dict before it’s recorded and passed to invoke, for something like null-stripping. Defaults to identity.

MCP tools

rebuno.mcp wraps Model Context Protocol tool descriptors, so MCP tools get the same treatment as native ones.
  • Descriptors can be attribute-style (the official mcp SDK’s Tool, a fastmcp tool) or plain dicts. Both work.
  • prefix namespaces the tool id. The LLM and the kernel see f"{prefix}_{name}", while the MCP server (via call) sees the bare name. An empty prefix uses the name as-is.
  • The result is flattened from a standard MCP CallToolResult by default, preferring structured content and otherwise joining text blocks. Override it with to_result.
  • Null arguments are stripped by default, since LLMs often fill optional fields with null and typed MCP parameters reject it.
wrap_mcp_tool does one descriptor; wrap_mcp_tools maps over a list.