@@ -90,8 +90,10 @@ impl Introspect {
9090 && schema
9191 . root_operation ( OperationType :: Mutation )
9292 . is_none_or ( |root_name| {
93+ // Allow introspection of the mutation type itself even when mutations are disabled
9394 extended_type. name ( ) != root_name
94- || ( type_name == root_name. as_str ( ) && self . allow_mutations )
95+ || type_name == root_name. as_str ( )
96+ || self . allow_mutations
9597 } )
9698 && schema
9799 . root_operation ( OperationType :: Subscription )
@@ -188,4 +190,131 @@ mod tests {
188190 assert ! ( description. contains( "T=type,I=input,E=enum,U=union,F=interface" ) ) ;
189191 assert ! ( description. contains( "s=String,i=Int,f=Float,b=Boolean,d=ID" ) ) ;
190192 }
193+
194+ #[ rstest]
195+ #[ tokio:: test]
196+ async fn test_introspect_query_depth_1_returns_fields ( schema : Valid < Schema > ) {
197+ let introspect = Introspect :: new (
198+ Arc :: new ( Mutex :: new ( schema) ) ,
199+ Some ( "Query" . to_string ( ) ) ,
200+ Some ( "Mutation" . to_string ( ) ) ,
201+ false ,
202+ ) ;
203+
204+ let result = introspect
205+ . execute ( Input {
206+ type_name : "Query" . to_string ( ) ,
207+ depth : 1 ,
208+ } )
209+ . await
210+ . expect ( "Introspect execution failed" ) ;
211+
212+ let content = result
213+ . content
214+ . iter ( )
215+ . filter_map ( |c| {
216+ use rmcp:: model:: RawContent ;
217+ use std:: ops:: Deref ;
218+ let c = c. deref ( ) ;
219+ match c {
220+ RawContent :: Text ( text) => Some ( text. text . clone ( ) ) ,
221+ _ => None ,
222+ }
223+ } )
224+ . collect :: < Vec < String > > ( )
225+ . join ( "\n " ) ;
226+
227+ // Query with depth 1 should return the Query type with its fields
228+ assert ! ( !result. content. is_empty( ) ) ;
229+ assert ! ( content. contains( "type Query" ) ) ;
230+ }
231+
232+ #[ rstest]
233+ #[ tokio:: test]
234+ async fn test_introspect_mutation_depth_1_returns_fields ( schema : Valid < Schema > ) {
235+ let introspect = Introspect :: new (
236+ Arc :: new ( Mutex :: new ( schema) ) ,
237+ Some ( "Query" . to_string ( ) ) ,
238+ Some ( "Mutation" . to_string ( ) ) ,
239+ false ,
240+ ) ;
241+
242+ let result = introspect
243+ . execute ( Input {
244+ type_name : "Mutation" . to_string ( ) ,
245+ depth : 1 ,
246+ } )
247+ . await
248+ . expect ( "Introspect execution failed" ) ;
249+
250+ let content = result
251+ . content
252+ . iter ( )
253+ . filter_map ( |c| {
254+ use rmcp:: model:: RawContent ;
255+ use std:: ops:: Deref ;
256+ let c = c. deref ( ) ;
257+ match c {
258+ RawContent :: Text ( text) => Some ( text. text . clone ( ) ) ,
259+ _ => None ,
260+ }
261+ } )
262+ . collect :: < Vec < String > > ( )
263+ . join ( "\n " ) ;
264+
265+ // Mutation with depth 1 should return the Mutation type with its fields, just like Query
266+ assert ! (
267+ !result. content. is_empty( ) ,
268+ "Mutation introspection should return content"
269+ ) ;
270+ assert ! (
271+ content. contains( "type Mutation" ) ,
272+ "Should contain Mutation type definition"
273+ ) ;
274+ }
275+
276+ #[ rstest]
277+ #[ tokio:: test]
278+ async fn test_introspect_mutation_depth_1_with_mutations_disabled ( schema : Valid < Schema > ) {
279+ // This test verifies the fix: when mutations are not allowed, mutation introspection should still work
280+ let introspect = Introspect :: new (
281+ Arc :: new ( Mutex :: new ( schema) ) ,
282+ Some ( "Query" . to_string ( ) ) ,
283+ None ,
284+ false ,
285+ ) ;
286+
287+ let result = introspect
288+ . execute ( Input {
289+ type_name : "Mutation" . to_string ( ) ,
290+ depth : 1 ,
291+ } )
292+ . await
293+ . expect ( "Introspect execution failed" ) ;
294+
295+ let content = result
296+ . content
297+ . iter ( )
298+ . filter_map ( |c| {
299+ use rmcp:: model:: RawContent ;
300+ use std:: ops:: Deref ;
301+ let c = c. deref ( ) ;
302+ match c {
303+ RawContent :: Text ( text) => Some ( text. text . clone ( ) ) ,
304+ _ => None ,
305+ }
306+ } )
307+ . collect :: < Vec < String > > ( )
308+ . join ( "\n " ) ;
309+
310+ // After the fix: mutation introspection should work even when mutations are disabled
311+ assert ! (
312+ !result. content. is_empty( ) ,
313+ "Mutation introspection should return content even when mutations are disabled"
314+ ) ;
315+ assert ! (
316+ content. contains( "type Mutation" ) ,
317+ "Should contain Mutation type definition"
318+ ) ;
319+ }
191320}
0 commit comments