Skip to main content

choir_mcp/
choir-mcp.rs

1//! `choir-mcp` — synchronous stdio MCP adapter over a choir node.
2//!
3//! ```text
4//! choir-mcp <api> [--auth-file <path>] [--auth-user <name>]
5//! ```
6//!
7//! All configuration is explicit CLI input. The optional auth file uses
8//! the node's `user:token`-per-line format; no environment variable is
9//! read for configuration.
10
11use choir_cli::mcp::{serve, HttpClient};
12
13const USAGE: &str = "usage: choir-mcp <api> [--auth-file <path>] [--auth-user <name>]
14
15  Speaks MCP over stdin and stdout. Point an agent's MCP configuration at
16  it; it is not meant to be run at a prompt.
17";
18
19fn usage() -> ! {
20    eprint!("{USAGE}");
21    std::process::exit(2);
22}
23
24fn main() {
25    let args: Vec<String> = std::env::args().skip(1).collect();
26    // Before the URL check, or `--help` is read as an address and
27    // answered with a complaint about its scheme.
28    if args.iter().any(|a| a == "--help" || a == "-h") {
29        print!("{USAGE}");
30        std::process::exit(0);
31    }
32    let Some(api) = args.first() else {
33        usage();
34    };
35    let mut auth_file = None;
36    let mut auth_user = None;
37    let mut index = 1;
38    while index < args.len() {
39        let Some(value) = args.get(index + 1) else {
40            usage();
41        };
42        match args[index].as_str() {
43            "--auth-file" if auth_file.is_none() => auth_file = Some(value.as_str()),
44            "--auth-user" if auth_user.is_none() => auth_user = Some(value.as_str()),
45            _ => usage(),
46        }
47        index += 2;
48    }
49    let client = match HttpClient::new(api, auth_file.map(std::path::Path::new), auth_user) {
50        Ok(client) => client,
51        Err(error) => {
52            eprintln!("choir-mcp: {error}");
53            std::process::exit(2);
54        }
55    };
56    let stdin = std::io::stdin();
57    let stdout = std::io::stdout();
58    if let Err(error) = serve(stdin.lock(), stdout, &client) {
59        eprintln!("choir-mcp: stdio failed: {error}");
60        std::process::exit(1);
61    }
62}