Tools & Composability
Tools are the atomic units of agent capability. Every action an agent takes, from querying a database to sending an email, is a tool invocation. AgentFlow’s tool system provides a global registry, decorator-based registration, SDK registration, approval gates, persisted assignments, and shared instances across agents.Registering tools
Server-side decorators
Tools checked into the AgentFlow server are defined with the@tool decorator and discovered at startup. Import the decorator from framework.decorators:
agent="RecordsAgent" for one target agent, or pass a list to share the same code-defined tool across agents:
agent= accepts str | list[str] | None.
SDK registration
Runtime tools are registered through the agent-scoped SDK resource. Useclient.agents.tools.register(...) for a local Python callable, or pass function_code to client.agents.tools.create(...) when you want to send source explicitly:
function_code may define either a sync or async function; async functions are awaited by the runtime:
agent.register_tool(...) SDK method. Use client.agents.tools.register(...) for local callables, client.agents.tools.create(...) for source-backed runtime tools, or from framework.decorators import tool for server-side source files.
Registry and identifiers
The tool registry is a singleton that deduplicates tool instances across agents:- Tools with the same name share one persisted definition and stable ID.
- Agent and sub-agent assignments are durable and tenant-scoped.
- Agent targeting via
agent="AgentName"oragent=["AgentA", "AgentB"]binds decorator tools to one or more agents. - Runtime registration can assign the same tool to additional agents or a target sub-agent.
tool.id as the canonical persisted identifier. Use tool.name as the model-facing callable name. API path parameters named tool_identifier accept either the ID or the name; direct execution path parameters named tool_id accept either form.
Tool execution
Direct execution
Tools can be run independently, outside of an agent conversation:conversation_id and message_id so events, artifacts, and cache refs have execution context. SDK helpers mint IDs when omitted, but production integrations should pass stable IDs from their own message model. Pass idempotency_key to reject duplicate in-flight submissions for a side-effecting run.
With stream: true, the response is an SSE stream of execution events. With stream: false, the same event objects are collected into a JSON response under events.
The SDK provides collected and streaming helpers:
stream_raw() when you want the backend SSE payloads exactly as emitted. Use stream() when you want the SDK’s typed event projection.
Within agent flow
When an agent decides to use a tool during a conversation, the execution is part of the event stream:Approval gates
Any tool can require human approval before execution. Whenrequire_approval=True, the agent pauses, emits an approval request event, and waits for a user decision:
- Agent decides to call
send_contract - Execution pauses; an approval request is created
- User reviews the tool arguments via the Approvals API
- User approves, approves with edited arguments, or denies
- If approved, tool executes; if denied, agent is informed and can adjust
Approval API
modified_arguments is optional. When present on an approved response, the tool executes with the reviewed arguments instead of the originally proposed arguments.
Lifecycle and permissions
Tool registration, updates, deletes, and assignments are persisted. A server restart should not resurrect a removed assignment or lose an updated runtime tool.| Operation | Behavior |
|---|---|
| Register | Creates or reuses the persisted tool definition, then assigns it to the target agent or sub-agent |
| Update | Writes a new persisted version of the definition, schema, metadata, approval config, or credentials |
| Unassign | Removes the agent or sub-agent assignment while leaving the tool available elsewhere |
| Delete | Removes the persisted tool definition and its assignments when the caller requests a durable delete |
client.agents.tools.delete(agent_id, tool_id_or_name). Sub-agent assignment removal is client.agents.subagents.unassign_tool(agent_id, subagent_id, tool=tool_id_or_name).
All tool management and direct execution endpoints require an AgentFlow bearer token. Management calls require tool administration permissions for the tenant and agent. Direct execution requires permission to run the target agent’s tools and still enforces approval gates. Downstream integrations run with the request principal’s context plus any credentials configured on the tool.
Built-in tool categories
AgentFlow ships with tools across multiple integration domains:| Category | Tool count | Examples |
|---|---|---|
| CRM / Records | 18+ | Account search, opportunity CRUD, contact management, pipeline queries |
| 10+ | Draft, send, reply, search, thread management, inbox operations | |
| Meetings | 8+ | Schedule, availability check, transcript analysis, agenda prep |
| Research | 6+ | Web search, company news, SEC filings, industry reports |
| Tasks | 6+ | Create, assign, track, prioritize, due date management |
| Data | 5+ | SQL queries, report generation, data visualization |
| Knowledge | 4+ | KB search, document retrieval, context injection |
| Internal | 4+ | Planning, retrieval, reasoning, reflection (framework capabilities) |
Result caching and summarizers
Tools that return large structured results (CRM queries, email threads, meeting lists) use the artifact-backed result cache to stay within LLM context limits. The cache summarizer is registered next to the artifact type, not on the@tool decorator:
register_cached_tool(...) is paired with the sibling @artifact(...) summarizer:
- The full result is stored in a TTL-scoped result cache.
- The summarizer produces a compact dict, typically about 200 tokens instead of 10,000+.
- The summary is returned to the LLM with a
cache_idreference. - A matching artifact type is auto-registered so the UI can render the data as a panel.
- The full data remains available through the conversation-scoped cached-results API while the entry is valid.
cache_id, clients can retrieve the full cached payload through the SDK instead of asking the model to reproduce the data:

