use std::net::SocketAddr;
use crate::serve::ServeOptions;
#[derive(Debug, Clone, Default, PartialEq, Eq, clap::Args)]
pub struct ServeArgs {
#[arg(long, value_name = "ADDR")]
pub bind: Option<SocketAddr>,
#[arg(long, conflicts_with_all = ["bind", "allowed_hosts", "allowed_origins"])]
pub stdio: bool,
#[arg(long = "allowed-host", value_name = "HOST")]
pub allowed_hosts: Vec<String>,
#[arg(long = "allowed-origin", value_name = "ORIGIN")]
pub allowed_origins: Vec<String>,
}
impl ServeArgs {
pub fn bind_or(&self, default_bind: SocketAddr) -> SocketAddr {
self.bind.unwrap_or(default_bind)
}
pub fn serve_options(&self, default_bind: SocketAddr) -> ServeOptions {
ServeOptions::new(self.bind_or(default_bind))
.with_allowed_hosts(self.allowed_hosts.iter().cloned())
.with_allowed_origins(self.allowed_origins.iter().cloned())
}
}
#[cfg(test)]
mod tests {
use super::*;
use clap::Parser;
#[derive(Parser, Debug)]
struct Cli {
#[command(flatten)]
serve: ServeArgs,
}
fn parse(args: &[&str]) -> Result<ServeArgs, clap::Error> {
Cli::try_parse_from(std::iter::once("t").chain(args.iter().copied())).map(|c| c.serve)
}
#[test]
fn defaults_to_the_servers_own_bind() {
let a = parse(&[]).unwrap();
assert_eq!(a, ServeArgs::default());
let o = a.serve_options("127.0.0.1:9000".parse().unwrap());
assert_eq!(o.bind, "127.0.0.1:9000".parse().unwrap());
assert!(o.allowed_hosts.is_empty() && o.allowed_origins.is_empty());
}
#[test]
fn repeatable_hosts_and_origins() {
let a = parse(&[
"--bind",
"0.0.0.0:8100",
"--allowed-host",
"localhost",
"--allowed-host",
"mcp.example.com",
"--allowed-origin",
"https://app.example.com",
])
.unwrap();
let o = a.serve_options("127.0.0.1:9000".parse().unwrap());
assert_eq!(o.bind, "0.0.0.0:8100".parse().unwrap());
assert_eq!(o.allowed_hosts, ["localhost", "mcp.example.com"]);
assert_eq!(o.allowed_origins, ["https://app.example.com"]);
}
#[test]
fn stdio_excludes_http_flags() {
assert!(parse(&["--stdio"]).unwrap().stdio);
assert!(parse(&["--stdio", "--bind", "127.0.0.1:1"]).is_err());
assert!(parse(&["--stdio", "--allowed-host", "x"]).is_err());
assert!(parse(&["--bind", "not-an-address"]).is_err());
}
}