Skip to content

Conversation

@felixweinberger
Copy link
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
Copy link

pkg-pr-new bot commented Nov 25, 2025

Open in StackBlitz

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

commit: f31b83a

Copy link
Member

@pcarleton pcarleton left a comment

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

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
const stream = this._streamMapping.get(streamId);
if (stream) {
// If a custom retry interval is provided, send it before closing
if (retryInterval !== undefined) {

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

#1023 #1171 #1023

@mattzcarey
Copy link
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
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.

6 participants