use clap::Parser;
use rmcp::{ServiceExt as _, transport::stdio};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
use sparql_mcp::{
Args, build_router, config::EndpointsConfig, error::AppResult, index, mcp::SparqlTools,
};
// OpenAPI generation: https://github.com/juhaku/utoipa/blob/master/examples/axum-multipart/src/main.rs
// MCP client: https://github.com/modelcontextprotocol/rust-sdk/blob/main/examples/clients/src/streamable_http.rs
#[tokio::main]
async fn main() -> AppResult<()> {
let args = Args::parse();
tracing_subscriber::registry()
.with(
tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| {
// In dev: info for all, debug for data_commons_mcp only
#[cfg(debug_assertions)]
return "warn,sparql_mcp=debug".to_string().into();
#[cfg(not(debug_assertions))]
return "warn,sparql_mcp=info".to_string().into();
}),
)
.with(tracing_subscriber::fmt::layer())
.init();
// Init search DB
let endpoints_config = if let Some(ref json_path) = args.config_json {
EndpointsConfig::from_json_file(json_path)?
} else {
EndpointsConfig::default()
};
let (db, schemas_map) =
index::init_db(&endpoints_config, &args.db_path, args.force_index).await?;
if args.stdio {
// Use STDIO transport
tracing::error!("STDIO transport is not supported yet, please use HTTP transport");
let server = SparqlTools::new(db.clone(), schemas_map.clone(), args.auto_index);
let service = server
.serve(stdio())
.await
.inspect_err(|e| {
tracing::error!("Server error: {:?}", e);
})
.unwrap();
service.waiting().await.unwrap();
} else {
// Use Streamable HTTP transport
let router = build_router(&args, &db, &schemas_map).await.unwrap();
let app = router.into_make_service();
let listener = tokio::net::TcpListener::bind(&args.bind_address).await?;
// Improve graceful shutdown
let shutdown_signal = async {
tokio::signal::ctrl_c()
.await
.expect("Failed to install CTRL+C signal handler");
tracing::info!("Shutting down...");
};
tracing::info!(
"⚡️ Starting Streamable HTTP MCP server at http://{}/mcp, {}with database in {}",
args.bind_address,
if args.mcp_only {
"".to_string()
} else {
format!(
"chat UI at http://{}, OpenAPI UI at http://{}/docs, ",
args.bind_address, args.bind_address
)
},
args.db_path,
);
axum::serve(listener, app)
.with_graceful_shutdown(shutdown_signal)
.await?;
}
Ok(())
}