mod config;
mod db;
mod error;
mod init;
mod server;
use clap::Parser;
use config::DatabasesConfig;
use db::ConnectionManager;
use rmcp::transport::stdio;
use rmcp::ServiceExt;
use server::SqlxMcpServer;
use tracing::{error, info};
use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
#[derive(Parser, Debug)]
#[command(name = "sqlx-mcp")]
#[command(author, version, about, long_about = None)]
struct Args {
#[arg(long, value_name = "AGENT")]
init: Option<Option<String>>,
#[arg(long)]
status: bool,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let args = Args::parse();
if args.status {
return init::show_status();
}
if let Some(agent_arg) = args.init {
return init::run_init(agent_arg);
}
run_server().await
}
async fn run_server() -> anyhow::Result<()> {
let _ = dotenvy::dotenv();
init_logging();
info!("Starting SQLx MCP Server v{}", env!("CARGO_PKG_VERSION"));
let config = match DatabasesConfig::load() {
Ok(cfg) => {
info!(
"Loaded configuration with {} database(s)",
cfg.databases.len()
);
cfg
}
Err(e) => {
error!("Configuration error: {}", e);
eprintln!("Error: {}", e);
eprintln!();
eprintln!("SQLx MCP Server requires a .databases.json configuration file.");
eprintln!();
eprintln!("Expected locations:");
for path in DatabasesConfig::config_paths() {
eprintln!(" - {}", path.display());
}
eprintln!();
eprintln!("Example .databases.json:");
eprintln!(r#"{{
"version": "1.0",
"databases": [
{{
"name": "mydb",
"engine": "mysql",
"host": "localhost",
"port": 3306,
"username": "root",
"password": "secret",
"database": "myapp"
}},
{{
"name": "cache",
"engine": "sqlite",
"path": "/tmp/cache.db"
}}
],
"default_connection": "mydb"
}}"#);
eprintln!();
eprintln!("Tip: Run 'sqlx-mcp --init' for interactive setup.");
std::process::exit(1);
}
};
let conn_manager = match ConnectionManager::new(&config).await {
Ok(cm) => {
info!(
"Connection manager initialized with {} connection(s)",
cm.connection_count()
);
cm
}
Err(e) => {
error!("Failed to initialize connections: {}", e);
eprintln!("Error: Failed to connect to database(s): {}", e);
eprintln!();
eprintln!("Please check your .databases.json configuration.");
std::process::exit(1);
}
};
let server = SqlxMcpServer::new(conn_manager);
info!("MCP server ready, waiting for connections...");
let service = server.serve(stdio()).await.inspect_err(|e| {
error!("Error starting MCP server: {}", e);
})?;
service.waiting().await?;
info!("SQLx MCP Server shutting down");
Ok(())
}
fn init_logging() {
let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
tracing_subscriber::registry()
.with(filter)
.with(fmt::layer().with_writer(std::io::stderr))
.init();
}