tool_call step. Policy
applies to it, it replays on resume, and it shows up in the audit trail.
@tool
Decorate an async function:
functools.wraps), so frameworks
that introspect your function bind it unchanged. Hand tools to a framework as a
plain list:
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_onceis 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 reasonindeterminateand the SDK raisesToolError, 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:
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:
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:
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
Offload blocking or CPU-bound work to a thread:Calling context
A tool records against the current execution. Calling one outside an active dispatch raisesRuntimeError. 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:
nameis the tool id the LLM sees (via__name__) and the kernel sees, so put any namespace prefix directly in it.args_schema’spropertiesandrequiredbuild 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_resultmaps the raw return before it’s recorded. Defaults to identity.transform_argsmaps the argument dict before it’s recorded and passed toinvoke, 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
mcpSDK’sTool, a fastmcp tool) or plain dicts. Both work. prefixnamespaces the tool id. The LLM and the kernel seef"{prefix}_{name}", while the MCP server (viacall) sees the barename. An empty prefix uses the name as-is.- The result is flattened from a standard MCP
CallToolResultby default, preferring structured content and otherwise joining text blocks. Override it withto_result. - Null arguments are stripped by default, since LLMs often fill optional fields
with
nulland typed MCP parameters reject it.
wrap_mcp_tool does one descriptor; wrap_mcp_tools maps over a list.