Skip to content

fix(mcp): don't close the MCP session on a missed heartbeat ping#41373

Open
RaccoonsNMangos wants to merge 1 commit into
microsoft:mainfrom
RaccoonsNMangos:patch-1
Open

fix(mcp): don't close the MCP session on a missed heartbeat ping#41373
RaccoonsNMangos wants to merge 1 commit into
microsoft:mainfrom
RaccoonsNMangos:patch-1

Conversation

@RaccoonsNMangos

Copy link
Copy Markdown

Problem

In HTTP/streamable transport, startHeartbeat pings the client and calls server.close() if the ping isn't answered within 5s, reaping the session and its browser context. Any client that doesn't promptly answer server-initiated pings — long-running tool calls, or aggregating proxies/gateways — gets session terminated (404). Fixes microsoft/playwright-mcp#1293 (related: #982, #1140, #1307).

Raising the 5s timeout doesn't help: server.ping() still rejects on the SDK's ~60s request timeout, which hits the same .catch(() => server.close()). The reap is caused by closing on a missed ping, not by the race-timeout value.

Change

Keep the heartbeat as a keepalive but no longer close the session on a missed ping. Dead transports are still cleaned up by transport.onclose, so a missed application-level ping no longer reaps a live session. The ping continues on the same 3s cadence in all cases.

Alternatives considered

  • Making the timeout configurable (fix(chromium): emit focus events in headful #982) — doesn't help clients that never answer pings.
  • Closing only after N consecutive misses — same limitation. Happy to adjust to whatever matches the heartbeat's intended design.

### Problem
In HTTP/streamable transport, `startHeartbeat` pings the client and calls `server.close()` if the
ping isn't answered within 5s, reaping the session and its browser context. Any client that
doesn't promptly answer server-initiated pings — long-running tool calls, or aggregating
proxies/gateways — gets `session terminated (404)`. Fixes microsoft/playwright-mcp#1293 (related:
microsoft#982, microsoft#1140, microsoft#1307).

Raising the 5s timeout doesn't help: `server.ping()` still rejects on the SDK's ~60s request
timeout, which hits the same `.catch(() => server.close())`. The reap is caused by closing on a
missed ping, not by the race-timeout value.

### Change
Keep the heartbeat as a keepalive but no longer close the session on a missed ping. Dead
transports are still cleaned up by `transport.onclose`, so a missed application-level ping no
longer reaps a live session. The ping continues on the same 3s cadence in all cases.

### Alternatives considered
- Making the timeout configurable (microsoft#982) — doesn't help clients that *never* answer pings.
- Closing only after N consecutive misses — same limitation.
Happy to adjust to whatever matches the heartbeat's intended design.

Signed-off-by: RaccoonsNMangos <nathanzen@proton.me>
@dgozman dgozman requested a review from yury-s June 19, 2026 15:46
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-second ping timeout breaks MCP Streamable HTTP protocol for operations >5s - Affects all HTTP-based MCP clients

1 participant