use std::path::PathBuf;
use std::process::ExitCode;
use clap::Parser;
use sqlrite::Connection;
mod error;
mod protocol;
mod stdio_redirect;
mod tools;
mod transport;
#[derive(Parser, Debug)]
#[command(
name = "sqlrite-mcp",
version,
about = "MCP server for SQLRite — exposes a database to LLM agents over stdio.",
long_about = None,
)]
struct Cli {
database: Option<PathBuf>,
#[arg(long)]
read_only: bool,
#[arg(long)]
in_memory: bool,
}
fn main() -> ExitCode {
let cli = Cli::parse();
let real_stdout = match stdio_redirect::redirect_stdout_to_stderr() {
Ok(f) => f,
Err(err) => {
eprintln!("error: failed to set up stdio for MCP protocol: {err}");
return ExitCode::from(1);
}
};
let db_path = cli
.database
.clone()
.or_else(|| std::env::var_os("SQLRITE_MCP_DATABASE").map(PathBuf::from));
if cli.in_memory && db_path.is_some() {
eprintln!("error: --in-memory and a database path are mutually exclusive.");
return ExitCode::from(2);
}
let conn_result = if cli.in_memory {
Connection::open_in_memory()
} else {
let Some(path) = db_path else {
eprintln!(
"error: no database specified. Pass a path, set \
SQLRITE_MCP_DATABASE, or use --in-memory.\n\
\n\
Try: sqlrite-mcp --help"
);
return ExitCode::from(2);
};
if cli.read_only {
Connection::open_read_only(&path)
} else {
Connection::open(&path)
}
};
let conn = match conn_result {
Ok(c) => c,
Err(err) => {
eprintln!("error: failed to open database: {err}");
return ExitCode::from(1);
}
};
let stdin = std::io::stdin();
match transport::run(stdin.lock(), real_stdout, conn, cli.read_only) {
Ok(()) => ExitCode::SUCCESS,
Err(err) => {
eprintln!("error: transport loop terminated: {err}");
ExitCode::from(1)
}
}
}