Skip to main content

faucet_cli/commands/
mcp.rs

1//! `faucet mcp` — an MCP server over stdio (issue #420).
2//!
3//! Reads newline-delimited JSON-RPC 2.0 messages from stdin and writes each
4//! response to stdout (one JSON object per line), dispatching through the
5//! shared [`crate::mcp`] handler. stdio is local-trust: there is no bearer /
6//! RBAC layer, so mutating tools require the explicit `--allow-mutations` flag.
7//!
8//! Logs go to **stderr** (installed in `run_main`) so the stdout JSON-RPC
9//! stream stays clean.
10
11use crate::cli::McpArgs;
12use crate::error::CliResult;
13use crate::mcp::{McpContext, serve_stdio};
14use tokio::io::BufReader;
15
16pub async fn run(args: McpArgs) -> CliResult<()> {
17    let cwd = std::env::current_dir()?;
18    let env_path =
19        crate::env_loader::resolve_env_file(args.env_file.as_deref(), args.no_env_file, &cwd)?;
20    crate::env_loader::load_env_file_if_present(env_path.as_deref())?;
21
22    // stdio mode has no shared `auth:` catalog; inline connector auth still
23    // works for configs passed to preview/validate/run_pipeline.
24    let auth = crate::auth_catalog::build_auth_catalog(None)?;
25    let ctx = McpContext::new(auth, args.allow_mutations);
26
27    tracing::info!(
28        allow_mutations = args.allow_mutations,
29        "faucet mcp stdio server started"
30    );
31
32    let mut stdout = tokio::io::stdout();
33    serve_stdio(&ctx, BufReader::new(tokio::io::stdin()), &mut stdout).await?;
34
35    tracing::info!("faucet mcp stdio server stopped (stdin closed)");
36    Ok(())
37}