use std::path::PathBuf;
use clap::Parser;
use khive_mcp::server::KhiveMcpServer;
use khive_runtime::{KhiveRuntime, RuntimeConfig};
#[derive(Parser, Debug)]
#[command(
name = "khive-mcp",
version,
about = "khive MCP server (stdio) — the only user-facing Rust binary"
)]
struct Args {
#[arg(long, env = "KHIVE_DB")]
db: Option<String>,
#[arg(long, env = "KHIVE_NAMESPACE", default_value = "local")]
namespace: String,
#[arg(long, env = "KHIVE_NO_EMBED")]
no_embed: bool,
#[arg(long, env = "KHIVE_LOG", default_value = "warn")]
log: String,
#[arg(long = "pack")]
pack: Vec<String>,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let args = Args::parse();
tracing_subscriber::fmt()
.with_writer(std::io::stderr)
.with_env_filter(args.log.clone())
.with_ansi(false)
.init();
let db_path = match args.db.as_deref() {
Some(":memory:") => None,
Some(path) => Some(PathBuf::from(path)),
None => {
let home = std::env::var("HOME").unwrap_or_else(|_| ".".into());
Some(PathBuf::from(format!("{home}/.khive/khive-graph.db")))
}
};
let embedding_model = if args.no_embed {
None
} else {
RuntimeConfig::default().embedding_model
};
let packs = if args.pack.is_empty() {
RuntimeConfig::default().packs
} else {
args.pack
};
let default_namespace = khive_runtime::Namespace::parse(&args.namespace)
.map_err(|e| anyhow::anyhow!("invalid --namespace {:?}: {e}", args.namespace))?;
let config = RuntimeConfig {
db_path,
default_namespace,
embedding_model,
packs,
..RuntimeConfig::default()
};
let runtime = KhiveRuntime::new(config)?;
let server = KhiveMcpServer::new(runtime).map_err(|e| anyhow::anyhow!("{e}"))?;
server.serve_stdio().await?;
Ok(())
}