pub trait Tool: Send + Sync {
// Required methods
fn name(&self) -> &str;
fn description(&self) -> &str;
fn input_schema(&self) -> Value;
fn execute<'life0, 'life1, 'async_trait>(
&'life0 self,
input: Value,
ctx: &'life1 ToolContext,
) -> Pin<Box<dyn Future<Output = Result<ToolOutput, ToolError>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait;
// Provided methods
fn class(&self) -> ToolClass { ... }
fn is_recursive(&self) -> bool { ... }
}Expand description
Trait for tools that the agent can use.
Implement this trait to create custom tools.
§Example
use tkach::{Tool, ToolContext, ToolOutput, ToolError};
use serde_json::{json, Value};
struct MyTool;
#[async_trait::async_trait]
impl Tool for MyTool {
fn name(&self) -> &str { "my_tool" }
fn description(&self) -> &str { "Does something useful" }
fn input_schema(&self) -> Value {
json!({
"type": "object",
"properties": {
"query": { "type": "string" }
},
"required": ["query"]
})
}
async fn execute(&self, input: Value, ctx: &ToolContext) -> Result<ToolOutput, ToolError> {
let query = input["query"].as_str().unwrap_or_default();
Ok(ToolOutput::text(format!("Result for: {query}")))
}
}Required Methods§
Sourcefn description(&self) -> &str
fn description(&self) -> &str
Human-readable description of what the tool does.
Sourcefn input_schema(&self) -> Value
fn input_schema(&self) -> Value
JSON Schema describing the tool’s input parameters.
Provided Methods§
Sourcefn class(&self) -> ToolClass
fn class(&self) -> ToolClass
Side-effect class. Defaults to Mutating — the safe choice for
tools that are not explicitly marked read-only. Override to return
ToolClass::ReadOnly only when you are certain the tool has no
observable side effects.
Sourcefn is_recursive(&self) -> bool
fn is_recursive(&self) -> bool
Whether this tool itself drives nested executor work — i.e.
the tool’s execute body holds the executor task open while
running another Agent::run underneath. The canonical example
is SubAgent.
The executor uses this to admit recursive tools through the
concurrent-mutator pool regardless of explicit promotion,
because non-recursive admission classes (the width-1
serial_mut pool, in particular) are shared across the
agent tree and would deadlock when a parent’s permit is
pinned during the child’s nested execute. Routing recursive
tools through concurrent_mut — which the executor forks
per nesting level — keeps nested fan-out free of pool
contention while still bounding it by the
max_concurrent_mutations cap of the current level.
Defaults to false. Override to true for tools whose
execute body drives nested executor work.
When you override this, the recursive call site must run
against an executor obtained from
ctx.executor.fork_for_subagent() — not directly against
ctx.executor. The fork is what gives the nested level its
own concurrent_mut and per-tool semaphores; without it, a
parent saturating those pools would deadlock the moment any
child tried to acquire a permit from the same shared
semaphore. The canonical pattern is to construct an
Agent::builder().executor(ctx.executor.fork_for_subagent())
and agent.run(...) from inside execute; SubAgent
implements exactly this shape.