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