Skip to main content

crates_docs/cli/
serve_cmd.rs

1//! Serve command implementation
2
3use crate::server::transport;
4use crate::CratesDocsServer;
5use std::path::PathBuf;
6
7/// Start server command
8#[allow(clippy::too_many_arguments)]
9pub async fn run_serve_command(
10    config_path: &PathBuf,
11    debug: bool,
12    mode: Option<String>,
13    host: Option<String>,
14    port: Option<u16>,
15    enable_oauth: Option<bool>,
16    oauth_client_id: Option<String>,
17    oauth_client_secret: Option<String>,
18    oauth_redirect_uri: Option<String>,
19) -> Result<(), Box<dyn std::error::Error>> {
20    // Load configuration
21    let config = load_config(
22        config_path,
23        host,
24        port,
25        mode,
26        enable_oauth,
27        oauth_client_id,
28        oauth_client_secret,
29        oauth_redirect_uri,
30    )?;
31
32    // Get the actual transport mode (for logging and startup)
33    let transport_mode = config.server.transport_mode.clone();
34
35    // Initialize logging system (prefer config file, debug mode uses debug level)
36    if debug {
37        // In debug mode, override log level from config file
38        let mut debug_config = config.logging.clone();
39        debug_config.level = "debug".to_string();
40        crate::init_logging_with_config(&debug_config)
41            .map_err(|e| format!("Failed to initialize logging system: {e}"))?;
42    } else {
43        crate::init_logging_with_config(&config.logging)
44            .map_err(|e| format!("Failed to initialize logging system: {e}"))?;
45    }
46
47    tracing::info!(
48        "Starting Crates Docs MCP Server v{}",
49        env!("CARGO_PKG_VERSION")
50    );
51
52    // Create server (async to support Redis)
53    let server: CratesDocsServer = CratesDocsServer::new_async(config)
54        .await
55        .map_err(|e| format!("Failed to create server: {e}"))?;
56
57    // Start server based on mode
58    match transport_mode.to_lowercase().as_str() {
59        "stdio" => {
60            tracing::info!("Using Stdio transport mode");
61            transport::run_stdio_server(&server)
62                .await
63                .map_err(|e| format!("Failed to start Stdio server: {e}"))?;
64        }
65        "http" => {
66            tracing::info!(
67                "Using HTTP transport mode, listening on {}:{}",
68                server.config().server.host,
69                server.config().server.port
70            );
71            transport::run_http_server(&server)
72                .await
73                .map_err(|e| format!("Failed to start HTTP server: {e}"))?;
74        }
75        "sse" => {
76            tracing::info!(
77                "Using SSE transport mode, listening on {}:{}",
78                server.config().server.host,
79                server.config().server.port
80            );
81            transport::run_sse_server(&server)
82                .await
83                .map_err(|e| format!("Failed to start SSE server: {e}"))?;
84        }
85        "hybrid" => {
86            tracing::info!(
87                "Using hybrid transport mode (HTTP + SSE), listening on {}:{}",
88                server.config().server.host,
89                server.config().server.port
90            );
91            transport::run_hybrid_server(&server)
92                .await
93                .map_err(|e| format!("Failed to start hybrid server: {e}"))?;
94        }
95        _ => {
96            return Err(format!("Unknown transport mode: {transport_mode}").into());
97        }
98    }
99
100    Ok(())
101}
102
103/// Load configuration
104#[allow(clippy::too_many_arguments)]
105fn load_config(
106    config_path: &PathBuf,
107    host: Option<String>,
108    port: Option<u16>,
109    mode: Option<String>,
110    enable_oauth: Option<bool>,
111    oauth_client_id: Option<String>,
112    oauth_client_secret: Option<String>,
113    oauth_redirect_uri: Option<String>,
114) -> Result<crate::config::AppConfig, Box<dyn std::error::Error>> {
115    let mut config = if config_path.exists() {
116        tracing::info!("Loading configuration from file: {}", config_path.display());
117        crate::config::AppConfig::from_file(config_path)
118            .map_err(|e| format!("Failed to load config file: {e}"))?
119    } else {
120        tracing::warn!(
121            "Config file does not exist, using default config: {}",
122            config_path.display()
123        );
124        crate::config::AppConfig::default()
125    };
126
127    // Only override config file when command line arguments are explicitly provided
128    if let Some(h) = host {
129        config.server.host = h;
130        tracing::info!(
131            "Command line argument overrides host: {}",
132            config.server.host
133        );
134    }
135    if let Some(p) = port {
136        config.server.port = p;
137        tracing::info!(
138            "Command line argument overrides port: {}",
139            config.server.port
140        );
141    }
142    if let Some(m) = mode {
143        config.server.transport_mode = m;
144        tracing::info!(
145            "Command line argument overrides transport_mode: {}",
146            config.server.transport_mode
147        );
148    }
149    if let Some(eo) = enable_oauth {
150        config.server.enable_oauth = eo;
151        tracing::info!(
152            "Command line argument overrides enable_oauth: {}",
153            config.server.enable_oauth
154        );
155    }
156
157    // Override command line OAuth parameters (if provided)
158    if let Some(client_id) = oauth_client_id {
159        config.oauth.client_id = Some(client_id);
160        config.oauth.enabled = true;
161    }
162    if let Some(client_secret) = oauth_client_secret {
163        config.oauth.client_secret = Some(client_secret);
164    }
165    if let Some(redirect_uri) = oauth_redirect_uri {
166        config.oauth.redirect_uri = Some(redirect_uri);
167    }
168
169    // Validate configuration
170    config
171        .validate()
172        .map_err(|e| format!("Configuration validation failed: {e}"))?;
173
174    Ok(config)
175}