use std::error::Error;
use std::sync::Arc;
use clap::Parser;
use dravr_tronc::mcp::transport::stdio;
use dravr_tronc::server::tracing_init;
use dravr_tronc::McpServer;
use embacle::types::RunnerError;
use embacle_mcp::ServerState;
use opentelemetry::global;
use opentelemetry_sdk::metrics::SdkMeterProvider;
use tokio::net::TcpListener;
use tokio::sync::RwLock;
use embacle_server::router;
use embacle_server::runner::{self, parse_runner_type};
#[derive(Parser)]
#[command(name = "embacle-server", version, about)]
struct Cli {
#[arg(long, default_value = "http")]
transport: String,
#[arg(long, default_value_t = 3000)]
port: u16,
#[arg(long, default_value = "127.0.0.1")]
host: String,
#[arg(long, default_value = "copilot")]
provider: String,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
let cli = Cli::parse();
tracing_init::init_with_notifications(&cli.transport);
let meter_provider = SdkMeterProvider::builder().build();
global::set_meter_provider(meter_provider);
let Some(effective_provider) = parse_runner_type(&cli.provider) else {
return Err(RunnerError::config(format!(
"Unknown provider: {}. Valid: {}",
cli.provider,
runner::valid_provider_names()
))
.into());
};
let effective_provider = match embacle::load_config() {
Ok(Some(ref cfg)) => {
if cli.provider == "copilot" {
cfg.defaults
.model
.as_deref()
.and_then(parse_runner_type)
.unwrap_or(effective_provider)
} else {
effective_provider
}
}
Ok(None) => effective_provider,
Err(e) => {
tracing::warn!(error = %e, "Failed to load config file, using CLI defaults");
effective_provider
}
};
let state = Arc::new(RwLock::new(ServerState::new(effective_provider)));
tracing::info!(
transport = %cli.transport,
provider = %effective_provider,
"Starting embacle server"
);
match cli.transport.as_str() {
"stdio" => {
let server = Arc::new(McpServer::new(
"embacle-mcp",
env!("CARGO_PKG_VERSION"),
embacle_mcp::build_tool_registry(),
Arc::clone(&state),
));
stdio::run(server).await?;
}
"http" => {
let app = router::build(state);
let addr = format!("{}:{}", cli.host, cli.port);
let listener = TcpListener::bind(&addr)
.await
.map_err(|e| RunnerError::internal(format!("Failed to bind {addr}: {e}")))?;
tracing::info!(address = %addr, "HTTP transport listening");
axum::serve(listener, app)
.await
.map_err(|e| RunnerError::internal(format!("Server error: {e}")))?;
}
other => {
return Err(RunnerError::config(format!(
"Unknown transport: {other}. Valid: http, stdio"
))
.into());
}
}
Ok(())
}