Skip to content

Streamable HTTP: a 2026-07-28 POST carrying Last-Event-ID is refused instead of ignored #1233

Description

@ddromanidis

Describe the bug

For protocol version 2026-07-28 the streamable HTTP server refuses any POST that carries a Last-Event-ID header, where the spec asks it to ignore the header.

mcp/streamable.go, servePOST (v1.7.0 line 1412, main line 1442):

if len(req.Header.Values(lastEventIDHeader)) > 0 {
    http.Error(w, "can't send Last-Event-ID for POST request", http.StatusBadRequest)
    return
}

The 2026-07-28 transport page, "Backward compatibility with older revisions", says a server that receives such traffic from an older client SHOULD respond as follows, and lists: "A Last-Event-ID header: ignore it; streams are not resumable." Resumable streams left with this revision, so the header is a harmless leftover from a client that speaks the previous one, and as written it turns that client into one that cannot call anything: every POST is a text/plain 400.

To Reproduce

Against any NewStreamableHTTPHandler with Stateless: true:

curl -s -X POST http://127.0.0.1:8080/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'Mcp-Protocol-Version: 2026-07-28' \
  -H 'Mcp-Method: server/discover' \
  -H 'Last-Event-ID: stream-1:7' \
  -d '{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientInfo":{"name":"probe","version":"1"},"io.modelcontextprotocol/clientCapabilities":{}}}}'

Observed: 400 can't send Last-Event-ID for POST request.

Expected behavior

The header is ignored and the request is served. Keeping the rejection for sessions negotiated at an older version, where a POST with Last-Event-ID really is malformed, seems fine; the check just should not apply when the request is 2026-07-28.

Additional context

go-sdk v1.7.0; the code is unchanged on main as of today. I am working around it with a handler in front of the SDK's that deletes the header before delegating.

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