Skip to content

Commit 6d5c01d

Browse files
committed
fix(examples): accept stringified prompt argument values
1 parent c330fed commit 6d5c01d

1 file changed

Lines changed: 48 additions & 0 deletions

File tree

examples/servers/src/common/counter.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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)]
4867
pub 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

Comments
 (0)