Skip to content

MCP remote connections silently die after idle periods - no onclose/onerror handling, no reconnection, no heartbeat #29190

Closed as not planned

Description

@bob-dawson

Problem

After being idle for a while, remote (HTTP/SSE) MCP connections silently drop, making all subsequent MCP tool calls fail with "Not connected" error. The status still shows "connected" even though the underlying transport is dead.

Root Cause

In packages/opencode/src/mcp/index.ts:

  1. No onclose/onerror handlers registered on MCP Client — When the SSE/HTTP transport disconnects, the SDK fires transport.onclose()/transport.onerror(), but opencode never sets client.onclose or client.onerror. The connection state (s.status[key]) stays "connected" while client._transport becomes undefined.

  2. No automatic reconnection — Once the transport dies, there is no code to attempt reconnect. The connection is permanently broken until opencode restart.

  3. No keep-alive/heartbeat — The SDK Client class has a ping() method, but opencode never calls it. Idle SSE streams are terminated by servers/proxies (typically after 60-120s), and there is no mechanism to detect or prevent this.

  4. StreamableHTTP reconnection too limited — The SDK defaults maxRetries: 2 for reconnection, and opencode does not pass custom reconnectionOptions to StreamableHTTPClientTransport. Brief glitches may recover, but longer outages won't.

  5. SSE transport has no reconnection at all — The deprecated SSEClientTransport fallback has zero reconnection logic.

Typical Failure Scenario

opencode connects to remote MCP -> opens SSE GET stream -> idle period ->
server/proxy closes idle SSE connection -> SDK transport dies ->
onclose/onerror silently ignored -> status still "connected" ->
next tool call -> "Not connected" error

Suggested Fixes

  1. Register client.onclose / client.onerror handlers to update s.status[key] from "connected" to "failed"
  2. Implement automatic reconnection in onclose handler (exponential backoff, reuse existing create() logic)
  3. Add periodic health check using client.ping() to proactively detect dead connections
  4. Pass reconnectionOptions: { maxRetries: 5 } (or higher) to StreamableHTTPClientTransport
  5. Handle session expiry — when POST returns 404/410, re-initialize the connection

Environment

  • opencode version: latest (built from source)
  • MCP SDK: @modelcontextprotocol/sdk 1.27.1
  • Affected config type: mcp.*.type = "remote" (HTTP/SSE-based MCP servers)
  • Not affected: mcp.*.type = "local" (stdio-based) since those are process-local and don't have idle connection issues

Activity

  1. github-actions commented on May 25, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of several existing open issues that cover the same root cause — silent MCP connection failures, stale status, and lack of reconnection logic. Please check:

    If your issue is a strict superset that combines all of these (adding the heartbeat/ping angle), it may be worth referencing them and noting what's not covered. Otherwise, consider whether one of the above is the right place to add details.

  2. bob-dawson commented on May 25, 2026

    @bob-dawson
    Author

    Duplicate of #23997 — same root cause (MCP remote connections drop after idle, no onclose/reconnect handling)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions