use clap::Args;
use database_mcp_config::{ConfigError, DatabaseBackend, DatabaseConfig};
use database_mcp_mysql::MysqlHandler;
use database_mcp_postgres::PostgresHandler;
use database_mcp_sqlite::SqliteHandler;
use tracing::info;
pub(crate) use database_mcp_server::Server;
#[derive(Debug, Args)]
#[command(next_help_heading = "Database")]
pub(crate) struct DatabaseArguments {
#[arg(long = "db-backend", env = "DB_BACKEND", default_value_t = DatabaseConfig::DEFAULT_BACKEND)]
pub(crate) backend: DatabaseBackend,
#[arg(long = "db-host", env = "DB_HOST", default_value = DatabaseConfig::DEFAULT_HOST)]
pub(crate) host: String,
#[arg(long = "db-port", env = "DB_PORT")]
pub(crate) port: Option<u16>,
#[arg(long = "db-user", env = "DB_USER")]
pub(crate) user: Option<String>,
#[arg(long = "db-password", env = "DB_PASSWORD")]
pub(crate) password: Option<String>,
#[arg(long = "db-name", env = "DB_NAME")]
pub(crate) name: Option<String>,
#[arg(long = "db-charset", env = "DB_CHARSET")]
pub(crate) charset: Option<String>,
#[arg(
long = "db-ssl",
env = "DB_SSL",
default_value_t = DatabaseConfig::DEFAULT_SSL,
)]
pub(crate) ssl: bool,
#[arg(long = "db-ssl-ca", env = "DB_SSL_CA")]
pub(crate) ssl_ca: Option<String>,
#[arg(long = "db-ssl-cert", env = "DB_SSL_CERT")]
pub(crate) ssl_cert: Option<String>,
#[arg(long = "db-ssl-key", env = "DB_SSL_KEY")]
pub(crate) ssl_key: Option<String>,
#[arg(
long = "db-ssl-verify-cert",
env = "DB_SSL_VERIFY_CERT",
default_value_t = DatabaseConfig::DEFAULT_SSL_VERIFY_CERT,
)]
pub(crate) ssl_verify_cert: bool,
#[arg(
long = "db-read-only",
env = "DB_READ_ONLY",
default_value_t = DatabaseConfig::DEFAULT_READ_ONLY,
)]
pub(crate) read_only: bool,
#[arg(
long = "db-max-pool-size",
env = "DB_MAX_POOL_SIZE",
default_value_t = DatabaseConfig::DEFAULT_MAX_POOL_SIZE,
value_parser = clap::value_parser!(u32).range(1..)
)]
pub(crate) max_pool_size: u32,
#[arg(
long = "db-connection-timeout",
env = "DB_CONNECTION_TIMEOUT",
value_parser = clap::value_parser!(u64).range(1..)
)]
pub(crate) connection_timeout: Option<u64>,
#[arg(
long = "db-query-timeout",
env = "DB_QUERY_TIMEOUT",
default_value_t = DatabaseConfig::DEFAULT_QUERY_TIMEOUT_SECS,
value_parser = clap::value_parser!(u64)
)]
pub(crate) query_timeout: u64,
}
impl TryFrom<&DatabaseArguments> for DatabaseConfig {
type Error = Vec<ConfigError>;
fn try_from(db: &DatabaseArguments) -> Result<Self, Self::Error> {
let backend = db.backend;
let config = Self {
backend,
host: db.host.clone(),
port: db.port.unwrap_or_else(|| backend.default_port()),
user: db.user.clone().unwrap_or_else(|| backend.default_user().into()),
password: db.password.clone(),
name: db.name.clone(),
charset: db.charset.clone(),
ssl: db.ssl,
ssl_ca: db.ssl_ca.clone(),
ssl_cert: db.ssl_cert.clone(),
ssl_key: db.ssl_key.clone(),
ssl_verify_cert: db.ssl_verify_cert,
read_only: db.read_only,
max_pool_size: db.max_pool_size,
connection_timeout: db.connection_timeout,
query_timeout: Some(db.query_timeout),
};
config.validate()?;
Ok(config)
}
}
#[must_use]
pub(crate) fn create_server(db_config: &DatabaseConfig) -> Server {
if db_config.read_only {
info!("Server running in READ-ONLY mode. Write operations are disabled.");
}
match db_config.backend {
DatabaseBackend::Sqlite => SqliteHandler::new(db_config).into(),
DatabaseBackend::Postgres => PostgresHandler::new(db_config).into(),
DatabaseBackend::Mysql | DatabaseBackend::Mariadb => MysqlHandler::new(db_config).into(),
}
}