Skip to content

fix: handle stale MCP client reconnection to prevent persistent EOF errors#312

Open
w495604217 wants to merge 1 commit intohangwin:masterfrom
w495604217:fix/stdio-client-reconnect-eof
Open

fix: handle stale MCP client reconnection to prevent persistent EOF errors#312
w495604217 wants to merge 1 commit intohangwin:masterfrom
w495604217:fix/stdio-client-reconnect-eof

Conversation

@w495604217
Copy link
Copy Markdown

Problem

When the HTTP transport to the Chrome extension drops (e.g., browser restart, extension reload, network interruption), the singleton mcpClient in mcp-server-stdio.ts becomes permanently unusable.

Root cause: ensureMcpClient() catches ping() failures in the outer try-catch, which closes the client and sets it to null — but subsequent calls may still fail because the error is silently swallowed (the function returns undefined instead of throwing). Additionally, handleToolCall() has no retry logic, so a single transient connection error immediately returns an error to the caller.

This causes persistent connection closed: EOF / client is closing: EOF errors that can only be resolved by restarting the entire MCP server process.

Fix

  1. Independent ping error handlingping() failures are now caught separately and fall through to client rebuild, instead of being caught by the outer try-catch
  2. Explicit old client cleanup — always close() the old client before creating a new one
  3. forceNew parameter — allows callers to explicitly request a fresh client
  4. Automatic retry in handleToolCall() — connection-related errors (EOF, closed, ECONNREFUSED, fetch failed, client is closing) trigger one automatic retry with a freshly built client
  5. Error re-throw — connection errors are re-thrown instead of silently swallowed

Testing

Verified with multiple MCP clients (Antigravity, custom Node.js clients) that the patched server correctly recovers from:

  • Chrome browser restarts
  • Extension reloads
  • Network interruptions
  • Long idle periods where the HTTP connection times out

…rrors

When the HTTP transport to the Chrome extension drops (e.g., browser
restart, extension reload, network interruption), the singleton mcpClient
becomes permanently unusable. The ping() call throws but its exception
is caught by the outer try-catch, which closes the client and sets it to
null. However, subsequent calls may still fail because:

1. ping() exceptions were not caught independently, causing the entire
   ensureMcpClient() to fail silently (returning undefined)
2. handleToolCall() had no retry logic, so a single transient connection
   error would immediately return an error to the caller

This fix:
- Catches ping() failures independently and falls through to rebuild
- Always explicitly closes the old client before creating a new one
- Adds a forceNew parameter to ensureMcpClient() for explicit rebuilds
- Adds one automatic retry in handleToolCall() for connection-related
  errors (EOF, closed, ECONNREFUSED, fetch failed, client is closing)
- Re-throws connection errors instead of silently swallowing them

Fixes persistent 'connection closed: EOF' errors reported by MCP clients
(e.g., Antigravity, Claude Desktop) after browser/extension restarts.
@adhimiw
Copy link
Copy Markdown

adhimiw commented Mar 12, 2026

nice

@juanroldanbrz
Copy link
Copy Markdown

Amazing! Athough:

PR #301 (PyEL666) — Cleaner variant: just removes the caching from getMcpServer() itself, making it a factory. Only changes mcp-server.ts, no changes needed to server/index.ts. Open since Feb 22.

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.

3 participants