From 28e51fde974cb1bfb14fefe72f03aee8533936fb Mon Sep 17 00:00:00 2001 From: Jeffrey Burt Date: Fri, 9 May 2025 07:38:47 -0700 Subject: [PATCH 1/6] split execute and instrospection --- crates/apollo-mcp-server/src/main.rs | 7 ++++++- crates/apollo-mcp-server/src/server.rs | 9 ++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/crates/apollo-mcp-server/src/main.rs b/crates/apollo-mcp-server/src/main.rs index 55bb947c..6325f03c 100644 --- a/crates/apollo-mcp-server/src/main.rs +++ b/crates/apollo-mcp-server/src/main.rs @@ -62,10 +62,14 @@ struct Args { #[arg(long)] sse_port: Option, - /// Expose the schema to the MCP client through `introspect` and `execute` tools + /// Expose the schema to the MCP client through `introspect` tool #[arg(long, short = 'i')] introspection: bool, + /// Allow the use of the `execute` tool with operations build from the schema tool + #[arg(long)] + execute_introspection: bool, + /// Enable use of uplink to get the schema and persisted queries (requires APOLLO_KEY and APOLLO_GRAPH_REF) #[arg(long, short = 'u')] uplink: bool, @@ -160,6 +164,7 @@ async fn main() -> anyhow::Result<()> { .explorer(args.explorer) .headers(default_headers) .introspection(args.introspection) + .execute_introspection(args.execute_introspection) .mutation_mode(args.allow_mutations) .disable_type_description(args.disable_type_description) .disable_schema_description(args.disable_schema_description) diff --git a/crates/apollo-mcp-server/src/server.rs b/crates/apollo-mcp-server/src/server.rs index 69373769..d3bd015d 100644 --- a/crates/apollo-mcp-server/src/server.rs +++ b/crates/apollo-mcp-server/src/server.rs @@ -46,6 +46,7 @@ pub struct Server { endpoint: String, headers: HeaderMap, introspection: bool, + execute_introspection: bool, explorer: bool, custom_scalar_map: Option, mutation_mode: MutationMode, @@ -72,6 +73,7 @@ impl Server { endpoint: String, headers: Headers, introspection: bool, + execute_introspection: bool, explorer: bool, custom_scalar_map: Option, mutation_mode: MutationMode, @@ -90,6 +92,7 @@ impl Server { endpoint, headers, introspection, + execute_introspection, explorer, custom_scalar_map, mutation_mode, @@ -138,6 +141,7 @@ struct Starting { endpoint: String, headers: HeaderMap, introspection: bool, + execute_introspection: bool, explorer: bool, custom_scalar_map: Option, mutation_mode: MutationMode, @@ -179,7 +183,9 @@ impl Starting { ) .await?; - let execute_tool = self.introspection.then(|| Execute::new(self.mutation_mode)); + let execute_tool = self + .execute_introspection + .then(|| Execute::new(self.mutation_mode)); let root_query_type = self .introspection @@ -399,6 +405,7 @@ impl StateMachine { endpoint: server.endpoint, headers: server.headers, introspection: server.introspection, + execute_introspection: server.execute_introspection, explorer: server.explorer, custom_scalar_map: server.custom_scalar_map, mutation_mode: server.mutation_mode, From 31f4356f30c5725159a68d2995f35c8f84adc968 Mon Sep 17 00:00:00 2001 From: Jeffrey Burt Date: Tue, 20 May 2025 11:31:44 -0700 Subject: [PATCH 2/6] make introspection required for execute_introspection --- crates/apollo-mcp-server/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/apollo-mcp-server/src/main.rs b/crates/apollo-mcp-server/src/main.rs index 6325f03c..a18779af 100644 --- a/crates/apollo-mcp-server/src/main.rs +++ b/crates/apollo-mcp-server/src/main.rs @@ -63,7 +63,7 @@ struct Args { sse_port: Option, /// Expose the schema to the MCP client through `introspect` tool - #[arg(long, short = 'i')] + #[arg(long, short = 'i', required_if_eq("execute_introspection", "true"))] introspection: bool, /// Allow the use of the `execute` tool with operations build from the schema tool From 5ebc02e1bd34da8527b16ff950db911229638cd9 Mon Sep 17 00:00:00 2001 From: Jeffrey Burt Date: Tue, 20 May 2025 12:14:16 -0700 Subject: [PATCH 3/6] add changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8522406b..d06bb6c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### 🚀 Features - Add an optional `--sse-address` argument to set the bind address of the MCP server. Defaults to 127.0.0.1. (#63) +- split out the execute tool into separate --execute-introspection flag, `--introspection` is now `--introspection --execute-introspection` ### 🐛 Fixes - Fixed PowerShell script (#55) From e93606177f49dd082ac3cc139f09fd6ae3a81899 Mon Sep 17 00:00:00 2001 From: Jeffrey Burt Date: Thu, 22 May 2025 15:23:51 -0700 Subject: [PATCH 4/6] move changelog to breaking --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d04d213d..2762aafe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,12 +13,16 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### 📚 Documentation --> +## [UNRELEASED] + +### ❗ BREAKING ❗ +- split out the execute tool into separate --execute-introspection flag, `--introspection` is now `--introspection --execute-introspection` + ## [0.2.0] - 2025-05-21 ### 🚀 Features - The `--operations` argument now supports hot reloading and directory paths. If a directory is specified, all .graphql files in the directory will be loaded as operations. The running server will update when files are added to or removed from the directory. (#69) - Add an optional `--sse-address` argument to set the bind address of the MCP server. Defaults to 127.0.0.1. (#63) -- split out the execute tool into separate --execute-introspection flag, `--introspection` is now `--introspection --execute-introspection` ### 🐛 Fixes - Fixed PowerShell script (#55) From 905c90f05e63a2c88376313e7ec7820baefc16bc Mon Sep 17 00:00:00 2001 From: Jeffrey Burt Date: Thu, 22 May 2025 15:23:57 -0700 Subject: [PATCH 5/6] update docs --- docs/source/command-reference.mdx | 6 ++++-- docs/source/guides/index.mdx | 2 +- graphql/TheSpaceDevs/README.md | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/source/command-reference.mdx b/docs/source/command-reference.mdx index 07cf9d5e..1436b6ab 100644 --- a/docs/source/command-reference.mdx +++ b/docs/source/command-reference.mdx @@ -62,12 +62,13 @@ apollo-mcp-server [OPTIONS] --directory | :-------------------------------------------------- |:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `-d, --directory ` | The working directory to use. Defaults the current working directory. | | `-s, --schema ` | The path to the GraphQL API schema file. | -| `-c, --custom-scalars-config ` | The path to the GraphQL custom_scalars_config file. [Learn more](/apollo-mcp-server/guides/#custom-scalars). | +| `-c, --custom-scalars-config ` | The path to the GraphQL custom_scalars_config file. [Learn more](/apollo-mcp-server/guides/#custom-scalars). | | `-e, --endpoint ` | The GraphQL endpoint the server will invoke.
[default: `http://127.0.0.1:4000`] | | `--header ` | Headers to send to the endpoint. | | `--sse-port ` | Start the server using the SSE transport on the given port (default: 5000). | | `--sse-address ` | The IP address to bind the SSE server to (default: 127.0.0.1). | -| `-i, --introspection` | Expose the schema to the MCP client through `introspect` and `execute` tools. [Learn more](/apollo-mcp-server/guides/#from-schema-introspection). | +| `-i, --introspection` | Expose the schema to the MCP client through the `introspect` tool. [Learn more](/apollo-mcp-server/guides/#from-schema-introspection). | +| `--execute-introspection` | Expose the schema to the MCP client through the `execute` tool. [Learn more](/apollo-mcp-server/guides/#from-schema-introspection). | | `-u, --uplink` | Enable use of uplink to get the schema and persisted queries (requires `APOLLO_KEY` and `APOLLO_GRAPH_REF`). [Learn more](/apollo-mcp-server/guides/#from-graphos-managed-persisted-queries). | | `-x, --explorer` | Expose a tool to open queries in Apollo Explorer (requires `APOLLO_KEY` and `APOLLO_GRAPH_REF`). | | `-o, --operations [...]` | Operation files to expose as MCP tools. [Learn more](/apollo-mcp-server/guides/#from-operation-schema-files). | @@ -93,6 +94,7 @@ The mapping of `rover dev` options to MCP Server options: | `--mcp-sse-port ` | `--sse-port ` | | `--mcp-sse-address
` | `--sse-address
` | | `--mcp-introspection` | `-i, --introspection` | +| `--mcp-execute-introspection` | `--execute-introspection` | | `--mcp-uplink` | `-u, --uplink` | | `--mcp-operations [...]` | `-o, --operations [...]` | | `--mcp-header ` | `--header ` | diff --git a/docs/source/guides/index.mdx b/docs/source/guides/index.mdx index 79c09d5a..563e387b 100644 --- a/docs/source/guides/index.mdx +++ b/docs/source/guides/index.mdx @@ -164,7 +164,7 @@ Use the `--header` option when running the MCP Server to pass the header to the For use cases where not all operations can be pre-defined, Apollo MCP Server supports tool creation based on introspection of the graph schema. This allows AI agents to explore a graph and execute operations dynamically. -To enable these schema-aware tools, run the MCP Server with the `--introspection` option, which exposes two new tools: +To enable these schema-aware tools, run the MCP Server with the `--introspection` option, which exposes the introspect tool, and `--execute-introspection` which enables the execute tool * `introspect` - returns information about schema types * `execute` - executes an operation on the GraphQL endpoint diff --git a/graphql/TheSpaceDevs/README.md b/graphql/TheSpaceDevs/README.md index 9243f96d..3bf36668 100644 --- a/graphql/TheSpaceDevs/README.md +++ b/graphql/TheSpaceDevs/README.md @@ -28,7 +28,7 @@ npx @modelcontextprotocol/inspector This option is typically used when you have built the source repository and use the binary outputs in the `target/build/*` folder. -There are operations located at `./operations/*.graphql` for you to use in your configuration. You can provide a set of operations in your MCP configuration along with the `--introspection` option that enables the LLM to generate a dynamic operation along with the ability to execute it. +There are operations located at `./operations/*.graphql` for you to use in your configuration. You can provide a set of operations in your MCP configuration along with the `--introspection` option that enables the LLM to generate a dynamic operation. Here is an example configuration you can use _(Note: you must provide your fill path to the binary in the command. Make sure to replace the command with the path to where you cloned the repository)_: @@ -55,7 +55,7 @@ Here is an example configuration you can use _(Note: you must provide your fill ## Using Server-Side-Events (SSE) with Apollo MCP server -There are operations located at `./operations/*.graphql` for you to use in your configuration. You can provide a set of operations in your MCP configuration along with the `--introspection` option that enables the LLM to generate a dynamic operation along with the ability to execute it. +There are operations located at `./operations/*.graphql` for you to use in your configuration. You can provide a set of operations in your MCP configuration along with the `--introspection` option that enables the LLM to generate a dynamic operation. ### Running SSE with `rover dev` From 6445d7743dc555047a0ebee7792cbc3ee2cccb65 Mon Sep 17 00:00:00 2001 From: Jeffrey Burt Date: Thu, 22 May 2025 15:28:13 -0700 Subject: [PATCH 6/6] fix merge conflict --- crates/apollo-mcp-server/src/server.rs | 147 ++----------------------- 1 file changed, 8 insertions(+), 139 deletions(-) diff --git a/crates/apollo-mcp-server/src/server.rs b/crates/apollo-mcp-server/src/server.rs index 810ac960..501f00f6 100644 --- a/crates/apollo-mcp-server/src/server.rs +++ b/crates/apollo-mcp-server/src/server.rs @@ -192,6 +192,7 @@ struct Configuring { endpoint: String, headers: HeaderMap, introspection: bool, + execute_introspection: bool, explorer: bool, custom_scalar_map: Option, mutation_mode: MutationMode, @@ -208,6 +209,7 @@ impl Configuring { endpoint: self.endpoint, headers: self.headers, introspection: self.introspection, + execute_introspection: self.execute_introspection, explorer: self.explorer, custom_scalar_map: self.custom_scalar_map, mutation_mode: self.mutation_mode, @@ -231,6 +233,7 @@ impl Configuring { endpoint: self.endpoint, headers: self.headers, introspection: self.introspection, + execute_introspection: self.execute_introspection, explorer: self.explorer, custom_scalar_map: self.custom_scalar_map, mutation_mode: self.mutation_mode, @@ -246,6 +249,7 @@ struct SchemaConfigured { endpoint: String, headers: HeaderMap, introspection: bool, + execute_introspection: bool, explorer: bool, custom_scalar_map: Option, mutation_mode: MutationMode, @@ -272,6 +276,7 @@ impl SchemaConfigured { endpoint: self.endpoint, headers: self.headers, introspection: self.introspection, + execute_introspection: self.execute_introspection, explorer: self.explorer, custom_scalar_map: self.custom_scalar_map, mutation_mode: self.mutation_mode, @@ -287,6 +292,7 @@ struct OperationsConfigured { endpoint: String, headers: HeaderMap, introspection: bool, + execute_introspection: bool, explorer: bool, custom_scalar_map: Option, mutation_mode: MutationMode, @@ -304,6 +310,7 @@ impl OperationsConfigured { endpoint: self.endpoint, headers: self.headers, introspection: self.introspection, + execute_introspection: self.execute_introspection, explorer: self.explorer, custom_scalar_map: self.custom_scalar_map, mutation_mode: self.mutation_mode, @@ -567,145 +574,6 @@ impl Running { } *peers = retained_peers; } -<<<<<<< HEAD - - /// Spawn a listener for any changes to the operation manifest. - fn spawn_change_listener(&self, mut change_receiver: mpsc::Receiver) { - let peers = self.peers.clone(); - let operations = self.operations.clone(); - let operation_poller = self.operation_poller.clone(); - let custom_scalars = self.custom_scalar_map.clone(); - let schema = self.schema.clone(); - let mutation_mode = self.mutation_mode; - let disable_type_description = self.disable_type_description; - let disable_schema_description = self.disable_schema_description; - tokio::spawn(async move { - while change_receiver.recv().await.is_some() { - match operation_poller - .operations( - &*schema.lock().await, - custom_scalars.as_ref(), - mutation_mode, - disable_type_description, - disable_schema_description, - ) - .await - { - Ok(new_operations) => *operations.lock().await = new_operations, - // TODO: ideally, we'd send the server to the error state here because it's - // no longer configured correctly. To do this, we'd have to receive the PQ - // updates through the same stream where schema changes are tracked. However, - // the router code ported into apollo-mcp-registry does not work that way - - // it has a separate PQ update mechanism. That will need to be rewritten, or - // maybe there's some way to bridge the PQ changes into the same stream. - Err(e) => error!("Failed to update operations: {:?}", e), - } - - Self::notify_tool_list_changed(peers.clone()).await; - } - }); - } -} - -struct StateMachine {} - -impl StateMachine { - pub(crate) async fn start(self, server: Server) -> Result<(), ServerError> { - let mut stream = stream::select_all(vec![ - server.schema_source.into_stream().boxed(), - Self::ctrl_c_stream().boxed(), - ]) - .take_while(|msg| future::ready(!matches!(msg, Event::Shutdown))) - .chain(stream::iter(vec![Event::Shutdown])) - .boxed(); - let mut state = State::Starting(Starting { - transport: server.transport, - operation_source: server.operation_source, - endpoint: server.endpoint, - headers: server.headers, - introspection: server.introspection, - execute_introspection: server.execute_introspection, - explorer: server.explorer, - custom_scalar_map: server.custom_scalar_map, - mutation_mode: server.mutation_mode, - disable_type_description: server.disable_type_description, - disable_schema_description: server.disable_schema_description, - }); - while let Some(event) = stream.next().await { - state = match event { - Event::UpdateSchema(schema_state) => { - let schema = Self::sdl_to_api_schema(schema_state)?; - match state { - State::Starting(starting) => starting.run(schema).await.into(), - State::Running(running) => running.update_schema(schema).await.into(), - other => other, - } - } - Event::NoMoreSchema => match state { - State::Starting { .. } => State::Error(ServerError::NoSchema), - _ => state, - }, - Event::Shutdown => match state { - State::Running(running) => { - running.cancellation_token.cancel(); - State::Stopping - } - _ => State::Stopping, - }, - }; - if matches!(&state, State::Error(_) | State::Stopping) { - break; - } - } - match state { - State::Error(e) => Err(e), - _ => Ok(()), - } - } - - fn sdl_to_api_schema(schema_state: SchemaState) -> Result, ServerError> { - match Supergraph::new(&schema_state.sdl) { - Ok(supergraph) => Ok(supergraph - .to_api_schema(ApiSchemaOptions::default()) - .map_err(ServerError::Federation)? - .schema() - .clone()), - Err(_) => Schema::parse_and_validate(schema_state.sdl, "schema.graphql") - .map_err(|e| ServerError::GraphQLSchema(e.into())), - } - } - - #[allow(clippy::expect_used)] - fn ctrl_c_stream() -> impl Stream { - #[cfg(not(unix))] - { - async { - tokio::signal::ctrl_c() - .await - .expect("Failed to install CTRL+C signal handler"); - } - .map(|_| Event::Shutdown) - .into_stream() - .boxed() - } - - #[cfg(unix)] - future::select( - tokio::signal::ctrl_c().map(|s| s.ok()).boxed(), - async { - tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()) - .expect("Failed to install SIGTERM signal handler") - .recv() - .await - } - .boxed(), - ) - .map(|_| Event::Shutdown) - .into_stream() - .boxed() - } -======= ->>>>>>> origin/main } impl ServerHandler for Running { @@ -843,6 +711,7 @@ impl StateMachine { endpoint: server.endpoint, headers: server.headers, introspection: server.introspection, + execute_introspection: server.execute_introspection, explorer: server.explorer, custom_scalar_map: server.custom_scalar_map, mutation_mode: server.mutation_mode,