use std::collections::BTreeMap;
use crate::cm_config::AgentConfig;
use crate::cm_mcp::resolve::{ResolvedMcpConfig, ResolvedMcpServer};
use crate::cm_internal::user_data::{load_mcp_servers_with_legacy_import, read_secret_mcp_bearer};
pub fn resolve_mcp_config(cfg: &AgentConfig) -> ResolvedMcpConfig {
let file = load_mcp_servers_with_legacy_import(
cfg.mcp_client.mcp_enabled,
cfg.mcp_client.mcp_command.trim(),
cfg.mcp_client.mcp_tool_timeout_secs,
);
let tool_timeout_secs = if file.tool_timeout_secs > 0 {
file.tool_timeout_secs
} else {
cfg.mcp_client.mcp_tool_timeout_secs.max(1)
};
ResolvedMcpConfig {
global_enabled: file.global_enabled,
tool_timeout_secs,
servers: file
.servers
.into_iter()
.map(|s| {
let mut headers = s.headers;
merge_mcp_bearer_header(&s.id, &mut headers);
ResolvedMcpServer {
id: s.id,
name: s.name,
slug: s.slug,
command: s.command,
args: s.args,
env: s.env,
cwd: s.cwd,
url: s.url,
headers,
enabled: s.enabled,
}
})
.collect(),
}
}
fn merge_mcp_bearer_header(server_id: &str, headers: &mut BTreeMap<String, String>) {
let Some(token) = read_secret_mcp_bearer(server_id) else {
return;
};
let token = token.trim();
if token.is_empty() {
return;
}
headers.retain(|k, _| !k.eq_ignore_ascii_case("authorization"));
headers.insert("Authorization".to_string(), format!("Bearer {token}"));
}
#[cfg(test)]
mod tests {
use super::merge_mcp_bearer_header;
use std::collections::BTreeMap;
#[test]
fn merge_bearer_skips_when_secret_absent() {
let mut headers = BTreeMap::new();
headers.insert("X-Custom".into(), "1".into());
merge_mcp_bearer_header("mcp_no_such_secret_id_zzzz", &mut headers);
assert_eq!(headers.len(), 1);
assert!(!headers.contains_key("Authorization"));
}
}