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}