use super::registry::Tool;
use super::schema::{ParamType, ToolParameter, ToolSchema};
use crate::error::Result;
use crate::mcp::McpManager;
use serde_json::Value;
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
pub struct ListMcpToolsTool {
pub mgr: Arc<McpManager>,
}
#[async_trait::async_trait]
impl Tool for ListMcpToolsTool {
fn schema(&self) -> ToolSchema {
ToolSchema::new(
"list_mcp_tools",
"List all available MCP tools from configured servers.",
vec![],
)
}
async fn call(&self, _args: &Value, _c: &AtomicBool) -> Result<String> {
let mgr = self.mgr.clone();
tokio::task::spawn_blocking(move || Ok(mgr.list_tools().to_string())).await.map_err(|e| crate::error::AacodeError::Other(format!("{e}")))?
}
}
pub struct CallMcpToolTool {
pub mgr: Arc<McpManager>,
}
#[async_trait::async_trait]
impl Tool for CallMcpToolTool {
fn schema(&self) -> ToolSchema {
ToolSchema::new(
"call_mcp_tool",
"Call an MCP tool. tool_name is 'server.tool' or just 'tool'.",
vec![
ToolParameter::new("tool_name", ParamType::String, true, "server.tool or tool", &["tool", "name", "function"]),
ToolParameter::new("arguments", ParamType::Object, false, "Tool arguments", &["args", "params", "input"]),
],
)
}
async fn call(&self, args: &Value, _c: &AtomicBool) -> Result<String> {
let name = args.get("tool_name").and_then(|v| v.as_str()).unwrap_or("").to_string();
let arguments = args.get("arguments").cloned().unwrap_or(serde_json::json!({}));
let mgr = self.mgr.clone();
tokio::task::spawn_blocking(move || Ok(mgr.call_tool(&name, arguments).to_string())).await.map_err(|e| crate::error::AacodeError::Other(format!("{e}")))?
}
}
pub struct McpStatusTool {
pub mgr: Arc<McpManager>,
}
#[async_trait::async_trait]
impl Tool for McpStatusTool {
fn schema(&self) -> ToolSchema {
ToolSchema::new("get_mcp_status", "Get MCP server status.", vec![])
}
async fn call(&self, _args: &Value, _c: &AtomicBool) -> Result<String> {
let mgr = self.mgr.clone();
tokio::task::spawn_blocking(move || Ok(mgr.status().to_string())).await.map_err(|e| crate::error::AacodeError::Other(format!("{e}")))?
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[tokio::test]
async fn status_tool_reports_empty() {
let mgr = Arc::new(McpManager::new(vec![], 5));
let t = McpStatusTool { mgr };
let cancel = AtomicBool::new(false);
let out = t.call(&json!({}), &cancel).await.unwrap();
let v: Value = serde_json::from_str(&out).unwrap();
assert_eq!(v["total_count"], 0);
}
#[tokio::test]
async fn list_tool_empty() {
let mgr = Arc::new(McpManager::new(vec![], 5));
let t = ListMcpToolsTool { mgr };
let cancel = AtomicBool::new(false);
let out = t.call(&json!({}), &cancel).await.unwrap();
assert!(out.contains("\"count\":0"));
}
}