From d524c5e5e06eb61e5b118378afde9ba4c68db61c Mon Sep 17 00:00:00 2001 From: Loocor Date: Wed, 3 Sep 2025 17:13:54 +0800 Subject: [PATCH] fix: resolve compatibility issues with servers sending LSP notifications - Gracefully ignore non-MCP notifications (like window/logMessage) for compatibility - Add proper MCP method validation based on 2025-06-18 specification - Fix connection failures with servers like 21Magic that send IDE integration messages Resolves connection closed errors during initialization with mixed-protocol servers. --- crates/rmcp/src/transport/async_rw.rs | 73 ++++++++++++++++++++------- 1 file changed, 55 insertions(+), 18 deletions(-) diff --git a/crates/rmcp/src/transport/async_rw.rs b/crates/rmcp/src/transport/async_rw.rs index 3d561fe71..acd1b4f65 100644 --- a/crates/rmcp/src/transport/async_rw.rs +++ b/crates/rmcp/src/transport/async_rw.rs @@ -179,8 +179,31 @@ fn without_carriage_return(s: &[u8]) -> &[u8] { } } -/// Check if a notification method is a standard MCP notification -/// should update this when MCP spec is updated about new notifications +/// Check if a method is a standard MCP method (request, response, or notification). +/// This includes both requests and notifications defined in the MCP specification. +/// +/// Based on MCP specification 2025-06-18: https://modelcontextprotocol.io/specification/2025-06-18 +fn is_standard_method(method: &str) -> bool { + matches!( + method, + "initialize" + | "ping" + | "prompts/get" + | "prompts/list" + | "resources/list" + | "resources/read" + | "resources/subscribe" + | "resources/unsubscribe" + | "resources/templates/list" + | "tools/call" + | "tools/list" + | "completion/complete" + | "logging/setLevel" + | "roots/list" + | "sampling/createMessage" + ) || is_standard_notification(method) +} + fn is_standard_notification(method: &str) -> bool { matches!( method, @@ -196,6 +219,29 @@ fn is_standard_notification(method: &str) -> bool { ) } +/// Determines if a notification should be ignored for compatibility. +fn should_ignore_notification(json_value: &serde_json::Value, method: &str) -> bool { + let is_notification = json_value.get("id").is_none(); + + // Ignore non-MCP notifications (like LSP messages) for compatibility + if is_notification && !is_standard_method(method) { + tracing::trace!( + "Ignoring non-MCP notification '{}' for compatibility", + method + ); + return true; + } + + // Ignore non-standard MCP notifications + matches!( + ( + method.starts_with("notifications/"), + is_standard_notification(method) + ), + (true, false) + ) +} + /// Try to parse a message with compatibility handling for non-standard notifications fn try_parse_with_compatibility( line: &[u8], @@ -205,22 +251,13 @@ fn try_parse_with_compatibility( match serde_json::from_slice(line) { Ok(item) => Ok(Some(item)), Err(e) => { - // Check if this is a non-standard notification that should be ignored - if line_str.contains("\"method\":\"notifications/") { - // Extract the method name to check if it's standard - if let Ok(json_value) = serde_json::from_str::(line_str) { - if let Some(method) = json_value.get("method").and_then(|m| m.as_str()) { - if method.starts_with("notifications/") - && !is_standard_notification(method) - { - tracing::debug!( - "Ignoring non-standard notification {} {}: {}", - method, - context, - line_str - ); - return Ok(None); // Skip this message - } + // Check if this is a notification that should be ignored for compatibility + if let Ok(json_value) = serde_json::from_str::(line_str) { + if let Some(method) = + json_value.get("method").and_then(serde_json::Value::as_str) + { + if should_ignore_notification(&json_value, method) { + return Ok(None); } } }