Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changesets/feat_auth_token_passthrough_disable.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### feat: Configuration for disabling authorization token passthrough - @swcollard PR #336

A new optional new MCP Server configuration parameter, `transport.auth.disable_auth_token_passthrough`, which is `false` by default, that when true, will no longer pass through validated Auth tokens to the GraphQL API.
4 changes: 4 additions & 0 deletions crates/apollo-mcp-server/src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ pub struct Config {

/// Supported OAuth scopes by this resource server
pub scopes: Vec<String>,

/// Whether to disable the auth token passthrough to upstream API
#[serde(default)]
pub disable_auth_token_passthrough: bool,
}

impl Config {
Expand Down
11 changes: 11 additions & 0 deletions crates/apollo-mcp-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ async fn main() -> anyhow::Result<()> {
.then(|| config.graphos.graph_ref())
.transpose()?;

let transport = config.transport.clone();

Ok(Server::builder()
.transport(config.transport)
Copy link

Copilot AI Sep 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The config.transport is being cloned unnecessarily. Since it's moved into the builder on line 115, you can use the cloned transport variable instead of cloning again.

Suggested change
.transport(config.transport)
.transport(transport)

Copilot uses AI. Check for mistakes.
.schema_source(schema_source)
Expand All @@ -125,6 +127,15 @@ async fn main() -> anyhow::Result<()> {
.mutation_mode(config.overrides.mutation_mode)
.disable_type_description(config.overrides.disable_type_description)
.disable_schema_description(config.overrides.disable_schema_description)
.disable_auth_token_passthrough(match transport {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we shorten this a bit via:

match transport {
    apollo_mcp_server::server::Transport::STDIO => false,
    _ { auth, .. } => auth.map(...).unwrap_or(false)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't quite get this syntax working? I think the match needs the enum name so it can pull the auth values from the struct. And the compiler isn't smart enough to pull it as an 'else' case?

apollo_mcp_server::server::Transport::Stdio => false,
apollo_mcp_server::server::Transport::SSE { auth, .. } => auth
.map(|a| a.disable_auth_token_passthrough)
.unwrap_or(false),
apollo_mcp_server::server::Transport::StreamableHttp { auth, .. } => auth
.map(|a| a.disable_auth_token_passthrough)
.unwrap_or(false),
})
.custom_scalar_map(
config
.custom_scalars
Expand Down
3 changes: 3 additions & 0 deletions crates/apollo-mcp-server/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub struct Server {
mutation_mode: MutationMode,
disable_type_description: bool,
disable_schema_description: bool,
disable_auth_token_passthrough: bool,
search_leaf_depth: usize,
index_memory_bytes: usize,
health_check: HealthCheckConfig,
Expand Down Expand Up @@ -112,6 +113,7 @@ impl Server {
mutation_mode: MutationMode,
disable_type_description: bool,
disable_schema_description: bool,
disable_auth_token_passthrough: bool,
search_leaf_depth: usize,
index_memory_bytes: usize,
health_check: HealthCheckConfig,
Expand All @@ -138,6 +140,7 @@ impl Server {
mutation_mode,
disable_type_description,
disable_schema_description,
disable_auth_token_passthrough,
search_leaf_depth,
index_memory_bytes,
health_check,
Expand Down
2 changes: 2 additions & 0 deletions crates/apollo-mcp-server/src/server/states.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ struct Config {
mutation_mode: MutationMode,
disable_type_description: bool,
disable_schema_description: bool,
disable_auth_token_passthrough: bool,
search_leaf_depth: usize,
index_memory_bytes: usize,
health_check: HealthCheckConfig,
Expand Down Expand Up @@ -76,6 +77,7 @@ impl StateMachine {
mutation_mode: server.mutation_mode,
disable_type_description: server.disable_type_description,
disable_schema_description: server.disable_schema_description,
disable_auth_token_passthrough: server.disable_auth_token_passthrough,
search_leaf_depth: server.search_leaf_depth,
index_memory_bytes: server.index_memory_bytes,
health_check: server.health_check,
Expand Down
10 changes: 8 additions & 2 deletions crates/apollo-mcp-server/src/server/states/running.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ pub(super) struct Running {
pub(super) mutation_mode: MutationMode,
pub(super) disable_type_description: bool,
pub(super) disable_schema_description: bool,
pub(super) disable_auth_token_passthrough: bool,
pub(super) health_check: Option<HealthCheck>,
}

Expand Down Expand Up @@ -211,7 +212,9 @@ impl ServerHandler for Running {
let mut headers = self.headers.clone();
if let Some(axum_parts) = context.extensions.get::<axum::http::request::Parts>() {
// Optionally extract the validated token and propagate it to upstream servers if present
if let Some(token) = axum_parts.extensions.get::<ValidToken>() {
if !self.disable_auth_token_passthrough
&& let Some(token) = axum_parts.extensions.get::<ValidToken>()
{
Comment on lines +215 to +217
Copy link

Copilot AI Sep 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition logic is duplicated between lines 215-217 and 248-250. Consider extracting this into a helper method to reduce code duplication and improve maintainability.

Copilot uses AI. Check for mistakes.
headers.typed_insert(token.deref().clone());
}

Expand Down Expand Up @@ -242,7 +245,9 @@ impl ServerHandler for Running {
let mut headers = self.headers.clone();
if let Some(axum_parts) = context.extensions.get::<axum::http::request::Parts>() {
// Optionally extract the validated token and propagate it to upstream servers if present
if let Some(token) = axum_parts.extensions.get::<ValidToken>() {
if !self.disable_auth_token_passthrough
&& let Some(token) = axum_parts.extensions.get::<ValidToken>()
{
headers.typed_insert(token.deref().clone());
}

Expand Down Expand Up @@ -355,6 +360,7 @@ mod tests {
mutation_mode: MutationMode::None,
disable_type_description: false,
disable_schema_description: false,
disable_auth_token_passthrough: false,
health_check: None,
};

Expand Down
1 change: 1 addition & 0 deletions crates/apollo-mcp-server/src/server/states/starting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ impl Starting {
mutation_mode: self.config.mutation_mode,
disable_type_description: self.config.disable_type_description,
disable_schema_description: self.config.disable_schema_description,
disable_auth_token_passthrough: self.config.disable_auth_token_passthrough,
health_check: health_check.clone(),
};

Expand Down