Skip to content

Commit fff41c1

Browse files
authored
Merge pull request #33 from apollographql/jeffrey/better-subscription-and-mutation-handling
Subscription and mutation handling
2 parents 8614f2d + bbb428d commit fff41c1

File tree

8 files changed

+1382
-258
lines changed

8 files changed

+1382
-258
lines changed

crates/mcp-apollo-server/src/errors.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
1-
use apollo_compiler::{Schema, ast::Document, validation::WithErrors};
1+
use apollo_compiler::{
2+
Node, Schema,
3+
ast::{Document, OperationDefinition},
4+
validation::WithErrors,
5+
};
26
use apollo_federation::error::FederationError;
37
use reqwest::header::{InvalidHeaderName, InvalidHeaderValue};
48
use rmcp::serde_json;
59
use tokio::task::JoinError;
610

11+
use crate::operations::MutationMode;
12+
713
/// An error in operation parsing
814
#[derive(Debug, thiserror::Error)]
915
pub enum OperationError {
@@ -27,6 +33,12 @@ pub enum OperationError {
2733

2834
#[error(transparent)]
2935
File(#[from] std::io::Error),
36+
37+
#[error("Mutation {0} not allowed while with --allow-mutations {1:?}")]
38+
MutationNotAllowed(Node<OperationDefinition>, MutationMode),
39+
40+
#[error("Subscriptions are not allowed: {0}")]
41+
SubscriptionNotAllowed(Node<OperationDefinition>),
3042
}
3143

3244
/// An error in server initialization
@@ -38,6 +50,9 @@ pub enum ServerError {
3850
#[error("Could not parse GraphQL schema: {0}")]
3951
GraphQLSchema(Box<WithErrors<Schema>>),
4052

53+
#[error("Could not parse GraphQL schema: {0}")]
54+
GraphQLDocumentSchema(Box<WithErrors<Document>>),
55+
4156
#[error("Federation error in GraphQL schema: {0}")]
4257
Federation(FederationError),
4358

crates/mcp-apollo-server/src/introspection.rs

+17-6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use crate::errors::McpError;
44
use crate::graphql;
5+
use crate::operations::{MutationMode, operation_defs};
56
use crate::schema_from_type;
67
use apollo_compiler::Schema;
78
use apollo_compiler::validation::Valid;
@@ -41,6 +42,7 @@ impl GetSchema {
4142
#[derive(Clone)]
4243
pub struct Execute {
4344
pub tool: Tool,
45+
mutation_mode: MutationMode,
4446
}
4547

4648
#[derive(JsonSchema, Deserialize)]
@@ -53,8 +55,9 @@ pub struct Input {
5355
}
5456

5557
impl Execute {
56-
pub fn new() -> Self {
58+
pub fn new(mutation_mode: MutationMode) -> Self {
5759
Self {
60+
mutation_mode,
5861
tool: Tool::new(
5962
EXECUTE_TOOL_NAME,
6063
"Execute a GraphQL operation. Use the `schema` tool to get the GraphQL schema. Always use the schema to create operations - do not try arbitrary operations. DO NOT try to execute introspection queries.",
@@ -70,11 +73,19 @@ impl graphql::Executable for Execute {
7073
}
7174

7275
fn operation(&self, input: Value) -> Result<String, McpError> {
73-
serde_json::from_value::<Input>(input)
74-
.map(|input| input.query)
75-
.map_err(|_| {
76-
McpError::new(ErrorCode::INVALID_PARAMS, "Invalid input".to_string(), None)
77-
})
76+
let input = serde_json::from_value::<Input>(input).map_err(|_| {
77+
McpError::new(ErrorCode::INVALID_PARAMS, "Invalid input".to_string(), None)
78+
})?;
79+
80+
// validate the operation
81+
operation_defs(
82+
&input.query,
83+
self.mutation_mode == MutationMode::All,
84+
self.mutation_mode,
85+
)
86+
.map_err(|e| McpError::new(ErrorCode::INVALID_PARAMS, e.to_string(), None))?;
87+
88+
Ok(input.query)
7889
}
7990

8091
fn variables(&self, input: Value) -> Result<Value, McpError> {

crates/mcp-apollo-server/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ mod introspection;
66
pub mod json_schema;
77
pub mod operations;
88
pub mod sanitize;
9+
pub(crate) mod schema_tree_shake;
910
pub mod server;
10-
pub(crate) mod tree_shake;

crates/mcp-apollo-server/src/main.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ use mcp_apollo_registry::uplink::schema::SchemaSource;
77
use mcp_apollo_registry::uplink::{SecretString, UplinkConfig};
88
use mcp_apollo_server::custom_scalar_map::CustomScalarMap;
99
use mcp_apollo_server::errors::ServerError;
10+
use mcp_apollo_server::operations::MutationMode;
1011
use mcp_apollo_server::operations::OperationSource;
11-
use mcp_apollo_server::server::{Server, Transport};
12+
use mcp_apollo_server::server::Server;
13+
use mcp_apollo_server::server::Transport;
1214
use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
1315
use std::env;
1416
use std::path::PathBuf;
@@ -74,6 +76,10 @@ struct Args {
7476
/// The path to the persisted query manifest containing operations
7577
#[arg(long)]
7678
manifest: Option<PathBuf>,
79+
80+
// Configure when to allow mutations
81+
#[clap(long, short = 'm', default_value_t, value_enum)]
82+
allow_mutations: MutationMode,
7783
}
7884

7985
#[tokio::main]
@@ -134,6 +140,7 @@ async fn main() -> anyhow::Result<()> {
134140
.explorer(args.explorer)
135141
.headers(default_headers)
136142
.introspection(args.introspection)
143+
.mutation_mode(args.allow_mutations)
137144
.and_custom_scalar_map(
138145
args.custom_scalars_config
139146
.map(|custom_scalars_config| CustomScalarMap::try_from(&custom_scalars_config))

0 commit comments

Comments
 (0)