@@ -19,34 +19,25 @@ use utils::{is_option, renamed_field, type_to_json_schema};
1919/// * `name` - An optional string representing the tool's name.
2020/// * `description` - An optional string describing the tool.
2121///
22- #[ cfg( feature = "2024_11_05" ) ]
23- struct McpToolMacroAttributes {
24- name : Option < String > ,
25- description : Option < String > ,
26- }
27-
28- /// Represents the attributes for the `mcp_tool` procedural macro.
29- ///
30- /// This struct parses and validates the `name` and `description` attributes provided
31- /// to the `mcp_tool` macro. Both attributes are required and must not be empty strings.
32- ///
33- /// # Fields
34- /// * `name` - An optional string representing the tool's name.
35- /// * `description` - An optional string describing the tool.
22+ /// The following fields are available only with the `2025_03_26` feature:
3623/// * `destructive_hint` - Optional boolean for `ToolAnnotations::destructive_hint`.
3724/// * `idempotent_hint` - Optional boolean for `ToolAnnotations::idempotent_hint`.
3825/// * `open_world_hint` - Optional boolean for `ToolAnnotations::open_world_hint`.
3926/// * `read_only_hint` - Optional boolean for `ToolAnnotations::read_only_hint`.
4027/// * `title` - Optional string for `ToolAnnotations::title`.
4128///
42- #[ cfg( feature = "2025_03_26" ) ]
4329struct McpToolMacroAttributes {
4430 name : Option < String > ,
4531 description : Option < String > ,
32+ #[ cfg( feature = "2025_03_26" ) ]
4633 destructive_hint : Option < bool > ,
34+ #[ cfg( feature = "2025_03_26" ) ]
4735 idempotent_hint : Option < bool > ,
36+ #[ cfg( feature = "2025_03_26" ) ]
4837 open_world_hint : Option < bool > ,
38+ #[ cfg( feature = "2025_03_26" ) ]
4939 read_only_hint : Option < bool > ,
40+ #[ cfg( feature = "2025_03_26" ) ]
5041 title : Option < String > ,
5142}
5243
@@ -75,13 +66,20 @@ impl Parse for McpToolMacroAttributes {
7566 /// - The `name` attribute is missing or empty.
7667 /// - The `description` attribute is missing or empty.
7768 fn parse ( attributes : syn:: parse:: ParseStream ) -> syn:: Result < Self > {
78- let mut name = None ;
79- let mut description = None ;
80- let mut destructive_hint = None ;
81- let mut idempotent_hint = None ;
82- let mut open_world_hint = None ;
83- let mut read_only_hint = None ;
84- let mut title = None ;
69+ let mut instance = Self {
70+ name : None ,
71+ description : None ,
72+ #[ cfg( feature = "2025_03_26" ) ]
73+ destructive_hint : None ,
74+ #[ cfg( feature = "2025_03_26" ) ]
75+ idempotent_hint : None ,
76+ #[ cfg( feature = "2025_03_26" ) ]
77+ open_world_hint : None ,
78+ #[ cfg( feature = "2025_03_26" ) ]
79+ read_only_hint : None ,
80+ #[ cfg( feature = "2025_03_26" ) ]
81+ title : None ,
82+ } ;
8583
8684 let meta_list: Punctuated < Meta , Token ! [ , ] > = Punctuated :: parse_terminated ( attributes) ?;
8785 for meta in meta_list {
@@ -131,33 +129,38 @@ impl Parse for McpToolMacroAttributes {
131129 }
132130 } ;
133131 match ident_str. as_str ( ) {
134- "name" => name = Some ( value) ,
135- "description" => description = Some ( value) ,
132+ "name" => instance . name = Some ( value) ,
133+ "description" => instance . description = Some ( value) ,
136134 _ => { }
137135 }
138136 }
139137 "destructive_hint" | "idempotent_hint" | "open_world_hint"
140138 | "read_only_hint" => {
141- let value = match & meta_name_value. value {
142- Expr :: Lit ( ExprLit {
143- lit : Lit :: Bool ( lit_bool) ,
144- ..
145- } ) => lit_bool. value ,
146- _ => {
147- return Err ( Error :: new_spanned (
148- & meta_name_value. value ,
149- "Expected a boolean literal" ,
150- ) ) ;
139+ #[ cfg( feature = "2025_03_26" ) ]
140+ {
141+ let value = match & meta_name_value. value {
142+ Expr :: Lit ( ExprLit {
143+ lit : Lit :: Bool ( lit_bool) ,
144+ ..
145+ } ) => lit_bool. value ,
146+ _ => {
147+ return Err ( Error :: new_spanned (
148+ & meta_name_value. value ,
149+ "Expected a boolean literal" ,
150+ ) ) ;
151+ }
152+ } ;
153+
154+ match ident_str. as_str ( ) {
155+ "destructive_hint" => instance. destructive_hint = Some ( value) ,
156+ "idempotent_hint" => instance. idempotent_hint = Some ( value) ,
157+ "open_world_hint" => instance. open_world_hint = Some ( value) ,
158+ "read_only_hint" => instance. read_only_hint = Some ( value) ,
159+ _ => { }
151160 }
152- } ;
153- match ident_str. as_str ( ) {
154- "destructive_hint" => destructive_hint = Some ( value) ,
155- "idempotent_hint" => idempotent_hint = Some ( value) ,
156- "open_world_hint" => open_world_hint = Some ( value) ,
157- "read_only_hint" => read_only_hint = Some ( value) ,
158- _ => { }
159161 }
160162 }
163+ #[ cfg( feature = "2025_03_26" ) ]
161164 "title" => {
162165 let value = match & meta_name_value. value {
163166 Expr :: Lit ( ExprLit {
@@ -171,21 +174,27 @@ impl Parse for McpToolMacroAttributes {
171174 ) ) ;
172175 }
173176 } ;
174- title = Some ( value) ;
177+ instance . title = Some ( value) ;
175178 }
176179 _ => { }
177180 }
178181 }
179182 }
180183
181184 // Validate presence and non-emptiness
182- if name. as_ref ( ) . map ( |s| s. trim ( ) . is_empty ( ) ) . unwrap_or ( true ) {
185+ if instance
186+ . name
187+ . as_ref ( )
188+ . map ( |s| s. trim ( ) . is_empty ( ) )
189+ . unwrap_or ( true )
190+ {
183191 return Err ( Error :: new (
184192 attributes. span ( ) ,
185193 "The 'name' attribute is required and must not be empty." ,
186194 ) ) ;
187195 }
188- if description
196+ if instance
197+ . description
189198 . as_ref ( )
190199 . map ( |s| s. trim ( ) . is_empty ( ) )
191200 . unwrap_or ( true )
@@ -196,20 +205,6 @@ impl Parse for McpToolMacroAttributes {
196205 ) ) ;
197206 }
198207
199- #[ cfg( feature = "2024_11_05" ) ]
200- let instance = Self { name, description } ;
201-
202- #[ cfg( feature = "2025_03_26" ) ]
203- let instance = Self {
204- name,
205- description,
206- destructive_hint,
207- idempotent_hint,
208- open_world_hint,
209- read_only_hint,
210- title,
211- } ;
212-
213208 Ok ( instance)
214209 }
215210}
@@ -304,21 +299,23 @@ pub fn mcp_tool(attributes: TokenStream, input: TokenStream) -> TokenStream {
304299 quote ! { None }
305300 } ;
306301
307- #[ cfg( feature = "2025_03_26" ) ]
308- let tool_token = quote ! {
309- #base_crate:: Tool {
310- name: #tool_name. to_string( ) ,
311- description: Some ( #tool_description. to_string( ) ) ,
312- input_schema: #base_crate:: ToolInputSchema :: new( required, properties) ,
313- annotations: #annotations
302+ let annotations_token = {
303+ #[ cfg( feature = "2025_03_26" ) ]
304+ {
305+ quote ! { annotations: #annotations }
306+ }
307+ #[ cfg( not( feature = "2025_03_26" ) ) ]
308+ {
309+ quote ! { }
314310 }
315311 } ;
316- # [ cfg ( feature = "2024_11_05" ) ]
312+
317313 let tool_token = quote ! {
318314 #base_crate:: Tool {
319315 name: #tool_name. to_string( ) ,
320316 description: Some ( #tool_description. to_string( ) ) ,
321317 input_schema: #base_crate:: ToolInputSchema :: new( required, properties) ,
318+ #annotations_token
322319 }
323320 } ;
324321
0 commit comments