Skip to main content

crates_docs/server/
transport.rs

1//! Transport module
2//!
3//! Provides Stdio, HTTP, and SSE transport support.
4
5use crate::error::Result;
6use crate::server::handler::CratesDocsHandler;
7use crate::server::CratesDocsServer;
8use rust_mcp_sdk::{
9    error::McpSdkError,
10    event_store,
11    mcp_server::{hyper_server, server_runtime, HyperServerOptions, McpServerOptions},
12    McpServer, StdioTransport, ToMcpServerHandler, TransportOptions,
13};
14use std::sync::Arc;
15
16/// Run Stdio server
17pub async fn run_stdio_server(server: &CratesDocsServer) -> Result<()> {
18    tracing::info!("Starting Stdio MCP server...");
19
20    let server_info = server.server_info();
21    let handler = CratesDocsHandler::new(Arc::new(server.clone()));
22
23    // Create Stdio transport
24    let transport = StdioTransport::new(TransportOptions::default())
25        .map_err(|e| crate::error::Error::Mcp(e.to_string()))?;
26
27    // Create MCP server
28    let mcp_server: Arc<rust_mcp_sdk::mcp_server::ServerRuntime> =
29        server_runtime::create_server(McpServerOptions {
30            server_details: server_info,
31            transport,
32            handler: handler.to_mcp_server_handler(),
33            task_store: None,
34            client_task_store: None,
35        });
36
37    tracing::info!("Stdio MCP server started, waiting for connections...");
38    mcp_server
39        .start()
40        .await
41        .map_err(|e: McpSdkError| crate::error::Error::Mcp(e.to_string()))?;
42
43    Ok(())
44}
45
46/// Run HTTP server (Streamable HTTP)
47pub async fn run_http_server(server: &CratesDocsServer) -> Result<()> {
48    let config = server.config();
49    tracing::info!(
50        "Starting HTTP MCP server on {}:{}...",
51        config.host,
52        config.port
53    );
54
55    let server_info = server.server_info();
56    let handler = CratesDocsHandler::new(Arc::new(server.clone()));
57
58    // Create Hyper server options with security settings from config
59    let options = HyperServerOptions {
60        host: config.host.clone(),
61        port: config.port,
62        transport_options: Arc::new(TransportOptions::default()),
63        sse_support: false, // Pure HTTP mode
64        event_store: Some(Arc::new(event_store::InMemoryEventStore::default())),
65        task_store: None,
66        client_task_store: None,
67        allowed_hosts: Some(config.allowed_hosts.clone()),
68        allowed_origins: Some(config.allowed_origins.clone()),
69        ..Default::default()
70    };
71
72    // Create HTTP server
73    let mcp_server =
74        hyper_server::create_server(server_info, handler.to_mcp_server_handler(), options);
75
76    tracing::info!(
77        "HTTP MCP server started, listening on {}:{}",
78        config.host,
79        config.port
80    );
81    mcp_server
82        .start()
83        .await
84        .map_err(|e: McpSdkError| crate::error::Error::Mcp(e.to_string()))?;
85
86    Ok(())
87}
88
89/// Run SSE server (Server-Sent Events)
90pub async fn run_sse_server(server: &CratesDocsServer) -> Result<()> {
91    let config = server.config();
92    tracing::info!(
93        "Starting SSE MCP server on {}:{}...",
94        config.host,
95        config.port
96    );
97
98    let server_info = server.server_info();
99    let handler = CratesDocsHandler::new(Arc::new(server.clone()));
100
101    // Create Hyper server options with SSE support and security settings from config
102    let options = HyperServerOptions {
103        host: config.host.clone(),
104        port: config.port,
105        transport_options: Arc::new(TransportOptions::default()),
106        sse_support: true, // Enable SSE support
107        event_store: Some(Arc::new(event_store::InMemoryEventStore::default())),
108        task_store: None,
109        client_task_store: None,
110        allowed_hosts: Some(config.allowed_hosts.clone()),
111        allowed_origins: Some(config.allowed_origins.clone()),
112        ..Default::default()
113    };
114
115    // Create SSE server
116    let mcp_server =
117        hyper_server::create_server(server_info, handler.to_mcp_server_handler(), options);
118
119    tracing::info!(
120        "SSE MCP server started, listening on {}:{}",
121        config.host,
122        config.port
123    );
124    mcp_server
125        .start()
126        .await
127        .map_err(|e: McpSdkError| crate::error::Error::Mcp(e.to_string()))?;
128
129    Ok(())
130}
131
132/// Run hybrid server (supports both HTTP and SSE)
133pub async fn run_hybrid_server(server: &CratesDocsServer) -> Result<()> {
134    let config = server.config();
135    tracing::info!(
136        "Starting hybrid MCP server on {}:{}...",
137        config.host,
138        config.port
139    );
140
141    let server_info = server.server_info();
142    let handler = CratesDocsHandler::new(Arc::new(server.clone()));
143
144    // Create Hyper server options with SSE support and security settings from config
145    let options = HyperServerOptions {
146        host: config.host.clone(),
147        port: config.port,
148        transport_options: Arc::new(TransportOptions::default()),
149        sse_support: true, // Enable SSE support
150        event_store: Some(Arc::new(event_store::InMemoryEventStore::default())),
151        task_store: None,
152        client_task_store: None,
153        allowed_hosts: Some(config.allowed_hosts.clone()),
154        allowed_origins: Some(config.allowed_origins.clone()),
155        ..Default::default()
156    };
157
158    // Create hybrid server (HTTP + SSE)
159    let mcp_server =
160        hyper_server::create_server(server_info, handler.to_mcp_server_handler(), options);
161
162    tracing::info!(
163        "Hybrid MCP server started, listening on {}:{} (HTTP + SSE)",
164        config.host,
165        config.port
166    );
167    mcp_server
168        .start()
169        .await
170        .map_err(|e: McpSdkError| crate::error::Error::Mcp(e.to_string()))?;
171
172    Ok(())
173}
174
175/// Transport mode
176#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
177pub enum TransportMode {
178    /// Stdio transport (for CLI integration)
179    Stdio,
180    /// HTTP transport (Streamable HTTP)
181    Http,
182    /// SSE transport (Server-Sent Events)
183    Sse,
184    /// Hybrid mode (supports both HTTP and SSE)
185    Hybrid,
186}
187
188impl std::str::FromStr for TransportMode {
189    type Err = String;
190
191    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
192        match s.to_lowercase().as_str() {
193            "stdio" => Ok(TransportMode::Stdio),
194            "http" => Ok(TransportMode::Http),
195            "sse" => Ok(TransportMode::Sse),
196            "hybrid" => Ok(TransportMode::Hybrid),
197            _ => Err(format!("Unknown transport mode: {s}")),
198        }
199    }
200}
201
202impl std::fmt::Display for TransportMode {
203    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204        match self {
205            TransportMode::Stdio => write!(f, "stdio"),
206            TransportMode::Http => write!(f, "http"),
207            TransportMode::Sse => write!(f, "sse"),
208            TransportMode::Hybrid => write!(f, "hybrid"),
209        }
210    }
211}
212
213/// Run server with the specified transport mode
214pub async fn run_server_with_mode(server: &CratesDocsServer, mode: TransportMode) -> Result<()> {
215    match mode {
216        TransportMode::Stdio => run_stdio_server(server).await,
217        TransportMode::Http => run_http_server(server).await,
218        TransportMode::Sse => run_sse_server(server).await,
219        TransportMode::Hybrid => run_hybrid_server(server).await,
220    }
221}