sdforge 0.3.1

Multi-protocol SDK framework with unified macro configuration
// Copyright (c) 2026 Kirky.X
// SPDX-License-Identifier: MIT
//! MCP server implementation built on the official rmcp SDK.
//!
//! This module provides MCP (Model Context Protocol) server functionality using
//! the official `rmcp` crate (v0.16+). It supports:
//!
//! - Compile-time tool registration via `inventory`
//! - Stateful and stateless server modes
//! - MCP 2026-07-28 protocol adaptation (headers, cache semantics, MRTR)
//! - Integration with SDForge's `#[service_api]` macro
//!
//! # Quick Start
//!
//! ```rust,ignore
//! use sdforge::mcp::{SdForgeTool, McpToolRegistration};
//! use sdforge::core::ApiMetadata;
//!
//! struct MyTool;
//! impl SdForgeTool for MyTool {
//!     fn name(&self) -> &str { "my_tool" }
//!     fn description(&self) -> &str { "Does something useful" }
//!     fn input_schema(&self) -> serde_json::Value {
//!         serde_json::json!({"type": "object"})
//!     }
//!     fn call(&self, input: Option<serde_json::Value>) -> Result<rmcp::model::CallToolResult, rmcp::model::ErrorData> {
//!         Ok(rmcp::model::CallToolResult::success(vec![
//!             rmcp::model::Content::text("Hello from my_tool".to_string()),
//!         ]))
//!     }
//! }
//!
//! inventory::submit!(McpToolRegistration::new(
//!     "my_tool",
//!     "v1",
//!     || std::sync::Arc::new(MyTool) as std::sync::Arc<dyn SdForgeTool>,
//!     || ApiMetadata::default(),
//! ));
//! ```

use crate::core::{ApiMetadata, Registration};
use crate::define_registration;
#[cfg(feature = "mcp")]
use rmcp::model::ErrorData;
use serde_json::Value;
use std::sync::Arc;

/// Type alias for rmcp's `ErrorData`, kept for API compatibility with the
/// `SdForgeTool` trait's `call` method return type.
///
/// In rmcp 0.16, the error type is `ErrorData` (not `McpError`). We re-export
/// it under the `McpError` name so existing code referencing `McpError` continues
/// to work without changes.
#[cfg(feature = "mcp")]
pub type McpError = ErrorData;

// ============================================================================
// Sub-modules
// ============================================================================

// Sub-modules for MCP 2026-07-28 protocol adaptation.
#[cfg(feature = "mcp")]
pub mod cache_semantics;
#[cfg(feature = "mcp")]
pub mod headers;
#[cfg(feature = "mcp")]
pub mod mrtr;
#[cfg(feature = "mcp")]
pub mod protocol;
#[cfg(feature = "mcp")]
pub mod stateless;

// Sub-modules for server and handler (extracted for maintainability).
#[cfg(feature = "mcp")]
mod handler;
#[cfg(feature = "mcp")]
mod server;

// Test module (only compiled in test builds).
#[cfg(test)]
#[cfg(feature = "mcp")]
mod tests;

// ============================================================================
// Re-exports of public types
// ============================================================================

#[cfg(feature = "mcp")]
pub use handler::McpToolInstance;
#[cfg(feature = "mcp")]
pub use headers::McpHeaderInfo;
#[cfg(feature = "mcp")]
pub use mrtr::{InputRequiredResult, MrtrSession};
#[cfg(feature = "mcp")]
pub use server::SdForgeMcpServer;
#[cfg(feature = "mcp")]
pub use stateless::StatelessServerHandler;

// ============================================================================
// SdForgeTool trait — bridges SDForge tools to rmcp's ServerHandler
// ============================================================================

/// Trait for MCP tools registered via SDForge's inventory system.
///
/// Each tool provides its name, description, JSON Schema for inputs, and a
/// `call` handler that returns a `CallToolResult`. This trait replaces the
/// old `mcp_sdk::tools::Tool` trait from the unmaintained 0.0.3 crate.
#[cfg(feature = "mcp")]
pub trait SdForgeTool: Send + Sync + 'static {
    /// The tool name (must be unique across all registered tools).
    fn name(&self) -> &str;

    /// Human-readable description of what the tool does.
    fn description(&self) -> &str;

    /// JSON Schema describing the tool's input parameters.
    fn input_schema(&self) -> Value;

    /// Execute the tool with optional JSON input.
    ///
    /// Returns `CallToolResult` on success or `McpError` on failure.
    fn call(&self, input: Option<Value>) -> Result<rmcp::model::CallToolResult, McpError>;
}

// ============================================================================
// McpToolRegistration — compile-time registration via inventory
// ============================================================================

// MCP tool registration for compile-time registration.
//
// Generated by `define_registration!` macro for the unified registration system.
// The instance type is `Arc<dyn SdForgeTool>` so tools can be shared across
// threads without copying.
#[cfg(feature = "mcp")]
define_registration!(McpToolRegistration, Arc<dyn SdForgeTool>, ApiMetadata);

#[cfg(not(feature = "mcp"))]
/// Stub struct for non-MCP builds
pub struct McpToolRegistration;

// ============================================================================
// get_mcp_tools — collect all registered tools from inventory
// ============================================================================

/// Get all registered MCP tools as runtime instances.
///
/// This function collects all `McpToolRegistration` entries from the
/// `inventory` registry and creates `McpToolInstance` objects with the
/// associated metadata.
#[cfg(feature = "mcp")]
pub fn get_mcp_tools() -> Vec<McpToolInstance> {
    inventory::iter::<McpToolRegistration>
        .into_iter()
        .map(|reg| {
            let tool = (reg.create_fn)();
            let reg_metadata = reg.metadata();
            McpToolInstance::new(
                tool,
                ApiMetadata::new(
                    reg.name().to_string(),
                    reg.version().to_string(),
                    reg.metadata().description().to_string(),
                    reg_metadata.cache_ttl(),
                    reg_metadata.is_streaming(),
                ),
            )
        })
        .collect()
}

// ============================================================================
// build — construct an MCP server ready to serve
// ============================================================================

/// Build a `SdForgeMcpServer` from registered tools.
///
/// This constructs a server that implements `rmcp::handler::server::ServerHandler`.
/// To start the server, use `ServiceExt::serve()` on the returned server with
/// a transport (e.g., `rmcp::transport::stdio()`).
///
/// # Example
///
/// ```rust,ignore
/// use rmcp::{ServiceExt, transport::stdio};
///
/// # #[tokio::main]
/// # async fn main() -> anyhow::Result<()> {
/// let server = sdforge::mcp::build();
/// let service = server.serve(stdio()).await?;
/// service.waiting().await?;
/// # Ok(())
/// # }
/// ```
#[cfg(feature = "mcp")]
pub fn build() -> SdForgeMcpServer {
    SdForgeMcpServer::new()
}