Skip to content

subscriptions/listen: let extensions add filter fields and events (Tasks taskIds) #3127

Open

Description

@sacha-ichbiah

In 2.4.0 the listen router only understands the four core subscription types:

  • honoredSubset keeps only toolsListChanged, promptsListChanged, resourcesListChanged and resourceSubscriptions. Any other field the client requests is dropped from notifications/subscriptions/acknowledged.
  • ServerEvent and listenFilterAccepts are a closed union of the matching four event kinds. The code comment says the filter governs "ONLY the four subscription-gated change types".

The Tasks extension defines a subscription filter field: notifications.taskIds in TaskSubscriptionNotificationsV2Schema. It ships helpers for it, contributeTaskFilterV2 on the client and readAcceptedTaskIdsV2 for reading the accepted filter. But a server can't honor that field through createMcpHandler or serveStdio. The ack drops it, and there's no way to publish a task status event through the bus.

So a server that supports task subscriptions has to intercept subscriptions/listen before the SDK handler and re-implement the whole stream:

  • SSE framing and the ack sent first;
  • keep-alive;
  • subscription limits;
  • subscription-ID stamping;
  • teardown.

All of that already exists in createListenRouter.

Proposal: an extension point that createMcpHandler and serveStdio both accept, for example:

listenExtensions?: Array<{
  // fields of the requested filter this extension honors, given server capabilities
  honor(requested: Record<string, unknown>, capabilities: ServerCapabilities): Record<string, unknown> | undefined;
  // whether an extension event matches the honored filter
  accepts(honored: Record<string, unknown>, event: ExtensionServerEvent): boolean;
}>

ServerEvent would gain an extension variant that maps straight onto its wire notification:

{ kind: "extension"; method: string; params?: Record<string, unknown> }

The SDK keeps owning the wire behavior: ack ordering, filtering, ID stamping and teardown. ext-tasks could then ship a ready-made listenExtensions entry next to contributeTaskFilterV2.

Related: modelcontextprotocol/ext-tasks#24 (missed transitions after reconnect). This proposal would give that recovery work one place to live in the SDK.

Context: our component currently keeps a roughly 530-line custom subscriptions/listen stream for this reason, and would delete it.

Activity

  1. added
    v2Ideas, requests and plans for v2 of the SDK which will incorporate major changes and fixes
    on Oct 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    v2Ideas, requests and plans for v2 of the SDK which will incorporate major changes and fixes

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions