use {
crate::mcp::client::McpClient,
crate::mcp::settings::McpServerConfig,
crate::mcp::types::{McpServerInfo, McpServerStatus, McpTool, McpToolResult},
anyhow::{Result, anyhow},
serde_json::Value,
std::collections::HashMap,
tracing::{info, warn},
};
pub struct McpManager {
clients: HashMap<String, McpClient>,
configs: HashMap<String, McpServerConfig>,
}
impl McpManager {
pub fn new(
servers: HashMap<String, McpServerConfig>,
allowed: Vec<String>,
excluded: Vec<String>,
) -> Self {
let configs: HashMap<String, McpServerConfig> = servers
.into_iter()
.filter(|(name, _)| {
let is_excluded = excluded.contains(name);
let is_allowed = allowed.is_empty() || allowed.contains(name);
!is_excluded && is_allowed
})
.collect();
let clients = configs
.keys()
.map(|name| (name.clone(), McpClient::new(name.clone())))
.collect();
Self { clients, configs }
}
pub fn connect_all(&mut self) {
let names: Vec<String> = self.configs.keys().cloned().collect();
for name in names {
if let Some(config) = self.configs.get(&name).cloned()
&& let Some(client) = self.clients.get_mut(&name)
{
match client.connect(&config) {
Ok(_) => info!(
"MCP server '{}' connected ({} tools)",
name,
client.tools.len()
),
Err(e) => warn!("MCP server '{}' failed to connect: {}", name, e),
}
}
}
}
pub fn server_infos(&self) -> Vec<McpServerInfo> {
self.clients
.values()
.map(|c| {
let desc = self
.configs
.get(&c.name)
.and_then(|cfg| cfg.description.clone())
.unwrap_or_default();
c.to_server_info(&desc)
})
.collect()
}
pub fn server_info(&self, name: &str) -> Option<McpServerInfo> {
self.clients.get(name).map(|c| {
let desc = self
.configs
.get(name)
.and_then(|cfg| cfg.description.clone())
.unwrap_or_default();
c.to_server_info(&desc)
})
}
pub fn all_tools(&self) -> Vec<McpTool> {
self.clients
.values()
.filter(|c| c.status == McpServerStatus::Connected)
.flat_map(|c| c.tools.clone())
.collect()
}
pub fn find_tool(&self, fqn: &str) -> Option<(&str, &McpTool)> {
for (name, client) in &self.clients {
if let Some(tool) = client.tools.iter().find(|t| t.fqn == fqn) {
return Some((name.as_str(), tool));
}
}
None
}
pub fn call_tool(&mut self, fqn: &str, args: HashMap<String, Value>) -> Result<McpToolResult> {
let server_name = self
.clients
.iter()
.find(|(_, c)| c.tools.iter().any(|t| t.fqn == fqn))
.map(|(name, _)| name.clone())
.ok_or_else(|| anyhow!("No MCP server found for tool FQN: {fqn}"))?;
let tool_name = self
.clients
.get(&server_name)
.and_then(|c| c.tools.iter().find(|t| t.fqn == fqn))
.map(|t| t.name.clone())
.ok_or_else(|| anyhow!("Tool not found: {fqn}"))?;
self.clients
.get_mut(&server_name)
.ok_or_else(|| anyhow!("Client not found for server: {server_name}"))?
.call_tool(&tool_name, args)
}
pub fn connected_count(&self) -> usize {
self.clients
.values()
.filter(|c| c.status == McpServerStatus::Connected)
.count()
}
pub fn total_count(&self) -> usize {
self.clients.len()
}
pub fn configs(&self) -> &HashMap<String, McpServerConfig> {
&self.configs
}
}