#[tool_router]macros and server only.Expand description
§tool_router
This macro is used to generate a tool router based on functions marked with #[rmcp::tool] in an implementation block.
It creates a function that returns a ToolRouter instance.
The generated function is used by #[tool_handler] by default (via Self::tool_router()),
so in most cases you do not need to store the router in a field.
§Usage
| field | type | usage |
|---|---|---|
router | Ident | The name of the router function to be generated. Defaults to tool_router. |
vis | Visibility | The visibility of the generated router function. Defaults to empty. |
server_handler | flag | When set, also emits #[::rmcp::tool_handler] on impl ServerHandler for Self so you can omit a separate #[tool_handler] block. |
allow_empty | flag | When set, accepts an impl block with no #[tool] fn. Without it, an empty router is a compile error. |
§Example
#[tool_router]
impl MyToolHandler {
#[tool]
pub fn my_tool() {
}
}
// #[tool_handler] calls Self::tool_router() automatically
#[tool_handler]
impl ServerHandler for MyToolHandler {}§Eliding #[tool_handler]
For a tools-only server, pass server_handler so the impl ServerHandler block is not written by hand:
#[tool_router(server_handler)]
impl MyToolHandler {
#[tool]
fn my_tool() {}
}This expands in two steps: first #[tool_router] emits the inherent impl plus
#[::rmcp::tool_handler] impl ServerHandler for MyToolHandler {}, then #[tool_handler]
fills in call_tool, list_tools, get_info, and related methods. If you combine tools with
prompts or tasks on the same impl ServerHandler block (stacked #[tool_handler] /
#[prompt_handler] attributes), keep using an explicit #[tool_handler] impl instead of server_handler.
Or specify the visibility and router name, which would be helpful when you want to combine multiple routers into one:
mod a {
#[tool_router(router = tool_router_a, vis = "pub")]
impl MyToolHandler {
#[tool]
fn my_tool_a() {
}
}
}
mod b {
#[tool_router(router = tool_router_b, vis = "pub")]
impl MyToolHandler {
#[tool]
fn my_tool_b() {
}
}
}
impl MyToolHandler {
fn new() -> Self {
Self {
tool_router: self::tool_router_a() + self::tool_router_b(),
}
}
}§Empty routers
Collecting tools is this attribute’s whole purpose, so an impl block with no #[tool] fn is a
compile error rather than a router that silently serves nothing. Pass allow_empty when that
is what you want:
#[tool_router(allow_empty)]
impl MyToolHandler {}The usual way to hit this by accident is a macro_rules! helper inside the impl block. An
attribute macro receives the unexpanded item, so #[tool] fns produced by such a helper are
invisible to #[tool_router]. Let the macro_rules! emit the whole annotated impl instead:
macro_rules! define_tools {
($($name:ident => $description:literal),* $(,)?) => {
#[tool_router]
impl MyToolHandler {
$(
#[tool(description = $description)]
async fn $name(&self) -> String { stringify!($name).to_owned() }
)*
}
};
}
define_tools!(my_tool => "what my tool does");