adk-rust-macros 0.5.0

Proc macros for ADK-Rust — #[tool] attribute for zero-boilerplate tool registration
Documentation

adk-rust-macros

Proc macros for ADK-Rust — #[tool] attribute for zero-boilerplate tool registration.

Overview

This crate provides the #[tool] attribute macro that turns an async function into a full adk_core::Tool implementation. No manual struct definitions, no trait boilerplate, no JSON schema wiring.

Installation

[dependencies]
adk-rust-macros = "0.5.0"
adk-tool = "0.5.0"
schemars = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

Quick Start

use adk_rust_macros::tool;
use schemars::JsonSchema;
use serde::Deserialize;

#[derive(Deserialize, JsonSchema)]
struct AddArgs {
    /// First number
    a: i64,
    /// Second number
    b: i64,
}

/// Add two numbers together.
#[tool]
async fn add(args: AddArgs) -> Result<serde_json::Value, adk_tool::AdkError> {
    Ok(serde_json::json!(args.a + args.b))
}

// Generated: pub struct Add; implements adk_core::Tool
// Register: agent_builder.tool(Arc::new(Add))

What the Macro Generates

For a function get_weather, the macro generates:

Input Output
Function name get_weather Tool name "get_weather"
Doc comment /// Get the current weather. Tool description "Get the current weather."
Arg type WeatherArgs JSON schema via schemars::schema_for!(WeatherArgs)
Struct GetWeather implementing Tool trait

The generated struct is zero-sized and implements adk_core::Tool with:

  • name() → snake_case function name
  • description() → doc comment text
  • parameters_schema() → JSON schema from the args type
  • execute() → deserializes args, calls your function

Usage Patterns

Simple tool (args only)

#[derive(Deserialize, JsonSchema)]
struct SearchArgs {
    /// The search query
    query: String,
    /// Maximum results to return
    #[serde(default = "default_limit")]
    limit: usize,
}

fn default_limit() -> usize { 10 }

/// Search the knowledge base for documents matching a query.
#[tool]
async fn search_docs(args: SearchArgs) -> Result<serde_json::Value, adk_tool::AdkError> {
    // Your implementation here
    Ok(serde_json::json!({ "results": [], "query": args.query }))
}

// Use: Arc::new(SearchDocs)

Tool with context access

use std::sync::Arc;
use adk_tool::ToolContext;

/// Read the current session state.
#[tool]
async fn read_state(
    ctx: Arc<dyn ToolContext>,
    args: ReadStateArgs,
) -> Result<serde_json::Value, adk_tool::AdkError> {
    // Access session, state, or other context
    Ok(serde_json::json!({ "key": args.key }))
}

No-args tool

/// Get the current server time.
#[tool]
async fn get_time() -> Result<serde_json::Value, adk_tool::AdkError> {
    Ok(serde_json::json!({ "time": "2026-03-26T12:00:00Z" }))
}

// Use: Arc::new(GetTime)

Schema Cleaning

The macro automatically cleans the generated JSON schema for LLM API compatibility:

  • Strips $schema and title fields (rejected by Gemini)
  • Simplifies nullable types: {"type": ["string", "null"]}{"type": "string"}
  • Unwraps anyOf wrappers for simple Option<T> fields

Naming Convention

Function Generated Struct
get_weather GetWeather
search_docs SearchDocs
add Add
send_email_notification SendEmailNotification

The function name (snake_case) becomes the tool name string. The struct name (PascalCase) is what you pass to Arc::new().

Requirements

  • Function must be async
  • Args type must implement serde::Deserialize and schemars::JsonSchema
  • Return type must be Result<serde_json::Value, adk_tool::AdkError>
  • Doc comments are used as the tool description (falls back to function name with underscores replaced by spaces)

License

Apache-2.0