rmcp_macros/
lib.rs

1#[allow(unused_imports)]
2use proc_macro::TokenStream;
3
4mod tool;
5mod tool_handler;
6mod tool_router;
7/// # tool
8///
9/// This macro is used to mark a function as a tool handler.
10///
11/// This will generate a function that return the attribute of this tool, with type `rmcp::model::Tool`.
12///
13/// ## Usage
14///
15/// | feied             | type                       | usage |
16/// | :-                | :-                         | :-    |
17/// | `name`            | `String`                   | The name of the tool. If not provided, it defaults to the function name. |
18/// | `description`     | `String`                   | A description of the tool. The document of this function will be used. |
19/// | `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>` |
20/// | `annotations`     | `ToolAnnotationsAttribute` | Additional tool information. Defaults to `None`. |
21///
22/// ## Example
23///
24/// ```rust,ignore
25/// #[tool(name = "my_tool", description = "This is my tool", annotations(title = "我的工具", read_only_hint = true))]
26/// pub async fn my_tool(param: Parameters<MyToolParam>) {
27///     // handling tool request
28/// }
29/// ```
30#[proc_macro_attribute]
31pub fn tool(attr: TokenStream, input: TokenStream) -> TokenStream {
32    tool::tool(attr.into(), input.into())
33        .unwrap_or_else(|err| err.to_compile_error())
34        .into()
35}
36
37/// # tool_router
38///
39/// This macro is used to generate a tool router based on functions marked with `#[rmcp::tool]` in an implementation block.
40///
41/// It creates a function that returns a `ToolRouter` instance.
42///
43/// 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.
44/// ## Usage
45///
46/// | feied     | type          | usage |
47/// | :-        | :-            | :-    |
48/// | `router`  | `Ident`       | The name of the router function to be generated. Defaults to `tool_router`. |
49/// | `vis`     | `Visibility`  | The visibility of the generated router function. Defaults to empty. |
50///
51/// ## Example
52///
53/// ```rust,ignore
54/// #[tool_router]
55/// impl MyToolHandler {
56///     #[tool]
57///     pub fn my_tool() {
58///         
59///     }
60///
61///     pub fn new() -> Self {
62///         Self {
63///             // the default name of tool router will be `tool_router`
64///             tool_router: Self::tool_router(),
65///         }
66///     }
67/// }
68/// ```
69///
70/// Or specify the visibility and router name, which would be helpful when you want to combine multiple routers into one:
71///
72/// ```rust,ignore
73/// mod a {
74///     #[tool_router(router = tool_router_a, vis = pub)]
75///     impl MyToolHandler {
76///         #[tool]
77///         fn my_tool_a() {
78///             
79///         }
80///     }
81/// }
82///
83/// mod b {
84///     #[tool_router(router = tool_router_b, vis = pub)]
85///     impl MyToolHandler {
86///         #[tool]
87///         fn my_tool_b() {
88///             
89///         }
90///     }
91/// }
92///
93/// impl MyToolHandler {
94///     fn new() -> Self {
95///         Self {
96///             tool_router: self::tool_router_a() + self::tool_router_b(),
97///         }
98///     }
99/// }
100/// ```
101#[proc_macro_attribute]
102pub fn tool_router(attr: TokenStream, input: TokenStream) -> TokenStream {
103    tool_router::tool_router(attr.into(), input.into())
104        .unwrap_or_else(|err| err.to_compile_error())
105        .into()
106}
107
108/// # tool_handler
109///
110/// This macro will generate the handler for `tool_call` and `list_tools` methods in the implementation block, by using an existing `ToolRouter` instance.
111///
112/// ## Usage
113///
114/// | field     | type          | usage |
115/// | :-        | :-            | :-    |
116/// | `router`  | `Expr`        | The expression to access the `ToolRouter` instance. Defaults to `self.tool_router`. |
117/// ## Example
118/// ```rust,ignore
119/// #[tool_handler]
120/// impl ServerHandler for MyToolHandler {
121///     // ...implement other handler
122/// }
123/// ```
124///
125/// or using a custom router expression:
126/// ```rust,ignore
127/// #[tool_handler(router = self.get_router().await)]
128/// impl ServerHandler for MyToolHandler {
129///    // ...implement other handler
130/// }
131/// ```
132///
133/// ## Explain
134///
135/// This macro will be expended to something like this:
136/// ```rust,ignore
137/// impl ServerHandler for MyToolHandler {
138///        async fn call_tool(
139///         &self,
140///         request: CallToolRequestParam,
141///         context: RequestContext<RoleServer>,
142///     ) -> Result<CallToolResult, rmcp::ErrorData> {
143///         let tcc = ToolCallContext::new(self, request, context);
144///         self.tool_router.call(tcc).await
145///     }
146///
147///     async fn list_tools(
148///         &self,
149///         _request: Option<PaginatedRequestParam>,
150///         _context: RequestContext<RoleServer>,
151///     ) -> Result<ListToolsResult, rmcp::ErrorData> {
152///         let items = self.tool_router.list_all();
153///         Ok(ListToolsResult::with_all_items(items))
154///     }
155/// }
156/// ```
157#[proc_macro_attribute]
158pub fn tool_handler(attr: TokenStream, input: TokenStream) -> TokenStream {
159    tool_handler::tool_handler(attr.into(), input.into())
160        .unwrap_or_else(|err| err.to_compile_error())
161        .into()
162}