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