Skip to main content

tool

Attribute Macro tool 

Source
#[tool]
Expand description

Marks an async fn as a tool.

This attribute generates nothing on its own. #[server] reads the mark, generates the tool’s schema, the argument struct and the handler, and registers all three — and it does so at module scope, which is the only scope where generated items may be declared.

That split is forced, not chosen. A #[tool] on a method expands inside the impl block, where Rust forbids declaring a struct or a function, and where even a const _ collides with every other const _ in the same impl. There is no way to emit a schema or an argument struct from a method’s own attribute, so a macro that tried would be generating code that cannot compile. A marker that generates nothing is honest about that; a marker that generated something would only generate something for free functions, and the difference would be a rule nobody could see.

So a #[tool] outside a #[server] block is a mark on nothing. #[server] is the only place a tool is built, and that is what makes the generated names (__{Type}_{fn}_TOOL, __{Type}_{fn}_HANDLER, __{Type}_{fn}_Args) predictable: there is exactly one place that mints them, and every one of them carries the type, so two servers in one module can each have an add.

§Examples

A worked example lives in crates/protocol-droid/tests/macros.rs, where every macro shape is exercised against real types and the listings are read back through the dispatcher. A doctest here would be a strict subset of that coverage and would also require this crate to depend on protocol-droid and on serde for compilation, which is the cycle the docstring in Cargo.toml says it does not create.