use anyhow::Result;
use nb_mcp_server::{Config, mcp, nb, paths};
use tracing_subscriber::{EnvFilter, fmt, layer::SubscriberExt, util::SubscriberInitExt};
fn parse_args() -> Config {
let mut config = Config::default();
let mut args = std::env::args().skip(1);
while let Some(arg) = args.next() {
match arg.as_str() {
"--notebook" | "-n" => {
config.notebook = args.next();
}
"--no-commit-signing" => {
config.commit_signing_disabled = true;
}
"--no-create-notebook" => {
config.create_notebook = false;
}
"--show-paths" => {
config.show_paths = true;
}
"--version" => {
println!("nb-mcp {}", env!("CARGO_PKG_VERSION"));
std::process::exit(0);
}
"--help" | "-h" => {
eprintln!("nb-mcp: MCP server for nb note-taking");
eprintln!();
eprintln!("Usage: nb-mcp [OPTIONS]");
eprintln!();
eprintln!("Options:");
eprintln!(" -n, --notebook <NAME> Default notebook (overrides NB_MCP_NOTEBOOK)");
eprintln!(" --no-commit-signing Disable commit and tag signing");
eprintln!(" in notebook repo");
eprintln!(" --no-create-notebook Disable automatic notebook creation");
eprintln!(" --show-paths Show notebook path and state directory");
eprintln!(" --version Show version");
eprintln!(" -h, --help Show this help");
std::process::exit(0);
}
_ => {
}
}
}
config
}
async fn show_paths(config: &Config) -> Result<()> {
let nb = nb::NbClient::new(
config.notebook.as_deref(),
config.create_notebook,
config.commit_signing_disabled,
)?;
let notebook_path = nb.notebook_path(config.notebook.as_deref()).await?;
let log_path = paths::get_log_path();
let state_dir = log_path.parent().unwrap_or(log_path.as_path());
println!("notebook_path: {}", notebook_path.display());
println!("state_dir: {}", state_dir.display());
Ok(())
}
fn setup_logging() {
let env_filter = EnvFilter::from_default_env().add_directive(tracing::Level::INFO.into());
let stderr_layer = fmt::layer().with_writer(std::io::stderr).compact();
let file_layer = match setup_file_logging() {
Some((writer, guard)) => {
std::mem::forget(guard);
Some(fmt::layer().with_writer(writer).with_ansi(false))
}
None => None,
};
tracing_subscriber::registry()
.with(env_filter)
.with(stderr_layer)
.with(file_layer)
.init();
}
fn setup_file_logging() -> Option<(
tracing_appender::non_blocking::NonBlocking,
tracing_appender::non_blocking::WorkerGuard,
)> {
let log_path = paths::get_log_path();
let log_dir = log_path.parent()?;
let log_filename = log_path.file_name()?.to_str()?;
paths::ensure_dir(log_dir).ok()?;
let file_appender = tracing_appender::rolling::never(log_dir, log_filename);
let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
Some((non_blocking, guard))
}
#[tokio::main]
async fn main() -> Result<()> {
let config = parse_args();
if config.show_paths {
show_paths(&config).await?;
return Ok(());
}
setup_logging();
let log_path = paths::get_log_path();
tracing::info!(log_file = %log_path.display(), "logging initialized");
mcp::run(config).await
}