Repository navigation
fix(sse): close GET /mcp streams when the opening token expires - #279
Merged
Merged
Conversation
rozzilla
reviewed
Oct 14, 2026
| closeReason = 'Closing SSE stream because the token that opened it expired; it may reconnect with a fresh token' | ||
| } | ||
|
|
||
| // setTimeout fires immediately on delays past 2^31-1 ms (~24.8 days) |
Contributor
There was a problem hiding this comment.
Long-lived tokens never expire. When exp is more than 24.8 days away, this skips setTimeout() entirely. With no shorter sseMaxConnectionMs, the SSE stream can continue after the token expires. we should schedule a capped timer and reschedule until exp, or reject long delays.
Member
Author
There was a problem hiding this comment.
Fixed in bc55055: the close timer now waits in unref'd steps capped at 2^31-1 ms and reschedules until the deadline (the earlier of exp and sseMaxConnectionMs), and the pending timer is cleared on close. Added a mock-timers test with a token that expires in 30 days: the stream stays open after time moves past the 24.8-day limit and closes once it moves past exp.
mcollina
force-pushed
the
fix/254-close-streams-at-token-exp
branch
from
October 14, 2026 10:10
a0559fd to
bc55055
Compare
rozzilla
approved these changes
Oct 14, 2026
Access is checked only when a long-lived SSE stream opens, so a stream kept receiving notifications after the bearer token that opened it had expired. When authorization is enabled and the token carries an `exp`, schedule an unref'd timer that ends the stream at exp, cleared when the stream closes. The client reconnects with a fresh token and resumes with Last-Event-ID. The existing sseMaxConnectionMs option (SEP-1699) already provides a configurable maximum stream lifetime; the stream now closes at min(exp, sseMaxConnectionMs). Delays beyond setTimeout's 2^31-1 ms limit are skipped instead of firing immediately. Fixes #254 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UF4S3w7iA83V9DVsasLJzM
The close timer and AbortSignal.timeout are both unref'd, so in CI the event loop could drain before the stream ended and node:test cancelled the suite. Wait with a ref'd timer instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UF4S3w7iA83V9DVsasLJzM
A token whose exp was more than 2^31-1 ms (~24.8 days) away skipped the close timer entirely, so the stream outlived the token. Wait in capped, unref'd steps until the deadline instead, and clear the pending timer on close. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UF4S3w7iA83V9DVsasLJzM
mcollina
force-pushed
the
fix/254-close-streams-at-token-exp
branch
from
October 14, 2026 11:16
bc55055 to
c71bfab
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
With authorization enabled, the bearer token is only checked when the legacy SSE
GET /mcpstream opens. After that the stream keeps delivering notifications even once the token has expired.Fix
request.tokenPayload) has anexp, the stream is ended atexpby an unref'd timer, which is cleared when the stream closes. The client reconnects with a fresh token and resumes withLast-Event-ID.sseMaxConnectionMs(SEP-1699), so no new option is added. The stream now closes atmin(exp, sseMaxConnectionMs).sseMaxConnectionMsJSDoc describe the behaviour.subscriptions/listenstreams (#170) are not covered here.Tests
test/sse-token-expiry.test.ts:expis about 2s away closes within 1s afterexp, even with a longersseMaxConnectionMsprocess.getActiveResourcesInfo(); this test fails if.unref()is removed)expsseMaxConnectionMsstill applies without authorizationFixes #254
🤖 Generated with Claude Code
https://claude.ai/code/session_01UF4S3w7iA83V9DVsasLJzM