Skip to main content

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/// The generated function is used by `#[tool_handler]` by default (via `Self::tool_router()`),
51/// so in most cases you do not need to store the router in a field.
52///
53/// ## Usage
54///
55/// | field            | type          | usage |
56/// | :-               | :-            | :-    |
57/// | `router`         | `Ident`       | The name of the router function to be generated. Defaults to `tool_router`. |
58/// | `vis`            | `Visibility`  | The visibility of the generated router function. Defaults to empty. |
59/// | `server_handler` | `flag`        | When set, also emits `#[::rmcp::tool_handler]` on `impl ServerHandler for Self` so you can omit a separate `#[tool_handler]` block. |
60///
61/// ## Example
62///
63/// ```rust,ignore
64/// #[tool_router]
65/// impl MyToolHandler {
66///     #[tool]
67///     pub fn my_tool() {
68///
69///     }
70/// }
71///
72/// // #[tool_handler] calls Self::tool_router() automatically
73/// #[tool_handler]
74/// impl ServerHandler for MyToolHandler {}
75/// ```
76///
77/// ### Eliding `#[tool_handler]`
78///
79/// For a tools-only server, pass `server_handler` so the `impl ServerHandler` block is not written by hand:
80///
81/// ```rust,ignore
82/// #[tool_router(server_handler)]
83/// impl MyToolHandler {
84///     #[tool]
85///     fn my_tool() {}
86/// }
87/// ```
88///
89/// This expands in two steps: first `#[tool_router]` emits the inherent impl plus
90/// `#[::rmcp::tool_handler] impl ServerHandler for MyToolHandler {}`, then `#[tool_handler]`
91/// fills in `call_tool`, `list_tools`, `get_info`, and related methods. If you combine tools with
92/// prompts or tasks on the **same** `impl ServerHandler` block (stacked `#[tool_handler]` /
93/// `#[prompt_handler]` attributes), keep using an explicit `#[tool_handler]` impl instead of `server_handler`.
94///
95/// Or specify the visibility and router name, which would be helpful when you want to combine multiple routers into one:
96///
97/// ```rust,ignore
98/// mod a {
99///     #[tool_router(router = tool_router_a, vis = "pub")]
100///     impl MyToolHandler {
101///         #[tool]
102///         fn my_tool_a() {
103///             
104///         }
105///     }
106/// }
107///
108/// mod b {
109///     #[tool_router(router = tool_router_b, vis = "pub")]
110///     impl MyToolHandler {
111///         #[tool]
112///         fn my_tool_b() {
113///             
114///         }
115///     }
116/// }
117///
118/// impl MyToolHandler {
119///     fn new() -> Self {
120///         Self {
121///             tool_router: self::tool_router_a() + self::tool_router_b(),
122///         }
123///     }
124/// }
125/// ```
126#[proc_macro_attribute]
127pub fn tool_router(attr: TokenStream, input: TokenStream) -> TokenStream {
128    tool_router::tool_router(attr.into(), input.into())
129        .unwrap_or_else(|err| err.to_compile_error())
130        .into()
131}
132
133/// # tool_handler
134///
135/// This macro generates the `call_tool`, `list_tools`, `get_tool`, and (optionally)
136/// `get_info` methods for a `ServerHandler` implementation, using a `ToolRouter`.
137///
138/// ## Usage
139///
140/// | field          | type     | usage |
141/// | :-             | :-       | :-    |
142/// | `router`       | `Expr`   | The expression to access the `ToolRouter` instance. Defaults to `Self::tool_router()`. |
143/// | `meta`         | `Expr`   | Optional metadata for `ListToolsResult`. |
144/// | `name`         | `String` | Custom server name. Defaults to `CARGO_CRATE_NAME`. |
145/// | `version`      | `String` | Custom server version. Defaults to `CARGO_PKG_VERSION`. |
146/// | `instructions` | `String` | Optional human-readable instructions about using this server. |
147///
148/// ## Minimal example (no boilerplate)
149///
150/// The macro automatically generates `get_info()` with tools capability enabled
151/// and reads the server name/version from `Cargo.toml`:
152///
153/// ```rust,ignore
154/// struct TimeServer;
155///
156/// #[tool_router]
157/// impl TimeServer {
158///     #[tool(description = "Get current time")]
159///     async fn get_time(&self) -> String { "12:00".into() }
160/// }
161///
162/// #[tool_handler]
163/// impl ServerHandler for TimeServer {}
164/// ```
165///
166/// ## Custom server info
167///
168/// ```rust,ignore
169/// #[tool_handler(name = "my-server", version = "1.0.0", instructions = "A helpful server")]
170/// impl ServerHandler for MyToolHandler {}
171/// ```
172///
173/// ## Custom router expression
174///
175/// ```rust,ignore
176/// #[tool_handler(router = self.tool_router)]
177/// impl ServerHandler for MyToolHandler {
178///    // ...implement other handler
179/// }
180/// ```
181///
182/// ## Manual `get_info()`
183///
184/// If you provide your own `get_info()`, the macro will not generate one:
185///
186/// ```rust,ignore
187/// #[tool_handler]
188/// impl ServerHandler for MyToolHandler {
189///     fn get_info(&self) -> ServerInfo {
190///         ServerInfo::new(ServerCapabilities::builder().enable_tools().build())
191///     }
192/// }
193/// ```
194#[proc_macro_attribute]
195pub fn tool_handler(attr: TokenStream, input: TokenStream) -> TokenStream {
196    tool_handler::tool_handler(attr.into(), input.into())
197        .unwrap_or_else(|err| err.to_compile_error())
198        .into()
199}
200
201/// # prompt
202///
203/// This macro is used to mark a function as a prompt handler.
204///
205/// This will generate a function that returns the attribute of this prompt, with type `rmcp::model::Prompt`.
206///
207/// ## Usage
208///
209/// | field             | type     | usage |
210/// | :-                | :-       | :-    |
211/// | `name`            | `String` | The name of the prompt. If not provided, it defaults to the function name. |
212/// | `description`     | `String` | A description of the prompt. The document of this function will be used if not provided. |
213/// | `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. |
214///
215/// ## Example
216///
217/// ```rust,ignore
218/// #[prompt(name = "code_review", description = "Reviews code for best practices")]
219/// pub async fn code_review_prompt(&self, Parameters(args): Parameters<CodeReviewArgs>) -> Result<Vec<PromptMessage>> {
220///     // Generate prompt messages based on arguments
221/// }
222/// ```
223#[proc_macro_attribute]
224pub fn prompt(attr: TokenStream, input: TokenStream) -> TokenStream {
225    prompt::prompt(attr.into(), input.into())
226        .unwrap_or_else(|err| err.to_compile_error())
227        .into()
228}
229
230/// # prompt_router
231///
232/// This macro generates a prompt router based on functions marked with `#[rmcp::prompt]` in an implementation block.
233///
234/// It creates a function that returns a `PromptRouter` instance.
235///
236/// ## Usage
237///
238/// | field     | type          | usage |
239/// | :-        | :-            | :-    |
240/// | `router`  | `Ident`       | The name of the router function to be generated. Defaults to `prompt_router`. |
241/// | `vis`     | `Visibility`  | The visibility of the generated router function. Defaults to empty. |
242///
243/// ## Example
244///
245/// ```rust,ignore
246/// #[prompt_router]
247/// impl MyPromptHandler {
248///     #[prompt]
249///     pub async fn greeting_prompt(&self, Parameters(args): Parameters<GreetingArgs>) -> Result<Vec<PromptMessage>, Error> {
250///         // Generate greeting prompt using args
251///     }
252///
253///     pub fn new() -> Self {
254///         Self {
255///             // the default name of prompt router will be `prompt_router`
256///             prompt_router: Self::prompt_router(),
257///         }
258///     }
259/// }
260/// ```
261#[proc_macro_attribute]
262pub fn prompt_router(attr: TokenStream, input: TokenStream) -> TokenStream {
263    prompt_router::prompt_router(attr.into(), input.into())
264        .unwrap_or_else(|err| err.to_compile_error())
265        .into()
266}
267
268/// # prompt_handler
269///
270/// This macro generates handler methods for `get_prompt` and `list_prompts` in the
271/// implementation block, using a `PromptRouter`. It also auto-generates `get_info()`
272/// with prompts capability enabled if not already provided.
273///
274/// ## Usage
275///
276/// | field     | type   | usage |
277/// | :-        | :-     | :-    |
278/// | `router`  | `Expr` | The expression to access the `PromptRouter` instance. Defaults to `Self::prompt_router()`. |
279/// | `meta`    | `Expr` | Optional metadata for `ListPromptsResult`. |
280///
281/// ## Example
282/// ```rust,ignore
283/// #[prompt_handler]
284/// impl ServerHandler for MyPromptHandler {
285///     // ...implement other handler methods
286/// }
287/// ```
288///
289/// or using a custom router expression:
290/// ```rust,ignore
291/// #[prompt_handler(router = self.prompt_router)]
292/// impl ServerHandler for MyPromptHandler {
293///    // ...implement other handler methods
294/// }
295/// ```
296#[proc_macro_attribute]
297pub fn prompt_handler(attr: TokenStream, input: TokenStream) -> TokenStream {
298    prompt_handler::prompt_handler(attr.into(), input.into())
299        .unwrap_or_else(|err| err.to_compile_error())
300        .into()
301}
302
303/// # task_handler
304///
305/// Generates basic task-handling methods (`enqueue_task` and `list_tasks`) for a server handler
306/// using a shared \[`OperationProcessor`\]. The default processor expression assumes a
307/// `self.processor` field holding an `Arc<Mutex<OperationProcessor>>`, but it can be customized
308/// via `#[task_handler(processor = ...)]`. Because the macro captures `self` inside spawned
309/// futures, the handler type must implement [`Clone`].
310#[proc_macro_attribute]
311pub fn task_handler(attr: TokenStream, input: TokenStream) -> TokenStream {
312    task_handler::task_handler(attr.into(), input.into())
313        .unwrap_or_else(|err| err.to_compile_error())
314        .into()
315}