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}