agent-sdk-macros 0.9.1

Ergonomic derive/declarative macros for the Agent SDK (#[derive(Tool)], #[derive(TypedTool)], tool!, #[derive(ToolName)])
Documentation

Ergonomic procedural and declarative macros for the Agent SDK.

This crate is the macro / ergonomics layer (Phase 13·E) sitting on top of the hand-written tool traits in agent-sdk-tools (the untyped Tool/SimpleTool baseline and the typed TypedTool API from 13·B). It turns today's boilerplate — hand-written name/description/input_schema methods, ToolName enums, and inline tool scaffolding — into derives and a declarative macro, without changing or replacing the traits themselves. Everything here is additive sugar: hand-written tools keep compiling.

You almost never depend on this crate directly. The macros are re-exported from the agent-sdk façade (and its prelude), so the generated code refers to ::agent_sdk::… paths. The examples below are written as a user would write them, against agent_sdk.

#[derive(Tool)]

Derives a SimpleTool impl (the untyped, Value-in baseline) from struct-level #[tool(...)] attributes. The derive wires up name / display_name / description / input_schema / tier; you supply the behaviour by implementing agent_sdk::ToolLogic (with type Input = Value).

use agent_sdk::{Tool, ToolContext, ToolLogic, ToolResult};
use serde_json::{json, Value};

#[derive(Tool)]
#[tool(
    name = "get_weather",
    description = "Get the current weather for a city",
    schema = json!({
        "type": "object",
        "properties": { "city": { "type": "string" } },
        "required": ["city"]
    }),
)]
struct WeatherTool;

impl ToolLogic<()> for WeatherTool {
    type Input = Value;
    async fn execute(&self, _ctx: &ToolContext<()>, input: Value) -> anyhow::Result<ToolResult> {
        let city = input["city"].as_str().unwrap_or("Unknown");
        Ok(ToolResult::success(format!("Weather in {city}: Sunny")))
    }
}

#[derive(TypedTool)]

Derives a TypedTool impl with a typed Input. The input_schema can either be hand-provided (schema = <expr>, matching 13·B's baseline) or auto-derived from the Input type via schemars (schema = "derive", requires the schema-derive feature). Your ToolLogic::execute receives the already-validated, typed Input.

use agent_sdk::{TypedTool, ToolContext, ToolLogic, ToolResult};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, schemars::JsonSchema)]
struct WeatherArgs { city: String }

#[derive(TypedTool)]
#[tool(name = "get_weather", description = "Weather", input = WeatherArgs, schema = "derive")]
struct WeatherTool;

impl ToolLogic<()> for WeatherTool {
    type Input = WeatherArgs;
    async fn execute(&self, _ctx: &ToolContext<()>, input: WeatherArgs) -> anyhow::Result<ToolResult> {
        Ok(ToolResult::success(format!("Weather in {}: Sunny", input.city)))
    }
}

#[derive(ToolName)]

Removes the ToolName marker-impl boilerplate: it derives the Serialize / Deserialize / ToolName trio (with snake_case renaming by default) for a plain enum, so a strongly-typed tool-name enum is a single derive.

tool!

A declarative macro for quick inline tools — handy in examples, tests, and one-off scripts where defining a named struct + impl is overkill.