194c44c
use std::net::SocketAddr;
use anyhow::Context;
use axum::Router;
use axum::http::StatusCode;
use config::scoped::{McpHostConfig, ScopedConfig};
use tokio::net::TcpListener;
use tower_http::limit::RequestBodyLimitLayer;
use tower_http::trace::TraceLayer;
use tracing::{info, warn};
const MAX_REQUEST_BODY_BYTES: usize = 2 * 1024 * 1024;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
export::telemetry::init_tracing();
let cfg = McpHostConfig::load().context("loading configuration")?;
if !cfg.mcp.enabled() {
info!("mcp host is disabled by config; exiting");
return Ok(());
}
info!("connecting to database / redis / storage / email");
let state = export::AppState::with_config(cfg.clone().into_app_config())
.await
.context("building AppState")?;
info!("dependencies ready");
if let Err(err) = export::telemetry::init_meter_provider() {
warn!(error = %err, "failed to init meter provider; metrics disabled");
}
let health = export::health::router_with(Some(state.database.clone()), state.redis.clone(), state.kenauth.clone());
let mcp = mcp_api::router(state).await;
let router: Router = Router::new()
.merge(health)
.nest("/v1", mcp)
.fallback(not_found)
.layer(RequestBodyLimitLayer::new(MAX_REQUEST_BODY_BYTES))
.layer(
TraceLayer::new_for_http()
.make_span_with(export::telemetry::HttpSpan)
.on_response(export::telemetry::HttpOnResponse),
)
.layer(export::telemetry::metrics_layer())
.layer(export::middleware::AppTraceLayer::new());
let bind_addr = cfg.mcp.bind_addr().to_string();
let port = cfg.mcp.port();
let addr: SocketAddr = format!("{bind_addr}:{port}")
.parse()
.with_context(|| format!("parsing bind address {bind_addr}:{port}"))?;
let listener = TcpListener::bind(addr)
.await
.with_context(|| format!("binding {addr}"))?;
info!(%addr, "mcp host listening");
axum::serve(
listener,
router.into_make_service_with_connect_info::<SocketAddr>(),
)
.with_graceful_shutdown(export::telemetry::shutdown_signal())
.await
.context("axum server error")?;
info!("mcp host shut down");
Ok(())
}
async fn not_found() -> StatusCode {
StatusCode::NOT_FOUND
}