use rust_mcp_sdk::auth::AuthProvider;
use rust_mcp_sdk::event_store::EventStore;
use rust_mcp_sdk::id_generator::IdGenerator;
use rust_mcp_sdk::mcp_http::DnsRebindingOptions;
use rust_mcp_sdk::mcp_http::HealthHandler;
use rust_mcp_sdk::mcp_http::McpMountOptions;
use rust_mcp_sdk::mcp_http::{
DEFAULT_MAX_REQUEST_BODY_SIZE, DEFAULT_MESSAGES_ENDPOINT, DEFAULT_SSE_ENDPOINT,
DEFAULT_STREAMABLE_HTTP_ENDPOINT,
};
use rust_mcp_sdk::schema::schema_utils::{ClientMessage, ServerMessage};
use rust_mcp_sdk::session_store::SessionStore;
use rust_mcp_sdk::task_store::{ClientTaskStore, ServerTaskStore};
use rust_mcp_sdk::McpObserver;
use rust_mcp_sdk::SessionId;
use rust_mcp_sdk::TransportOptions;
use std::net::{SocketAddr, ToSocketAddrs};
use std::sync::Arc;
use std::time::Duration;
const DEFAULT_CLIENT_PING_INTERVAL: Duration = Duration::from_secs(12);
pub struct ActixServerOptions {
pub host: String,
pub port: u16,
pub session_id_generator: Option<Arc<dyn IdGenerator<SessionId>>>,
pub custom_streamable_http_endpoint: Option<String>,
pub transport_options: Arc<TransportOptions>,
pub event_store: Option<Arc<dyn EventStore>>,
pub task_store: Option<Arc<ServerTaskStore>>,
pub client_task_store: Option<Arc<ClientTaskStore>>,
pub enable_json_response: Option<bool>,
pub ping_interval: Duration,
pub sse_support: bool,
pub custom_sse_endpoint: Option<String>,
pub custom_messages_endpoint: Option<String>,
pub auth: Option<Arc<dyn AuthProvider>>,
pub health_endpoint: Option<String>,
pub health_handler: Option<Arc<dyn HealthHandler>>,
pub message_observer: Option<Arc<dyn McpObserver<ClientMessage, ServerMessage>>>,
pub max_request_body_size: Option<usize>,
pub dns_rebinding: DnsRebindingOptions,
pub session_store: Option<Arc<dyn SessionStore>>,
pub enable_ssl: bool,
pub ssl_cert_path: Option<String>,
pub ssl_key_path: Option<String>,
}
impl ActixServerOptions {
pub fn validate(&self) -> Result<(), String> {
if self.host.is_empty() {
return Err("host must not be empty".into());
}
if self.enable_ssl && (self.ssl_cert_path.is_none() || self.ssl_key_path.is_none()) {
return Err(
"Both 'ssl_cert_path' and 'ssl_key_path' must be provided when SSL is enabled."
.into(),
);
}
Ok(())
}
pub fn resolve_server_address(&self) -> Result<SocketAddr, String> {
self.validate()?;
let host = self
.host
.strip_prefix("http://")
.or_else(|| self.host.strip_prefix("https://"))
.unwrap_or(&self.host)
.to_string();
let mut iter = (host.as_str(), self.port)
.to_socket_addrs()
.map_err(|e| format!("Failed to resolve address: {}", e))?;
iter.next()
.ok_or_else(|| format!("Could not resolve: {}:{}", self.host, self.port))
}
pub fn base_url(&self) -> String {
format!(
"{}://{}:{}",
if self.enable_ssl { "https" } else { "http" },
self.host,
self.port
)
}
pub fn streamable_http_url(&self) -> String {
format!("{}{}", self.base_url(), self.streamable_http_endpoint())
}
pub fn sse_url(&self) -> String {
format!("{}{}", self.base_url(), self.sse_endpoint())
}
pub fn sse_message_url(&self) -> String {
format!("{}{}", self.base_url(), self.sse_messages_endpoint())
}
pub fn sse_endpoint(&self) -> &str {
self.custom_sse_endpoint
.as_deref()
.unwrap_or(DEFAULT_SSE_ENDPOINT)
}
pub fn sse_messages_endpoint(&self) -> &str {
self.custom_messages_endpoint
.as_deref()
.unwrap_or(DEFAULT_MESSAGES_ENDPOINT)
}
pub fn streamable_http_endpoint(&self) -> &str {
self.custom_streamable_http_endpoint
.as_deref()
.unwrap_or(DEFAULT_STREAMABLE_HTTP_ENDPOINT)
}
pub fn max_request_body_size(&self) -> usize {
self.max_request_body_size
.unwrap_or(DEFAULT_MAX_REQUEST_BODY_SIZE)
}
pub fn resolve_mount_options(&self) -> McpMountOptions {
McpMountOptions {
streamable_http_endpoint: self.streamable_http_endpoint().to_string(),
sse_endpoint: self.sse_endpoint().to_string(),
sse_messages_endpoint: self.sse_messages_endpoint().to_string(),
health_endpoint: self.health_endpoint.clone(),
max_request_body_size: self.max_request_body_size(),
}
}
}
impl Default for ActixServerOptions {
fn default() -> Self {
Self {
host: "127.0.0.1".into(),
port: 8080,
session_id_generator: None,
custom_streamable_http_endpoint: None,
transport_options: Default::default(),
event_store: None,
task_store: None,
client_task_store: None,
enable_json_response: None,
ping_interval: DEFAULT_CLIENT_PING_INTERVAL,
sse_support: true,
custom_sse_endpoint: None,
custom_messages_endpoint: None,
auth: None,
health_endpoint: None,
health_handler: None,
message_observer: None,
max_request_body_size: None,
dns_rebinding: DnsRebindingOptions::default(),
session_store: None,
enable_ssl: false,
ssl_cert_path: None,
ssl_key_path: None,
}
}
}