@@ -44,9 +44,29 @@ pub struct ExamplePromptArgs {
4444 pub message : String ,
4545}
4646
47+ /// MCP spec types prompt arguments as `Record<string, string>`, so
48+ /// spec-compliant clients stringify all values. Accept both wire forms.
49+ fn deserialize_i32_from_string_or_int < ' de , D > ( deserializer : D ) -> Result < i32 , D :: Error >
50+ where
51+ D : serde:: Deserializer < ' de > ,
52+ {
53+ use serde:: Deserialize ;
54+ #[ derive( Deserialize ) ]
55+ #[ serde( untagged) ]
56+ enum StringOrInt {
57+ Int ( i32 ) ,
58+ Str ( String ) ,
59+ }
60+ match StringOrInt :: deserialize ( deserializer) ? {
61+ StringOrInt :: Int ( n) => Ok ( n) ,
62+ StringOrInt :: Str ( s) => s. parse :: < i32 > ( ) . map_err ( serde:: de:: Error :: custom) ,
63+ }
64+ }
65+
4766#[ derive( Debug , serde:: Serialize , serde:: Deserialize , schemars:: JsonSchema ) ]
4867pub struct CounterAnalysisArgs {
4968 /// The target value you're trying to reach
69+ #[ serde( deserialize_with = "deserialize_i32_from_string_or_int" ) ]
5070 pub goal : i32 ,
5171 /// Preferred strategy: 'fast' or 'careful'
5272 #[ serde( skip_serializing_if = "Option::is_none" ) ]
@@ -334,6 +354,34 @@ mod tests {
334354 assert_eq ! ( args[ 1 ] . required, Some ( false ) ) ;
335355 }
336356
357+ #[ test]
358+ fn test_counter_analysis_args_accepts_integer_goal ( ) {
359+ let json = serde_json:: json!( { "goal" : 20 } ) ;
360+ let args: CounterAnalysisArgs = serde_json:: from_value ( json) . unwrap ( ) ;
361+ assert_eq ! ( args. goal, 20 ) ;
362+ }
363+
364+ #[ test]
365+ fn test_counter_analysis_args_accepts_string_goal ( ) {
366+ let json = serde_json:: json!( { "goal" : "20" } ) ;
367+ let args: CounterAnalysisArgs = serde_json:: from_value ( json) . unwrap ( ) ;
368+ assert_eq ! ( args. goal, 20 ) ;
369+ }
370+
371+ #[ test]
372+ fn test_counter_analysis_args_accepts_negative_string_goal ( ) {
373+ let json = serde_json:: json!( { "goal" : "-7" } ) ;
374+ let args: CounterAnalysisArgs = serde_json:: from_value ( json) . unwrap ( ) ;
375+ assert_eq ! ( args. goal, -7 ) ;
376+ }
377+
378+ #[ test]
379+ fn test_counter_analysis_args_rejects_non_numeric_string_goal ( ) {
380+ let json = serde_json:: json!( { "goal" : "not a number" } ) ;
381+ let result: Result < CounterAnalysisArgs , _ > = serde_json:: from_value ( json) ;
382+ assert ! ( result. is_err( ) ) ;
383+ }
384+
337385 #[ tokio:: test]
338386 async fn test_prompt_router_has_routes ( ) {
339387 let router = Counter :: prompt_router ( ) ;
0 commit comments