Skip to content

streamable-http adapter drops SSE events after empty keepalive preamble #9

Description

@jonpspri

Symptom

Image client (ghcr.io/contextforge-org/mcp-reverse-proxy:latest, 2026-09-14 digest sha256:5e94395…) fails gateway discovery against a fast Streamable-HTTP MCP server:

  • client logs Error handling MCP message: Input is a zero-length, empty document x4
  • initialize response never relayed -> gateway reverse-proxy initialize timed out after 60.0s
  • register_complete: error -> empty catalog, empty tool list; downstream redis-outage/client-stop/restart checks fail for the same reason (5 total failures)

The August run with a clone client parsed the byte-identical server response (265 bytes) fine.

Root cause

StreamableHttpAdapter.send extracted the FIRST data: line of an SSE body and stopped. The server opens the stream with a keepalive preamble before the JSON event:

data:
id: 0
retry: 3000

data: {json}

The first data: line has an empty payload, so the handler received "" and downstream orjson.loads("") raised exactly the reported error. The 404-retry path duplicated the same parser. Secondary defects in the same code: only the first event of a multi-event body was relayed, and multi-line data: frames were not joined per the SSE spec.

Fix

  • New shared parser mcp_reverse_proxy/transports/sse_events.py (SSE line protocol: data lines join with newlines, blank line dispatches, empty keepalive events skipped, trailing unterminated event flushed).
  • streamablehttp_adapter relays EVERY dispatched event in order (both primary and 404-retry paths); plain-JSON bodies fall back to the raw body as before.
  • sse_adapter now uses the same parser (behavior preserved: typed events only).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions