fix(cr-2026-04-22): list_transcripts tool returns -32602 on malformed params

MAJOR from the 2026-04-22 review (crates/mcp/src/lib.rs:188-195):
the handler called serde_json::from_value(args).unwrap_or_default(),
so a request like { "limit": "twenty" } silently became the default
limit of 20. Every other tool handler in this file map_errs to
-32602 Invalid arguments; this one was the outlier.

Switches to the same map_err pattern. Empty params still
deserialise cleanly to Args::default (via #[serde(default)] on the
Option<i64> field), so callers that send no args are unaffected —
only genuinely malformed shapes now error.

Regression test: tools/call with list_transcripts and a
string-typed limit must return code -32602 with an "Invalid
arguments" message.
This commit is contained in:
2026-04-22 09:14:42 +01:00
parent d25b095788
commit 840012822f

View File

@@ -191,7 +191,13 @@ async fn list_transcripts_tool(pool: &SqlitePool, args: Value) -> Result<Value,
#[serde(default)]
limit: Option<i64>,
}
let args: Args = serde_json::from_value(args).unwrap_or_default();
// Every other tool handler returns -32602 on malformed args;
// previously this one silently fell back to defaults, hiding
// client bugs (2026-04-22 review MAJOR). An empty Value
// deserialises cleanly into Args::default, so the "no args"
// path still works — only genuinely malformed payloads error.
let args: Args = serde_json::from_value(args)
.map_err(|e| error(-32602, format!("Invalid arguments: {e}")))?;
let limit = args.limit.unwrap_or(20).clamp(1, 200);
let rows = kon_storage::list_transcripts(pool, limit)
@@ -420,6 +426,32 @@ mod tests {
assert!(err.message.contains("expected value"));
}
#[tokio::test]
async fn list_transcripts_rejects_malformed_params_with_invalid_arguments() {
// Regression for the 2026-04-22 review MAJOR: previously the
// handler did `from_value(args).unwrap_or_default()`, so
// `{"limit": "not-a-number"}` silently became `limit = 20`.
// Every other handler returns -32602 on shape mismatch; this
// one must now do the same.
let request = json!({
"jsonrpc": "2.0",
"id": 99,
"method": "tools/call",
"params": {
"name": "list_transcripts",
"arguments": { "limit": "twenty" },
},
});
let pool = sqlx::SqlitePool::connect("sqlite::memory:").await.unwrap();
let response = handle_message(&pool, request).await.expect("has response");
assert!(response.result.is_none());
let err = response.error.expect("expected error");
assert_eq!(err.code, -32602, "invalid arguments must surface as -32602");
assert!(err.message.contains("Invalid arguments"));
}
#[tokio::test]
async fn unknown_method_returns_method_not_found_error() {
let request = json!({