Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
15 changes: 8 additions & 7 deletions docs/source/config-file.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,14 @@

These fields are under the top-level `transport` key, nested under the `auth` key. Learn more about [authorization and authentication](/apollo-mcp-server/auth).

| Option | Type | Default | Description |
| :----------------------- | :------------- | :------ | :------------------------------------------------------------------------------------------------- |
| `servers` | `List<URL>` | | List of upstream delegated OAuth servers (must support OIDC metadata discovery endpoint) |
| `audiences` | `List<string>` | | List of accepted audiences from upstream signed JWTs |
| `resource` | `string` | | The externally available URL pointing to this MCP server. Can be `localhost` when testing locally. |
| `resource_documentation` | `string` | | Optional link to more documentation relating to this MCP server |
| `scopes` | `List<string>` | | List of queryable OAuth scopes from the upstream OAuth servers |
| Option | Type | Default | Description |
| :-------------------------------- | :------------- | :------ | :------------------------------------------------------------------------------------------------- |
| `servers` | `List<URL>` | | List of upstream delegated OAuth servers (must support OIDC metadata discovery endpoint) |
| `audiences` | `List<string>` | | List of accepted audiences from upstream signed JWTs |
| `resource` | `string` | | The externally available URL pointing to this MCP server. Can be `localhost` when testing locally. |

Check warning on line 172 in docs/source/config-file.mdx

View check run for this annotation

Apollo Librarian / AI Style Review

docs/source/config-file.mdx#L172

Use the imperative for instructions. The suggested phrasing is also more direct. ```suggestion | <code>resource</code> | <code>string</code> | | The externally available URL for this MCP server. Use <code>localhost</code> when testing locally. | ```
| `resource_documentation` | `string` | | Optional link to more documentation relating to this MCP server |

Check notice on line 173 in docs/source/config-file.mdx

View check run for this annotation

Apollo Librarian / AI Style Review

docs/source/config-file.mdx#L173

The suggested phrasing is more direct and concise. ```suggestion | <code>resource_documentation</code> | <code>string</code> | | Optional link to documentation for this MCP server | ```
| `scopes` | `List<string>` | | List of queryable OAuth scopes from the upstream OAuth servers |
| `disable_auth_token_passthrough` | `bool` | `false` | Optional flag to disable passing validated Authorization header to downstream API |

Below is an example configuration using `StreamableHTTP` transport with authentication:

Expand Down