@@ -17,7 +17,7 @@ use tracing::{debug, error};
1717
1818use crate :: {
1919 custom_scalar_map:: CustomScalarMap ,
20- errors:: { McpError , OperationError , ServerError } ,
20+ errors:: { McpError , ServerError } ,
2121 explorer:: { EXPLORER_TOOL_NAME , Explorer } ,
2222 graphql:: { self , Executable as _} ,
2323 introspection:: tools:: {
@@ -58,18 +58,21 @@ impl Running {
5858 . iter ( )
5959 . cloned ( )
6060 . map ( |operation| operation. into_inner ( ) )
61- . map ( |operation| {
62- operation. into_operation (
61+ . filter_map ( |operation| {
62+ match operation. into_operation (
6363 & schema,
6464 self . custom_scalar_map . as_ref ( ) ,
6565 self . mutation_mode ,
6666 self . disable_type_description ,
6767 self . disable_schema_description ,
68- )
68+ ) {
69+ Ok ( operation) => operation,
70+ Err ( error) => {
71+ error ! ( "Invalid operation: {}" , error) ;
72+ None
73+ }
74+ }
6975 } )
70- . collect :: < Result < Vec < Option < Operation > > , OperationError > > ( ) ?
71- . into_iter ( )
72- . flatten ( )
7376 . collect ( ) ;
7477
7578 debug ! (
@@ -91,23 +94,28 @@ impl Running {
9194 self ,
9295 operations : Vec < RawOperation > ,
9396 ) -> Result < Running , ServerError > {
97+ debug ! ( "Operations updated:\n {:?}" , operations) ;
98+
9499 // Update the operations based on the current schema
95100 {
96101 let schema = & * self . schema . lock ( ) . await ;
97102 let updated_operations: Vec < Operation > = operations
98103 . into_iter ( )
99- . map ( |operation| {
100- operation. into_operation (
104+ . filter_map ( |operation| {
105+ match operation. into_operation (
101106 schema,
102107 self . custom_scalar_map . as_ref ( ) ,
103108 self . mutation_mode ,
104109 self . disable_type_description ,
105110 self . disable_schema_description ,
106- )
111+ ) {
112+ Ok ( operation) => operation,
113+ Err ( error) => {
114+ error ! ( "Invalid operation: {}" , error) ;
115+ None
116+ }
117+ }
107118 } )
108- . collect :: < Result < Vec < Option < Operation > > , OperationError > > ( ) ?
109- . into_iter ( )
110- . flatten ( )
111119 . collect ( ) ;
112120
113121 debug ! (
@@ -268,3 +276,53 @@ fn convert_arguments<T: serde::de::DeserializeOwned>(
268276 serde_json:: from_value ( Value :: from ( arguments. arguments ) )
269277 . map_err ( |_| McpError :: new ( ErrorCode :: INVALID_PARAMS , "Invalid input" . to_string ( ) , None ) )
270278}
279+
280+ #[ cfg( test) ]
281+ mod tests {
282+ use super :: * ;
283+
284+ #[ tokio:: test]
285+ async fn invalid_operations_should_not_crash_server ( ) {
286+ let schema = Schema :: parse ( "type Query { id: String }" , "schema.graphql" )
287+ . unwrap ( )
288+ . validate ( )
289+ . unwrap ( ) ;
290+
291+ let running = Running {
292+ schema : Arc :: new ( Mutex :: new ( schema) ) ,
293+ operations : Arc :: new ( Mutex :: new ( vec ! [ ] ) ) ,
294+ headers : HeaderMap :: new ( ) ,
295+ endpoint : "http://localhost:4000" . to_string ( ) ,
296+ execute_tool : None ,
297+ introspect_tool : None ,
298+ explorer_tool : None ,
299+ custom_scalar_map : None ,
300+ peers : Arc :: new ( RwLock :: new ( vec ! [ ] ) ) ,
301+ cancellation_token : CancellationToken :: new ( ) ,
302+ mutation_mode : MutationMode :: None ,
303+ disable_type_description : false ,
304+ disable_schema_description : false ,
305+ } ;
306+
307+ let operations = vec ! [
308+ RawOperation :: from( (
309+ "query Valid { id }" . to_string( ) ,
310+ Some ( "valid.graphql" . to_string( ) ) ,
311+ ) ) ,
312+ RawOperation :: from( (
313+ "query Invalid {{ id }" . to_string( ) ,
314+ Some ( "invalid.graphql" . to_string( ) ) ,
315+ ) ) ,
316+ RawOperation :: from( (
317+ "query { id }" . to_string( ) ,
318+ Some ( "unnamed.graphql" . to_string( ) ) ,
319+ ) ) ,
320+ ] ;
321+
322+ let updated_running = running. update_operations ( operations) . await . unwrap ( ) ;
323+ let updated_operations = updated_running. operations . lock ( ) . await ;
324+
325+ assert_eq ! ( updated_operations. len( ) , 1 ) ;
326+ assert_eq ! ( updated_operations. first( ) . unwrap( ) . as_ref( ) . name, "Valid" ) ;
327+ }
328+ }
0 commit comments