Skip to content

Modern subscriptions: say "the server closed the subscription without acknowledging it" instead of silently retrying eight times #2097

Closed

Description

@cliffhall

Split out of #2063, which resolved into a documentation/spec question everywhere except this one point.

The gap

A modern-era (2026-07-28) subscriptions/listen is a long-lived request. The first message on the stream must be notifications/subscriptions/acknowledged; the JSON-RPC result for the listen id is reserved as the graceful-closure marker (spec: Subscriptions → Graceful Closure).

A server that answers the listen request with a result immediately — never acknowledging — is therefore saying "acknowledged and closed in the same breath". In @modelcontextprotocol/client, Client._onresponse settles that subscription with cause graceful and rejects the pending acknowledgment promise, so the Inspector's re-listen rejects.

What the Inspector does with that today (core/mcp/inspectorClient.ts):

  • refreshModernSubscription rejects → onModernReconnectFailed() → scheduleModernReconnect()
  • up to MODERN_RECONNECT_MAX_ATTEMPTS = 8 consecutive failures, with capped exponential backoff
  • then the stream badge lands on ended

From the user's side that is: the Subscriptions badge flickers reconnecting, eight subscriptions/listen requests with increasing SDK-assigned ids go out over roughly a minute, and it finally settles on ended — with no statement anywhere about why. That is exactly the report in #2063, where it read as the Inspector "accepting" an invalid response.

Expected

Say what happened. Something on the order of:

The server closed the subscription without acknowledging it. A subscriptions/listen request must first be answered with a notifications/subscriptions/acknowledged notification; a JSON-RPC result for the listen id means graceful closure.

Concretely:

  1. Distinguish the case. A graceful settlement that arrives before any acknowledgment is not the same as a graceful shutdown of an established stream. Only the latter is the expected ended path.
  2. Don't retry it. Eight re-lists against a server that will answer the same way each time is noise, not recovery — the condition is deterministic. This case should end the stream on the first occurrence.
  3. Surface the reason in the UI. The Subscriptions section badge already carries a status; the never-acknowledged case needs an accompanying message rather than a bare ended.
  4. Optionally note the missing resultType discriminator, which the reporter's payload also lacked and which the spec's example carries.

Notes

Activity

  1. added this to the v2.5.0 milestone on Aug 24, 2026
  2. added
    bugSomething isn't working
    v2Issues and PRs for v2
    on Aug 24, 2026
  3. cliffhall commented on Aug 24, 2026

    @cliffhall
    MemberAuthor

    Triage: Priority Low (total 5)

    • Severity 3 — a real diagnostic gap: the Inspector settles on a bare ended badge with no statement of why, which reads as the Inspector accepting an invalid response
    • Urgency 1 — no time pressure; nothing is blocked on it, and the condition only arises against a server that never acknowledges
    • Bonuses: +1 bug label

    Milestone v2.5.0 rather than the current v2.4.0 (due 2026-08-26, 22 issues open) — a Low-priority diagnostic improvement plainly is not making that release.

    Board: #28, Status Todo.

  4. self-assigned this
    on Aug 28, 2026
  5. added a commit that references this issue on Aug 28, 2026
    9136ab6
  6. added a commit that references this issue on Sep 2, 2026
    8535ae0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingv2Issues and PRs for v2

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions