@@ -2,11 +2,45 @@ use std::collections::HashSet;
22
33use proc_macro2:: TokenStream ;
44use quote:: { ToTokens , quote} ;
5+ use serde_json:: json;
56use syn:: {
6- Expr , FnArg , Ident , ItemFn , ItemImpl , MetaList , PatType , Token , Type , Visibility , parse :: Parse ,
7- parse_quote, spanned:: Spanned ,
7+ Expr , FnArg , Ident , ItemFn , ItemImpl , Lit , MetaList , PatType , Token , Type , Visibility ,
8+ parse :: Parse , parse_quote, spanned:: Spanned ,
89} ;
910
11+ /// Stores tool annotation attributes
12+ #[ derive( Default , Clone ) ]
13+ struct ToolAnnotationAttrs ( pub serde_json:: Map < String , serde_json:: Value > ) ;
14+
15+ impl Parse for ToolAnnotationAttrs {
16+ fn parse ( input : syn:: parse:: ParseStream ) -> syn:: Result < Self > {
17+ let mut attrs = serde_json:: Map :: new ( ) ;
18+
19+ while !input. is_empty ( ) {
20+ let key: Ident = input. parse ( ) ?;
21+ input. parse :: < Token ! [ : ] > ( ) ?;
22+ let value: Lit = input. parse ( ) ?;
23+ let value = match value {
24+ Lit :: Str ( s) => json ! ( s. value( ) ) ,
25+ Lit :: Bool ( b) => json ! ( b. value) ,
26+ _ => {
27+ return Err ( syn:: Error :: new (
28+ key. span ( ) ,
29+ "annotations must be string or boolean literals" ,
30+ ) ) ;
31+ }
32+ } ;
33+ attrs. insert ( key. to_string ( ) , value) ;
34+ if input. is_empty ( ) {
35+ break ;
36+ }
37+ input. parse :: < Token ! [ , ] > ( ) ?;
38+ }
39+
40+ Ok ( ToolAnnotationAttrs ( attrs) )
41+ }
42+ }
43+
1044#[ derive( Default ) ]
1145struct ToolImplItemAttrs {
1246 tool_box : Option < Option < Ident > > ,
@@ -45,13 +79,16 @@ struct ToolFnItemAttrs {
4579 name : Option < Expr > ,
4680 description : Option < Expr > ,
4781 vis : Option < Visibility > ,
82+ annotations : Option < ToolAnnotationAttrs > ,
4883}
4984
5085impl Parse for ToolFnItemAttrs {
5186 fn parse ( input : syn:: parse:: ParseStream ) -> syn:: Result < Self > {
5287 let mut name = None ;
5388 let mut description = None ;
5489 let mut vis = None ;
90+ let mut annotations = None ;
91+
5592 while !input. is_empty ( ) {
5693 let key: Ident = input. parse ( ) ?;
5794 input. parse :: < Token ! [ =] > ( ) ?;
@@ -68,6 +105,13 @@ impl Parse for ToolFnItemAttrs {
68105 let value: Visibility = input. parse ( ) ?;
69106 vis = Some ( value) ;
70107 }
108+ "annotations" => {
109+ // Parse the annotations as a nested structure
110+ let content;
111+ syn:: braced!( content in input) ;
112+ let value = content. parse ( ) ?;
113+ annotations = Some ( value) ;
114+ }
71115 _ => {
72116 return Err ( syn:: Error :: new ( key. span ( ) , "unknown attribute" ) ) ;
73117 }
@@ -82,6 +126,7 @@ impl Parse for ToolFnItemAttrs {
82126 name,
83127 description,
84128 vis,
129+ annotations,
85130 } )
86131 }
87132}
@@ -470,14 +515,25 @@ pub(crate) fn tool_fn_item(attr: TokenStream, mut input_fn: ItemFn) -> syn::Resu
470515 } ;
471516 let input_fn_attrs = & input_fn. attrs ;
472517 let input_fn_vis = & input_fn. vis ;
518+
519+ let annotations_code = if let Some ( annotations) = & tool_macro_attrs. fn_item . annotations {
520+ let annotations =
521+ serde_json:: to_string ( & annotations. 0 ) . expect ( "failed to serialize annotations" ) ;
522+ quote ! {
523+ Some ( serde_json:: from_str:: <rmcp:: model:: ToolAnnotations >( & #annotations) . expect( "Could not parse tool annotations" ) )
524+ }
525+ } else {
526+ quote ! { None }
527+ } ;
528+
473529 quote ! {
474530 #( #input_fn_attrs) *
475531 #input_fn_vis fn #tool_attr_fn_ident( ) -> rmcp:: model:: Tool {
476532 rmcp:: model:: Tool {
477533 name: #name. into( ) ,
478534 description: Some ( #description. into( ) ) ,
479535 input_schema: #schema. into( ) ,
480- annotations: None
536+ annotations: #annotations_code ,
481537 }
482538 }
483539 }
0 commit comments