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"));
}
}