1
+ //cargo test --test test_json_schema_detection --features "client server macros"
2
+ use rmcp:: {
3
+ Json , ServerHandler ,
4
+ handler:: server:: router:: tool:: ToolRouter ,
5
+ tool, tool_handler, tool_router,
6
+ } ;
7
+ use schemars:: JsonSchema ;
8
+ use serde:: { Deserialize , Serialize } ;
9
+
10
+ #[ derive( Serialize , Deserialize , JsonSchema ) ]
11
+ pub struct TestData {
12
+ pub value : String ,
13
+ }
14
+
15
+ #[ tool_handler( router = self . tool_router) ]
16
+ impl ServerHandler for TestServer { }
17
+
18
+ #[ derive( Debug , Clone ) ]
19
+ pub struct TestServer {
20
+ tool_router : ToolRouter < Self > ,
21
+ }
22
+
23
+ impl Default for TestServer {
24
+ fn default ( ) -> Self {
25
+ Self :: new ( )
26
+ }
27
+ }
28
+
29
+ #[ tool_router( router = tool_router) ]
30
+ impl TestServer {
31
+ pub fn new ( ) -> Self {
32
+ Self {
33
+ tool_router : Self :: tool_router ( ) ,
34
+ }
35
+ }
36
+
37
+ /// Tool that returns Json<T> - should have output schema
38
+ #[ tool( name = "with-json" ) ]
39
+ pub async fn with_json ( & self ) -> Result < Json < TestData > , String > {
40
+ Ok ( Json ( TestData { value : "test" . to_string ( ) } ) )
41
+ }
42
+
43
+ /// Tool that returns regular type - should NOT have output schema
44
+ #[ tool( name = "without-json" ) ]
45
+ pub async fn without_json ( & self ) -> Result < String , String > {
46
+ Ok ( "test" . to_string ( ) )
47
+ }
48
+
49
+ /// Tool that returns Result with inner Json - should have output schema
50
+ #[ tool( name = "result-with-json" ) ]
51
+ pub async fn result_with_json ( & self ) -> Result < Json < TestData > , rmcp:: ErrorData > {
52
+ Ok ( Json ( TestData { value : "test" . to_string ( ) } ) )
53
+ }
54
+
55
+ /// Tool with explicit output_schema attribute - should have output schema
56
+ #[ tool( name = "explicit-schema" , output_schema = rmcp:: handler:: server:: tool:: cached_schema_for_type:: <TestData >( ) ) ]
57
+ pub async fn explicit_schema ( & self ) -> Result < String , String > {
58
+ Ok ( "test" . to_string ( ) )
59
+ }
60
+ }
61
+
62
+ #[ tokio:: test]
63
+ async fn test_json_type_generates_schema ( ) {
64
+ let server = TestServer :: new ( ) ;
65
+ let tools = server. tool_router . list_all ( ) ;
66
+
67
+ // Find the with-json tool
68
+ let json_tool = tools. iter ( ) . find ( |t| t. name == "with-json" ) . unwrap ( ) ;
69
+ assert ! ( json_tool. output_schema. is_some( ) , "Json<T> return type should generate output schema" ) ;
70
+ }
71
+
72
+ #[ tokio:: test]
73
+ async fn test_non_json_type_no_schema ( ) {
74
+ let server = TestServer :: new ( ) ;
75
+ let tools = server. tool_router . list_all ( ) ;
76
+
77
+ // Find the without-json tool
78
+ let non_json_tool = tools. iter ( ) . find ( |t| t. name == "without-json" ) . unwrap ( ) ;
79
+ assert ! ( non_json_tool. output_schema. is_none( ) , "Regular return type should NOT generate output schema" ) ;
80
+ }
81
+
82
+ #[ tokio:: test]
83
+ async fn test_result_with_json_generates_schema ( ) {
84
+ let server = TestServer :: new ( ) ;
85
+ let tools = server. tool_router . list_all ( ) ;
86
+
87
+ // Find the result-with-json tool
88
+ let result_json_tool = tools. iter ( ) . find ( |t| t. name == "result-with-json" ) . unwrap ( ) ;
89
+ assert ! ( result_json_tool. output_schema. is_some( ) , "Result<Json<T>, E> return type should generate output schema" ) ;
90
+ }
91
+
92
+ #[ tokio:: test]
93
+ async fn test_explicit_schema_override ( ) {
94
+ let server = TestServer :: new ( ) ;
95
+ let tools = server. tool_router . list_all ( ) ;
96
+
97
+ // Find the explicit-schema tool
98
+ let explicit_tool = tools. iter ( ) . find ( |t| t. name == "explicit-schema" ) . unwrap ( ) ;
99
+ assert ! ( explicit_tool. output_schema. is_some( ) , "Explicit output_schema attribute should work" ) ;
100
+ }
0 commit comments