fix(mcp): make hosted sessions self-heal after dropped SSE streams - #30
Draft
OllieinCanada wants to merge 1 commit into
Draft
fix(mcp): make hosted sessions self-heal after dropped SSE streams#30OllieinCanada wants to merge 1 commit into
OllieinCanada wants to merge 1 commit into
Conversation
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
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.
Summary
mcp-gov0.32.0 to the stable v1.0.0 API.pingevery 20 seconds on the standalone GET/SSE stream.User impact
Hosted MCP clients receive traffic during otherwise idle streams, preventing the proxy-idle path that left registrations stale for roughly two minutes. Once the upstream lifecycle fix is released and consumed, a dropped stream can reconnect under the same session as soon as the old handler is gone, while a terminated session receives the 404 response that tells a compliant client to initialize again.
Root cause
The hosted server used mcp-go v0.32.0 without a heartbeat. Its standalone GET returned 202, and a disconnect could remain invisible to the application behind an intermediary, leaving the session registration occupied and making an immediate reconnect fail with 400. A local black-box trace reproduced the protocol/status defects:
The stable v1.0.0 transport corrects GET status, provides MCP ping heartbeats, and removes a registration when the handler observes cancellation. It still needed upstream hardening for simultaneous GET versus DELETE, cleanup of a blocked SSE write, active-stream termination, and stateful stale-session validation; those changes are isolated in mcp-go PR #970.
Original failure timeline:
Mcp-Session-Id.Design
Render owns:
mcp-go PR #970 owns the generally useful transport lifecycle:
pingevery 20 secondsThis does not claim replay or complete
Last-Event-IDresumability.Important invariants
Testing
Render MCP:
go test ./cmd ./pkg/logging -run 'TestStreamable|TestNewHTTPMux|TestNewHooks' -count=1 -vgo build ./...go test <all packages except e2e> -count=1go vet <all packages except e2e>golangci-lint v2.13.2 run --new-from-rev=upstream/main→0 issues.go generate ./...completed; unrelated generator-version churn in checked-in fakes was excluded.git diff --checkThe Render heartbeat/reconnect test proves initialize → GET 200 → valid MCP ping → canceled handler exit → immediate same-session GET 200. The DELETE/stale-session test is checked in with an explicit skip linked to mcp-go PR #970; it must be enabled before this draft becomes ready.
mcp-go PR #970:
./serversuite excluding three pre-existing Windows zero-duration timer assertions;go vet ./server;golangci-lint v2.13.2→0 issues.The repository's Ubuntu CI is the definitive
-racerun. Local race execution was unavailable because the Windows environment had no C compiler and the C:-backed WSL filesystem became read-only under disk pressure. No credentialed end-to-end tests or hosted endpoint polling were run.Compatibility & rollout
The server continues negotiating MCP 2025-03-26 and explicitly retains stateful sessions. The heartbeat is a valid MCP JSON-RPC
ping, not an application-specific payload. The dependency remains an official stable release; after #970 ships, this PR should bump only to that stable version, remove the skip, rerun the complete suite, and become ready for review.Source-level behavior has been verified locally. Hosted behavior is not claimed verified until Render deploys the eventual merged change.
Security & failure modes
The design preserves duplicate-stream protection rather than allowing takeover. Upstream cleanup uses a bounded context and interrupts blocked writes so one non-reading client cannot prevent later session cleanup. Invalid and terminated session IDs return not-found semantics without exposing session contents.
Risks & rollback
The main compatibility risk is the mcp-go v1 API/transport migration; the full non-e2e build and tests cover the repository against that version. Rollback is the single Render commit and dependency downgrade. The draft must not merge until an official release containing #970 passes Render and upstream CI.
Fixes #26