From debd29afc3fdd154b6e991c98abf321afa31e843 Mon Sep 17 00:00:00 2001 From: MOLW <85048713+MatrixAdventurer@users.noreply.github.com> Date: Wed, 11 Nov 2026 22:37:00 +0800 Subject: [PATCH 1/2] fix(mcp): reject invalid JSON-RPC ids before stdio/SSE dispatch (#391) --- memoria/crates/memoria-mcp/src/server.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/memoria/crates/memoria-mcp/src/server.rs b/memoria/crates/memoria-mcp/src/server.rs index ec3f8eb1..9de1ee1b 100644 --- a/memoria/crates/memoria-mcp/src/server.rs +++ b/memoria/crates/memoria-mcp/src/server.rs @@ -264,7 +264,7 @@ fn sse_router( // payloads still need an Invalid Request response, even without id. // Preserve the legacy endpoint's jsonrpc-field compatibility here; // tightening envelope validation is separate from notification handling. - if !req.is_object() || method.is_empty() { + if !req.is_object() || method.is_empty() || req.get("id").is_some_and(|id| !(id.is_string() || id.is_number() || id.is_null())) { let response_id = req.get("id").filter(|id| id.is_string() || id.is_number()).cloned().unwrap_or(Value::Null); let resp = serde_json::json!({"jsonrpc":"2.0","id":response_id, "error":{"code":-32600,"message":"Invalid Request"}}); @@ -354,8 +354,9 @@ async fn run_io( .cloned() .unwrap_or(Value::Null); let is_object = value.is_object(); + let valid_id = value.get("id").is_none_or(|id| id.is_string() || id.is_number() || id.is_null()); let req = match serde_json::from_value::(value) { - Ok(req) if is_object && !req.method.is_empty() => req, + Ok(req) if is_object && valid_id && !req.method.is_empty() => req, _ => { let resp = json!({"jsonrpc":"2.0","id":response_id, "error":{"code":-32600,"message":"Invalid Request"}}); From eb5662067afbd210d2f5bc1c2759fc178056e881 Mon Sep 17 00:00:00 2001 From: MOLW <85048713+MatrixAdventurer@users.noreply.github.com> Date: Wed, 11 Nov 2026 22:37:10 +0800 Subject: [PATCH 2/2] fix(api): reject malformed JSON-RPC ids before HTTP dispatch (#391) --- memoria/crates/memoria-api/src/routes/mcp.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/memoria/crates/memoria-api/src/routes/mcp.rs b/memoria/crates/memoria-api/src/routes/mcp.rs index 339b7b76..ec84b045 100644 --- a/memoria/crates/memoria-api/src/routes/mcp.rs +++ b/memoria/crates/memoria-api/src/routes/mcp.rs @@ -260,6 +260,16 @@ pub async fn mcp_handler( ); } + // Reject non-conforming IDs before ping, notification, or tool dispatch. + if req.get("id").is_some_and(|id| !(id.is_string() || id.is_number() || id.is_null())) { + validation_err!( + "/mcp/_invalid_request", + -32600, + json!({"jsonrpc": "2.0", "id": null, + "error": {"code": -32600, "message": "Invalid Request: id must be string, number or null"}}) + ); + } + // "jsonrpc" MUST equal exactly "2.0". if req.get("jsonrpc").and_then(|v| v.as_str()) != Some("2.0") { let id = req.get("id").cloned().unwrap_or(serde_json::Value::Null);