Skip to main content

crates_docs/server/
mod.rs

1//! Server module
2//!
3//! Provides MCP server implementation with support for multiple transport protocols.
4
5pub mod auth;
6pub mod handler;
7pub mod transport;
8
9use crate::cache::Cache;
10use crate::config::AppConfig;
11use crate::error::Result;
12use crate::tools::ToolRegistry;
13use rust_mcp_sdk::schema::{
14    Implementation, InitializeResult, ProtocolVersion, ServerCapabilities, ServerCapabilitiesTools,
15};
16use std::sync::Arc;
17
18/// Re-export `ServerConfig` from config module for backward compatibility
19pub use crate::config::ServerConfig;
20
21/// MCP server
22#[derive(Clone)]
23pub struct CratesDocsServer {
24    config: AppConfig,
25    tool_registry: Arc<ToolRegistry>,
26    cache: Arc<dyn Cache>,
27}
28
29impl CratesDocsServer {
30    /// Create server from parts (common initialization logic)
31    fn from_parts(config: AppConfig, cache: Arc<dyn Cache>) -> crate::error::Result<Self> {
32        // Create document service with cache configuration
33        let doc_service = Arc::new(crate::tools::docs::DocService::with_config(
34            cache.clone(),
35            &config.cache,
36        )?);
37
38        // Create tool registry
39        let tool_registry = Arc::new(crate::tools::create_default_registry(&doc_service));
40
41        Ok(Self {
42            config,
43            tool_registry,
44            cache,
45        })
46    }
47
48    /// Create a new server instance (synchronous)
49    ///
50    /// Note: This method only supports memory cache. For Redis, use the `new_async` method.
51    pub fn new(config: AppConfig) -> Result<Self> {
52        let cache_box: Box<dyn Cache> = crate::cache::create_cache(&config.cache)?;
53        let cache: Arc<dyn Cache> = Arc::from(cache_box);
54        Self::from_parts(config, cache)
55    }
56
57    /// Create a new server instance (asynchronous)
58    ///
59    /// Supports memory cache and Redis cache (requires cache-redis feature).
60    #[allow(unused_variables)]
61    #[allow(clippy::unused_async)]
62    pub async fn new_async(config: AppConfig) -> Result<Self> {
63        // Decide which creation method to use based on cache type and feature
64        #[cfg(feature = "cache-redis")]
65        {
66            let cache_box: Box<dyn Cache> = crate::cache::create_cache_async(&config.cache).await?;
67            let cache: Arc<dyn Cache> = Arc::from(cache_box);
68            Self::from_parts(config, cache)
69        }
70
71        #[cfg(not(feature = "cache-redis"))]
72        {
73            // No cache-redis feature, fall back to synchronous creation
74            let cache_box: Box<dyn Cache> = crate::cache::create_cache(&config.cache)?;
75            let cache: Arc<dyn Cache> = Arc::from(cache_box);
76            Self::from_parts(config, cache)
77        }
78    }
79
80    /// Get server configuration
81    #[must_use]
82    pub fn config(&self) -> &AppConfig {
83        &self.config
84    }
85
86    /// Get tool registry
87    #[must_use]
88    pub fn tool_registry(&self) -> &Arc<ToolRegistry> {
89        &self.tool_registry
90    }
91
92    /// Get cache
93    #[must_use]
94    pub fn cache(&self) -> &Arc<dyn Cache> {
95        &self.cache
96    }
97
98    /// Get server information
99    #[must_use]
100    pub fn server_info(&self) -> InitializeResult {
101        InitializeResult {
102            server_info: Implementation {
103                name: self.config.server.name.clone(),
104                version: self.config.server.version.clone(),
105                title: Some("Crates Docs MCP Server".to_string()),
106                description: self.config.server.description.clone(),
107                icons: self.config.server.icons.clone(),
108                website_url: self.config.server.website_url.clone(),
109            },
110            capabilities: ServerCapabilities {
111                tools: Some(ServerCapabilitiesTools { list_changed: None }),
112                resources: None,
113                prompts: None,
114                experimental: None,
115                completions: None,
116                logging: None,
117                tasks: None,
118            },
119            protocol_version: ProtocolVersion::V2025_11_25.into(),
120            instructions: Some(
121                "Use this server to query Rust crate documentation. Supports crate lookup, crate search, and health check."
122                    .to_string(),
123            ),
124            meta: None,
125        }
126    }
127
128    /// Run Stdio server
129    pub async fn run_stdio(&self) -> Result<()> {
130        transport::run_stdio_server(self).await
131    }
132
133    /// Run HTTP server
134    pub async fn run_http(&self) -> Result<()> {
135        transport::run_http_server(self).await
136    }
137
138    /// Run SSE server
139    pub async fn run_sse(&self) -> Result<()> {
140        transport::run_sse_server(self).await
141    }
142}