mod server;
use std::env;
use anyhow::Result;
use tracing::Level;
use tracing_subscriber::FmtSubscriber;
use symgraph::cli::{
context_command, index_command, prune_command, search_command, status_command, where_command,
};
fn main() -> Result<()> {
let mut args: Vec<String> = env::args().collect();
if args.len() < 2 {
print_usage();
return Ok(());
}
if let Some(i) = args.iter().position(|a| a == "--db") {
if i + 1 < args.len() {
env::set_var("SYMGRAPH_DB", &args[i + 1]);
args.drain(i..=i + 1);
}
}
match args[1].as_str() {
"serve" => {
let port = args
.iter()
.position(|a| a == "--port")
.and_then(|i| args.get(i + 1))
.and_then(|p| p.parse::<u16>().ok());
let bind = args
.iter()
.position(|a| a == "--bind")
.and_then(|i| args.get(i + 1))
.cloned();
let in_memory = args.iter().any(|a| a == "--in-memory");
let auth_token = env::var("SYMGRAPH_AUTH_TOKEN")
.ok()
.filter(|s| !s.is_empty());
match (bind, port) {
(Some(bind), _) => server::start_http(server::HttpConfig {
bind,
in_memory,
auth_token,
})?,
(None, Some(port)) => server::start_http(server::HttpConfig {
bind: format!("127.0.0.1:{}", port),
in_memory,
auth_token,
})?,
(None, None) => server::start_stdio(in_memory)?,
}
}
"index" => {
setup_logging();
let path = args.get(2).map(|s| s.as_str()).unwrap_or(".");
index_command(path)?;
}
"status" => {
let path = args.get(2).map(|s| s.as_str()).unwrap_or(".");
status_command(path)?;
}
"where" => {
let path = args.get(2).map(|s| s.as_str()).unwrap_or(".");
where_command(path)?;
}
"prune" => {
prune_command()?;
}
"search" => {
if args.len() < 3 {
eprintln!("Usage: symgraph search <query>");
return Ok(());
}
let path = ".";
let query = &args[2];
search_command(path, query)?;
}
"context" => {
if args.len() < 3 {
eprintln!("Usage: symgraph context <task>");
return Ok(());
}
let path = ".";
let task = args[2..].join(" ");
context_command(path, &task)?;
}
"help" | "--help" | "-h" => {
print_usage();
}
"--version" | "-V" | "version" => {
print_version();
}
cmd => {
eprintln!("Unknown command: {}", cmd);
print_usage();
}
}
Ok(())
}
fn print_usage() {
println!(
r#"symgraph: Semantic code intelligence MCP server
USAGE:
symgraph <COMMAND> [OPTIONS]
COMMANDS:
serve Start the MCP server (stdio transport)
serve --port <PORT> Start the MCP server (HTTP on 127.0.0.1:<PORT>)
serve --bind <ADDR:PORT> Start the MCP server on an explicit bind address
serve --in-memory Use in-memory database (no filesystem writes)
index [path] Index a codebase (default: current directory)
status [path] Show index statistics
search <query> Search for symbols by name
context <task> Build context for a task description
where [path] Show the resolved index location for a project
prune Remove cached indexes for repos that no longer exist
help Show this help message
OPTIONS:
--db <path> Use an explicit index database path (any command)
ENVIRONMENT:
SYMGRAPH_ROOT Project root directory (default: current directory)
SYMGRAPH_DB Explicit index database path (overrides storage)
SYMGRAPH_STORAGE Index location strategy: git | cache | local.
Default: reuse existing .symgraph/, else the git dir
(<git-common-dir>/symgraph), else an OS cache dir.
SYMGRAPH_IN_MEMORY=1 Use in-memory database (alternative to --in-memory)
SYMGRAPH_AUTH_TOKEN Bearer token required on /mcp (required for non-
loopback binds; optional on 127.0.0.1)
EXAMPLES:
symgraph index # Index current directory
symgraph index ~/projects/myapp # Index specific directory
symgraph serve # Start MCP server (stdio)
symgraph serve --port 8080 # Start MCP server (HTTP on port 8080)
symgraph serve --in-memory # Start MCP server with in-memory database
symgraph search "authenticate" # Find symbols matching "authenticate"
symgraph context "add user login" # Build context for implementing login
"#
);
}
fn print_version() {
println!("symgraph {}", env!("CARGO_PKG_VERSION"));
}
fn setup_logging() {
let subscriber = FmtSubscriber::builder()
.with_max_level(Level::INFO)
.with_target(false)
.with_writer(std::io::stderr)
.finish();
tracing::subscriber::set_global_default(subscriber).ok();
}