vercel/ai · error · MCPClientError

MCP SSE Transport Error: Not connected

Error message

MCP SSE Transport Error: Not connected

What it means

SseMCPTransport.send() posts JSON-RPC messages to the endpoint URL discovered during connection. If no endpoint was captured or the connection flag is false, the transport is not in a usable session state and send() throws MCPClientError immediately.

Source

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

    });
  }

  async close(): Promise<void> {
    this.connected = false;
    this.endpoint = undefined;
    this.sseConnection?.close();
    this.abortController?.abort();
    this.onclose?.();
  }

  async send(
    message: JSONRPCMessage,
    options?: { signal?: AbortSignal },
  ): Promise<void> {
    options?.signal?.throwIfAborted();

    if (!this.endpoint || !this.connected) {
      throw new MCPClientError({
        message: 'MCP SSE Transport Error: Not connected',
      });
    }

    const endpoint = this.endpoint as URL;
    const transportSignal = this.abortController?.signal;
    const requestSignal =
      options?.signal == null
        ? transportSignal
        : transportSignal == null
          ? options.signal
          : AbortSignal.any([transportSignal, options.signal]);

    const attempt = async (triedAuth: boolean = false): Promise<void> => {
      try {
        const headers = await this.commonHeaders({
          'Content-Type': 'application/json',
        });

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Recreate the client/transport (or restart the transport) and wait for connection to be established before sending
  2. Serialize your send calls after the connect promise resolves; don't fire requests during connection setup
  3. After any connection error, treat the transport as dead: build a fresh one instead of retrying send() on the same instance

Example fix

// before
transport.close();
await transport.send(message); // throws
// after
transport.close();
const transport2 = new SseMCPTransport({ url });
await client2 = createMCPClient({ transport: transport2 });
await transport2.send(message);
Defensive patterns

Strategy: try-catch

Validate before calling

// expose/track connection state and check before sending
async function ensureConnected(client, makeTransport) {
  if (!isConnected()) { client = await createMCPClient({ transport: makeTransport() }); }
}

Type guard

function isNotConnectedError(e: unknown): boolean {
  return MCPClientError.isInstance(e) && e.message.includes('Not connected');
}

Try / catch

try {
  await transport.send(message);
} catch (error) {
  if (isNotConnectedError(error)) {
    transport = await reconnect(); // recreate transport and await connection
    await transport.send(message);
  } else throw error;
}

Prevention

When it happens

Trigger: Calling send() before start()/connect() finished establishing the SSE endpoint; calling send() after the connection dropped (e.g. after error 724) or after close(); using a transport whose establishConnection failed silently.

Common situations: Application-level retries reusing a stale transport after a disconnect; sending a request concurrently with connect() before the endpoint event arrives; forgetting that close() was called on shutdown.

Related errors


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