#[cfg(not(unix))]
compile_error!(
"agentic-mcp only supports Unix-like platforms (Linux/macOS). Windows is not supported."
);
use agentic_config::loader::load_merged;
use agentic_tools_mcp::OutputMode;
use agentic_tools_mcp::RegistryServer;
use agentic_tools_mcp::ServiceExt;
use agentic_tools_mcp::stdio;
use agentic_tools_registry::AgenticTools;
use agentic_tools_registry::AgenticToolsConfig;
use clap::Parser;
use colored::Colorize;
use serde::Deserialize;
use std::collections::HashSet;
use std::fs;
use std::sync::Arc;
#[derive(Parser, Debug)]
#[command(name = "agentic-mcp")]
#[command(about = "Unified MCP server for all agentic-tools", version)]
struct Args {
#[arg(long, value_name = "NAMES")]
allow: Option<String>,
#[arg(long = "server-config", value_name = "PATH")]
server_config: Option<String>,
#[arg(long)]
list_tools: bool,
#[arg(long, value_parser = ["text", "structured"])]
output: Option<String>,
#[arg(long)]
cli_ls: bool,
#[arg(long)]
ask_agent: bool,
#[arg(long)]
cli_grep: bool,
#[arg(long)]
cli_glob: bool,
#[arg(long)]
cli_just_search: bool,
#[arg(long)]
cli_just_execute: bool,
}
#[derive(Deserialize)]
struct FileConfig {
allowlist: Option<HashSet<String>>,
output: Option<String>,
}
fn parse_config(args: &Args) -> (AgenticToolsConfig, Option<String>) {
let mut allowlist: Option<HashSet<String>> = None;
let mut file_output: Option<String> = None;
if let Some(path) = args.server_config.as_deref() {
match fs::read_to_string(path) {
Ok(s) => {
if let Ok(fc) = serde_json::from_str::<FileConfig>(&s) {
allowlist = fc.allowlist;
file_output = fc.output;
} else {
eprintln!("Warning: Failed to parse config JSON; ignoring");
}
}
Err(e) => {
eprintln!("Warning: Failed to read config file: {e}; ignoring");
}
}
}
if let Some(ref s) = args.allow {
let set: HashSet<String> = s
.split(',')
.map(|v| v.trim().to_string())
.filter(|v| !v.is_empty())
.collect();
if !set.is_empty() {
allowlist = Some(set);
}
}
let mut flag_set: HashSet<String> = HashSet::new();
if args.cli_ls {
flag_set.insert("cli_ls".to_string());
}
if args.ask_agent {
flag_set.insert("ask_agent".to_string());
}
if args.cli_grep {
flag_set.insert("cli_grep".to_string());
}
if args.cli_glob {
flag_set.insert("cli_glob".to_string());
}
if args.cli_just_search {
flag_set.insert("cli_just_search".to_string());
}
if args.cli_just_execute {
flag_set.insert("cli_just_execute".to_string());
}
if !flag_set.is_empty() {
allowlist.get_or_insert_with(HashSet::new).extend(flag_set);
}
(
AgenticToolsConfig {
allowlist,
..Default::default()
},
file_output,
)
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt::init();
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
let args = Args::parse();
let cwd = std::env::current_dir()?;
let loaded = load_merged(&cwd)?;
for w in &loaded.warnings {
eprintln!("{} {}", "WARN".yellow(), w);
}
let (mut reg_cfg, file_output) = parse_config(&args);
reg_cfg.subagents = loaded.config.subagents.clone();
reg_cfg.reasoning = loaded.config.reasoning.clone();
reg_cfg.web_retrieval = loaded.config.web_retrieval.clone();
reg_cfg.cli_tools = loaded.config.cli_tools.clone();
reg_cfg.exa = loaded.config.services.exa.clone();
reg_cfg.anthropic = loaded.config.services.anthropic.clone();
let reg = AgenticTools::new(reg_cfg);
if args.list_tools {
let mut names = reg.list_names();
names.sort();
eprintln!("Available tools ({}):", names.len());
for n in names {
eprintln!(" - {n}");
}
return Ok(());
}
let output_mode = match (args.output.as_deref(), file_output.as_deref()) {
(Some("structured"), _) | (None, Some("structured")) => OutputMode::Structured,
_ => OutputMode::Text, };
eprintln!(
"Starting agentic-mcp ({} tools) with output mode: {:?}",
reg.len(),
output_mode
);
let server = RegistryServer::new(Arc::new(reg))
.with_info("agentic-mcp", env!("CARGO_PKG_VERSION"))
.with_output_mode(output_mode);
let transport = stdio();
let service = server.serve(transport).await?;
service.waiting().await?;
Ok(())
}