Repository navigation
MCP remote client has no transport-level retry on socket/connection errors #25287
Copy link
Copy link
Closed as not planned
Closed as not planned
Copy link
Description
Problem
When a remote MCP server (type: "remote" with StreamableHTTPClientTransport) becomes temporarily unreachable — e.g. the server process restarts, the laptop suspends/resumes, or a TCP keep-alive goes stale — the MCP client has no recovery mechanism.
What happens today
- Server restarts → all in-memory sessions lost
- Client sends a
tools/call→@modelcontextprotocol/sdk's internal reqwest pool has a dead keep-alive connection - Socket-level error occurs (not even an HTTP 404) — the request never reaches the server
client.callTool()throws an error- In
packages/opencode/src/mcp/index.ts, theconvertMcpToolexecutefunction has no catch/retry logic — the error propagates toEffect.catchwhich logs it and returns undefined - The MCP server is marked as
"failed"and never reconnects
Why server-side middleware can't fix this
- Socket errors never reach the server. The client's HTTP library fails before sending a request.
- Even if the request does reach the server (e.g. HTTP 404 for stale session), the server can't tell the client's internal SDK state about a new session ID — that state lives inside
@modelcontextprotocol/sdk's transport layer.
Expected behavior
When a tool call fails due to a transport error, the MCP client should:
- Detect that the connection is dead (socket error, ECONNRESET, etc.)
- Close the old transport/client
- Create a new transport and reconnect (re-initialize)
- Retry the original tool call with the new session
Suggested fix
In convertMcpTool (packages/opencode/src/mcp/index.ts), wrap the execute function with transport-level retry:
execute: async (args: unknown) => {
try {
return await client.callTool(
{ name: mcpTool.name, arguments: (args || {}) as Record<string, unknown> },
CallToolResultSchema,
{ resetTimeoutOnProgress: true, timeout },
)
} catch (e) {
// If this is a transport-level error, try reconnecting once
if (isTransportError(e) && clientKey && mcpConfig) {
log.warn("MCP transport error, attempting reconnect", { clientKey, error: e.message })
try {
await client.close()
const result = await createAndStore(clientKey, { ...mcpConfig, enabled: true })
if (result.status === "connected" && state.clients[clientKey]) {
return await state.clients[clientKey].callTool(
{ name: mcpTool.name, arguments: (args || {}) as Record<string, unknown> },
CallToolResultSchema,
{ resetTimeoutOnProgress: true, timeout },
)
}
} catch (retryError) {
log.error("MCP reconnect failed", { clientKey, error: retryError })
}
}
throw e
}
}A simpler alternative: use the existing connect function to re-establish the connection, leveraging the transport fallback chain (StreamableHTTP → SSE).
Environment
- OpenCode: latest (v1.x)
- MCP SDK:
@modelcontextprotocol/sdk - MCP Server: codesearch serve (streamable HTTP)
- OS: Windows (but issue applies to all platforms — any server restart triggers it)
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
github-actions commentedon May 1, 2026
This issue might be a duplicate of existing issues. Please check:
If your issue is distinct from all of the above (e.g. specifically about socket-level errors before the request reaches the server, rather than session invalidation), please clarify the difference in your report.
flupkede commentedon May 1, 2026
@rekram1-node I'm happy to update it. Just let me know.
flupkede commentedon May 1, 2026
Relationship to existing issues
Thank you for the pointer — this is indeed related but distinct from the existing issues:
MCP streamable-HTTP transport: 'Session not found' from server permanently breaks tool calls (no auto-reconnect) #25137 focuses specifically on HTTP 404 "Session not found" responses. My issue covers that case plus the more common real-world scenario where the error never reaches the server at all (TCP socket-level failure / dead keep-alive connection). In practice, after a server restart or laptop suspend, the SDK's reqwest connection pool has a stale connection that fails at the transport layer before any HTTP request is sent.
Bug: MCP tools permanently lost mid-session after single transient listTools() failure — no retry, no reconnect #17099, fix(mcp): MCP server loses connection mid-session causing intermittent failures #23997, MCP server connection lost after context compaction — tools unavailable until new session #23556 describe the symptom (tools permanently lost) from different trigger angles but none propose a concrete fix.
Key distinction: two failure modes
After investigating the
@modelcontextprotocol/sdksource and OpenCode'sconvertMcpToolin detail, the fix belongs in OpenCode'stools()function — wrappingconvertMcpToolwith transport-aware retry logic that leverages the existingconnect()→createAndStore()path.Happy to submit a PR if maintainers agree on the approach.
water-in-stone commentedon May 4, 2026
Thanks to your analysis, I have created a MR with thorough testing. @flupkede
water-in-stone commentedon May 4, 2026
@jlongster I have created a MR with thorough testing. PTAL.
github-actions commentedon Aug 28, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.