fix(cr-2026-04-22): MCP stdio replies with parse-error on malformed JSON

MAJOR from the 2026-04-22 review (crates/mcp/src/main.rs:26-30): the
stdio transport logged malformed JSON lines to stderr and continued
without sending any JSON-RPC response. Clients saw silence instead of
the -32700 Parse Error they could key off. handle_message has a
parse-error branch for shape mismatch, but it never ran for bytes
that failed to parse as JSON at all.

Exposes a new public helper kon_mcp::parse_error_response(detail)
that mirrors the existing internal error_response pattern, filling
id with null per JSON-RPC 2.0 §5.1 (parse error, no id recoverable).
main.rs now writes that response out before continuing the read
loop.

Regression test on the helper asserts: jsonrpc "2.0", id null,
code -32700, message starts with "Parse error" and includes the
underlying serde detail.
This commit is contained in:
2026-04-22 09:13:33 +01:00
parent 7ece0df0ac
commit d25b095788
2 changed files with 34 additions and 8 deletions

View File

@@ -23,18 +23,22 @@ async fn main() -> anyhow::Result<()> {
continue;
}
let raw: serde_json::Value = match serde_json::from_str(trimmed) {
Ok(value) => value,
let response = match serde_json::from_str::<serde_json::Value>(trimmed) {
Ok(raw) => match kon_mcp::handle_message(&pool, raw).await {
Some(response) => response,
None => continue, // notification — no reply
},
Err(err) => {
eprintln!("[kon-mcp] ignoring malformed line: {err}");
continue;
// Per JSON-RPC 2.0 §5.1: a Parse Error responds with
// code -32700 and id null. Previously this branch
// logged and continued, dropping the response —
// clients saw silence instead of a structured error
// (2026-04-22 review MAJOR).
eprintln!("[kon-mcp] parse error: {err}");
kon_mcp::parse_error_response(&err.to_string())
}
};
let Some(response) = kon_mcp::handle_message(&pool, raw).await else {
continue; // notification — no reply
};
let payload = serde_json::to_string(&response)?;
stdout.write_all(payload.as_bytes()).await?;
stdout.write_all(b"\n").await?;