Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
223 changes: 40 additions & 183 deletions crates/rmcp-macros/README.md
Original file line number Diff line number Diff line change
@@ -1,208 +1,65 @@
# rmcp-macros

`rmcp-macros` is a procedural macro library for the Rust Model Context Protocol (RMCP) SDK, providing macros that facilitate the development of RMCP applications.
<style>
.rustdoc-hidden { display: none; }
</style>

## Features
<div class="rustdoc-hidden">

This library primarily provides the following macros:
# rmcp-macros

- `#[tool]`: Mark an async/sync function as an RMCP tool and generate metadata + schema glue
- `#[tool_router]`: Collect all `#[tool]` functions in an impl block into a router value
- `#[tool_handler]`: Implement the `call_tool` and `list_tools` entry points by delegating to a router expression
- `#[task_handler]`: Wire up the task lifecycle (list/enqueue/get/cancel) on top of an `OperationProcessor`
[![Crates.io](https://img.shields.io/crates/v/rmcp-macros.svg)](https://crates.io/crates/rmcp-macros)
[![Documentation](https://docs.rs/rmcp-macros/badge.svg)](https://docs.rs/rmcp-macros)

## Usage
</div>

### tool
`rmcp-macros` is a procedural macro library for the Rust Model Context Protocol (RMCP) SDK, providing macros that facilitate the development of RMCP applications.

This macro is used to mark a function as a tool handler.
## Available Macros

This will generate a function that return the attribute of this tool, with type `rmcp::model::Tool`.
| Macro | Description |
|-------|-------------|
| [`#[tool]`][tool] | Mark a function as an MCP tool handler |
| [`#[tool_router]`][tool_router] | Generate a tool router from an impl block |
| [`#[tool_handler]`][tool_handler] | Generate `call_tool` and `list_tools` handler methods |
| [`#[prompt]`][prompt] | Mark a function as an MCP prompt handler |
| [`#[prompt_router]`][prompt_router] | Generate a prompt router from an impl block |
| [`#[prompt_handler]`][prompt_handler] | Generate `get_prompt` and `list_prompts` handler methods |
| [`#[task_handler]`][task_handler] | Wire up the task lifecycle on top of an `OperationProcessor` |

#### Tool attributes
[tool]: https://docs.rs/rmcp-macros/latest/rmcp_macros/attr.tool.html
[tool_router]: https://docs.rs/rmcp-macros/latest/rmcp_macros/attr.tool_router.html
[tool_handler]: https://docs.rs/rmcp-macros/latest/rmcp_macros/attr.tool_handler.html
[prompt]: https://docs.rs/rmcp-macros/latest/rmcp_macros/attr.prompt.html
[prompt_router]: https://docs.rs/rmcp-macros/latest/rmcp_macros/attr.prompt_router.html
[prompt_handler]: https://docs.rs/rmcp-macros/latest/rmcp_macros/attr.prompt_handler.html
[task_handler]: https://docs.rs/rmcp-macros/latest/rmcp_macros/attr.task_handler.html

| field | type | usage |
| :- | :- | :- |
| `name` | `String` | The name of the tool. If not provided, it defaults to the function name. |
| `description` | `String` | A description of the tool. The document of this function will be used. |
| `input_schema` | `Expr` | A JSON Schema object defining the expected parameters for the tool. If not provide, if will use the json schema of its argument with type `Parameters<T>` |
| `annotations` | `ToolAnnotationsAttribute` | Additional tool information. Defaults to `None`. |
## Quick Example

#### Tool example
```rust,ignore
use rmcp::{tool, tool_router, tool_handler, ServerHandler, model::*};

```rust
#[tool(name = "my_tool", description = "This is my tool", annotations(title = "我的工具", read_only_hint = true))]
pub async fn my_tool(param: Parameters<MyToolParam>) {
// handling tool request
#[derive(Clone)]
struct MyServer {
tool_router: rmcp::handler::server::tool::ToolRouter<Self>,
}
```

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

In most case, you need to add a field for handler to store the router information and initialize it when creating handler, or store it with a static variable.

#### Router attributes

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

#### Router example

```rust
#[tool_router]
impl MyToolHandler {
#[tool]
pub fn my_tool() {

}

pub fn new() -> Self {
Self {
// the default name of tool router will be `tool_router`
tool_router: Self::tool_router(),
}
impl MyServer {
#[tool(description = "Say hello")]
async fn hello(&self) -> String {
"Hello, world!".into()
}
}
```

Or specify the visibility and router name, which would be helpful when you want to combine multiple routers into one:

```rust
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(),
}
}
}
```

### tool_handler

This macro will generate the handler for `tool_call` and `list_tools` methods in the implementation block, by using an existing `ToolRouter` instance.

#### Handler attributes

| field | type | usage |
| :- | :- | :- |
| `router` | `Expr` | The expression to access the `ToolRouter` instance. Defaults to `self.tool_router`. |

#### Handler example

```rust
#[tool_handler]
impl ServerHandler for MyToolHandler {
// ...implement other handler
}
```

or using a custom router expression:

```rust
#[tool_handler(router = self.get_router().await)]
impl ServerHandler for MyToolHandler {
// ...implement other handler
}
```

#### Handler expansion

This macro will be expended to something like this:

```rust
impl ServerHandler for MyToolHandler {
async fn call_tool(
&self,
request: CallToolRequestParam,
context: RequestContext<RoleServer>,
) -> Result<CallToolResult, rmcp::ErrorData> {
let tcc = ToolCallContext::new(self, request, context);
self.tool_router.call(tcc).await
}

async fn list_tools(
&self,
_request: Option<PaginatedRequestParam>,
_context: RequestContext<RoleServer>,
) -> Result<ListToolsResult, rmcp::ErrorData> {
let items = self.tool_router.list_all();
Ok(ListToolsResult::with_all_items(items))
}
}
```

### task_handler

This macro wires the task lifecycle endpoints (`list_tasks`, `enqueue_task`, `get_task`, `cancel_task`) to an implementation of `OperationProcessor`. It keeps the handler lean by delegating scheduling, status tracking, and cancellation semantics to the processor.

#### Task handler attributes

| field | type | usage |
| :- | :- | :- |
| `processor` | `Expr` | Expression that yields an `Arc<dyn OperationProcessor>` (or compatible trait object). Defaults to `self.processor.clone()`. |

#### Task handler example

```rust
#[derive(Clone)]
pub struct TaskHandler {
processor: Arc<dyn OperationProcessor<RoleServer> + Send + Sync>,
}

#[task_handler(processor = self.processor.clone())]
impl ServerHandler for TaskHandler {}
```

#### Task handler expansion

At expansion time the macro implements the task-specific handler methods by forwarding to the processor expression, roughly equivalent to:

```rust
impl ServerHandler for TaskHandler {
async fn list_tasks(&self, request: TaskListRequest, ctx: RequestContext<RoleServer>) -> Result<TaskListResult, rmcp::ErrorData> {
self.processor.list_tasks(request, ctx).await
}

async fn enqueue_task(&self, request: TaskEnqueueRequest, ctx: RequestContext<RoleServer>) -> Result<TaskEnqueueResult, rmcp::ErrorData> {
self.processor.enqueue_task(request, ctx).await
impl ServerHandler for MyServer {
fn get_info(&self) -> ServerInfo {
ServerInfo::default()
}

// get_task and cancel_task are generated in the same manner.
}
```


## Advanced Features

- Support for custom tool names and descriptions
- Automatic generation of tool descriptions from documentation comments
- JSON Schema generation for tool parameters
See the [full documentation](https://docs.rs/rmcp-macros) for detailed usage of each macro.

## License

Expand Down
2 changes: 2 additions & 0 deletions crates/rmcp-macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![doc = include_str!("../README.md")]

#[allow(unused_imports)]
use proc_macro::TokenStream;

Expand Down
Loading