Skip to main content

TypedTool

Trait TypedTool 

Source
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§

Source

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§

Source

fn name(&self) -> &'static str

The tool’s name as sent to (and parsed from) the model.

Source

fn description(&self) -> &'static str

Human-readable description of what the tool does.

Source

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).

Source

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§

Source

fn display_name(&self) -> &'static str

Human-readable display name for UI. Defaults to an empty string.

Source

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".

Implementors§