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 a new server instance (synchronous)
31    ///
32    /// Note: This method only supports memory cache. For Redis, use the `new_async` method.
33    pub fn new(config: AppConfig) -> Result<Self> {
34        let cache_box: Box<dyn Cache> = crate::cache::create_cache(&config.cache)?;
35        let cache: Arc<dyn Cache> = Arc::from(cache_box);
36
37        // Create document service
38        let doc_service = Arc::new(crate::tools::docs::DocService::new(cache.clone()));
39
40        // Create tool registry
41        let tool_registry = Arc::new(crate::tools::create_default_registry(&doc_service));
42
43        Ok(Self {
44            config,
45            tool_registry,
46            cache,
47        })
48    }
49
50    /// Create a new server instance (asynchronous)
51    ///
52    /// Supports memory cache and Redis cache (requires cache-redis feature).
53    #[allow(unused_variables)]
54    #[allow(clippy::unused_async)]
55    pub async fn new_async(config: AppConfig) -> Result<Self> {
56        // Decide which creation method to use based on cache type and feature
57        #[cfg(feature = "cache-redis")]
58        {
59            let cache_box: Box<dyn Cache> = crate::cache::create_cache_async(&config.cache).await?;
60            let cache: Arc<dyn Cache> = Arc::from(cache_box);
61
62            // Create document service
63            let doc_service = Arc::new(crate::tools::docs::DocService::new(cache.clone()));
64
65            // Create tool registry
66            let tool_registry = Arc::new(crate::tools::create_default_registry(&doc_service));
67
68            Ok(Self {
69                config,
70                tool_registry,
71                cache,
72            })
73        }
74
75        #[cfg(not(feature = "cache-redis"))]
76        {
77            // No cache-redis feature, fall back to synchronous creation
78            let cache_box: Box<dyn Cache> = crate::cache::create_cache(&config.cache)?;
79            let cache: Arc<dyn Cache> = Arc::from(cache_box);
80
81            // Create document service
82            let doc_service = Arc::new(crate::tools::docs::DocService::new(cache.clone()));
83
84            // Create tool registry
85            let tool_registry = Arc::new(crate::tools::create_default_registry(&doc_service));
86
87            Ok(Self {
88                config,
89                tool_registry,
90                cache,
91            })
92        }
93    }
94
95    /// Get server configuration
96    #[must_use]
97    pub fn config(&self) -> &AppConfig {
98        &self.config
99    }
100
101    /// Get tool registry
102    #[must_use]
103    pub fn tool_registry(&self) -> &Arc<ToolRegistry> {
104        &self.tool_registry
105    }
106
107    /// Get cache
108    #[must_use]
109    pub fn cache(&self) -> &Arc<dyn Cache> {
110        &self.cache
111    }
112
113    /// Get server information
114    #[must_use]
115    pub fn server_info(&self) -> InitializeResult {
116        InitializeResult {
117            server_info: Implementation {
118                name: self.config.server.name.clone(),
119                version: self.config.server.version.clone(),
120                title: Some("Crates Docs MCP Server".to_string()),
121                description: self.config.server.description.clone(),
122                icons: self.config.server.icons.clone(),
123                website_url: self.config.server.website_url.clone(),
124            },
125            capabilities: ServerCapabilities {
126                tools: Some(ServerCapabilitiesTools { list_changed: None }),
127                resources: None,
128                prompts: None,
129                experimental: None,
130                completions: None,
131                logging: None,
132                tasks: None,
133            },
134            protocol_version: ProtocolVersion::V2025_11_25.into(),
135            instructions: Some(
136                "Use this server to query Rust crate documentation. Supports crate lookup, crate search, and health check."
137                    .to_string(),
138            ),
139            meta: None,
140        }
141    }
142
143    /// Run Stdio server
144    pub async fn run_stdio(&self) -> Result<()> {
145        transport::run_stdio_server(self).await
146    }
147
148    /// Run HTTP server
149    pub async fn run_http(&self) -> Result<()> {
150        transport::run_http_server(self).await
151    }
152
153    /// Run SSE server
154    pub async fn run_sse(&self) -> Result<()> {
155        transport::run_sse_server(self).await
156    }
157}