Discover HTTP MCP servers from selected executors (#39941)

## What changed

- Read `mcp_servers` configuration and requirements from each selected remote
  executor and add eligible HTTP servers to the thread's MCP runtime.
- Bind discovered servers to the thread's concrete executor snapshot and apply
  environment MCP policy and requirements. Discovery is best effort, and
  executor-local servers are not treated as required at startup.
- Ignore unsupported stdio servers and HTTP configurations that depend on
  environment-provided headers or header helpers.

## Testing

- Added an app-server integration test covering discovery, authenticated HTTP
  tool invocation, requirements enforcement, and exclusion of stdio servers.

GitOrigin-RevId: 6e1cdcebdbb1cc21a5a2285fbc5617d8d5997182
This commit is contained in:
jif
2026-08-21 14:23:33 +00:00
committed by copyberry
parent 275ef855fa
commit 00a7b888b2
6 changed files with 445 additions and 15 deletions
+18 -2
View File
@@ -567,6 +567,7 @@ pub struct SandboxState {
#[derive(Clone)]
pub struct McpRuntimeContext {
environment_manager: Arc<EnvironmentManager>,
selected_environments: HashMap<String, Arc<Environment>>,
local_process_cwd: PathBuf,
local_http_client: Arc<dyn HttpClient>,
}
@@ -610,11 +611,21 @@ impl McpRuntimeContext {
);
Self {
environment_manager,
selected_environments: HashMap::new(),
local_process_cwd,
local_http_client,
}
}
/// Pins the concrete environment handles captured for this thread or model step.
pub fn with_selected_environments(
mut self,
selected_environments: HashMap<String, Arc<Environment>>,
) -> Self {
self.selected_environments = selected_environments;
self
}
pub(crate) fn local_process_cwd(&self) -> PathBuf {
self.local_process_cwd.clone()
}
@@ -632,8 +643,13 @@ impl McpRuntimeContext {
// HTTP is the one current exception: it can use the ambient HTTP client
// even when no local Environment is configured.
if let Some(environment) = self
.environment_manager
.get_environment(&config.environment_id)
.selected_environments
.get(&config.environment_id)
.cloned()
.or_else(|| {
self.environment_manager
.get_environment(&config.environment_id)
})
{
return Ok(Some(environment));
}