Repository navigation
Priming streamable http events use hardcoded event id of: "0" #791
Description
Describe the bug
From what I am seeing the event ID of priming events of an SSE event stream are always using an event id of: "0". I would expect the first priming event of the stream to contain the "http_request_id" as part of the event id: 0/<http_request_id> so it will be possible to resume the stream if disconnected.
To Reproduce
Run the counter example server:
cargo run -p mcp-server-examples --example servers_counter_streamhttpThen run the following bash script with curl:
#!/usr/bin/env bash
set -euo pipefail
URL="${1:-http://localhost:8000/mcp}"
# POST initialize and capture session ID from response header
SESSION_ID=$(curl -s -D - -o /dev/null \
-X POST "$URL" \
-H "Accept: application/json, text/event-stream" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": { "name": "test-client", "version": "1.0.0" }
}
}' | grep -i "^mcp-session-id:" | awk '{print $2}' | tr -d '\r')
if [[ -z "$SESSION_ID" ]]; then
echo "ERROR: No Mcp-Session-Id header in response" >&2
exit 1
fi
echo "Session ID: $SESSION_ID"
export MCP_SESSION_ID="$SESSION_ID"
# POST notifications/initialized to complete the MCP handshake
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
-X POST "$URL" \
-H "Accept: application/json, text/event-stream" \
-H "Content-Type: application/json" \
-H "Mcp-Session-Id: $MCP_SESSION_ID" \
-H "Mcp-Protocol-Version: 2025-06-18" \
-d '{
"jsonrpc": "2.0",
"method": "notifications/initialized"
}')
if [[ "$STATUS" != "202" ]]; then
echo "ERROR: notifications/initialized returned $STATUS, expected 202" >&2
exit 1
fi
echo "Handshake complete (202 Accepted)"
echo "MCP_SESSION_ID=$MCP_SESSION_ID"
for i in {1..2}; do
echo "Calling tool ... (iteration $i)"
curl 'http://127.0.0.1:8000/mcp' \
-H 'accept: application/json, text/event-stream' \
-H 'content-type: application/json' \
-H "Mcp-Protocol-Version: 2025-06-18" \
-H "mcp-session-id: $MCP_SESSION_ID" \
--data-raw '{"method":"tools/call","params":{"name":"say_hello","arguments":{},"_meta":{"progressToken":1}},"jsonrpc":"2.0","id":'"$i"'}'
doneOutput shows that both tool calls contain a priming event with "id: 0":
Session ID: ef43085b-0b2b-4b9f-ba59-6e5d26118376
Handshake complete (202 Accepted)
MCP_SESSION_ID=ef43085b-0b2b-4b9f-ba59-6e5d26118376
Calling tool ... (iteration 1)
data:
id: 0
retry: 3000
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"hello"}],"isError":false}}
id: 0/0
Calling tool ... (iteration 2)
data:
id: 0
retry: 3000
data: {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"hello"}],"isError":false}}
id: 0/1
Expected behavior
Priming event id of: "0/<http_request_id>"
Additional context
From the spec: https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#resumability-and-redelivery
Event IDs SHOULD encode sufficient information to identify the originating stream, enabling the server to correlate a Last-Event-ID to the correct stream.
Activity
glicht commentedon Apr 10, 2026
@DaleSeo thanks for taking this. The fix in your branch looks good. In a test I ran, I am able to resume on the priming event.
I would like to raise another issue that the resume only works as long as the ongoing request is executing, which is sub optimal.
For example using
servers_counter_streamhttpexample and the toollong_task(which runs for 10 seconds) with the following script works and I am able to get the result when performing a GET request to resume:But if I change the script so the curl with the
tools/callhas a timeout of 8 and then with the sleep of 3., so total time since start of request is 11, the resume GET request will not work.From what I understand the cache of the results from the stream is discarded once the request completes processing and thus the resume doesn't work when it extends beyond the time of the request.
DaleSeo commentedon Apr 10, 2026
@glicht Thanks for confirming that the priming fix works and for suggesting the follow-up!
I've added a second fix in the same PR #799 that keeps the cache after completion. The sender is replaced with a closed dummy, so no new events are routed, but the cached events remain available for resume. The stream ends cleanly after replaying.
I'd appreciate it if you could take another look when you have a chance.