#[llm_tool]Expand description
Re-export the #[llm_tool] proc macro for defining tools from plain functions.
§Usage
use llm_tool::{RustTool, ToolContext, ToolRegistry, llm_tool};
/// Adds two numbers together (with a twist).
#[llm_tool]
fn wonky_add(
/// First number.
a: i64,
/// Second number.
b: i64,
) -> Result<String, String> {
Ok(format!("{}", a + b + 1))
}
let mut registry = ToolRegistry::new();
registry.register(WonkyAdd);
assert_eq!(registry.definitions().len(), 1);Transforms a function into a RustTool implementation.
The macro generates:
- A
{FnName}Paramsstruct derivingDeserializeandJsonSchema - A
{FnName}unit struct (PascalCase) implementingRustTool
The tool name is the function name (snake_case).
The tool description comes from one of the sources below.
Parameter names and types come from the function signature.
Doc comments on parameters become schema descriptions.
§Description sources (in priority order)
| Syntax | Cost | Feature |
|---|---|---|
#[llm_tool] + doc comment | Zero (static &str) | — |
#[llm_tool(description = "inline text")] | Zero (static &str) | — |
#[llm_tool(template = "tools/x.tmpl.md")] | Zero (compiled) | prompt-templates |
#[llm_tool(template = "...", params(k = "v"))] | Zero (compiled) | prompt-templates |
#[llm_tool(template = "...", context = fn)] | Runtime Cow::Owned | prompt-templates |
§Inline description
Override or replace the doc comment with an inline string:
#[llm_tool(description = "Get the current weather for a city.")]
fn get_weather(/* … */) -> Result<String, ToolError> { /* … */ }§Template descriptions (feature: prompt-templates)
Load the description from a .tmpl.md file:
#[llm_tool(template = "tools/weather.tmpl.md")]
fn get_weather(/* … */) -> Result<String, ToolError> { /* … */ }For templates with variables, provide compile-time key-value pairs:
#[llm_tool(template = "tools/weather.tmpl.md", params(api = "v3", env = "prod"))]
fn get_weather(/* … */) -> Result<String, ToolError> { /* … */ }The macro reads the template, validates all declared variables are provided, renders the description, and embeds the result as a static string — zero runtime cost.
For runtime context (e.g. values from config), provide a context function:
#[llm_tool(template = "tools/weather.tmpl.md", context = build_ctx)]
fn get_weather(/* … */) -> Result<String, ToolError> { /* … */ }The context function signature is fn(&ToolStruct) -> Context.
Templates are parsed once at startup via LazyLock.
§Typed parameters
Parameters may use &str — the generated params struct stores an owned
String and the macro auto-borrows it before passing to your function body.
§Return types
The return type can be Result<T, E> or just T (infallible):
T:String(wrapped as-is),ToolOutput(passed through), anyT: Serialize(auto-serialized to JSON), or anyT: Into<ToolOutput>E: anyE: Into<ToolError>— built-in forString,ToolError,std::io::Error,serde_json::Error