#[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.