rmcp_macros/
lib.rs

1#![doc = include_str!("../README.md")]
2
3#[allow(unused_imports)]
4use proc_macro::TokenStream;
5
6mod common;
7mod prompt;
8mod prompt_handler;
9mod prompt_router;
10mod task_handler;
11mod tool;
12mod tool_handler;
13mod tool_router;
14/// # tool
15///
16/// This macro is used to mark a function as a tool handler.
17///
18/// This will generate a function that return the attribute of this tool, with type `rmcp::model::Tool`.
19///
20/// ## Usage
21///
22/// | field             | type                       | usage |
23/// | :-                | :-                         | :-    |
24/// | `name`            | `String`                   | The name of the tool. If not provided, it defaults to the function name. |
25/// | `description`     | `String`                   | A description of the tool. The document of this function will be used. |
26/// | `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>` |
27/// | `annotations`     | `ToolAnnotationsAttribute` | Additional tool information. Defaults to `None`. |
28///
29/// ## Example
30///
31/// ```rust,ignore
32/// #[tool(name = "my_tool", description = "This is my tool", annotations(title = "我的工具", read_only_hint = true))]
33/// pub async fn my_tool(param: Parameters<MyToolParam>) {
34///     // handling tool request
35/// }
36/// ```
37#[proc_macro_attribute]
38pub fn tool(attr: TokenStream, input: TokenStream) -> TokenStream {
39    tool::tool(attr.into(), input.into())
40        .unwrap_or_else(|err| err.to_compile_error())
41        .into()
42}
43
44/// # tool_router
45///
46/// This macro is used to generate a tool router based on functions marked with `#[rmcp::tool]` in an implementation block.
47///
48/// It creates a function that returns a `ToolRouter` instance.
49///
50/// 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.
51/// ## Usage
52///
53/// | field     | type          | usage |
54/// | :-        | :-            | :-    |
55/// | `router`  | `Ident`       | The name of the router function to be generated. Defaults to `tool_router`. |
56/// | `vis`     | `Visibility`  | The visibility of the generated router function. Defaults to empty. |
57///
58/// ## Example
59///
60/// ```rust,ignore
61/// #[tool_router]
62/// impl MyToolHandler {
63///     #[tool]
64///     pub fn my_tool() {
65///         
66///     }
67///
68///     pub fn new() -> Self {
69///         Self {
70///             // the default name of tool router will be `tool_router`
71///             tool_router: Self::tool_router(),
72///         }
73///     }
74/// }
75/// ```
76///
77/// Or specify the visibility and router name, which would be helpful when you want to combine multiple routers into one:
78///
79/// ```rust,ignore
80/// mod a {
81///     #[tool_router(router = tool_router_a, vis = "pub")]
82///     impl MyToolHandler {
83///         #[tool]
84///         fn my_tool_a() {
85///             
86///         }
87///     }
88/// }
89///
90/// mod b {
91///     #[tool_router(router = tool_router_b, vis = "pub")]
92///     impl MyToolHandler {
93///         #[tool]
94///         fn my_tool_b() {
95///             
96///         }
97///     }
98/// }
99///
100/// impl MyToolHandler {
101///     fn new() -> Self {
102///         Self {
103///             tool_router: self::tool_router_a() + self::tool_router_b(),
104///         }
105///     }
106/// }
107/// ```
108#[proc_macro_attribute]
109pub fn tool_router(attr: TokenStream, input: TokenStream) -> TokenStream {
110    tool_router::tool_router(attr.into(), input.into())
111        .unwrap_or_else(|err| err.to_compile_error())
112        .into()
113}
114
115/// # tool_handler
116///
117/// This macro will generate the handler for `tool_call` and `list_tools` methods in the implementation block, by using an existing `ToolRouter` instance.
118///
119/// ## Usage
120///
121/// | field     | type          | usage |
122/// | :-        | :-            | :-    |
123/// | `router`  | `Expr`        | The expression to access the `ToolRouter` instance. Defaults to `self.tool_router`. |
124/// ## Example
125/// ```rust,ignore
126/// #[tool_handler]
127/// impl ServerHandler for MyToolHandler {
128///     // ...implement other handler
129/// }
130/// ```
131///
132/// or using a custom router expression:
133/// ```rust,ignore
134/// #[tool_handler(router = self.get_router().await)]
135/// impl ServerHandler for MyToolHandler {
136///    // ...implement other handler
137/// }
138/// ```
139///
140/// ## Explain
141///
142/// This macro will be expended to something like this:
143/// ```rust,ignore
144/// impl ServerHandler for MyToolHandler {
145///        async fn call_tool(
146///         &self,
147///         request: CallToolRequestParam,
148///         context: RequestContext<RoleServer>,
149///     ) -> Result<CallToolResult, rmcp::ErrorData> {
150///         let tcc = ToolCallContext::new(self, request, context);
151///         self.tool_router.call(tcc).await
152///     }
153///
154///     async fn list_tools(
155///         &self,
156///         _request: Option<PaginatedRequestParam>,
157///         _context: RequestContext<RoleServer>,
158///     ) -> Result<ListToolsResult, rmcp::ErrorData> {
159///         let items = self.tool_router.list_all();
160///         Ok(ListToolsResult::with_all_items(items))
161///     }
162/// }
163/// ```
164#[proc_macro_attribute]
165pub fn tool_handler(attr: TokenStream, input: TokenStream) -> TokenStream {
166    tool_handler::tool_handler(attr.into(), input.into())
167        .unwrap_or_else(|err| err.to_compile_error())
168        .into()
169}
170
171/// # prompt
172///
173/// This macro is used to mark a function as a prompt handler.
174///
175/// This will generate a function that returns the attribute of this prompt, with type `rmcp::model::Prompt`.
176///
177/// ## Usage
178///
179/// | field             | type     | usage |
180/// | :-                | :-       | :-    |
181/// | `name`            | `String` | The name of the prompt. If not provided, it defaults to the function name. |
182/// | `description`     | `String` | A description of the prompt. The document of this function will be used if not provided. |
183/// | `arguments`       | `Expr`   | An expression that evaluates to `Option<Vec<PromptArgument>>` defining the prompt's arguments. If not provided, it will automatically generate arguments from the `Parameters<T>` type found in the function signature. |
184///
185/// ## Example
186///
187/// ```rust,ignore
188/// #[prompt(name = "code_review", description = "Reviews code for best practices")]
189/// pub async fn code_review_prompt(&self, Parameters(args): Parameters<CodeReviewArgs>) -> Result<Vec<PromptMessage>> {
190///     // Generate prompt messages based on arguments
191/// }
192/// ```
193#[proc_macro_attribute]
194pub fn prompt(attr: TokenStream, input: TokenStream) -> TokenStream {
195    prompt::prompt(attr.into(), input.into())
196        .unwrap_or_else(|err| err.to_compile_error())
197        .into()
198}
199
200/// # prompt_router
201///
202/// This macro generates a prompt router based on functions marked with `#[rmcp::prompt]` in an implementation block.
203///
204/// It creates a function that returns a `PromptRouter` instance.
205///
206/// ## Usage
207///
208/// | field     | type          | usage |
209/// | :-        | :-            | :-    |
210/// | `router`  | `Ident`       | The name of the router function to be generated. Defaults to `prompt_router`. |
211/// | `vis`     | `Visibility`  | The visibility of the generated router function. Defaults to empty. |
212///
213/// ## Example
214///
215/// ```rust,ignore
216/// #[prompt_router]
217/// impl MyPromptHandler {
218///     #[prompt]
219///     pub async fn greeting_prompt(&self, Parameters(args): Parameters<GreetingArgs>) -> Result<Vec<PromptMessage>, Error> {
220///         // Generate greeting prompt using args
221///     }
222///
223///     pub fn new() -> Self {
224///         Self {
225///             // the default name of prompt router will be `prompt_router`
226///             prompt_router: Self::prompt_router(),
227///         }
228///     }
229/// }
230/// ```
231#[proc_macro_attribute]
232pub fn prompt_router(attr: TokenStream, input: TokenStream) -> TokenStream {
233    prompt_router::prompt_router(attr.into(), input.into())
234        .unwrap_or_else(|err| err.to_compile_error())
235        .into()
236}
237
238/// # prompt_handler
239///
240/// This macro generates handler methods for `get_prompt` and `list_prompts` in the implementation block, using an existing `PromptRouter` instance.
241///
242/// ## Usage
243///
244/// | field     | type   | usage |
245/// | :-        | :-     | :-    |
246/// | `router`  | `Expr` | The expression to access the `PromptRouter` instance. Defaults to `self.prompt_router`. |
247///
248/// ## Example
249/// ```rust,ignore
250/// #[prompt_handler]
251/// impl ServerHandler for MyPromptHandler {
252///     // ...implement other handler methods
253/// }
254/// ```
255///
256/// or using a custom router expression:
257/// ```rust,ignore
258/// #[prompt_handler(router = self.get_prompt_router())]
259/// impl ServerHandler for MyPromptHandler {
260///    // ...implement other handler methods
261/// }
262/// ```
263#[proc_macro_attribute]
264pub fn prompt_handler(attr: TokenStream, input: TokenStream) -> TokenStream {
265    prompt_handler::prompt_handler(attr.into(), input.into())
266        .unwrap_or_else(|err| err.to_compile_error())
267        .into()
268}
269
270/// # task_handler
271///
272/// Generates basic task-handling methods (`enqueue_task` and `list_tasks`) for a server handler
273/// using a shared \[`OperationProcessor`\]. The default processor expression assumes a
274/// `self.processor` field holding an `Arc<Mutex<OperationProcessor>>`, but it can be customized
275/// via `#[task_handler(processor = ...)]`. Because the macro captures `self` inside spawned
276/// futures, the handler type must implement [`Clone`].
277#[proc_macro_attribute]
278pub fn task_handler(attr: TokenStream, input: TokenStream) -> TokenStream {
279    task_handler::task_handler(attr.into(), input.into())
280        .unwrap_or_else(|err| err.to_compile_error())
281        .into()
282}