Skip to content

mcp: read a colon-less SSE line as a field with an empty value - #1374

Merged
guglielmo-san merged 2 commits into
modelcontextprotocol:mainfrom
gu-feng418:fix/sse-colonless-field-line
Oct 12, 2026
Merged

guglielmo-san merged 2 commits into
modelcontextprotocol:mainfrom
gu-feng418:fix/sse-colonless-field-line

Conversation

@gu-feng418

Copy link
Copy Markdown
Contributor

The SSE parsing rules read a line with no : as a field name whose value is
the empty string, so an unknown such line is ignored and a bare data line
contributes an empty data line. scanEventsLimited instead reported
errMalformedEvent for it, and processStream treats that error as terminal
and calls c.fail, so one stray line ended the whole client session. The
legacy SSEClientTransport shares the same scanner.

This matters for gateways and proxies that interleave a non-standard keepalive
line into an event stream: the connection is unusable after a single line that
the spec defines as valid.

The scanner now takes the whole line as the field name and an empty value, and
the existing switch below keeps ignoring unrecognized names. Comment lines
(starting with :) and the size cap are untouched, so errMalformedEvent is
still reported for an event that exceeds maxEventSize.

Two cases in mcp/event_test.go asserted the old rejection, so they now assert
the spec reading; TestStreamableClientConnect_ColonlessSSELine covers the
session-level repro from the issue.

Fixes #1373

The SSE parsing rules treat a line with no colon as a field name whose value
is the empty string, so an unknown such line is ignored and a bare "data" line
adds an empty data line. scanEventsLimited instead reported errMalformedEvent,
which processStream considers terminal: one stray line failed the whole client
session.

@chrikrah chrikrah left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gu-feng418 this is right, and I would merge it. The session-level repro from #1373 fails on the merge base and passes at f2ce77c, with the full suite green on both trees.

# go 1.27.1 linux/amd64; head f2ce77c, merge base 944ac19
$ go test ./mcp/ -run TestProbeColonlessSSELine -count=1 -v   # base 944ac19
PROBE extra="heartbeat"
PROBE extra="data"
--- FAIL: TestProbeColonlessSSELine/heartbeat
--- FAIL: TestProbeColonlessSSELine/data
FAIL	github.com/modelcontextprotocol/go-sdk/mcp

$ go test ./mcp/ -run 'TestProbeColonlessSSELine|TestStreamableClientConnect_ColonlessSSELine' -count=1 -v   # head f2ce77c
--- PASS: TestProbeColonlessSSELine
--- PASS: TestStreamableClientConnect_ColonlessSSELine
ok  	github.com/modelcontextprotocol/go-sdk/mcp	0.020s

$ go build ./... && go test ./... -count=1
# head f2ce77c: exit 0, 14 packages ok
# base 944ac19: exit 0, 14 packages ok

non-blocking: SSEClientTransport shares this scanner, and the body says so, but the new session test covers only StreamableClientTransport. One case on the legacy transport would pin that half of the claim.

@guglielmo-san #1373 asked whether the scanner should follow the SSE rules, and the two cases you rewrote in mcp/event_test.go are that decision. Do you want the spec reading, or should a colon-less line keep failing?

@guglielmo-san
guglielmo-san merged commit 8c761a2 into modelcontextprotocol:main Oct 12, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SSE: one line without a colon ends the whole client session

3 participants