@@ -161,13 +161,20 @@ pub fn component_exports_to_json_schema(
161161pub fn vals_to_json ( vals : & [ Val ] ) -> Value {
162162 match vals. len ( ) {
163163 0 => Value :: Null ,
164- 1 => val_to_json ( & vals[ 0 ] ) ,
164+ 1 => {
165+ let mut wrapper = Map :: new ( ) ;
166+ wrapper. insert ( "result" . to_string ( ) , val_to_json ( & vals[ 0 ] ) ) ;
167+ Value :: Object ( wrapper)
168+ }
165169 _ => {
166- let mut map = Map :: new ( ) ;
170+ let mut tuple_map = Map :: new ( ) ;
167171 for ( i, v) in vals. iter ( ) . enumerate ( ) {
168- map . insert ( format ! ( "val{i}" ) , val_to_json ( v) ) ;
172+ tuple_map . insert ( format ! ( "val{i}" ) , val_to_json ( v) ) ;
169173 }
170- Value :: Object ( map)
174+
175+ let mut wrapper = Map :: new ( ) ;
176+ wrapper. insert ( "result" . to_string ( ) , Value :: Object ( tuple_map) ) ;
177+ Value :: Object ( wrapper)
171178 }
172179 }
173180}
@@ -394,25 +401,55 @@ fn component_func_to_schema(name: &str, func: &ComponentFunc, output: bool) -> s
394401 tool_obj. insert ( "inputSchema" . to_string ( ) , input_schema) ;
395402
396403 if output {
397- let mut results_iter = func. results ( ) ;
398- let output_schema = match results_iter. len ( ) {
399- 0 => None ,
400- 1 => Some ( type_to_json_schema ( & results_iter. next ( ) . unwrap ( ) ) ) ,
401- _ => {
402- let schemas: Vec < _ > = results_iter. map ( |ty| type_to_json_schema ( & ty) ) . collect ( ) ;
403- Some ( json ! ( {
404- "type" : "array" ,
405- "items" : schemas
406- } ) )
407- }
408- } ;
409- if let Some ( o) = output_schema {
404+ let results: Vec < _ > = func. results ( ) . collect ( ) ;
405+ if let Some ( o) = canonical_output_schema_for_results ( & results) {
410406 tool_obj. insert ( "outputSchema" . to_string ( ) , o) ;
411407 }
412408 }
413409 json ! ( tool_obj)
414410}
415411
412+ fn canonical_output_schema_for_results ( results : & [ Type ] ) -> Option < Value > {
413+ if results. is_empty ( ) {
414+ return None ;
415+ }
416+
417+ let result_schema = if results. len ( ) == 1 {
418+ type_to_json_schema ( & results[ 0 ] )
419+ } else {
420+ let mut props = Map :: new ( ) ;
421+ let mut required = Vec :: new ( ) ;
422+
423+ for ( idx, ty) in results. iter ( ) . enumerate ( ) {
424+ let key = format ! ( "val{idx}" ) ;
425+ props. insert ( key. clone ( ) , type_to_json_schema ( ty) ) ;
426+ required. push ( Value :: String ( key) ) ;
427+ }
428+
429+ let mut tuple_schema = Map :: new ( ) ;
430+ tuple_schema. insert ( "type" . to_string ( ) , Value :: String ( "object" . to_string ( ) ) ) ;
431+ tuple_schema. insert ( "properties" . to_string ( ) , Value :: Object ( props) ) ;
432+ tuple_schema. insert ( "required" . to_string ( ) , Value :: Array ( required) ) ;
433+ Value :: Object ( tuple_schema)
434+ } ;
435+
436+ Some ( build_result_wrapper ( result_schema) )
437+ }
438+
439+ fn build_result_wrapper ( result_schema : Value ) -> Value {
440+ let mut props = Map :: new ( ) ;
441+ props. insert ( "result" . to_string ( ) , result_schema) ;
442+
443+ let mut wrapper = Map :: new ( ) ;
444+ wrapper. insert ( "type" . to_string ( ) , Value :: String ( "object" . to_string ( ) ) ) ;
445+ wrapper. insert ( "properties" . to_string ( ) , Value :: Object ( props) ) ;
446+ wrapper. insert (
447+ "required" . to_string ( ) ,
448+ Value :: Array ( vec ! [ Value :: String ( "result" . to_string( ) ) ] ) ,
449+ ) ;
450+ Value :: Object ( wrapper)
451+ }
452+
416453fn gather_exported_functions_with_metadata (
417454 export_name : & str ,
418455 previous_name : Option < String > ,
@@ -842,6 +879,13 @@ mod tests {
842879
843880 use super :: * ;
844881
882+ fn result_schema < ' a > ( schema : & ' a Value ) -> & ' a Value {
883+ schema
884+ . get ( "properties" )
885+ . and_then ( |props| props. get ( "result" ) )
886+ . unwrap_or ( schema)
887+ }
888+
845889 #[ test]
846890 fn test_vals_to_json_empty ( ) {
847891 let json_val = vals_to_json ( & [ ] ) ;
@@ -852,17 +896,54 @@ mod tests {
852896 fn test_vals_to_json_single ( ) {
853897 let val = Val :: Bool ( true ) ;
854898 let json_val = vals_to_json ( std:: slice:: from_ref ( & val) ) ;
855- assert_eq ! ( json_val, val_to_json( & val) ) ;
899+ assert_eq ! ( json_val, json!( { "result" : true } ) ) ;
900+ }
901+
902+ #[ test]
903+ fn test_canonical_output_schema_single_scalar ( ) {
904+ let schema = canonical_output_schema_for_results ( & [ Type :: String ] ) . unwrap ( ) ;
905+ assert_eq ! (
906+ schema,
907+ json!( {
908+ "type" : "object" ,
909+ "properties" : {
910+ "result" : { "type" : "string" }
911+ } ,
912+ "required" : [ "result" ]
913+ } )
914+ ) ;
915+ }
916+
917+ #[ test]
918+ fn test_canonical_output_schema_multi_result ( ) {
919+ let schema = canonical_output_schema_for_results ( & [ Type :: String , Type :: S64 ] ) . unwrap ( ) ;
920+ assert_eq ! (
921+ schema,
922+ json!( {
923+ "type" : "object" ,
924+ "properties" : {
925+ "result" : {
926+ "type" : "object" ,
927+ "properties" : {
928+ "val0" : { "type" : "string" } ,
929+ "val1" : { "type" : "number" }
930+ } ,
931+ "required" : [ "val0" , "val1" ]
932+ }
933+ } ,
934+ "required" : [ "result" ]
935+ } )
936+ ) ;
856937 }
857938
858939 #[ test]
859940 fn test_vals_to_json_multiple_values ( ) {
860941 let wit_vals = vec ! [ Val :: String ( "example" . to_string( ) ) , Val :: S64 ( 42 ) ] ;
861942 let json_result = vals_to_json ( & wit_vals) ;
862-
863- let obj = json_result. as_object ( ) . unwrap ( ) ;
864- assert_eq ! ( obj . get ( " val0") . unwrap ( ) , & json! ( "example" ) ) ;
865- assert_eq ! ( obj . get ( "val1" ) . unwrap ( ) , & json! ( 42 ) ) ;
943+ assert_eq ! (
944+ json_result,
945+ json! ( { "result" : { " val0": "example" , "val1" : 42 } } )
946+ ) ;
866947 }
867948
868949 #[ test]
@@ -1053,9 +1134,10 @@ mod tests {
10531134 }
10541135
10551136 let output_schema = tool. get ( "outputSchema" ) . unwrap ( ) ;
1137+ let result_schema_ref = result_schema ( output_schema) ;
10561138 if expected_exports[ i] == "list-directory" {
10571139 assert ! (
1058- output_schema . get( "oneOf" ) . unwrap( ) . as_array( ) . unwrap( ) [ 0 ]
1140+ result_schema_ref . get( "oneOf" ) . unwrap( ) . as_array( ) . unwrap( ) [ 0 ]
10591141 . get( "properties" )
10601142 . unwrap( )
10611143 . get( "ok" )
@@ -1066,7 +1148,7 @@ mod tests {
10661148 ) ;
10671149 } else {
10681150 assert ! (
1069- output_schema . get( "oneOf" ) . unwrap( ) . as_array( ) . unwrap( ) [ 0 ]
1151+ result_schema_ref . get( "oneOf" ) . unwrap( ) . as_array( ) . unwrap( ) [ 0 ]
10701152 . get( "properties" )
10711153 . unwrap( )
10721154 . get( "ok" )
@@ -1113,7 +1195,7 @@ mod tests {
11131195 assert ! ( properties. contains_key( "wit" ) ) ;
11141196
11151197 let output_schema = generate_tool. get ( "outputSchema" ) . unwrap ( ) ;
1116- assert ! ( output_schema. get( "oneOf" ) . is_some( ) ) ;
1198+ assert ! ( result_schema ( output_schema) . get( "oneOf" ) . is_some( ) ) ;
11171199 }
11181200
11191201 #[ test]
@@ -1293,14 +1375,15 @@ mod tests {
12931375 assert ! ( properties. contains_key( "c" ) ) ;
12941376 assert ! ( properties. contains_key( "d" ) ) ;
12951377 let output_schema = root_b. get ( "outputSchema" ) . unwrap ( ) ;
1296- assert_eq ! ( output_schema. get( "type" ) . unwrap( ) , "string" ) ;
1378+ assert_eq ! ( result_schema ( output_schema) . get( "type" ) . unwrap( ) , "string" ) ;
12971379
12981380 let root_c = find_tool ( tools, "c" ) . unwrap ( ) ;
12991381 let output_schema = root_c. get ( "outputSchema" ) . unwrap ( ) ;
1300- assert_eq ! ( output_schema. get( "type" ) . unwrap( ) , "array" ) ;
1301- assert_eq ! ( output_schema. get( "minItems" ) . unwrap( ) , 4 ) ;
1302- assert_eq ! ( output_schema. get( "maxItems" ) . unwrap( ) , 4 ) ;
1303- let prefix_items = output_schema
1382+ let result_schema_ref = result_schema ( output_schema) ;
1383+ assert_eq ! ( result_schema_ref. get( "type" ) . unwrap( ) , "array" ) ;
1384+ assert_eq ! ( result_schema_ref. get( "minItems" ) . unwrap( ) , 4 ) ;
1385+ assert_eq ! ( result_schema_ref. get( "maxItems" ) . unwrap( ) , 4 ) ;
1386+ let prefix_items = result_schema_ref
13041387 . get ( "prefixItems" )
13051388 . unwrap ( )
13061389 . as_array ( )
@@ -1333,7 +1416,11 @@ mod tests {
13331416 assert ! ( input_props. contains_key( "x" ) ) ; // string
13341417
13351418 let output_schema = foo_b. get ( "outputSchema" ) . unwrap ( ) ;
1336- let cases = output_schema. get ( "oneOf" ) . unwrap ( ) . as_array ( ) . unwrap ( ) ;
1419+ let cases = result_schema ( output_schema)
1420+ . get ( "oneOf" )
1421+ . unwrap ( )
1422+ . as_array ( )
1423+ . unwrap ( ) ;
13371424 assert_eq ! ( cases. len( ) , 3 ) ;
13381425
13391426 let case_a = & cases[ 0 ] ;
@@ -1406,7 +1493,7 @@ mod tests {
14061493 assert ! ( input_props. contains_key( "x" ) ) ; // variant type
14071494
14081495 let output_schema = foo_c. get ( "outputSchema" ) . unwrap ( ) ;
1409- assert_eq ! ( output_schema. get( "type" ) . unwrap( ) , "string" ) ;
1496+ assert_eq ! ( result_schema ( output_schema) . get( "type" ) . unwrap( ) , "string" ) ;
14101497 }
14111498 }
14121499
@@ -1448,10 +1535,10 @@ mod tests {
14481535 fn test_vals_to_json_multiple ( ) {
14491536 let wit_vals = vec ! [ Val :: String ( "example" . to_string( ) ) , Val :: S64 ( 42 ) ] ;
14501537 let json_result = vals_to_json ( & wit_vals) ;
1451-
1452- let obj = json_result. as_object ( ) . unwrap ( ) ;
1453- assert_eq ! ( obj . get ( " val0") . unwrap ( ) , & json! ( "example" ) ) ;
1454- assert_eq ! ( obj . get ( "val1" ) . unwrap ( ) , & json! ( 42 ) ) ;
1538+ assert_eq ! (
1539+ json_result,
1540+ json! ( { "result" : { " val0": "example" , "val1" : 42 } } )
1541+ ) ;
14551542 }
14561543
14571544 #[ test]
0 commit comments