model-context-protocol 0.2.2

A Rust implementation of the Model Context Protocol (MCP) for AI tool integration
Documentation
//! Adapter for macro-generated MCP servers.
//!
//! This module provides `MacroServerAdapter` which wraps types generated by
//! `#[mcp_server]` macros and implements the `ToolProvider` trait.

use std::sync::Arc;

use serde_json::Value;

use crate::protocol::McpToolDefinition;
use crate::tool::{BoxFuture, DynTool, McpTool, ToolCallResult, ToolProvider};

/// Trait for types generated by `#[mcp_server]` macros.
///
/// This is automatically implemented when you use `#[mcp_server]` on an impl block.
pub trait MacroServer: Send + Sync {
    /// List available tools.
    fn list_tools(&self) -> Vec<McpToolDefinition>;

    /// Call a tool by name.
    fn call_tool(&self, name: &str, args: Value) -> ToolCallResult;
}

/// Adapter that wraps a macro-generated server and implements `ToolProvider`.
///
/// # Example
///
/// ```ignore
/// use mcp::{MacroServerAdapter, McpServer};
///
/// #[mcp_server(name = "calculator")]
/// pub struct Calculator;
///
/// #[mcp_server]
/// impl Calculator {
///     #[mcp_tool(description = "Add two numbers")]
///     fn add(&self, a: f64, b: f64) -> f64 {
///         a + b
///     }
/// }
///
/// let server = McpServer::builder()
///     .name("calculator")
///     .version("1.0.0")
///     .with_tools_from(MacroServerAdapter::new(Calculator))
///     .build();
/// ```
pub struct MacroServerAdapter<T: MacroServer> {
    server: Arc<T>,
}

impl<T: MacroServer + 'static> MacroServerAdapter<T> {
    /// Create a new adapter for a macro-generated server.
    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()
    }
}

/// Internal wrapper that implements `McpTool` for a single tool from a macro server.
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");
    }
}