MP-301g · Module 2
Reconnection Strategies
3 min read
Connections fail. The network blips, the server restarts, the load balancer rotates. A robust MCP client must detect connection loss and reconnect without losing the user's context. The detection mechanism depends on the transport: stdio clients detect a closed pipe (EPIPE or EOF on stdout), Streamable HTTP clients detect a dropped SSE stream or HTTP timeout. In both cases, the client should attempt reconnection immediately with exponential backoff — first retry after 100ms, then 200ms, 400ms, up to a maximum of 30 seconds. Add jitter (random offset) to prevent thundering herd when many clients reconnect simultaneously after a server restart.
Session resumption after reconnection depends on whether the server supports it. If the server is stateful and the session still exists, the client resends the Mcp-Session-Id and continues where it left off. If the session expired or the server is stateless, the client must re-execute the initialize handshake and rebuild any subscriptions. The client should cache the last-known capabilities and subscriptions locally so it can rebuild the session without user intervention. From the user's perspective, a good reconnection is invisible.
// Exponential backoff with jitter for MCP reconnection
class ReconnectPolicy {
private attempt = 0;
private readonly baseMs = 100;
private readonly maxMs = 30_000;
private readonly jitterFactor = 0.5;
nextDelay(): number {
const exponential = Math.min(
this.baseMs * Math.pow(2, this.attempt),
this.maxMs
);
const jitter = exponential * this.jitterFactor * Math.random();
this.attempt++;
return exponential + jitter;
}
reset(): void {
this.attempt = 0;
}
async wait(): Promise<void> {
const delay = this.nextDelay();
await new Promise((r) => setTimeout(r, delay));
}
}
// Usage in reconnection loop
async function reconnectLoop(
connect: () => Promise<void>,
policy: ReconnectPolicy
): Promise<void> {
while (true) {
try {
await connect();
policy.reset(); // success — reset backoff
return;
} catch {
await policy.wait(); // wait with backoff + jitter
}
}
}