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.name the model emits.
fn description(&self) -> &str
Human description shown to the model.
fn is_long_running(&self) -> bool
Default false. When true, the agent emits the tool’s FunctionResponse with will_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. When Some, the runner resolves the credential before calling run and injects it into ToolContext::auth_credential; if interactive consent is needed it emits an adk_request_credential event instead of dispatching. See Auth.
fn requires_confirmation(&self, args: &Value) -> bool
Default false. When true for a given call, the agent pauses with an adk_request_confirmation request 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 None for 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 FunctionResponse payload.
async fn process_llm_request(&self, req: &mut LlmRequest, ctx: &mut ToolContext) -> Result<()>
Hook called before every model call. Default: append declaration() into req.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:

FieldTypeEffect
invocationArc<InvocationContext>Read access to app name, user id, session, services, and the cancellation token.
function_call_idOption<String>Id of the FunctionCall being served (matches FunctionCall::id).
state_deltaStateDeltaKey/value writes merged into session state when the event is appended.
artifact_deltaIndexMap<String, u64>Filename → new version, populated by save_artifact.
skip_summarizationboolWhen 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_agentOption<String>Hands the rest of the invocation to the named agent in the tree.
escalateboolUnwinds escalation — e.g. breaks a LoopAgent iteration.
long_runningboolMarks this call as a long-running operation handle, pausing the invocation.
auth_credentialOption<AuthCredential>Resolved credential, injected by the runner when the tool declared an auth_config().
tool_confirmationOption<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:

  1. Preprocess. Every attached tool gets process_llm_request(&mut req, ...) — most append their declaration; passive tools inject config — and is registered in req.tools_dict under its name.
  2. Call the model. The response becomes an event; missing FunctionCall ids are synthesized so resume and replay stay stable.
  3. No function calls? The event is the final response (after optional code execution of any ExecutableCode parts).
  4. Dispatch. Each call is looked up in tools_dict and pushed through the gate pipeline — confirmation → auth → run. The run itself is wrapped by the agent’s callbacks: before_tool may rewrite the args or short-circuit with a ready-made result, after_tool may rewrite the result, and on_tool_error may recover a failed call (otherwise {"error": ...} is surfaced to the model). Unknown names error with ToolError::Unknown.
  5. Collect actions. transfer_to_agent reroutes the invocation to a sub-agent — an unknown or unreachable target produces a recoverable {"error": "unknown agent ..."} tool response instead of aborting the invocation; escalate emits an escalation marker and stops; long-running or consent-gated calls get will_continue: Some(true) and pause the invocation.
  6. Loop. Tool responses are appended to req.contents as a Role::Tool turn and the cycle repeats.

Where tools come from

  • [#[tool] macro](/docs/function-tools) — annotate an async fn; get a typed, schema-deriving Arc<dyn Tool> constructor for free.
  • FunctionTool — wrap any async closure explicitly, with manual name/description/schema.
  • Built-instransfer_to_agent, exit_loop, google_search, load_memory, load_artifacts, and friends.
  • AgentTool — expose a whole agent as a callable tool.
  • OpenAPI tools — one RestApiTool per 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

rustrust
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()?;
  • [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.