max / goingson
1 file changed,
+16 insertions,
-2 deletions
| @@ -30,6 +30,7 @@ struct Args { | |||
| 30 | 30 | grants: HashSet<String>, | |
| 31 | 31 | grant_all: bool, | |
| 32 | 32 | compact: bool, | |
| 33 | + | stdio: bool, | |
| 33 | 34 | } | |
| 34 | 35 | ||
| 35 | 36 | impl Args { | |
| @@ -40,6 +41,7 @@ impl Args { | |||
| 40 | 41 | let mut grants = HashSet::new(); | |
| 41 | 42 | let mut grant_all = false; | |
| 42 | 43 | let mut compact = false; | |
| 44 | + | let mut stdio = false; | |
| 43 | 45 | ||
| 44 | 46 | let mut it = std::env::args().skip(1); | |
| 45 | 47 | while let Some(arg) = it.next() { | |
| @@ -56,6 +58,7 @@ impl Args { | |||
| 56 | 58 | } | |
| 57 | 59 | "--grant-all" => grant_all = true, | |
| 58 | 60 | "--compact" => compact = true, | |
| 61 | + | "--stdio" => stdio = true, | |
| 59 | 62 | "-h" | "--help" => { | |
| 60 | 63 | print_help(); | |
| 61 | 64 | std::process::exit(0); | |
| @@ -63,7 +66,7 @@ impl Args { | |||
| 63 | 66 | other => return Err(format!("unknown argument: {other}")), | |
| 64 | 67 | } | |
| 65 | 68 | } | |
| 66 | - | Ok(Self { db, host, port, grants, grant_all, compact }) | |
| 69 | + | Ok(Self { db, host, port, grants, grant_all, compact, stdio }) | |
| 67 | 70 | } | |
| 68 | 71 | } | |
| 69 | 72 | ||
| @@ -74,8 +77,10 @@ fn next(it: &mut impl Iterator<Item = String>, flag: &str) -> Result<String, Str | |||
| 74 | 77 | fn print_help() { | |
| 75 | 78 | eprintln!( | |
| 76 | 79 | "go-mcp — MCP server for GoingsOn tasks/projects\n\n\ | |
| 77 | - | Usage: go-mcp [--db <path>] [--host <ip>] [--port <n>]\n\ | |
| 80 | + | Usage: go-mcp [--db <path>] [--stdio] [--host <ip>] [--port <n>]\n\ | |
| 78 | 81 | \x20 [--grant <cap>]... | [--grant-all] [--compact]\n\n\ | |
| 82 | + | Transport: default is Streamable HTTP; --stdio speaks newline-delimited\n\ | |
| 83 | + | \x20 JSON-RPC over stdin/stdout (for `claude mcp add go-mcp -- go-mcp --stdio`).\n\n\ | |
| 79 | 84 | Write capabilities (grant to enable the matching tool):\n\ | |
| 80 | 85 | \x20 go.project.create go.task.create go.task.bulk_import\n\ | |
| 81 | 86 | \x20 go.task.update go.task.complete\n" | |
| @@ -84,7 +89,10 @@ fn print_help() { | |||
| 84 | 89 | ||
| 85 | 90 | #[tokio::main] | |
| 86 | 91 | async fn main() -> Result<(), Box<dyn std::error::Error>> { | |
| 92 | + | // Logs go to stderr: in --stdio mode stdout is the protocol channel and any | |
| 93 | + | // stray byte there corrupts the JSON-RPC stream. | |
| 87 | 94 | tracing_subscriber::fmt() | |
| 95 | + | .with_writer(std::io::stderr) | |
| 88 | 96 | .with_env_filter( | |
| 89 | 97 | tracing_subscriber::EnvFilter::try_from_default_env() | |
| 90 | 98 | .unwrap_or_else(|_| "info,go_mcp=debug".into()), | |
| @@ -136,6 +144,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> { | |||
| 136 | 144 | grants: Some(grants), | |
| 137 | 145 | }; | |
| 138 | 146 | ||
| 147 | + | if args.stdio { | |
| 148 | + | tracing::info!(db = %db_path.display(), "go-mcp serving over stdio"); | |
| 149 | + | server::stdio::serve(registry, config).await?; | |
| 150 | + | return Ok(()); | |
| 151 | + | } | |
| 152 | + | ||
| 139 | 153 | let bind = format!("{}:{}", args.host, args.port); | |
| 140 | 154 | let bound = server::serve(registry, &bind, config).await?; | |
| 141 | 155 | tracing::info!(db = %db_path.display(), url = %bound.url(), "go-mcp listening"); |