codetether_agent/tool/
mcp_tools.rs1use super::{Tool, ToolResult};
4use anyhow::Result;
5use async_trait::async_trait;
6use serde_json::Value;
7use std::sync::Arc;
8
9pub struct McpToolManager {
11 client: Arc<crate::mcp::McpClient>,
12}
13
14impl McpToolManager {
15 pub async fn connect_subprocess(command: &str, args: &[&str]) -> Result<Self> {
17 let client = crate::mcp::McpClient::connect_subprocess(command, args).await?;
18 Ok(Self { client })
19 }
20
21 pub async fn wrappers(&self) -> Vec<McpToolWrapper> {
23 self.client
24 .tools()
25 .await
26 .into_iter()
27 .map(|tool| McpToolWrapper::new(Arc::clone(&self.client), tool))
28 .collect()
29 }
30
31 pub fn client(&self) -> Arc<crate::mcp::McpClient> {
33 Arc::clone(&self.client)
34 }
35}
36
37#[derive(Clone)]
39pub struct McpToolWrapper {
40 client: Arc<crate::mcp::McpClient>,
41 tool: crate::mcp::McpTool,
42 id: String,
43}
44
45impl McpToolWrapper {
46 pub fn new(client: Arc<crate::mcp::McpClient>, tool: crate::mcp::McpTool) -> Self {
47 let id = format!("mcp:{}", tool.name);
48 Self { client, tool, id }
49 }
50}
51
52#[async_trait]
53impl Tool for McpToolWrapper {
54 fn id(&self) -> &str {
55 &self.id
56 }
57
58 fn name(&self) -> &str {
59 &self.tool.name
60 }
61
62 fn description(&self) -> &str {
63 self.tool
64 .description
65 .as_deref()
66 .unwrap_or("Remote MCP tool")
67 }
68
69 fn parameters(&self) -> Value {
70 self.tool.input_schema.clone()
71 }
72
73 async fn execute(&self, args: Value) -> Result<ToolResult> {
74 let result = self.client.call_tool(&self.tool.name, args).await?;
75
76 let output = result
77 .content
78 .iter()
79 .map(|item| match item {
80 crate::mcp::ToolContent::Text { text } => text.clone(),
81 crate::mcp::ToolContent::Image { data, mime_type } => {
82 format!("[image: {} ({} bytes)]", mime_type, data.len())
83 }
84 crate::mcp::ToolContent::Resource { resource } => {
85 serde_json::to_string(resource).unwrap_or_default()
86 }
87 })
88 .collect::<Vec<_>>()
89 .join("\n");
90
91 if result.is_error {
92 Ok(ToolResult::error(output))
93 } else {
94 Ok(ToolResult::success(output))
95 }
96 }
97}