pub trait TypedTool<Ctx>: Send + Sync {
type Input: DeserializeOwned + Serialize + Send + 'static;
// Required methods
fn name(&self) -> &'static str;
fn description(&self) -> &'static str;
fn input_schema(&self) -> Value;
fn execute(
&self,
ctx: &ToolContext<Ctx>,
input: Self::Input,
) -> impl Future<Output = Result<ToolResult, Error>> + Send;
// Provided methods
fn display_name(&self) -> &'static str { ... }
fn tier(&self) -> ToolTier { ... }
}Expand description
A tool whose model-emitted arguments are validated against a typed,
deserializable Input before execute
runs.
Today a raw serde_json::Value is handed straight to Tool::execute,
so a malformed tool call reaches tool code unvalidated. TypedTool closes
that gap: you declare a Serialize / Deserialize argument struct as
Input, and the runtime deserializes the model’s args
into it at the dispatch boundary. On a deserialization/validation failure
the runtime synthesises a structured error ToolResult (carrying the
serde error message) so the model can self-correct on its next turn —
execute is never called with invalid arguments.
§Relationship to Tool
TypedTool is the typed, opt-in sugar layer; Tool remains the
untyped baseline. A TypedTool becomes a full Tool through
TypedToolAdapter (mirroring how SimpleTool becomes a Tool via
SimpleToolAdapter). Register one with
ToolRegistry::register_typed, which wraps it in the adapter for you;
the adapter performs the deserialize-then-dispatch (or
deserialize-then-synthesise-error) described above.
§Back-compat / migration
Existing Tool impls (and SimpleTool / DynamicToolName tools)
keep compiling and running unchanged — they stay on the Value-in
baseline, which is the identity passthrough (a Value always
“deserializes” into a Value). Migrate a tool to typed args by moving its
impl Tool<Ctx> to impl TypedTool<Ctx>, setting type Input = MyArgs,
and changing execute’s signature from input: Value to input: MyArgs.
The hand-written input_schema JSON stays
user-declared; this trait does not auto-derive a schema from Input.
§Example
use agent_sdk_tools::tools::{TypedTool, ToolContext};
use agent_sdk_foundation::types::ToolResult;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use std::future::Future;
#[derive(Debug, Serialize, Deserialize)]
struct WeatherArgs {
city: String,
}
struct WeatherTool;
impl TypedTool<()> for WeatherTool {
type Input = WeatherArgs;
fn name(&self) -> &'static str { "get_weather" }
fn description(&self) -> &'static str { "Get current weather for a city" }
fn input_schema(&self) -> Value {
json!({
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
})
}
fn execute(
&self,
_ctx: &ToolContext<()>,
input: WeatherArgs,
) -> impl Future<Output = anyhow::Result<ToolResult>> + Send {
async move { Ok(ToolResult::success(format!("Weather in {}: Sunny", input.city))) }
}
}Like SimpleTool, a TypedTool has a single fixed &'static str
name (mapping to DynamicToolName via
TypedToolAdapter). Reach for a hand-written Tool with a
strongly-typed ToolName when the name must be computed at runtime or
constrained to an enum.
Required Associated Types§
Sourcetype Input: DeserializeOwned + Serialize + Send + 'static
type Input: DeserializeOwned + Serialize + Send + 'static
The typed input the model’s arguments are deserialized into before
execute runs.
Must be DeserializeOwned (to parse model args), Serialize (so
the typed value round-trips for logging/storage), and Send + 'static
(to cross the async dispatch boundary).
Required Methods§
Sourcefn description(&self) -> &'static str
fn description(&self) -> &'static str
Human-readable description of what the tool does.
Sourcefn input_schema(&self) -> Value
fn input_schema(&self) -> Value
User-declared JSON schema for the tool’s input parameters.
This stays hand-written JSON — it is not auto-derived from
Input. Keeping the schema explicit lets the
declared provider-facing contract diverge from the Rust type when that
is useful (descriptions, examples, provider-specific keywords).
Sourcefn execute(
&self,
ctx: &ToolContext<Ctx>,
input: Self::Input,
) -> impl Future<Output = Result<ToolResult, Error>> + Send
fn execute( &self, ctx: &ToolContext<Ctx>, input: Self::Input, ) -> impl Future<Output = Result<ToolResult, Error>> + Send
Execute the tool with the already-validated, typed input.
The runtime guarantees input deserialized cleanly from the model’s
arguments; a malformed call is turned into a structured error
ToolResult before this method is reached, so implementations never
see invalid arguments.
§Errors
Returns an error if tool execution fails.
Provided Methods§
Sourcefn display_name(&self) -> &'static str
fn display_name(&self) -> &'static str
Human-readable display name for UI. Defaults to an empty string.
Sourcefn tier(&self) -> ToolTier
fn tier(&self) -> ToolTier
Permission tier for this tool. Defaults to ToolTier::Observe.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".