{"record":{"id":"3b53b25ebec4a0dc","repo":"vercel/ai","slug":"mcp-http-transport-error-transport-already-starte","errorCode":null,"errorMessage":"MCP HTTP Transport Error: Transport already started. Note: client.connect() calls start() automatically.","messagePattern":"MCP HTTP Transport Error: Transport already started\\. Note: client\\.connect\\(\\) calls start\\(\\) automatically\\.","errorType":"exception","errorClass":"MCPClientError","httpStatus":null,"severity":"error","filePath":"packages/mcp/src/tool/mcp-http-transport.ts","lineNumber":219,"sourceCode":"    }\n\n    if (!this.authPromise) {\n      this.authPromise = auth(this.authProvider, {\n        serverUrl: this.url,\n        resourceMetadataUrl,\n        scope,\n        fetchFn: this.fetchFn,\n      }).finally(() => {\n        this.authPromise = undefined;\n      });\n    }\n\n    return this.authPromise;\n  }\n\n  async start(): Promise<void> {\n    if (this.abortController) {\n      throw new MCPClientError({\n        message:\n          'MCP HTTP Transport Error: Transport already started. Note: client.connect() calls start() automatically.',\n      });\n    }\n    this.abortController = new AbortController();\n\n    if (\n      this.protocolVersion != null &&\n      this.protocolVersion !== LATEST_PROTOCOL_VERSION\n    ) {\n      this.startInboundSse();\n    }\n  }\n\n  async close(options?: { signal?: AbortSignal }): Promise<void> {\n    this.inboundSseConnection?.close();\n    this.abortController?.abort();\n","sourceCodeStart":201,"sourceCodeEnd":237,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/mcp/src/tool/mcp-http-transport.ts#L201-L237","documentation":"HttpMCPTransport.start() guards against double-start by checking its internal AbortController; a second start() call throws MCPClientError. The message notes that client.connect() already calls start() for you, so manually invoking start() after connect() is the typical cause.","triggerScenarios":"Calling transport.start() explicitly after (or while) client.connect() is running; sharing one transport instance between two clients; retrying connect() on the same transport instance after a failed connection attempt without creating a new transport.","commonSituations":"Following low-level transport examples (which call start() manually) together with the higher-level createMCPClient/connect flow; retry loops that reuse the transport object; dependency injection wiring the same transport into multiple clients.","solutions":["Do not call start() manually — just use client.connect(), which starts the transport","Create a new HttpMCPTransport (or new client) for each connection attempt; never reuse a started transport","Ensure each client gets its own transport instance"],"exampleFix":"// before\nconst client = await createMCPClient({ transport });\nawait transport.start(); // throws\n// after\nconst client = await createMCPClient({ transport }); // connect() calls start() internally","handlingStrategy":"try-catch","validationCode":"// track start state yourself if you manage the transport manually\nlet started = false;\nasync function ensureStarted(transport) {\n  if (!started) { await transport.start(); started = true; }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await client.connect(); // do NOT call transport.start() yourself\n} catch (error) {\n  if (MCPClientError.isInstance(error) && error.message.includes('Transport already started')) {\n    // reuse the existing connection instead of starting again\n  } else throw error;\n}","preventionTips":["Rely solely on client.connect(); never call transport.start() manually","Create a new transport per client and per reconnect attempt","Never share a single transport instance between multiple clients"],"tags":["lifecycle","double-start","transport"],"backgroundTag":"transport-already-started","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}