#![cfg_attr(
test,
allow(
clippy::unwrap_used,
clippy::expect_used,
reason = "tests use unwrap and expect to keep fixture setup concise"
)
)]
use std::process::ExitCode;
use rmcp::ServiceExt;
use rmcp::transport::stdio;
use tracing_subscriber::EnvFilter;
mod params;
mod server;
mod tools;
pub fn run_stdio_server() -> ExitCode {
if std::env::args()
.skip(1)
.any(|arg| arg == "--version" || arg == "-V" || arg == "-v")
{
#[expect(
clippy::print_stdout,
reason = "version query writes to stdout by design"
)]
{
println!("fallow-mcp {}", env!("CARGO_PKG_VERSION"));
}
return ExitCode::SUCCESS;
}
let runtime = match tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
{
Ok(runtime) => runtime,
Err(error) => {
#[expect(
clippy::print_stderr,
reason = "startup failure diagnostic writes to stderr by design"
)]
{
eprintln!("fallow-mcp: failed to start tokio runtime: {error}");
}
return ExitCode::FAILURE;
}
};
match runtime.block_on(serve_stdio()) {
Ok(()) => ExitCode::SUCCESS,
Err(error) => {
#[expect(
clippy::print_stderr,
reason = "server failure diagnostic writes to stderr by design"
)]
{
eprintln!("fallow-mcp: {error}");
}
ExitCode::FAILURE
}
}
}
async fn serve_stdio() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt()
.with_writer(std::io::stderr)
.with_env_filter(EnvFilter::from_default_env())
.with_ansi(false)
.init();
let server = server::FallowMcp::new();
let service = server.serve(stdio()).await?;
service.waiting().await?;
Ok(())
}