elicitation_macros
Procedural macros that power the elicitation framework's tool-generation,
tracing instrumentation, and newtype-wrapping infrastructure.
This crate is re-exported by the main elicitation crate — users import the
macros from there, not directly from this crate.
Macros at a glance
| Macro | Kind | Purpose |
|---|---|---|
#[instrumented_impl] |
attribute on impl |
Auto-adds #[tracing::instrument] to every method |
#[elicit_tools(T1, T2, …)] |
attribute on impl |
Generates MCP elicitation tool methods for listed types |
#[elicit_trait_tools_router] |
attribute on impl |
Wraps trait methods as individually discoverable MCP tools |
elicit_safe! |
declarative | Marks a type as safe to surface in MCP schemas |
elicit_newtype! |
declarative | Creates an Arc-wrapped newtype over an existing type |
reflect_methods! |
declarative | Generates MCP tool boilerplate from method signatures |
contract_type |
attribute on type | Attaches requires/ensures verification metadata |
#[instrumented_impl]
Adds #[tracing::instrument] to every public method in an impl block,
choosing the right instrumentation level automatically:
| Method name pattern | Instrumentation |
|---|---|
new, from_*, try_*, default |
#[instrument(ret, err)] — logs return value and errors |
get, as_*, to_*, into_inner |
#[instrument(level = "trace", ret)] — low-noise accessor tracing |
| everything else | #[instrument(skip(self))] — spans without logging self |
use instrumented_impl;
// Expands to:
// new → #[instrument(ret, err)]
// host → #[instrument(level = "trace", ret)]
// validate → #[instrument(skip(self))]
Becomes a no-op under #[cfg(kani)] so formal verification harnesses are
not disrupted by tracing instrumentation.
#[elicit_tools(T1, T2, …)]
Generates a standalone MCP elicitation endpoint for each listed type. Place
it above #[tool_router] on a server impl block:
use ;
use ;
For each type T, this generates:
pub async
The ElicitToolOutput<T> wrapper ensures the MCP schema is always an object
(never a bare enum or primitive), which rmcp requires.
#[elicit_trait_tools_router(TraitName, field, [method1, method2, …])]
Delegates trait methods to an inner field and registers each as an MCP tool. Useful when you implement a trait on a server struct and want every method individually callable:
use elicit_trait_tools_router;
For each method foo, the macro generates:
- A
FooParamsstruct (derivingElicitandJsonSchema) - A
foo_toolmethod decorated with#[tool] - Delegation:
self.client.foo(params.into())
elicit_safe!
Marks a type as safe to surface in MCP schemas — used by the framework to confirm that a type can appear as a tool input or output without violating MCP schema constraints.
use elicit_safe;
elicit_safe!;
Generates a marker trait implementation that the framework checks at compile time before registering a type with the MCP tool registry.
elicit_newtype!
Creates a newtype wrapper that places the inner value behind Arc, making
consuming builder types (like reqwest::RequestBuilder) Clone-able across
async and MCP tool boundaries:
use elicit_newtype;
elicit_newtype!;
elicit_newtype!;
Generated code:
struct RequestBuilder(Arc<reqwest::RequestBuilder>)impl Clone for RequestBuilder— clones theArcimpl Deref / DerefMut— transparent access to the inner typeimpl From<reqwest::RequestBuilder> for RequestBuilderelicit_safe!(RequestBuilder)— MCP schema safety marker
This is the foundation of the elicit_reqwest crate, where all nine
reqwest/http/url types are wrapped this way.
reflect_methods!
Reads the method signatures in an impl block and generates the MCP tool
boilerplate for each one — parameter structs with JSON schemas, tool wrapper
methods, and type conversions — without any hand-written duplication:
use reflect_methods;
elicit_newtype!;
For each method foo(param: ParamType) -> ReturnType, the macro generates:
// 1. Parameter struct
// 2. Tool wrapper
pub async
Type conversions applied automatically:
&str→String&[T]→Vec<T>&T→T(requiresClone)
Methods returning Self (consuming builders) are skipped — they cannot be
wrapped meaningfully as stateless tool calls.
contract_type
An attribute macro that attaches requires and ensures expressions to a
type as const-fn methods, making the verification metadata queryable at
compile time and readable by the TypeSpec explorer at runtime:
use contract_type;
;
Generates:
These are consumed by the ElicitSpec composition in #[derive(Elicit)]
to build the TypeSpec inventory entry for the type automatically.
How the macros compose
A typical newtype MCP plugin is built layer by layer:
elicit_newtype!(Client) ← 1. Arc-wrap, Clone, Deref
↓
#[reflect_methods] impl Client ← 2. Generate param structs + tool wrappers
↓
#[instrumented_impl] impl Client ← 3. Add tracing spans to every method
↓
PluginRegistry::new()
.register("http", ClientPlugin::new()) ← 4. Register with rmcp
For user-defined types the flow is simpler:
#[derive(Elicit, JsonSchema)] ← 1. Generate Prompt/Survey/Select + elicit_checked()
↓
#[elicit_tools(MyType)] ← 2. (optional) standalone tool endpoint
#[tool_router]
impl MyServer { … } ← 3. rmcp discovers all #[tool] methods
License
Licensed under either of Apache License 2.0 or MIT License at your option.