use std::collections::HashMap; use rig::tool::{ToolDyn as RigTool, ToolEmbeddingDyn, ToolSet}; use rmcp::{ RoleClient, model::{CallToolRequestParam, CallToolResult, Tool as McpTool}, service::{RunningService, ServerSink}, }; pub struct McpToolAdaptor { tool: McpTool, server: ServerSink, } impl RigTool for McpToolAdaptor { fn name(&self) -> String { self.tool.name.to_string() } fn definition( &self, _prompt: String, ) -> std::pin::Pin + Send + Sync + '_>> { Box::pin(std::future::ready(rig::completion::ToolDefinition { name: self.name(), description: self .tool .description .as_deref() .unwrap_or_default() .to_string(), parameters: self.tool.schema_as_json_value(), })) } fn call( &self, args: String, ) -> std::pin::Pin< Box> + Send + Sync + '_>, > { let server = self.server.clone(); Box::pin(async move { let call_mcp_tool_result = server .call_tool(CallToolRequestParam { name: self.tool.name.clone(), arguments: serde_json::from_str(&args) .map_err(rig::tool::ToolError::JsonError)?, }) .await .inspect(|result| tracing::info!(?result)) .inspect_err(|error| tracing::error!(%error)) .map_err(|e| rig::tool::ToolError::ToolCallError(Box::new(e)))?; Ok(convert_mcp_call_tool_result_to_string(call_mcp_tool_result)) }) } } impl ToolEmbeddingDyn for McpToolAdaptor { fn context(&self) -> serde_json::Result { serde_json::to_value(self.tool.clone()) } fn embedding_docs(&self) -> Vec { vec![ self.tool .description .as_deref() .unwrap_or_default() .to_string(), ] } } pub struct McpManager { pub clients: HashMap>, } impl McpManager { pub async fn get_tool_set(&self) -> anyhow::Result { let mut tool_set = ToolSet::default(); let mut task = tokio::task::JoinSet::>::new(); for client in self.clients.values() { let server = client.peer().clone(); task.spawn(get_tool_set(server)); } let results = task.join_all().await; for result in results { match result { Err(e) => { tracing::error!(error = %e, "Failed to get tool set"); } Ok(tools) => { tool_set.add_tools(tools); } } } Ok(tool_set) } } pub fn convert_mcp_call_tool_result_to_string(result: CallToolResult) -> String { serde_json::to_string(&result).unwrap() } pub async fn get_tool_set(server: ServerSink) -> anyhow::Result { let tools = server.list_all_tools().await?; let mut tool_builder = ToolSet::builder(); for tool in tools { tracing::info!("get tool: {}", tool.name); let adaptor = McpToolAdaptor { tool: tool.clone(), server: server.clone(), }; tool_builder = tool_builder.dynamic_tool(adaptor); } let tool_set = tool_builder.build(); Ok(tool_set) }