claude_agent/tools/
mcp.rs

1//! MCP tool wrapper for seamless integration with ToolRegistry.
2
3use std::sync::Arc;
4
5use async_trait::async_trait;
6use serde_json::Value;
7
8use super::{ExecutionContext, Tool};
9use crate::mcp::{McpManager, McpToolDefinition, make_mcp_name, parse_mcp_name};
10use crate::types::{ToolDefinition, ToolResult};
11
12/// Wrapper that adapts an MCP tool to the Tool trait.
13///
14/// This enables MCP tools to be registered in ToolRegistry and called
15/// through the standard tool execution pipeline.
16pub struct McpToolWrapper {
17    manager: Arc<McpManager>,
18    qualified_name: String,
19    server_name: String,
20    tool_name: String,
21    definition: McpToolDefinition,
22}
23
24impl McpToolWrapper {
25    pub fn new(
26        manager: Arc<McpManager>,
27        server_name: impl Into<String>,
28        tool: McpToolDefinition,
29    ) -> Self {
30        let server_name = server_name.into();
31        let qualified_name = make_mcp_name(&server_name, &tool.name);
32        Self {
33            manager,
34            qualified_name,
35            server_name,
36            tool_name: tool.name.clone(),
37            definition: tool,
38        }
39    }
40
41    pub fn qualified_name(&self) -> &str {
42        &self.qualified_name
43    }
44
45    pub fn server_name(&self) -> &str {
46        &self.server_name
47    }
48
49    pub fn tool_name(&self) -> &str {
50        &self.tool_name
51    }
52}
53
54#[async_trait]
55impl Tool for McpToolWrapper {
56    fn name(&self) -> &str {
57        &self.qualified_name
58    }
59
60    fn description(&self) -> &str {
61        &self.definition.description
62    }
63
64    fn input_schema(&self) -> Value {
65        self.definition.input_schema.clone()
66    }
67
68    fn definition(&self) -> ToolDefinition {
69        ToolDefinition::new(
70            &self.qualified_name,
71            &self.definition.description,
72            self.definition.input_schema.clone(),
73        )
74    }
75
76    async fn execute(&self, input: Value, _context: &ExecutionContext) -> ToolResult {
77        match self.manager.call_tool(&self.qualified_name, input).await {
78            Ok(result) => {
79                if result.is_error {
80                    ToolResult::error(result.to_string_content())
81                } else {
82                    ToolResult::success(result.to_string_content())
83                }
84            }
85            Err(e) => ToolResult::error(e.to_string()),
86        }
87    }
88}
89
90/// Creates MCP tool wrappers for all tools from an McpManager.
91pub async fn create_mcp_tools(manager: Arc<McpManager>) -> Vec<Arc<dyn Tool>> {
92    let tools_list = manager.list_tools().await;
93    let mut wrappers: Vec<Arc<dyn Tool>> = Vec::with_capacity(tools_list.len());
94
95    for (qualified_name, definition) in tools_list {
96        if let Some((server, _tool)) = parse_mcp_name(&qualified_name) {
97            let wrapper = McpToolWrapper::new(Arc::clone(&manager), server, definition);
98            wrappers.push(Arc::new(wrapper));
99        }
100    }
101
102    wrappers
103}