Skip to content

fix(mcp): call onclose when standalone SSE stream aborts on client disconnect#1804

Open
ctonneslan wants to merge 2 commits intohonojs:mainfrom
ctonneslan:fix/mcp-sse-onclose-on-abort
Open

fix(mcp): call onclose when standalone SSE stream aborts on client disconnect#1804
ctonneslan wants to merge 2 commits intohonojs:mainfrom
ctonneslan:fix/mcp-sse-onclose-on-abort

Conversation

@ctonneslan
Copy link

Summary

Calls onclose when the standalone GET SSE stream aborts (client disconnect), so applications can detect when an MCP client is gone.

Problem

When a client opens a GET SSE stream and then disconnects (process exit, network drop), the transport's stream.onAbort handler fires and cleans up #streamMapping, but the disconnection is never propagated to the onclose callback.

onclose is only called inside close(), which is triggered by an explicit DELETE request. In practice, MCP clients (Cursor, Claude Code, etc.) do not send DELETE when they disconnect. The SSE stream dropping is the only signal available.

This means applications have no way to know when a client has gone away.

Fix

When the standalone GET SSE stream (identified by _GET_stream ID) aborts, call this.onclose?.() to notify the application. This only fires for the standalone stream, not for per-request POST SSE streams.

Changes

  • packages/mcp/src/streamable-http.ts: Added onclose call in the standalone SSE stream's onAbort handler

Fixes #1762

When a client disconnects from the GET SSE stream (network drop,
process exit, etc.), the stream abort handler only cleans up internal
state but never notifies the application via onclose. Since MCP clients
typically do not send DELETE requests when they disconnect, the SSE
stream dropping is the only signal that the client is gone.

Now calls onclose when the standalone GET SSE stream (identified by
the _GET_stream ID) aborts, matching the semantic intent of onclose.

Fixes honojs#1762
@changeset-bot
Copy link

changeset-bot bot commented Mar 17, 2026

🦋 Changeset detected

Latest commit: ac33639

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@hono/mcp Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@yusukebe
Copy link
Member

@ctonneslan Thank you for the PR.

Hey @MathurAditya724 , can you handle this?

@codecov
Copy link

codecov bot commented Mar 17, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.73%. Comparing base (bbf1bdb) to head (79b01f4).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1804   +/-   ##
=======================================
  Coverage   91.73%   91.73%           
=======================================
  Files         113      113           
  Lines        3785     3787    +2     
  Branches      957      958    +1     
=======================================
+ Hits         3472     3474    +2     
  Misses        281      281           
  Partials       32       32           
Flag Coverage Δ
mcp 90.55% <100.00%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

StreamableHTTPTransport: SSE stream abort should notify via onclose (or new callback)

2 participants