crates_docs/cli/
serve_cmd.rs1use crate::server::transport;
4use crate::CratesDocsServer;
5use std::path::PathBuf;
6
7#[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 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 let transport_mode = config.server.transport_mode.clone();
34
35 if debug {
37 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 let server: CratesDocsServer = CratesDocsServer::new_async(config)
54 .await
55 .map_err(|e| format!("Failed to create server: {e}"))?;
56
57 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#[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 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 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 config
171 .validate()
172 .map_err(|e| format!("Configuration validation failed: {e}"))?;
173
174 Ok(config)
175}