vercel/ai · error · MCPClientError

MCP SSE Transport Error: Connection closed unexpectedly

Error message

MCP SSE Transport Error: Connection closed unexpectedly

What it means

The SSE stream ended (reader returned done) while the transport still considered itself connected, i.e. the server closed the connection without a proper shutdown. processEvents throws MCPClientError so callers of establishConnection/send learn the session died instead of hanging.

Source

Thrown at packages/mcp/src/tool/mcp-sse-transport.ts:161

            this.onerror?.(error);
            return reject(error);
          }

          const stream = response.body
            .pipeThrough(new TextDecoderStream())
            .pipeThrough(new EventSourceParserStream());

          const reader = stream.getReader();

          const processEvents = async () => {
            try {
              while (true) {
                const { done, value } = await reader.read();

                if (done) {
                  if (this.connected) {
                    this.connected = false;
                    throw new MCPClientError({
                      message:
                        'MCP SSE Transport Error: Connection closed unexpectedly',
                    });
                  }
                  return;
                }

                const { event, data } = value;

                if (event === 'endpoint') {
                  if (this.endpoint) {
                    continue;
                  }

                  const endpoint = new URL(data, this.url);

                  if (endpoint.origin !== this.url.origin) {
                    this.connected = false;

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Wrap client operations in retry logic that recreates the client/transport and re-calls tools() after this error
  2. Increase/proxy keep-alive and idle timeouts (e.g. nginx proxy_read_timeout) or send periodic traffic to keep the SSE stream alive
  3. Verify the MCP server is not crashing or being redeployed mid-session; check server logs at the disconnect time
  4. Reconnect and re-list tools before continuing, since session state is lost

Example fix

// before
const client = await createMCPClient({ transport });
const tools = await client.tools(); // throws if server dropped
// after
async function withRetry(fn) {
  try { return await fn(); }
  catch (e) {
    if (MCPClientError.isInstance(e)) { client = await createMCPClient({ transport: newTransport() }); return fn(); }
    throw e;
  }
}
Defensive patterns

Strategy: retry

Validate before calling

// check connection state before issuing requests
if (!clientIsHealthy()) { client = await createMCPClient({ transport: newTransport() }); }

Type guard

function isConnectionClosedError(e: unknown): boolean {
  return MCPClientError.isInstance(e) && e.message.includes('Connection closed unexpectedly');
}

Try / catch

try {
  result = await client.tools();
} catch (error) {
  if (isConnectionClosedError(error)) {
    await delay(backoff++ * 1000);
    client = await createMCPClient({ transport: newTransport() });
    result = await client.tools();
  } else throw error;
}

Prevention

When it happens

Trigger: SSE server closes the response stream (crash, deploy, idle timeout, proxy terminating long-lived connections) while this.connected is still true; network interruption causing the body stream to end early.

Common situations: Long-running sessions behind reverse proxies (nginx/ALB) with aggressive idle timeouts; serverless or autoscaling platforms recycling connections; MCP server restart during an active session.

Understand the failure class

Related errors


AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30). Data as JSON: /api/errors/96b1a4778eb3273b. Report an issue: GitHub.