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: 3a58ea1

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

Comment on lines +112 to +114
// Track transports by session ID for session reuse
const transports = new Map<string, StreamableHTTPServerTransport>();

Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// Track transports by session ID for session reuse
const transports = new Map<string, StreamableHTTPServerTransport>();

don't think you need this any more

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought so too at first, but we do still need this at line 120 below:

// Reuse existing transport or create new one
let transport = sessionId ? transports.get(sessionId) : undefined;

Base automatically changed from fweinberger/sep-1699 to main November 25, 2025 17:19
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.
@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

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