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::error::Result;
11use crate::tools::ToolRegistry;
12use rust_mcp_sdk::schema::{
13    Icon, IconTheme, Implementation, InitializeResult, ProtocolVersion, ServerCapabilities,
14    ServerCapabilitiesTools,
15};
16use std::sync::Arc;
17
18/// Server configuration
19#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
20pub struct ServerConfig {
21    /// Server name
22    pub name: String,
23
24    /// Server version
25    pub version: String,
26
27    /// Server description
28    pub description: Option<String>,
29
30    /// Server icons
31    pub icons: Vec<Icon>,
32
33    /// Website URL
34    pub website_url: Option<String>,
35
36    /// Host address
37    pub host: String,
38
39    /// Port
40    pub port: u16,
41
42    /// Transport mode
43    pub transport_mode: String,
44
45    /// Enable SSE support
46    pub enable_sse: bool,
47
48    /// Enable OAuth authentication
49    pub enable_oauth: bool,
50
51    /// Maximum concurrent connections
52    pub max_connections: usize,
53
54    /// Request timeout (seconds)
55    pub request_timeout_secs: u64,
56
57    /// Response timeout (seconds)
58    pub response_timeout_secs: u64,
59
60    /// Allowed hosts for CORS (e.g., `["localhost", "127.0.0.1"]`)
61    pub allowed_hosts: Vec<String>,
62
63    /// Allowed origins for CORS (e.g., `["http://localhost:*"]`)
64    /// Use `"*"` only in development, specify exact origins in production
65    pub allowed_origins: Vec<String>,
66
67    /// Cache configuration
68    pub cache: crate::cache::CacheConfig,
69
70    /// OAuth configuration
71    pub oauth: crate::server::auth::OAuthConfig,
72
73    /// Logging configuration
74    pub logging: crate::config::LoggingConfig,
75
76    /// Performance configuration
77    pub performance: crate::config::PerformanceConfig,
78}
79
80impl Default for ServerConfig {
81    fn default() -> Self {
82        Self {
83            name: "crates-docs".to_string(),
84            version: crate::VERSION.to_string(),
85            description: Some(
86                "High-performance Rust crate documentation query MCP server".to_string(),
87            ),
88            icons: vec![
89                Icon {
90                    src: "https://docs.rs/static/favicon-32x32.png".to_string(),
91                    mime_type: Some("image/png".to_string()),
92                    sizes: vec!["32x32".to_string()],
93                    theme: Some(IconTheme::Light),
94                },
95                Icon {
96                    src: "https://docs.rs/static/favicon-32x32.png".to_string(),
97                    mime_type: Some("image/png".to_string()),
98                    sizes: vec!["32x32".to_string()],
99                    theme: Some(IconTheme::Dark),
100                },
101            ],
102            website_url: Some("https://github.com/KingingWang/crates-docs".to_string()),
103            host: "127.0.0.1".to_string(),
104            port: 8080,
105            transport_mode: "hybrid".to_string(),
106            enable_sse: true,
107            enable_oauth: false,
108            max_connections: 100,
109            request_timeout_secs: 30,
110            response_timeout_secs: 60,
111            // Secure defaults: only allow localhost by default
112            allowed_hosts: vec!["localhost".to_string(), "127.0.0.1".to_string()],
113            allowed_origins: vec!["http://localhost:*".to_string()],
114            cache: crate::cache::CacheConfig::default(),
115            oauth: crate::server::auth::OAuthConfig::default(),
116            logging: crate::config::LoggingConfig::default(),
117            performance: crate::config::PerformanceConfig::default(),
118        }
119    }
120}
121
122/// MCP server
123#[derive(Clone)]
124pub struct CratesDocsServer {
125    config: ServerConfig,
126    tool_registry: Arc<ToolRegistry>,
127    cache: Arc<dyn Cache>,
128}
129
130impl CratesDocsServer {
131    /// Create a new server instance (synchronous)
132    ///
133    /// Note: This method only supports memory cache. For Redis, use the `new_async` method.
134    pub fn new(config: ServerConfig) -> Result<Self> {
135        let cache_box: Box<dyn Cache> = crate::cache::create_cache(&config.cache)?;
136        let cache: Arc<dyn Cache> = Arc::from(cache_box);
137
138        // Create document service
139        let doc_service = Arc::new(crate::tools::docs::DocService::new(cache.clone()));
140
141        // Create tool registry
142        let tool_registry = Arc::new(crate::tools::create_default_registry(&doc_service));
143
144        Ok(Self {
145            config,
146            tool_registry,
147            cache,
148        })
149    }
150
151    /// Create a new server instance (asynchronous)
152    ///
153    /// Supports memory cache and Redis cache (requires cache-redis feature).
154    #[allow(unused_variables)]
155    #[allow(clippy::unused_async)]
156    pub async fn new_async(config: ServerConfig) -> Result<Self> {
157        // Decide which creation method to use based on cache type and feature
158        #[cfg(feature = "cache-redis")]
159        {
160            let cache_box: Box<dyn Cache> = crate::cache::create_cache_async(&config.cache).await?;
161            let cache: Arc<dyn Cache> = Arc::from(cache_box);
162
163            // Create document service
164            let doc_service = Arc::new(crate::tools::docs::DocService::new(cache.clone()));
165
166            // Create tool registry
167            let tool_registry = Arc::new(crate::tools::create_default_registry(&doc_service));
168
169            Ok(Self {
170                config,
171                tool_registry,
172                cache,
173            })
174        }
175
176        #[cfg(not(feature = "cache-redis"))]
177        {
178            // No cache-redis feature, fall back to synchronous creation
179            let cache_box: Box<dyn Cache> = crate::cache::create_cache(&config.cache)?;
180            let cache: Arc<dyn Cache> = Arc::from(cache_box);
181
182            // Create document service
183            let doc_service = Arc::new(crate::tools::docs::DocService::new(cache.clone()));
184
185            // Create tool registry
186            let tool_registry = Arc::new(crate::tools::create_default_registry(&doc_service));
187
188            Ok(Self {
189                config,
190                tool_registry,
191                cache,
192            })
193        }
194    }
195
196    /// Get server configuration
197    #[must_use]
198    pub fn config(&self) -> &ServerConfig {
199        &self.config
200    }
201
202    /// Get tool registry
203    #[must_use]
204    pub fn tool_registry(&self) -> &Arc<ToolRegistry> {
205        &self.tool_registry
206    }
207
208    /// Get cache
209    #[must_use]
210    pub fn cache(&self) -> &Arc<dyn Cache> {
211        &self.cache
212    }
213
214    /// Get server information
215    #[must_use]
216    pub fn server_info(&self) -> InitializeResult {
217        InitializeResult {
218            server_info: Implementation {
219                name: self.config.name.clone(),
220                version: self.config.version.clone(),
221                title: Some("Crates Docs MCP Server".to_string()),
222                description: self.config.description.clone(),
223                icons: self.config.icons.clone(),
224                website_url: self.config.website_url.clone(),
225            },
226            capabilities: ServerCapabilities {
227                tools: Some(ServerCapabilitiesTools { list_changed: None }),
228                resources: None,
229                prompts: None,
230                experimental: None,
231                completions: None,
232                logging: None,
233                tasks: None,
234            },
235            protocol_version: ProtocolVersion::V2025_11_25.into(),
236            instructions: Some(
237                "Use this server to query Rust crate documentation. Supports crate lookup, crate search, and health check."
238                    .to_string(),
239            ),
240            meta: None,
241        }
242    }
243
244    /// Run Stdio server
245    pub async fn run_stdio(&self) -> Result<()> {
246        transport::run_stdio_server(self).await
247    }
248
249    /// Run HTTP server
250    pub async fn run_http(&self) -> Result<()> {
251        transport::run_http_server(self).await
252    }
253
254    /// Run SSE server
255    pub async fn run_sse(&self) -> Result<()> {
256        transport::run_sse_server(self).await
257    }
258}