@@ -8,26 +8,8 @@ use crate::{
88 RoleServer , model:: JsonObject , schemars:: generate:: SchemaSettings , service:: RequestContext ,
99} ;
1010
11- /// A shortcut for generating a JSON schema for a type.
12- pub fn schema_for_type < T : JsonSchema > ( ) -> JsonObject {
13- // explicitly to align json schema version to official specifications.
14- // refer to https://github.com/modelcontextprotocol/modelcontextprotocol/pull/655 for details.
15- let mut settings = SchemaSettings :: draft2020_12 ( ) ;
16- settings. transforms = vec ! [ Box :: new( schemars:: transform:: AddNullable :: default ( ) ) ] ;
17- let generator = settings. into_generator ( ) ;
18- let schema = generator. into_root_schema_for :: < T > ( ) ;
19- let object = serde_json:: to_value ( schema) . expect ( "failed to serialize schema" ) ;
20- match object {
21- serde_json:: Value :: Object ( object) => object,
22- _ => panic ! (
23- "Schema serialization produced non-object value: expected JSON object but got {:?}" ,
24- object
25- ) ,
26- }
27- }
28-
29- /// Call [`schema_for_type`] with a cache
30- pub fn cached_schema_for_type < T : JsonSchema + std:: any:: Any > ( ) -> Arc < JsonObject > {
11+ /// Generates a JSON schema for a type
12+ pub fn schema_for_type < T : JsonSchema + std:: any:: Any > ( ) -> Arc < JsonObject > {
3113 thread_local ! {
3214 static CACHE_FOR_TYPE : std:: sync:: RwLock <HashMap <TypeId , Arc <JsonObject >>> = Default :: default ( ) ;
3315 } ;
@@ -39,12 +21,26 @@ pub fn cached_schema_for_type<T: JsonSchema + std::any::Any>() -> Arc<JsonObject
3921 {
4022 x. clone ( )
4123 } else {
42- let schema = schema_for_type :: < T > ( ) ;
43- let schema = Arc :: new ( schema) ;
24+ // explicitly to align json schema version to official specifications.
25+ // refer to https://github.com/modelcontextprotocol/modelcontextprotocol/pull/655 for details.
26+ let mut settings = SchemaSettings :: draft2020_12 ( ) ;
27+ settings. transforms = vec ! [ Box :: new( schemars:: transform:: AddNullable :: default ( ) ) ] ;
28+ let generator = settings. into_generator ( ) ;
29+ let schema = generator. into_root_schema_for :: < T > ( ) ;
30+ let object = serde_json:: to_value ( schema) . expect ( "failed to serialize schema" ) ;
31+ let object = match object {
32+ serde_json:: Value :: Object ( object) => object,
33+ _ => panic ! (
34+ "Schema serialization produced non-object value: expected JSON object but got {:?}" ,
35+ object
36+ ) ,
37+ } ;
38+ let schema = Arc :: new ( object) ;
4439 cache
4540 . write ( )
4641 . expect ( "schema cache lock poisoned" )
4742 . insert ( TypeId :: of :: < T > ( ) , schema. clone ( ) ) ;
43+
4844 schema
4945 }
5046 } )
@@ -69,7 +65,7 @@ pub fn schema_for_output<T: JsonSchema + std::any::Any>() -> Result<Arc<JsonObje
6965 // Generate and validate schema
7066 let schema = schema_for_type :: < T > ( ) ;
7167 let result = match schema. get ( "type" ) {
72- Some ( serde_json:: Value :: String ( t) ) if t == "object" => Ok ( Arc :: new ( schema) ) ,
68+ Some ( serde_json:: Value :: String ( t) ) if t == "object" => Ok ( schema. clone ( ) ) ,
7369 Some ( serde_json:: Value :: String ( t) ) => Err ( format ! (
7470 "MCP specification requires tool outputSchema to have root type 'object', but found '{}'." ,
7571 t
@@ -196,6 +192,71 @@ mod tests {
196192 value : i32 ,
197193 }
198194
195+ #[ derive( serde:: Serialize , serde:: Deserialize , JsonSchema ) ]
196+ struct AnotherTestObject {
197+ value : i32 ,
198+ }
199+
200+ #[ test]
201+ fn test_schema_for_type_handles_primitive ( ) {
202+ let schema = schema_for_type :: < i32 > ( ) ;
203+
204+ assert_eq ! ( schema. get( "type" ) , Some ( & serde_json:: json!( "integer" ) ) ) ;
205+ }
206+
207+ #[ test]
208+ fn test_schema_for_type_handles_array ( ) {
209+ let schema = schema_for_type :: < Vec < i32 > > ( ) ;
210+
211+ assert_eq ! ( schema. get( "type" ) , Some ( & serde_json:: json!( "array" ) ) ) ;
212+ let items = schema. get ( "items" ) . and_then ( |v| v. as_object ( ) ) ;
213+ assert_eq ! (
214+ items. unwrap( ) . get( "type" ) ,
215+ Some ( & serde_json:: json!( "integer" ) )
216+ ) ;
217+ }
218+
219+ #[ test]
220+ fn test_schema_for_type_handles_struct ( ) {
221+ let schema = schema_for_type :: < TestObject > ( ) ;
222+
223+ assert_eq ! ( schema. get( "type" ) , Some ( & serde_json:: json!( "object" ) ) ) ;
224+ let properties = schema. get ( "properties" ) . and_then ( |v| v. as_object ( ) ) ;
225+ assert ! ( properties. unwrap( ) . contains_key( "value" ) ) ;
226+ }
227+
228+ #[ test]
229+ fn test_schema_for_type_caches_primitive_types ( ) {
230+ let schema1 = schema_for_type :: < i32 > ( ) ;
231+ let schema2 = schema_for_type :: < i32 > ( ) ;
232+
233+ assert ! ( Arc :: ptr_eq( & schema1, & schema2) ) ;
234+ }
235+
236+ #[ test]
237+ fn test_schema_for_type_caches_struct_types ( ) {
238+ let schema1 = schema_for_type :: < TestObject > ( ) ;
239+ let schema2 = schema_for_type :: < TestObject > ( ) ;
240+
241+ assert ! ( Arc :: ptr_eq( & schema1, & schema2) ) ;
242+ }
243+
244+ #[ test]
245+ fn test_schema_for_type_different_types_different_schemas ( ) {
246+ let schema1 = schema_for_type :: < TestObject > ( ) ;
247+ let schema2 = schema_for_type :: < AnotherTestObject > ( ) ;
248+
249+ assert ! ( !Arc :: ptr_eq( & schema1, & schema2) ) ;
250+ }
251+
252+ #[ test]
253+ fn test_schema_for_type_arc_can_be_shared ( ) {
254+ let schema = schema_for_type :: < TestObject > ( ) ;
255+ let cloned = schema. clone ( ) ;
256+
257+ assert ! ( Arc :: ptr_eq( & schema, & cloned) ) ;
258+ }
259+
199260 #[ test]
200261 fn test_schema_for_output_rejects_primitive ( ) {
201262 let result = schema_for_output :: < i32 > ( ) ;
0 commit comments