Preserve MCP compatibility with older executors (#39979)

## Why

Older executors do not support resolving HTTP header values from their own
environment. Sending environment-backed bearer tokens to them can prevent MCP
servers from starting.

## What changed

- Advertise executor support for environment-backed HTTP headers with the
  `httpHeaderEnvVars` capability.
- Delegate bearer-token resolution only when the selected executor advertises
  that capability; otherwise, resolve tokens available in the host environment.
- Exclude executor-owned MCP servers that require environment-backed bearer
  tokens when the executor cannot resolve them, while retaining compatible
  host-owned and executor-owned servers.

## Testing

Added protocol and environment-variable coverage plus an end-to-end test that
simulates an older executor and verifies compatible MCP servers remain usable.

GitOrigin-RevId: 43545926f6f7e0f3b1091eb29d34647f6372bc4a
This commit is contained in:
jif
2026-08-21 17:21:30 +00:00
committed by copyberry
parent 696b4502df
commit 8edb95f274
6 changed files with 249 additions and 28 deletions
+20 -13
View File
@@ -68,20 +68,24 @@ impl Environment {
&self,
cwd: PathUri,
) -> Result<Vec<(String, McpServerConfig)>, ExecServerError> {
let response = tokio::time::timeout(std::time::Duration::from_secs(10), async {
if !self.info().await?.capabilities.environment_config_read {
return Ok(None);
}
self.read_environment_config(EnvironmentConfigReadParams {
cwd,
config_paths: vec![vec!["mcp_servers".to_string()]],
requirements_paths: vec![vec!["mcp_servers".to_string()]],
let (response, http_header_env_vars) =
tokio::time::timeout(std::time::Duration::from_secs(10), async {
let capabilities = self.info().await?.capabilities;
if !capabilities.environment_config_read {
return Ok((None, false));
}
self.read_environment_config(EnvironmentConfigReadParams {
cwd,
config_paths: vec![vec!["mcp_servers".to_string()]],
requirements_paths: vec![vec!["mcp_servers".to_string()]],
})
.await
.map(|response| (Some(response), capabilities.http_header_env_vars))
})
.await
.map(Some)
})
.await
.map_err(|_| ExecServerError::Protocol("executor MCP discovery timed out".to_string()))??;
.map_err(|_| {
ExecServerError::Protocol("executor MCP discovery timed out".to_string())
})??;
let Some(response) = response else {
return Ok(Vec::new());
};
@@ -107,7 +111,10 @@ impl Environment {
.cloned()
.unwrap_or_else(|| toml::Value::Table(toml::map::Map::new()));
if let Some(servers) = servers.as_table_mut() {
servers.retain(|_, server| server.get("url").is_some());
servers.retain(|_, server| {
server.get("url").is_some()
&& (http_header_env_vars || server.get("bearer_token_env_var").is_none())
});
}
let servers = servers
.try_into::<HashMap<String, McpServerConfig>>()