use std::sync::Arc;
use serde_json::Value;
use crate::protocol::McpToolDefinition;
use crate::tool::{BoxFuture, DynTool, McpTool, ToolCallResult, ToolProvider};
pub trait MacroServer: Send + Sync {
fn list_tools(&self) -> Vec<McpToolDefinition>;
fn call_tool(&self, name: &str, args: Value) -> ToolCallResult;
}
pub struct MacroServerAdapter<T: MacroServer> {
server: Arc<T>,
}
impl<T: MacroServer + 'static> MacroServerAdapter<T> {
pub fn new(server: T) -> Self {
Self {
server: Arc::new(server),
}
}
}
impl<T: MacroServer + 'static> ToolProvider for MacroServerAdapter<T> {
fn tools(&self) -> Vec<DynTool> {
self.server
.list_tools()
.into_iter()
.map(|tool_def| {
let wrapper = MacroToolWrapper {
definition: tool_def.clone(),
server: self.server.clone(),
};
Arc::new(wrapper) as DynTool
})
.collect()
}
}
struct MacroToolWrapper<T: MacroServer> {
definition: McpToolDefinition,
server: Arc<T>,
}
impl<T: MacroServer + 'static> McpTool for MacroToolWrapper<T> {
fn definition(&self) -> McpToolDefinition {
self.definition.clone()
}
fn call<'a>(&'a self, args: Value) -> BoxFuture<'a, ToolCallResult> {
let name = self.definition.name.clone();
Box::pin(async move { self.server.call_tool(&name, args) })
}
}
#[cfg(test)]
mod tests {
use super::*;
struct TestServer;
impl MacroServer for TestServer {
fn list_tools(&self) -> Vec<McpToolDefinition> {
vec![McpToolDefinition::new("test")
.with_description("Test tool")
.with_schema(serde_json::json!({"type": "object"}))]
}
fn call_tool(&self, name: &str, _args: Value) -> ToolCallResult {
if name == "test" {
Ok(vec![crate::protocol::ToolContent::text("ok")])
} else {
Err(format!("Unknown: {}", name))
}
}
}
#[test]
fn test_adapter_provides_tools() {
let adapter = MacroServerAdapter::new(TestServer);
let tools = adapter.tools();
assert_eq!(tools.len(), 1);
assert_eq!(tools[0].definition().name, "test");
}
}