Disconnecting during a Last-Event-ID replay leaves a dead SSE stream and the session answers 409 forever
Maintainers usually reply within 1 day
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 76/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- typescript
- Domain
- backend-api-design
Research direction
Start in the GET handler in src/routes/mcp.ts and trace when the stream is added, replayed, and cleaned up on close. Reproduce the described abort during delayed getMessagesFrom replay, then verify the socket is checked after replay and later GETs for the session no longer return 409.
Written by the indexing model from the issue text.
Description
Summary
If a legacy SSE client disconnects while its Last-Event-ID replay is still running, the stream it leaves behind is never cleaned up. From then on, every GET for that session gets 409.
Cause
In the GET handler (src/routes/mcp.ts), streams.add(reply) and await replayMessagesFromEventId(...) both run before any close handler is registered. A client that leaves during the replay is therefore never removed from localStreams. The heartbeat set up afterwards writes to the destroyed socket without throwing and keeps refreshing the session TTL, so nothing removes the dead stream.
Repro
- Run a real server, with
getMessagesFromdelayed by 300ms to stand in for Redis XRANGE latency. - Open a GET with
Last-Event-IDand abort the client after 100ms. - Every later GET for that session returns 409, and it still does 5s later with
sessionTtlMs: 2000.
Suggested fix
Register the close and cleanup handlers immediately after streams.add(reply), before any await. Check raw.destroyed once the replay finishes.
🤖 Generated with Claude Code
- Dominant language
- TypeScript
- Stars
- 58
- Forks
- 15
- Avg merge
- 1d 7h
- Merged PRs (30d)
- 53
Getting set up
- Ships a Dockerfile or Docker Compose file
- No pull request template
- No contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from platformatic/mcp
-
README: the Resource URI Schemas example's file://documents/* pattern only matches that literal URIOpen
Difficulty 1/5 Under an hour Newbie friendliness 88/100
platformatic/mcp#361 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
platformatic/mcp#360 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
platformatic/mcp#359 ·
Maintainers usually reply within 1 day
-
Legacy POST accepts messages that are not JSON-RPC 2.0Possibly taken @mcollina claimed this 2 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
platformatic/mcp#344 ·
Maintainers usually reply within 1 day
-
Legacy SSE GET ignores resolveAuthorizationContext expiresAtPossibly taken A pull request linked to this issue is open or already merged. Open
Difficulty 2/5 1-3 hours Newbie friendliness 73/100
platformatic/mcp#336 ·
Maintainers usually reply within 1 day
All issues in platformatic/mcp
Similar issues
-
bug P3 UI/UX
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
StellarState/SS-backend#712 ·
Maintainers usually reply within 1 day
-
area: backend bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
snapotter-hq/SnapOtter#3473 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
gitroomhq/postiz-app#2304 ·
Maintainers usually reply within 1 day
-
harlan-agent-ready-to-implement routine:ci-review
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
harlan-zw/unlighthouse#454 · 2 comments ·
Maintainers usually reply within 1 day