Skip to content

feat: add closeSSEStream callback to RequestHandlerExtra#1166

Merged
felixweinberger merged 5 commits into
mainfrom
fweinberger/closeSSEStream-callback
Dec 1, 2025
Merged

feat: add closeSSEStream callback to RequestHandlerExtra#1166
felixweinberger merged 5 commits into
mainfrom
fweinberger/closeSSEStream-callback

Conversation

@felixweinberger

Copy link
Copy Markdown
Contributor

Summary

Adds extra.closeSSEStream callback to RequestHandlerExtra, addressing findleyr's feedback on #1129.

This decouples tool code from the transport - tools can now close SSE streams without needing to track or access transports explicitly.

Changes

  • Add CloseSSEStreamOptions type with optional retryInterval field
  • Extend MessageExtraInfo with optional closeSSEStream callback
  • Add closeSSEStream to RequestHandlerExtra
  • Callback only provided when eventStore is configured
  • Support per-invocation retry intervals

New API

server.tool('long-task', {}, async (_args, extra) => {
    // Close stream to trigger polling - no transport access needed!
    extra.closeSSEStream?.({ retryInterval: 2000 });
});

Dependencies

This PR depends on #1129 (SEP-1699 SSE polling) being merged first.

Test plan

  • New tests for callback availability with/without eventStore
  • Test for per-invocation retry interval
  • Verify existing closeSSEStream tests still pass

@felixweinberger
felixweinberger requested a review from a team as a code owner November 25, 2025 16:33
@felixweinberger
felixweinberger changed the base branch from main to fweinberger/sep-1699 November 25, 2025 16:33
@pkg-pr-new

pkg-pr-new Bot commented Nov 25, 2025

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/sdk@1166

commit: f31b83a

@pcarleton pcarleton left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm 1 nit on example

Comment thread src/examples/server/ssePollingExample.ts
Base automatically changed from fweinberger/sep-1699 to main November 25, 2025 17:19
@felixweinberger
felixweinberger force-pushed the fweinberger/closeSSEStream-callback branch from da2e98c to 3a58ea1 Compare November 25, 2025 17:40
Comment thread src/server/streamableHttp.ts Outdated
const stream = this._streamMapping.get(streamId);
if (stream) {
// If a custom retry interval is provided, send it before closing
if (retryInterval !== undefined) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this cause problems for the client?
If the "event" field is missing, the SSE spec says that it should be treated as a "message" event.

@Hantar77

Copy link
Copy Markdown

#1023 #1171 #1023

@mattzcarey

Copy link
Copy Markdown
Contributor

What do you think about putting all these runtime functions under their own thing. We are starting to have a few here and putting at the top level already feels messy.

Address findleyr's feedback to decouple tool code from transport:
- Add CloseSSEStreamOptions type for per-invocation retry intervals
- Add closeSSEStream callback to MessageExtraInfo and RequestHandlerExtra
- Callback only provided when eventStore is configured
- Support custom retry interval via options.retryInterval
- Update ssePollingExample to use the new callback API

Tools can now close SSE streams directly via extra.closeSSEStream()
without needing to track or access transports explicitly.
Address review feedback: sending `retry: N\n\n` could trigger SSE
event dispatch behavior in some client implementations. Using a
single `\n` ensures the retry field is processed without potentially
dispatching an empty event.
Remove the optional retryInterval parameter from closeSSEStream
callback for simplicity. The transport's default retry interval
(from priming events) is sufficient. This aligns with Python SDK.
@felixweinberger
felixweinberger force-pushed the fweinberger/closeSSEStream-callback branch from a37d53d to 47d0ad1 Compare December 1, 2025 14:40
@felixweinberger

Copy link
Copy Markdown
Contributor Author

What do you think about putting all these runtime functions under their own thing. We are starting to have a few here and putting at the top level already feels messy.

Probably a good idea 🤔 wary of refactoring right now for backwards compatibility, but could be something to put on the list for v2 what do you think?

@felixweinberger
felixweinberger merged commit fab7e1e into main Dec 1, 2025
10 checks passed
@felixweinberger
felixweinberger deleted the fweinberger/closeSSEStream-callback branch December 1, 2025 16:18
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.

5 participants