Skip to content

Commit 9b2b29e

Browse files
authored
feat: add support for imported serde_json::Number in JsonSchema derive (#130)
1 parent 6eab1ac commit 9b2b29e

2 files changed

Lines changed: 73 additions & 0 deletions

File tree

crates/rust-mcp-macros/src/utils.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,31 @@ pub fn type_to_json_schema(ty: &Type, attrs: &[Attribute]) -> proc_macro2::Token
322322
}
323323
}
324324
}
325+
// Handle imported serde_json::Number (single-segment case, common when `use serde_json::Number;`)
326+
else if ident == "Number" {
327+
let min_num_quote = minimum.as_ref().map(|min| {
328+
quote! {
329+
map.insert("minimum".to_string(), serde_json::Value::Number(serde_json::Number::from(#min)));
330+
}
331+
});
332+
let max_num_quote = maximum.as_ref().map(|max| {
333+
quote! {
334+
map.insert("maximum".to_string(), serde_json::Value::Number(serde_json::Number::from(#max)));
335+
}
336+
});
337+
return quote! {
338+
{
339+
let mut map = serde_json::Map::new();
340+
map.insert("type".to_string(), serde_json::Value::String("number".to_string()));
341+
#description_quote
342+
#title_quote
343+
#min_num_quote
344+
#max_num_quote
345+
#default_quote
346+
map
347+
}
348+
};
349+
}
325350
// Handle nested structs
326351
else if might_be_struct(ty) {
327352
let path = &type_path.path;
@@ -429,6 +454,38 @@ pub fn type_to_json_schema(ty: &Type, attrs: &[Attribute]) -> proc_macro2::Token
429454
}
430455
};
431456
}
457+
} else if type_path.path.segments.len() == 2 && type_path.path.leading_colon.is_none() {
458+
let segments: Vec<_> = type_path.path.segments.iter().collect();
459+
let seg0 = &segments[0];
460+
let seg1 = &segments[1];
461+
if seg0.ident == "serde_json"
462+
&& seg0.arguments.is_empty()
463+
&& seg1.ident == "Number"
464+
&& seg1.arguments.is_empty()
465+
{
466+
let min_num_quote = minimum.as_ref().map(|min| {
467+
quote! {
468+
map.insert("minimum".to_string(), serde_json::Value::Number(serde_json::Number::from(#min)));
469+
}
470+
});
471+
let max_num_quote = maximum.as_ref().map(|max| {
472+
quote! {
473+
map.insert("maximum".to_string(), serde_json::Value::Number(serde_json::Number::from(#max)));
474+
}
475+
});
476+
return quote! {
477+
{
478+
let mut map = serde_json::Map::new();
479+
map.insert("type".to_string(), serde_json::Value::String("number".to_string()));
480+
#description_quote
481+
#title_quote
482+
#min_num_quote
483+
#max_num_quote
484+
#default_quote
485+
map
486+
}
487+
};
488+
}
432489
}
433490
// Fallback for unknown types
434491
quote! {
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
use rust_mcp_macros::JsonSchema;
2+
use serde_json::Number;
3+
4+
#[test]
5+
fn test_schema_number() {
6+
#[allow(unused)]
7+
#[derive(JsonSchema)]
8+
struct TestStruct {
9+
pub b: Number,
10+
}
11+
12+
assert_eq!(
13+
serde_json::to_string(&TestStruct::json_schema()).unwrap(),
14+
r#"{"properties":{"b":{"type":"number"}},"required":["b"],"type":"object"}"#
15+
)
16+
}

0 commit comments

Comments
 (0)