use clap::Parser;
use std::path::PathBuf;
#[derive(Parser, Debug, Clone)]
pub struct LoggingArgs {
#[arg(
long,
default_value = "false",
help = "启用详细诊断模式,追踪连接生命周期和超时问题(默认关闭)"
)]
pub diagnostic: bool,
#[arg(long, help = "日志输出目录,将自动生成日志文件名")]
pub log_dir: Option<PathBuf>,
#[arg(long, conflicts_with = "log_dir", help = "日志文件完整路径")]
pub log_file: Option<PathBuf>,
#[arg(
long,
env = "OTEL_EXPORTER_OTLP_ENDPOINT",
help = "OTLP 追踪端点 (如 http://localhost:4317)"
)]
pub otlp_endpoint: Option<String>,
#[arg(
long,
default_value = "mcp-proxy",
help = "追踪服务名称(用于 Jaeger 等追踪后端标识)"
)]
pub service_name: String,
}
#[derive(Parser, Debug)]
#[command(name = "mcp-proxy")]
#[command(version = env!("CARGO_PKG_VERSION"))]
#[command(about = "MCP 协议转换代理工具", long_about = None)]
pub struct Cli {
#[command(subcommand)]
pub command: Option<Commands>,
#[arg(value_name = "URL", help = "MCP 服务的 URL 地址(直接模式)")]
pub url: Option<String>,
#[arg(short, long, global = true)]
pub verbose: bool,
#[arg(short, long, global = true)]
pub quiet: bool,
}
#[derive(clap::Subcommand, Debug)]
pub enum Commands {
Convert(ConvertArgs),
Check(CheckArgs),
Detect(DetectArgs),
Proxy(crate::client::proxy_server::ProxyArgs),
Health(HealthArgs),
}
#[derive(Parser, Debug, Clone)]
pub struct ConvertArgs {
#[arg(value_name = "URL", help = "MCP 服务的 URL 地址")]
pub url: Option<String>,
#[arg(long, conflicts_with = "config_file", help = "MCP 服务配置 JSON")]
pub config: Option<String>,
#[arg(long, conflicts_with = "config", help = "MCP 服务配置文件路径")]
pub config_file: Option<PathBuf>,
#[arg(short, long, help = "MCP 服务名称(多服务配置时必需)")]
pub name: Option<String>,
#[arg(long, value_enum, help = "指定远程服务协议类型(不指定则自动检测)")]
pub protocol: Option<crate::client::proxy_server::ProxyProtocol>,
#[arg(short, long, help = "认证 header")]
pub auth: Option<String>,
#[arg(short = 'H', long, value_parser = parse_key_val, help = "自定义 HTTP headers (KEY=VALUE 格式)")]
pub header: Vec<(String, String)>,
#[arg(long, default_value = "0", help = "重试次数,0 表示无限重试")]
pub retries: u32,
#[arg(
long,
value_delimiter = ',',
help = "工具白名单(逗号分隔),只允许指定的工具"
)]
pub allow_tools: Option<Vec<String>>,
#[arg(
long,
value_delimiter = ',',
help = "工具黑名单(逗号分隔),排除指定的工具"
)]
pub deny_tools: Option<Vec<String>>,
#[arg(
long,
default_value = "30",
help = "客户端 ping 间隔(秒),0 表示禁用"
)]
pub ping_interval: u64,
#[arg(
long,
default_value = "10",
help = "客户端 ping 超时(秒),超时则认为连接断开"
)]
pub ping_timeout: u64,
#[command(flatten)]
pub logging: LoggingArgs,
}
#[derive(Parser, Debug)]
pub struct CheckArgs {
#[arg(value_name = "URL")]
pub url: String,
#[arg(short, long)]
pub auth: Option<String>,
#[arg(long, default_value = "10")]
pub timeout: u64,
}
#[derive(Parser, Debug)]
pub struct DetectArgs {
#[arg(value_name = "URL")]
pub url: String,
#[arg(short, long)]
pub auth: Option<String>,
}
#[derive(Parser, Debug)]
#[command(after_help = "\
退出码:
0 服务健康 - MCP 连接握手成功
1 服务不健康 - 连接失败、超时或握手失败
示例:
# 基本用法
mcp-proxy health http://localhost:8080/mcp
# 带认证
mcp-proxy health http://localhost:8080/mcp -a \"Bearer token123\"
# 指定协议和超时
mcp-proxy health http://localhost:8080/mcp --protocol sse --timeout 5
# 静默模式(仅返回退出码,适合脚本使用)
mcp-proxy health http://localhost:8080/mcp -q
# 在 shell 脚本中使用
if mcp-proxy health http://localhost:8080/mcp -q; then
echo \"MCP 服务正常\"
else
echo \"MCP 服务不可用\"
fi
")]
pub struct HealthArgs {
#[arg(value_name = "URL")]
pub url: String,
#[arg(short, long, help = "认证 header")]
pub auth: Option<String>,
#[arg(short = 'H', long, value_parser = parse_key_val, help = "自定义 HTTP headers (KEY=VALUE 格式)")]
pub header: Vec<(String, String)>,
#[arg(long, default_value = "10")]
pub timeout: u64,
#[arg(long, value_enum, help = "指定远程服务协议类型(不指定则自动检测)")]
pub protocol: Option<crate::client::proxy_server::ProxyProtocol>,
}
pub fn parse_key_val(s: &str) -> Result<(String, String), String> {
let pos = s
.find('=')
.ok_or_else(|| format!("无效的 KEY=VALUE 格式: {}", s))?;
Ok((s[..pos].to_string(), s[pos + 1..].to_string()))
}