Tools overview
The DynTool trait that every tool implements, the ToolContext passed to each call, and the dispatch loop that connects model function calls to your code.
A tool in adk-rs is anything implementing the DynTool trait: a name, a description, a JSON-Schema declaration the model sees, and an async run that receives JSON args plus a mutable ToolContext. Through that context a tool can do far more than return a value — it can mutate session state, save artifacts, transfer control to another agent, or pause the whole invocation.
The DynTool trait
fn name(&self) -> &str- Tool name; must match the
FunctionCall.namethe model emits. fn description(&self) -> &str- Human description shown to the model.
fn is_long_running(&self) -> bool- Default
false. Whentrue, the agent emits the tool’sFunctionResponsewithwill_continue: Some(true)and pauses; the caller resumes by resubmitting the final result later. See cancellation & resume. fn auth_config(&self) -> Option<&AuthConfig>- Default
None. WhenSome, the runner resolves the credential before callingrunand injects it intoToolContext::auth_credential; if interactive consent is needed it emits anadk_request_credentialevent instead of dispatching. See Auth. fn requires_confirmation(&self, args: &Value) -> bool- Default
false. Whentruefor a given call, the agent pauses with anadk_request_confirmationrequest instead of dispatching — human-in-the-loop per call, so you can confirm only destructive operations. See tool confirmation. fn confirmation_hint(&self, args: &Value) -> String- Hint shown to the user when confirmation is requested. Defaults to a generic message naming the tool.
fn declaration(&self) -> Option<FunctionDeclaration>- JSON-Schema declaration of the parameters. Return
Nonefor passive tools (e.g. Gemini server-side built-ins) that should not be advertised to the model. async fn run(&self, args: Value, ctx: &mut ToolContext) -> Result<Value>- Execute with JSON args; return a JSON value that becomes the
FunctionResponsepayload. async fn process_llm_request(&self, req: &mut LlmRequest, ctx: &mut ToolContext) -> Result<()>- Hook called before every model call. Default: append
declaration()intoreq.config.tools. Passive tools override this to inject wire-level config (search grounding, memory preloads) instead.
ToolContext: what a tool can do
Every run call receives a fresh ToolContext built around the shared InvocationContext (session, services, run config). Fields a tool sets on the context become actions the agent applies after the call returns:
| Field | Type | Effect |
|---|---|---|
invocation | Arc<InvocationContext> | Read access to app name, user id, session, services, and the cancellation token. |
function_call_id | Option<String> | Id of the FunctionCall being served (matches FunctionCall::id). |
state_delta | StateDelta | Key/value writes merged into session state when the event is appended. |
artifact_delta | IndexMap<String, u64> | Filename → new version, populated by save_artifact. |
skip_summarization | bool | When a tool sets it, the agent ends the turn right after the tool-response event — the tool response becomes the final answer, with no further model call. |
transfer_to_agent | Option<String> | Hands the rest of the invocation to the named agent in the tree. |
escalate | bool | Unwinds escalation — e.g. breaks a LoopAgent iteration. |
long_running | bool | Marks this call as a long-running operation handle, pausing the invocation. |
auth_credential | Option<AuthCredential> | Resolved credential, injected by the runner when the tool declared an auth_config(). |
tool_confirmation | Option<ToolConfirmation> | The user’s approval (with any payload), set before run when confirmation was required and granted. |
Tool-written state_delta and artifact_delta ride on the tool-response event’s actions, so the session service persists them when the event is appended.
async fn save_artifact(&mut self, filename: &str, part: Part) -> Result<u64>- Persists a part via the configured artifact service, records the new version in
artifact_delta, and returns it. Errors if no artifact service is configured. async fn load_artifact(&self, filename: &str, version: Option<u64>) -> Result<Option<Part>>- Loads an artifact (latest version when
None). fn with_function_call_id(self, id: impl Into<String>) -> Self- Builder-style setter for
function_call_id.
The dispatch loop inside LlmAgent
Each turn of an LlmAgent runs the same cycle, capped by max_iterations and the run config’s max_llm_calls:
- Preprocess. Every attached tool gets
process_llm_request(&mut req, ...)— most append their declaration; passive tools inject config — and is registered inreq.tools_dictunder its name. - Call the model. The response becomes an event; missing
FunctionCallids are synthesized so resume and replay stay stable. - No function calls? The event is the final response (after optional code execution of any
ExecutableCodeparts). - Dispatch. Each call is looked up in
tools_dictand pushed through the gate pipeline — confirmation → auth →run. The run itself is wrapped by the agent’s callbacks:before_toolmay rewrite the args or short-circuit with a ready-made result,after_toolmay rewrite the result, andon_tool_errormay recover a failed call (otherwise{"error": ...}is surfaced to the model). Unknown names error withToolError::Unknown. - Collect actions.
transfer_to_agentreroutes the invocation to a sub-agent — an unknown or unreachable target produces a recoverable{"error": "unknown agent ..."}tool response instead of aborting the invocation;escalateemits an escalation marker and stops; long-running or consent-gated calls getwill_continue: Some(true)and pause the invocation. - Loop. Tool responses are appended to
req.contentsas aRole::Toolturn and the cycle repeats.
Where tools come from
- [
#[tool]macro](/docs/function-tools) — annotate an async fn; get a typed, schema-derivingArc<dyn Tool>constructor for free. FunctionTool— wrap any async closure explicitly, with manual name/description/schema.- Built-ins —
transfer_to_agent,exit_loop,google_search,load_memory,load_artifacts, and friends. AgentTool— expose a whole agent as a callable tool.- OpenAPI tools — one
RestApiToolper operation in an OpenAPI 3.x spec. - MCP toolset — every tool advertised by a Model Context Protocol server, over stdio or streamable HTTP.
For dynamic sources there is also the Toolset trait (async fn list_tools(&self, ctx: &ReadonlyContext) plus an optional shutdown), with StaticToolset as the trivial fixed-list implementation. McpToolset and the OpenAPI generator build on it.
Attaching tools to an agent
use adk_rs::agents::LlmAgent;
use adk_rs::tools::{exit_loop, transfer_to_agent_tool};
let agent = LlmAgent::builder("orchestrator")
.model(model)
.instruction("Coordinate the team; exit when done.")
.tool(transfer_to_agent_tool())
.tool(exit_loop())
.tools(my_openapi_tools) // any IntoIterator<Item = Arc<dyn DynTool>>
.build()?;Related pages
- [Function tools & #[tool]](/docs/function-tools) — the ergonomic path to custom tools.
- Tool confirmation — the human-in-the-loop gate in depth.
- Auth — credential resolution for authenticated tools.
- Multi-agent — transfer and escalation across an agent tree.