reasonkit-core 0.1.8

The Reasoning Engine — Auditable Reasoning for Production AI | Rust-Native | Turn Prompts into Protocols
//! Official MCP SDK Integration
//!
//! This module provides integration with the official rmcp SDK for
//! Model Context Protocol implementation.
//!
//! # Features
//! - Full MCP spec compliance (OAuth, streamable HTTP)
//! - Multiple transports (stdio, SSE, WebSocket, HTTP)
//! - Production-ready with 453K+ monthly downloads
//!
//! Enable with: `cargo build --features mcp-official`

use serde::{Deserialize, Serialize};

// Re-export rmcp for direct access
pub use rmcp;

/// MCP server configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpServerConfig {
    /// Server name for identification
    pub name: String,
    /// Server version
    pub version: String,
    /// Transport type
    pub transport: McpTransport,
    /// Enable OAuth authentication
    pub oauth_enabled: bool,
}

/// Supported MCP transports
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum McpTransport {
    /// Standard I/O (for CLI tools)
    Stdio,
    /// Server-Sent Events (for web clients)
    Sse { endpoint: String },
    /// WebSocket (for real-time bidirectional)
    WebSocket { url: String },
    /// HTTP (for REST-style)
    Http { base_url: String },
}

impl Default for McpServerConfig {
    fn default() -> Self {
        Self {
            name: "reasonkit-mcp".to_string(),
            version: env!("CARGO_PKG_VERSION").to_string(),
            transport: McpTransport::Stdio,
            oauth_enabled: false,
        }
    }
}

/// MCP tool definition for ThinkTools
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpToolDefinition {
    /// Tool name (must be unique)
    pub name: String,
    /// Tool description for LLM
    pub description: String,
    /// JSON Schema for input parameters
    pub input_schema: serde_json::Value,
}

/// MCP resource definition
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpResourceDefinition {
    /// Resource URI
    pub uri: String,
    /// Resource name
    pub name: String,
    /// MIME type
    pub mime_type: Option<String>,
    /// Resource description
    pub description: Option<String>,
}

/// Builder for MCP tool definitions
pub struct McpToolBuilder {
    name: String,
    description: String,
    properties: serde_json::Map<String, serde_json::Value>,
    required: Vec<String>,
}

impl McpToolBuilder {
    /// Create a new tool builder
    pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
        Self {
            name: name.into(),
            description: description.into(),
            properties: serde_json::Map::new(),
            required: Vec::new(),
        }
    }

    /// Add a string parameter
    pub fn string_param(
        mut self,
        name: impl Into<String>,
        description: impl Into<String>,
        required: bool,
    ) -> Self {
        let name = name.into();
        self.properties.insert(
            name.clone(),
            serde_json::json!({
                "type": "string",
                "description": description.into()
            }),
        );
        if required {
            self.required.push(name);
        }
        self
    }

    /// Add a number parameter
    pub fn number_param(
        mut self,
        name: impl Into<String>,
        description: impl Into<String>,
        required: bool,
    ) -> Self {
        let name = name.into();
        self.properties.insert(
            name.clone(),
            serde_json::json!({
                "type": "number",
                "description": description.into()
            }),
        );
        if required {
            self.required.push(name);
        }
        self
    }

    /// Add a boolean parameter
    pub fn bool_param(
        mut self,
        name: impl Into<String>,
        description: impl Into<String>,
        required: bool,
    ) -> Self {
        let name = name.into();
        self.properties.insert(
            name.clone(),
            serde_json::json!({
                "type": "boolean",
                "description": description.into()
            }),
        );
        if required {
            self.required.push(name);
        }
        self
    }

    /// Build the tool definition
    pub fn build(self) -> McpToolDefinition {
        McpToolDefinition {
            name: self.name,
            description: self.description,
            input_schema: serde_json::json!({
                "type": "object",
                "properties": self.properties,
                "required": self.required
            }),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_tool_builder() {
        let tool = McpToolBuilder::new("search", "Search for information")
            .string_param("query", "Search query", true)
            .number_param("limit", "Max results", false)
            .build();

        assert_eq!(tool.name, "search");
        assert!(tool.input_schema["required"]
            .as_array()
            .unwrap()
            .contains(&serde_json::json!("query")));
    }

    #[test]
    fn test_config_default() {
        let config = McpServerConfig::default();
        assert_eq!(config.name, "reasonkit-mcp");
        assert!(!config.oauth_enabled);
    }
}