@ai-sdk/mcp 2.0.7 → 2.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/mcp",
3
- "version": "2.0.7",
3
+ "version": "2.0.8",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -556,39 +556,57 @@ class DefaultMCPClient implements MCPClient {
556
556
  id: messageId,
557
557
  };
558
558
 
559
+ const rejectWithAbortError = () => {
560
+ reject(
561
+ new MCPClientError({
562
+ message: 'Request was aborted',
563
+ cause: signal?.reason,
564
+ }),
565
+ );
566
+ };
567
+
559
568
  const cleanup = () => {
560
569
  this.responseHandlers.delete(messageId);
570
+ signal?.removeEventListener('abort', onAbort);
571
+ };
572
+
573
+ const rejectAndCleanup = (error: unknown) => {
574
+ cleanup();
575
+ reject(error);
576
+ };
577
+
578
+ const onAbort = () => {
579
+ cleanup();
580
+ rejectWithAbortError();
561
581
  };
562
582
 
563
583
  this.responseHandlers.set(messageId, response => {
564
584
  if (signal?.aborted) {
565
- return reject(
566
- new MCPClientError({
567
- message: 'Request was aborted',
568
- cause: signal.reason,
569
- }),
570
- );
585
+ cleanup();
586
+ return rejectWithAbortError();
571
587
  }
572
588
 
573
589
  if (response instanceof Error) {
574
- return reject(response);
590
+ return rejectAndCleanup(response);
575
591
  }
576
592
 
577
593
  try {
578
594
  const result = resultSchema.parse(response.result);
595
+ cleanup();
579
596
  resolve(result);
580
597
  } catch (error) {
581
598
  const parseError = new MCPClientError({
582
599
  message: 'Failed to parse server response',
583
600
  cause: error,
584
601
  });
585
- reject(parseError);
602
+ rejectAndCleanup(parseError);
586
603
  }
587
604
  });
588
605
 
606
+ signal?.addEventListener('abort', onAbort, { once: true });
607
+
589
608
  this.transport.send(jsonrpcRequest).catch(error => {
590
- cleanup();
591
- reject(error);
609
+ rejectAndCleanup(error);
592
610
  });
593
611
  });
594
612
  }
@@ -186,7 +186,7 @@ export class HttpMCPTransport implements MCPTransport {
186
186
  }
187
187
  this.abortController = new AbortController();
188
188
 
189
- void this.openInboundSse();
189
+ this.startInboundSse();
190
190
  }
191
191
 
192
192
  async close(): Promise<void> {
@@ -260,7 +260,7 @@ export class HttpMCPTransport implements MCPTransport {
260
260
  // If inbound SSE was not available earlier (e.g. 405 before init), try again now
261
261
  // Do not await to avoid blocking send()
262
262
  if (!this.inboundSseConnection) {
263
- void this.openInboundSse();
263
+ this.startInboundSse();
264
264
  }
265
265
  return;
266
266
  }
@@ -355,7 +355,12 @@ export class HttpMCPTransport implements MCPTransport {
355
355
  }
356
356
  };
357
357
 
358
- processEvents();
358
+ void processEvents().catch(error => {
359
+ if (error instanceof Error && error.name === 'AbortError') {
360
+ return;
361
+ }
362
+ this.onerror?.(error);
363
+ });
359
364
  return;
360
365
  }
361
366
 
@@ -400,12 +405,24 @@ export class HttpMCPTransport implements MCPTransport {
400
405
 
401
406
  const delay = this.getNextReconnectionDelay(this.inboundReconnectAttempts);
402
407
  this.inboundReconnectAttempts += 1;
403
- setTimeout(async () => {
408
+ setTimeout(() => {
404
409
  if (this.abortController?.signal.aborted) return;
405
- await this.openInboundSse(false, this.lastInboundEventId);
410
+ this.startInboundSse(false, this.lastInboundEventId);
406
411
  }, delay);
407
412
  }
408
413
 
414
+ private startInboundSse(
415
+ triedAuth: boolean = false,
416
+ resumeToken?: string,
417
+ ): void {
418
+ void this.openInboundSse(triedAuth, resumeToken).catch(error => {
419
+ if (error instanceof Error && error.name === 'AbortError') {
420
+ return;
421
+ }
422
+ this.onerror?.(error);
423
+ });
424
+ }
425
+
409
426
  // Open optional inbound SSE stream; best-effort and resumable
410
427
  private async openInboundSse(
411
428
  triedAuth: boolean = false,
@@ -510,10 +527,25 @@ export class HttpMCPTransport implements MCPTransport {
510
527
  };
511
528
 
512
529
  this.inboundSseConnection = {
513
- close: () => reader.cancel(),
530
+ close: () => {
531
+ void reader.cancel().catch(error => {
532
+ if (error instanceof Error && error.name === 'AbortError') {
533
+ return;
534
+ }
535
+ this.onerror?.(error);
536
+ });
537
+ },
514
538
  };
515
539
  this.inboundReconnectAttempts = 0;
516
- processEvents();
540
+ void processEvents().catch(error => {
541
+ if (error instanceof Error && error.name === 'AbortError') {
542
+ return;
543
+ }
544
+ this.onerror?.(error);
545
+ if (!this.abortController?.signal.aborted) {
546
+ this.scheduleInboundSseReconnection();
547
+ }
548
+ });
517
549
  } catch (error) {
518
550
  if (error instanceof Error && error.name === 'AbortError') {
519
551
  return;