pub mod auth;
pub mod auth_middleware;
pub mod handler;
pub mod transport;
use crate::cache::Cache;
use crate::config::AppConfig;
use crate::error::Result;
use crate::tools::ToolRegistry;
use rust_mcp_sdk::schema::{
Implementation, InitializeResult, ProtocolVersion, ServerCapabilities, ServerCapabilitiesTools,
};
use std::sync::Arc;
pub use crate::config::ServerConfig;
pub use handler::CratesDocsHandler;
pub use transport::HyperServerConfig;
#[derive(Clone)]
pub struct CratesDocsServer {
config: AppConfig,
tool_registry: Arc<ToolRegistry>,
cache: Arc<dyn Cache>,
}
impl CratesDocsServer {
fn from_parts(config: AppConfig, cache: Arc<dyn Cache>) -> crate::error::Result<Self> {
let _ = crate::utils::init_global_http_client(&config.performance);
let doc_service = Arc::new(crate::tools::docs::DocService::with_config(
cache.clone(),
&config.cache,
)?);
let tool_registry = Arc::new(crate::tools::create_default_registry(&doc_service));
Ok(Self {
config,
tool_registry,
cache,
})
}
pub fn new(config: AppConfig) -> Result<Self> {
let cache_box: Box<dyn Cache> = crate::cache::create_cache(&config.cache)?;
let cache: Arc<dyn Cache> = Arc::from(cache_box);
Self::from_parts(config, cache)
}
#[allow(unused_variables)]
#[allow(clippy::unused_async)]
pub async fn new_async(config: AppConfig) -> Result<Self> {
#[cfg(feature = "cache-redis")]
{
let cache_box: Box<dyn Cache> = crate::cache::create_cache_async(&config.cache).await?;
let cache: Arc<dyn Cache> = Arc::from(cache_box);
Self::from_parts(config, cache)
}
#[cfg(not(feature = "cache-redis"))]
{
let cache_box: Box<dyn Cache> = crate::cache::create_cache(&config.cache)?;
let cache: Arc<dyn Cache> = Arc::from(cache_box);
Self::from_parts(config, cache)
}
}
#[must_use]
pub fn config(&self) -> &AppConfig {
&self.config
}
#[must_use]
pub fn tool_registry(&self) -> &Arc<ToolRegistry> {
&self.tool_registry
}
#[must_use]
pub fn cache(&self) -> &Arc<dyn Cache> {
&self.cache
}
#[must_use]
pub fn server_info(&self) -> InitializeResult {
InitializeResult {
server_info: Implementation {
name: self.config.server.name.clone(),
version: self.config.server.version.clone(),
title: Some("Crates Docs MCP Server".to_string()),
description: self.config.server.description.clone(),
icons: self.config.server.icons.clone(),
website_url: self.config.server.website_url.clone(),
},
capabilities: ServerCapabilities {
tools: Some(ServerCapabilitiesTools { list_changed: None }),
resources: None,
prompts: None,
experimental: None,
completions: None,
logging: None,
tasks: None,
},
protocol_version: ProtocolVersion::V2025_11_25.into(),
instructions: Some(
"Use this server to query Rust crate documentation. Supports crate lookup, crate search, and health check."
.to_string(),
),
meta: None,
}
}
pub async fn run_stdio(&self) -> Result<()> {
transport::run_stdio_server(self).await
}
pub async fn run_http(&self) -> Result<()> {
transport::run_hyper_server(self, transport::HyperServerConfig::http()).await
}
pub async fn run_sse(&self) -> Result<()> {
transport::run_hyper_server(self, transport::HyperServerConfig::sse()).await
}
}