#[cfg(test)]
#[path = "manager_test.rs"]
mod tests;
use super::{CallToolResult, Tool};
use super::{McpClient, client::Client};
use crate::app::Initializer;
use crate::config::McpServerConfig;
use crate::task_success;
use eyre::{Context, Result};
use std::{collections::HashMap, sync::Arc};
#[derive(Default)]
pub struct Manager {
tools: HashMap<Tool, Arc<dyn McpClient>>, }
impl Manager {
pub async fn from(mut self, servers: &[McpServerConfig]) -> Result<Self> {
for server in servers.iter().filter(|s| s.enabled.unwrap_or(true)) {
Initializer::add_task(
format!("init_mcp_{}", server.provider).as_str(),
format!("Initializing MCP server \"{}\"", server.provider).as_str(),
);
let client = Client::new(&server.provider, &server.server)
.await
.wrap_err("creating client")?;
self.add_server(Arc::new(client)).await?;
task_success!(format!("init_mcp_{}", server.provider).as_str())
}
Ok(self)
}
pub async fn add_server(&mut self, client: Arc<dyn McpClient>) -> Result<()> {
client
.list_tools()
.await
.wrap_err("listing tools")?
.into_iter()
.for_each(|tool| {
if let Some((k, _)) = self.tools.get_key_value(&tool) {
let k = k.clone();
if k.description.as_deref().unwrap_or_default().len()
> tool.description.as_deref().unwrap_or_default().len()
{
return;
}
self.tools.remove(&k);
}
self.tools.insert(tool, client.clone());
});
Ok(())
}
}
#[async_trait::async_trait]
impl McpClient for Manager {
async fn list_tools(&self) -> Result<Vec<Tool>> {
Ok(self.tools.keys().cloned().collect::<Vec<_>>())
}
async fn call_tool(
&self,
tool: &str,
args: Option<serde_json::Value>,
) -> Result<CallToolResult> {
let client = self
.tools
.iter()
.find(|(k, _)| k.name.as_str() == tool)
.ok_or_else(|| eyre::eyre!("tool {} not found", tool))?
.1
.clone();
Ok(client.call_tool(tool, args).await?)
}
async fn shutdown(&self) -> Result<()> {
for client in self.tools.values() {
if let Err(e) = client.shutdown().await {
log::error!("Error shutting down client: {}", e);
}
}
Ok(())
}
}