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/// | `allow_empty` | `flag` | When set, accepts an impl block with no `#[tool]` fn. Without it, an empty router is a compile error. |
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///
127/// ### Empty routers
128///
129/// Collecting tools is this attribute's whole purpose, so an impl block with no `#[tool]` fn is a
130/// compile error rather than a router that silently serves nothing. Pass `allow_empty` when that
131/// is what you want:
132///
133/// ```rust,ignore
134/// #[tool_router(allow_empty)]
135/// impl MyToolHandler {}
136/// ```
137///
138/// The usual way to hit this by accident is a `macro_rules!` helper *inside* the impl block. An
139/// attribute macro receives the unexpanded item, so `#[tool]` fns produced by such a helper are
140/// invisible to `#[tool_router]`. Let the `macro_rules!` emit the whole annotated impl instead:
141///
142/// ```rust,ignore
143/// macro_rules! define_tools {
144/// ($($name:ident => $description:literal),* $(,)?) => {
145/// #[tool_router]
146/// impl MyToolHandler {
147/// $(
148/// #[tool(description = $description)]
149/// async fn $name(&self) -> String { stringify!($name).to_owned() }
150/// )*
151/// }
152/// };
153/// }
154///
155/// define_tools!(my_tool => "what my tool does");
156/// ```
157#[proc_macro_attribute]
158pub fn tool_router(attr: TokenStream, input: TokenStream) -> TokenStream {
159 tool_router::tool_router(attr.into(), input.into())
160 .unwrap_or_else(|err| err.to_compile_error())
161 .into()
162}
163
164/// # tool_handler
165///
166/// This macro generates the `call_tool`, `list_tools`, `get_tool`, and (optionally)
167/// `get_info` methods for a `ServerHandler` implementation, using a `ToolRouter`.
168///
169/// ## Usage
170///
171/// | field | type | usage |
172/// | :- | :- | :- |
173/// | `router` | `Expr` | The expression to access the `ToolRouter` instance. Defaults to `Self::tool_router()`. |
174/// | `meta` | `Expr` | Optional metadata for `ListToolsResult`. |
175/// | `name` | `String` | Custom server name. Defaults to `CARGO_CRATE_NAME`. |
176/// | `version` | `String` | Custom server version. Defaults to `CARGO_PKG_VERSION`. |
177/// | `instructions` | `String` | Optional human-readable instructions about using this server. |
178///
179/// ## Minimal example (no boilerplate)
180///
181/// The macro automatically generates `get_info()` with tools capability enabled
182/// and reads the server name/version from `Cargo.toml`:
183///
184/// ```rust,ignore
185/// struct TimeServer;
186///
187/// #[tool_router]
188/// impl TimeServer {
189/// #[tool(description = "Get current time")]
190/// async fn get_time(&self) -> String { "12:00".into() }
191/// }
192///
193/// #[tool_handler]
194/// impl ServerHandler for TimeServer {}
195/// ```
196///
197/// ## Custom server info
198///
199/// ```rust,ignore
200/// #[tool_handler(name = "my-server", version = "1.0.0", instructions = "A helpful server")]
201/// impl ServerHandler for MyToolHandler {}
202/// ```
203///
204/// ## Custom router expression
205///
206/// ```rust,ignore
207/// #[tool_handler(router = self.tool_router)]
208/// impl ServerHandler for MyToolHandler {
209/// // ...implement other handler
210/// }
211/// ```
212///
213/// ## Manual `get_info()`
214///
215/// If you provide your own `get_info()`, the macro will not generate one:
216///
217/// ```rust,ignore
218/// #[tool_handler]
219/// impl ServerHandler for MyToolHandler {
220/// fn get_info(&self) -> ServerInfo {
221/// ServerInfo::new(ServerCapabilities::builder().enable_tools().build())
222/// }
223/// }
224/// ```
225#[proc_macro_attribute]
226pub fn tool_handler(attr: TokenStream, input: TokenStream) -> TokenStream {
227 tool_handler::tool_handler(attr.into(), input.into())
228 .unwrap_or_else(|err| err.to_compile_error())
229 .into()
230}
231
232/// # prompt
233///
234/// This macro is used to mark a function as a prompt handler.
235///
236/// This will generate a function that returns the attribute of this prompt, with type `rmcp::model::Prompt`.
237///
238/// ## Usage
239///
240/// | field | type | usage |
241/// | :- | :- | :- |
242/// | `name` | `String` | The name of the prompt. If not provided, it defaults to the function name. |
243/// | `description` | `String` | A description of the prompt. The document of this function will be used if not provided. |
244/// | `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. |
245///
246/// ## Example
247///
248/// ```rust,ignore
249/// #[prompt(name = "code_review", description = "Reviews code for best practices")]
250/// pub async fn code_review_prompt(&self, Parameters(args): Parameters<CodeReviewArgs>) -> Result<Vec<PromptMessage>> {
251/// // Generate prompt messages based on arguments
252/// }
253/// ```
254#[proc_macro_attribute]
255pub fn prompt(attr: TokenStream, input: TokenStream) -> TokenStream {
256 prompt::prompt(attr.into(), input.into())
257 .unwrap_or_else(|err| err.to_compile_error())
258 .into()
259}
260
261/// # prompt_router
262///
263/// This macro generates a prompt router based on functions marked with `#[rmcp::prompt]` in an implementation block.
264///
265/// It creates a function that returns a `PromptRouter` instance.
266///
267/// ## Usage
268///
269/// | field | type | usage |
270/// | :- | :- | :- |
271/// | `router` | `Ident` | The name of the router function to be generated. Defaults to `prompt_router`. |
272/// | `vis` | `Visibility` | The visibility of the generated router function. Defaults to empty. |
273///
274/// ## Example
275///
276/// ```rust,ignore
277/// #[prompt_router]
278/// impl MyPromptHandler {
279/// #[prompt]
280/// pub async fn greeting_prompt(&self, Parameters(args): Parameters<GreetingArgs>) -> Result<Vec<PromptMessage>, Error> {
281/// // Generate greeting prompt using args
282/// }
283///
284/// pub fn new() -> Self {
285/// Self {
286/// // the default name of prompt router will be `prompt_router`
287/// prompt_router: Self::prompt_router(),
288/// }
289/// }
290/// }
291/// ```
292#[proc_macro_attribute]
293pub fn prompt_router(attr: TokenStream, input: TokenStream) -> TokenStream {
294 prompt_router::prompt_router(attr.into(), input.into())
295 .unwrap_or_else(|err| err.to_compile_error())
296 .into()
297}
298
299/// # prompt_handler
300///
301/// This macro generates handler methods for `get_prompt` and `list_prompts` in the
302/// implementation block, using a `PromptRouter`. It also auto-generates `get_info()`
303/// with prompts capability enabled if not already provided.
304///
305/// ## Usage
306///
307/// | field | type | usage |
308/// | :- | :- | :- |
309/// | `router` | `Expr` | The expression to access the `PromptRouter` instance. Defaults to `Self::prompt_router()`. |
310/// | `meta` | `Expr` | Optional metadata for `ListPromptsResult`. |
311///
312/// ## Example
313/// ```rust,ignore
314/// #[prompt_handler]
315/// impl ServerHandler for MyPromptHandler {
316/// // ...implement other handler methods
317/// }
318/// ```
319///
320/// or using a custom router expression:
321/// ```rust,ignore
322/// #[prompt_handler(router = self.prompt_router)]
323/// impl ServerHandler for MyPromptHandler {
324/// // ...implement other handler methods
325/// }
326/// ```
327#[proc_macro_attribute]
328pub fn prompt_handler(attr: TokenStream, input: TokenStream) -> TokenStream {
329 prompt_handler::prompt_handler(attr.into(), input.into())
330 .unwrap_or_else(|err| err.to_compile_error())
331 .into()
332}