Skip to content

Commit 1af7f95

Browse files
authored
Merge pull request #176 from apollographql/GT-285
Invalid operation should not crash the MCP Server
2 parents cafb43c + 9bebf88 commit 1af7f95

File tree

2 files changed

+82
-21
lines changed

2 files changed

+82
-21
lines changed

crates/apollo-mcp-server/src/server/states/running.rs

Lines changed: 71 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use tracing::{debug, error};
1717

1818
use 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+
}

crates/apollo-mcp-server/src/server/states/starting.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ use tokio_util::sync::CancellationToken;
1313
use tracing::{debug, error, info};
1414

1515
use crate::{
16-
errors::{OperationError, ServerError},
16+
errors::ServerError,
1717
explorer::Explorer,
1818
introspection::tools::{execute::Execute, introspect::Introspect},
19-
operations::{MutationMode, Operation, RawOperation},
19+
operations::{MutationMode, RawOperation},
2020
server::Transport,
2121
};
2222

@@ -35,18 +35,21 @@ impl Starting {
3535
let operations: Vec<_> = self
3636
.operations
3737
.into_iter()
38-
.map(|operation| {
39-
operation.into_operation(
38+
.filter_map(|operation| {
39+
match operation.into_operation(
4040
&self.schema,
4141
self.config.custom_scalar_map.as_ref(),
4242
self.config.mutation_mode,
4343
self.config.disable_type_description,
4444
self.config.disable_schema_description,
45-
)
45+
) {
46+
Ok(operation) => operation,
47+
Err(error) => {
48+
error!("Invalid operation: {}", error);
49+
None
50+
}
51+
}
4652
})
47-
.collect::<Result<Vec<Option<Operation>>, OperationError>>()?
48-
.into_iter()
49-
.flatten()
5053
.collect();
5154

5255
debug!(

0 commit comments

Comments
 (0)