use std::sync::Arc;
use tokio::sync::RwLock;
use excel_mcp_server::server::ExcelMcpServer;
use excel_mcp_server::store::WorkbookStore;
#[tokio::main]
async fn main() {
tracing_subscriber::fmt()
.with_writer(std::io::stderr)
.with_ansi(false)
.init();
if let Err(e) = run().await {
tracing::error!("Fatal error: {:?}", e);
std::process::exit(1);
}
}
async fn run() -> anyhow::Result<()> {
let transport = std::env::args().nth(1).unwrap_or_default();
match transport.as_str() {
"http" | "--http" => run_http().await,
_ => run_stdio().await,
}
}
async fn run_stdio() -> anyhow::Result<()> {
use rmcp::{transport::stdio, ServiceExt};
tracing::info!("Excel MCP Server starting on stdio");
let store = Arc::new(RwLock::new(WorkbookStore::new()));
let server = ExcelMcpServer::new(store);
let service = server
.serve(stdio())
.await
.inspect_err(|e| tracing::error!("Failed to start MCP service: {:?}", e))?;
service.waiting().await?;
Ok(())
}
async fn run_http() -> anyhow::Result<()> {
use rmcp::transport::streamable_http_server::{
session::local::LocalSessionManager, StreamableHttpServerConfig, StreamableHttpService,
};
use tower_http::cors::CorsLayer;
let bind = std::env::var("BIND_ADDRESS").unwrap_or_else(|_| "127.0.0.1:8080".to_string());
let ct = tokio_util::sync::CancellationToken::new();
let mcp_service = StreamableHttpService::new(
|| {
let store = Arc::new(RwLock::new(WorkbookStore::new()));
Ok(ExcelMcpServer::new(store))
},
LocalSessionManager::default().into(),
StreamableHttpServerConfig::default().with_cancellation_token(ct.child_token()),
);
let web_dir = std::path::PathBuf::from(
std::env::var("WEB_CLIENT_DIR").unwrap_or_else(|_| "./web-client".to_string()),
);
let cors = CorsLayer::very_permissive();
let mut router = axum::Router::new().nest_service("/mcp", mcp_service);
if web_dir.exists() {
tracing::info!("Serving web client from {}", web_dir.display());
router = router.fallback_service(
tower_http::services::ServeDir::new(&web_dir).append_index_html_on_directories(true),
);
}
let router = router.layer(cors);
let listener = tokio::net::TcpListener::bind(&bind).await?;
tracing::info!("Excel MCP Server listening on http://{}/mcp", bind);
if web_dir.exists() {
tracing::info!("Web client at http://{}/", bind);
}
axum::serve(listener, router)
.with_graceful_shutdown(async move {
tokio::signal::ctrl_c().await.unwrap();
tracing::info!("Shutting down...");
ct.cancel();
})
.await?;
Ok(())
}