#![forbid(unsafe_code)]
#![warn(missing_docs)]
#![cfg_attr(noyalib_coverage, allow(unstable_features))]
#![cfg_attr(noyalib_coverage, feature(coverage_attribute))]
use noyalib_mcp::{HandleOutcome, handle_message};
use std::io::{self, BufRead, Write};
use std::process::ExitCode;
const HELP: &str = "\
noyalib-mcp — Model Context Protocol server for noyalib's lossless
YAML editing.
USAGE:
noyalib-mcp Start the JSON-RPC stdio loop (the
normal mode an MCP client invokes).
noyalib-mcp --version | -V Print version and exit.
noyalib-mcp --help | -h Print this help and exit.
NOTES:
This binary speaks newline-delimited JSON-RPC 2.0 over stdio per
the MCP 2025-06 spec. It is not designed for interactive use —
configure your MCP-aware client (Claude, Cursor, Zed, …) to spawn
it instead. Example for Claude:
claude mcp add noyalib /usr/local/bin/noyalib-mcp
REPORTING BUGS:
https://github.com/sebastienrousseau/noyalib/issues
";
#[cfg_attr(noyalib_coverage, coverage(off))]
fn main() -> ExitCode {
if let Some(arg) = std::env::args().nth(1) {
match arg.as_str() {
"--version" | "-V" => {
println!("noyalib-mcp {}", env!("CARGO_PKG_VERSION"));
return ExitCode::SUCCESS;
}
"--help" | "-h" => {
print!("{HELP}");
return ExitCode::SUCCESS;
}
other => {
eprintln!("noyalib-mcp: unknown argument `{other}`");
eprintln!("Run `noyalib-mcp --help` for usage.");
return ExitCode::from(2);
}
}
}
match run() {
Ok(()) => ExitCode::SUCCESS,
Err(e) => {
eprintln!("noyalib-mcp: {e}");
ExitCode::from(3)
}
}
}
fn run() -> io::Result<()> {
let stdin = io::stdin();
let mut stdout = io::stdout().lock();
let mut line = String::new();
let mut handle = stdin.lock();
loop {
line.clear();
let n = handle.read_line(&mut line)?;
if n == 0 {
return Ok(());
}
let trimmed = line.trim();
if trimmed.is_empty() {
continue;
}
match handle_message(trimmed) {
HandleOutcome::Reply(payload) => {
writeln!(stdout, "{payload}")?;
stdout.flush()?;
}
HandleOutcome::Silent => {}
}
}
}