From 935c8cad88eb35a16af023ff1b2eb5239a0a9699 Mon Sep 17 00:00:00 2001 From: Alaundo Date: Sat, 21 Mar 2026 20:15:54 +0100 Subject: [PATCH] fix(mcp): handle Streamable HTTP MCP responses with SSE framing MCP servers using Streamable HTTP (e.g., Hindsight) wrap JSON-RPC responses in SSE framing (event: message\ndata: {...}\n\n). The SSE transport handler expected raw JSON, causing 'Invalid MCP SSE JSON-RPC response' errors when connecting to these servers. Extract the JSON payload from SSE data: lines before deserializing. Falls back to raw body parsing for servers that return plain JSON. Fixes connection to MCP servers implementing the Streamable HTTP transport (MCP spec 2025-03-26). --- crates/openfang-runtime/src/mcp.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/crates/openfang-runtime/src/mcp.rs b/crates/openfang-runtime/src/mcp.rs index 0fbfbb98..c2e6131b 100644 --- a/crates/openfang-runtime/src/mcp.rs +++ b/crates/openfang-runtime/src/mcp.rs @@ -365,7 +365,21 @@ impl McpConnection { .await .map_err(|e| format!("Failed to read SSE response: {e}"))?; - let rpc_response: JsonRpcResponse = serde_json::from_str(&body) + // Handle Streamable HTTP MCP responses that use SSE framing + // (e.g. "event: message\ndata: {...}\n\n"). Extract the JSON + // from the last `data:` line if the body looks like SSE. + let json_body = if body.trim_start().starts_with("event:") || body.trim_start().starts_with("data:") { + body.lines() + .filter_map(|line| line.strip_prefix("data: ").or_else(|| line.strip_prefix("data:"))) + .filter(|s| !s.is_empty()) + .last() + .unwrap_or(&body) + .to_string() + } else { + body + }; + + let rpc_response: JsonRpcResponse = serde_json::from_str(&json_body) .map_err(|e| format!("Invalid MCP SSE JSON-RPC response: {e}"))?; if let Some(err) = rpc_response.error {