pub mod args;
pub mod commands;
pub mod error;
pub mod output;
use std::io::IsTerminal;
use clap::Parser;
use tracing::Level;
use crate::error::ProxyResult;
#[derive(Parser, Debug)]
#[command(
name = "turbomcp-proxy",
version,
about = "Universal MCP adapter - introspection, proxying, and code generation",
long_about = "A world-class tool for working with Model Context Protocol (MCP) servers.\n\
Inspect capabilities, proxy connections, and generate typed adapters.",
author
)]
pub struct Cli {
#[command(subcommand)]
pub command: commands::Command,
#[arg(short, long, action = clap::ArgAction::Count, global = true)]
pub verbose: u8,
#[arg(short, long, global = true, conflicts_with = "verbose")]
pub quiet: bool,
#[arg(short = 'f', long, value_enum, default_value = "human", global = true)]
pub format: output::OutputFormat,
#[arg(long, global = true)]
pub no_color: bool,
}
impl Cli {
pub async fn execute(self) -> ProxyResult<()> {
self.init_tracing();
if self.no_color || !std::io::stdout().is_terminal() {
colored::control::set_override(false);
}
self.command.execute(self.format).await
}
fn init_tracing(&self) {
let level = if self.quiet {
Level::ERROR
} else {
match self.verbose {
0 => Level::WARN,
1 => Level::INFO,
2 => Level::DEBUG,
_ => Level::TRACE,
}
};
tracing_subscriber::fmt()
.with_max_level(level)
.with_target(false)
.with_thread_ids(false)
.with_thread_names(false)
.init();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cli_parsing() {
let cli = Cli::try_parse_from([
"turbomcp-proxy",
"inspect",
"--backend",
"stdio",
"--cmd",
"python",
]);
assert!(cli.is_ok());
}
#[test]
fn test_verbosity_levels() {
let cli = Cli::try_parse_from([
"turbomcp-proxy",
"-vvv",
"inspect",
"--backend",
"stdio",
"--cmd",
"test",
])
.unwrap();
assert_eq!(cli.verbose, 3);
}
#[test]
fn test_quiet_conflicts_with_verbose() {
let cli = Cli::try_parse_from([
"turbomcp-proxy",
"-v",
"--quiet",
"inspect",
"--backend",
"stdio",
"--cmd",
"test",
]);
assert!(cli.is_err());
}
}