#![forbid(unsafe_code)]
#![deny(missing_docs)]
use axon_emcp::{knowledge, scaffold, server, telemetry};
use std::path::PathBuf;
use std::process::ExitCode;
use tracing_subscriber::EnvFilter;
#[tokio::main]
async fn main() -> ExitCode {
let args: Vec<String> = std::env::args().collect();
if let Some(sub) = args.get(1) {
match sub.as_str() {
"scaffold-primitive" => return run_scaffold_primitive(&args[2..]),
"telemetry" => return run_telemetry(&args[2..]),
"--help" | "-h" => return run_help(),
"--version" | "-V" => return run_version(),
_ => {}
}
}
tracing_subscriber::fmt()
.with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")))
.with_writer(std::io::stderr)
.with_target(false)
.compact()
.init();
tracing::info!(
version = env!("CARGO_PKG_VERSION"),
"axon-emcp starting — ℰMCP OFICIAL"
);
let catalog = match knowledge::Catalog::load_default() {
Ok(c) => c,
Err(e) => {
tracing::error!(error = %e, "failed to load knowledge base — refusing to start");
return ExitCode::from(2);
}
};
tracing::info!(
primitives = catalog.primitive_count(),
"knowledge base loaded"
);
let tel_config = telemetry::TelemetryConfig::from_env();
let jsonl_sink = tel_config.jsonl_sink.clone();
let deployment_id = tel_config.deployment_id.clone();
let max_samples = tel_config.max_samples;
let tel = telemetry::Telemetry::new(tel_config);
tracing::info!(
deployment_id = %deployment_id,
jsonl_sink = ?jsonl_sink,
max_samples,
"telemetry installed"
);
match server::run_stdio(catalog, tel).await {
Ok(()) => {
tracing::info!("axon-emcp shutting down cleanly");
ExitCode::SUCCESS
}
Err(e) => {
tracing::error!(error = %e, "axon-emcp transport error");
ExitCode::FAILURE
}
}
}
fn run_scaffold_primitive(args: &[String]) -> ExitCode {
if args.is_empty() {
eprintln!("usage: axon-emcp scaffold-primitive <name>");
eprintln!();
eprintln!("Stamps src/knowledge/primitives/<name>.md with the frontmatter pre-populated");
eprintln!("from axon_frontend::PRIMITIVE_REGISTRY. The name must already exist in the");
eprintln!("registry (add it there first if it's a new primitive).");
return ExitCode::from(2);
}
let name = &args[0];
let knowledge_dir = match resolve_knowledge_dir() {
Ok(p) => p,
Err(e) => {
eprintln!("error: {e}");
return ExitCode::from(1);
}
};
match scaffold::run(name, &knowledge_dir) {
Ok(msg) => {
eprintln!("{msg}");
ExitCode::SUCCESS
}
Err(e) => {
eprintln!("error: {e}");
ExitCode::from(1)
}
}
}
fn resolve_knowledge_dir() -> Result<PathBuf, String> {
if let Ok(s) = std::env::var("AXON_EMCP_KNOWLEDGE_DIR") {
let p = PathBuf::from(&s);
if p.is_dir() {
return Ok(p);
}
return Err(format!(
"AXON_EMCP_KNOWLEDGE_DIR points to non-directory: {s}"
));
}
let cwd = std::env::current_dir()
.map_err(|e| format!("could not read cwd: {e}"))?;
for candidate in [
cwd.join("src").join("axon-emcp").join("knowledge"),
cwd.join("knowledge"),
] {
if candidate.is_dir() {
return Ok(candidate);
}
}
Err(format!(
"could not find knowledge directory — run from the repo root, \
or set AXON_EMCP_KNOWLEDGE_DIR to point to src/axon-emcp/knowledge/. \
cwd = {}",
cwd.display()
))
}
fn run_help() -> ExitCode {
eprintln!(
"{name} — the official ℰMCP server for AXON, plus contributor subcommands.\n\
\n\
USAGE:\n \
{name} [SUBCOMMAND]\n\
\n\
SUBCOMMANDS:\n \
(none) Start the MCP server (default — speaks JSON-RPC 2.0 on stdio)\n \
scaffold-primitive <name>\n Stamp a markdown doc skeleton for one primitive\n \
telemetry summarize <file>\n Aggregate a JSONL telemetry log into the snapshot shape\n \
--help, -h Print this help\n \
--version, -V Print the crate version\n\
\n\
ENV:\n \
AXON_EMCP_KNOWLEDGE_DIR Override the corpus root (default: in-tree, then embedded)\n \
AXON_EMCP_TELEMETRY_FILE Append JSONL telemetry events to this path (default: disabled)\n \
AXON_EMCP_DEPLOYMENT_ID Correlation tag stamped on every event (default: empty)\n \
AXON_EMCP_TELEMETRY_MAX_SAMPLES Latency histogram window per tool (default: 1000)\n \
RUST_LOG Tracing filter (e.g. axon_emcp=debug)\n",
name = env!("CARGO_PKG_NAME")
);
ExitCode::SUCCESS
}
fn run_version() -> ExitCode {
eprintln!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
ExitCode::SUCCESS
}
fn run_telemetry(args: &[String]) -> ExitCode {
let Some(sub) = args.first() else {
eprintln!("usage: axon-emcp telemetry <summarize> <file>");
return ExitCode::from(2);
};
match sub.as_str() {
"summarize" => {
let Some(path) = args.get(1) else {
eprintln!("usage: axon-emcp telemetry summarize <file>");
return ExitCode::from(2);
};
let path = PathBuf::from(path);
match telemetry::summarize_jsonl(&path) {
Ok(v) => {
println!("{}", serde_json::to_string_pretty(&v).unwrap());
ExitCode::SUCCESS
}
Err(e) => {
eprintln!("error: failed to summarize `{}`: {e}", path.display());
ExitCode::from(1)
}
}
}
other => {
eprintln!(
"unknown telemetry subcommand `{other}` — valid: summarize"
);
ExitCode::from(2)
}
}
}