Expand description
Procedural macros for the protocol-droid toolkit.
This crate is separate from protocol-droid because a proc-macro crate is
compiled for the host and consumed only by other crates, so it cannot be a
module of the library that would use it.
It intentionally carries no domain, application or adapters modules.
The other crates in this workspace are built around a hexagonal layering,
and that layering describes where business logic lives. This crate has no
business logic: it is pure tooling that runs at compile time. Giving it four
empty directories to satisfy a pattern would be cargo cult, and would hide
the fact that the layout is chosen per crate rather than imposed blindly.
§What the macros generate
#[tool] on an async fn produces a Tool and a ToolHandler from the
function’s own signature: its parameters become the tool’s inputSchema,
and its return type becomes the answer. #[resource] and #[prompt] mark
a plain fn whose return value is the primitive itself, and need no
generated code beyond the registration that calls it.
#[server] on an impl block collects every marked method below it into
a Server.
§What they deliberately do not generate
The wire document. A generated handler decodes its arguments and returns
through application::server::result, which owns the shape of a
tools/call result. A macro that built that JSON itself would be free to
disagree with the domain type and with the codec that lowers it, and
nothing would notice until a client read the difference.
§Why #[server] goes on an impl block
An attribute macro only ever sees the item it is attached to, so a #[server]
on a struct would not see the impl beside it, and every tool in that impl
would be invisible to it. Putting it on the impl puts the macro where the
methods are, which is the only place the tools can be read from.