Resolve HTTP MCP bearer tokens in executor environments (#39926)

## Why

Executor-owned HTTP MCP servers need to read their bearer credentials from the
selected executor environment instead of the host process.

## What changed

- Preserve `bearer_token_env_var` for executor-owned HTTP MCP configurations and
  resolve it when the executor sends each request.
- Extend delegated HTTP headers with executor-local environment references while
  rejecting missing, empty, or protected credential variables.
- Keep transport-provided bearer authentication compatible with MCP redirect and
  OAuth handling without sending a placeholder authorization value.

## Testing

- Cover authenticated executor-owned MCP requests end to end.
- Cover delegated header resolution and rejection of protected variables.
- Cover parsing executor-owned bearer configuration and transport-provided bearer
  behavior.

GitOrigin-RevId: 442bf7382198ffb69eba797eb36d4c74faabda88
This commit is contained in:
jif authored and copyberry committed 2026-08-21 12:50:15 +00:00
1 parent ff0e95007c
commit 93c54bca38
21 files changed
+361 -57

No files matched your search

+2 -11
View File
@@ -183,22 +183,13 @@ fn bind_environment_env_vars(config: &mut McpServerConfig) -> Result<(), String>
let is_local_environment = config.is_local_environment();
let env_vars = match &mut config.transport {
McpServerTransportConfig::Stdio { env_vars, .. } => env_vars,
// Never resolve executor-owned environment references in the host process.
// Remove this rejection once the owning executor resolves these fields.
// Bearer credentials resolve on the executor; other header variables do not yet.
McpServerTransportConfig::StreamableHttp {
bearer_token_env_var,
env_http_headers,
..
env_http_headers, ..
} => {
if is_local_environment {
return Ok(());
}
if bearer_token_env_var.is_some() {
return Err(
"`bearer_token_env_var` requires executor-side environment resolution for an executor-owned HTTP MCP"
.to_string(),
);
}
if env_http_headers
.as_ref()
.is_some_and(|headers| !headers.is_empty())