@ai-sdk/mcp 1.0.59 → 1.0.61

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": "1.0.59",
3
+ "version": "1.0.61",
4
4
  "license": "Apache-2.0",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
@@ -33,8 +33,8 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "pkce-challenge": "^5.0.0",
36
- "@ai-sdk/provider-utils": "4.0.36",
37
- "@ai-sdk/provider": "3.0.13"
36
+ "@ai-sdk/provider": "3.0.14",
37
+ "@ai-sdk/provider-utils": "4.0.38"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/node": "20.17.24",
@@ -508,39 +508,57 @@ class DefaultMCPClient implements MCPClient {
508
508
  id: messageId,
509
509
  };
510
510
 
511
+ const rejectWithAbortError = () => {
512
+ reject(
513
+ new MCPClientError({
514
+ message: 'Request was aborted',
515
+ cause: signal?.reason,
516
+ }),
517
+ );
518
+ };
519
+
511
520
  const cleanup = () => {
512
521
  this.responseHandlers.delete(messageId);
522
+ signal?.removeEventListener('abort', onAbort);
523
+ };
524
+
525
+ const rejectAndCleanup = (error: unknown) => {
526
+ cleanup();
527
+ reject(error);
528
+ };
529
+
530
+ const onAbort = () => {
531
+ cleanup();
532
+ rejectWithAbortError();
513
533
  };
514
534
 
515
535
  this.responseHandlers.set(messageId, response => {
516
536
  if (signal?.aborted) {
517
- return reject(
518
- new MCPClientError({
519
- message: 'Request was aborted',
520
- cause: signal.reason,
521
- }),
522
- );
537
+ cleanup();
538
+ return rejectWithAbortError();
523
539
  }
524
540
 
525
541
  if (response instanceof Error) {
526
- return reject(response);
542
+ return rejectAndCleanup(response);
527
543
  }
528
544
 
529
545
  try {
530
546
  const result = resultSchema.parse(response.result);
547
+ cleanup();
531
548
  resolve(result);
532
549
  } catch (error) {
533
550
  const parseError = new MCPClientError({
534
551
  message: 'Failed to parse server response',
535
552
  cause: error,
536
553
  });
537
- reject(parseError);
554
+ rejectAndCleanup(parseError);
538
555
  }
539
556
  });
540
557
 
558
+ signal?.addEventListener('abort', onAbort, { once: true });
559
+
541
560
  this.transport.send(jsonrpcRequest).catch(error => {
542
- cleanup();
543
- reject(error);
561
+ rejectAndCleanup(error);
544
562
  });
545
563
  });
546
564
  }
@@ -140,7 +140,7 @@ export class HttpMCPTransport implements MCPTransport {
140
140
  }
141
141
  this.abortController = new AbortController();
142
142
 
143
- void this.openInboundSse();
143
+ this.startInboundSse();
144
144
  }
145
145
 
146
146
  async close(): Promise<void> {
@@ -208,7 +208,7 @@ export class HttpMCPTransport implements MCPTransport {
208
208
  // If inbound SSE was not available earlier (e.g. 405 before init), try again now
209
209
  // Do not await to avoid blocking send()
210
210
  if (!this.inboundSseConnection) {
211
- void this.openInboundSse();
211
+ this.startInboundSse();
212
212
  }
213
213
  return;
214
214
  }
@@ -297,7 +297,12 @@ export class HttpMCPTransport implements MCPTransport {
297
297
  }
298
298
  };
299
299
 
300
- processEvents();
300
+ void processEvents().catch(error => {
301
+ if (error instanceof Error && error.name === 'AbortError') {
302
+ return;
303
+ }
304
+ this.onerror?.(error);
305
+ });
301
306
  return;
302
307
  }
303
308
 
@@ -342,12 +347,24 @@ export class HttpMCPTransport implements MCPTransport {
342
347
 
343
348
  const delay = this.getNextReconnectionDelay(this.inboundReconnectAttempts);
344
349
  this.inboundReconnectAttempts += 1;
345
- setTimeout(async () => {
350
+ setTimeout(() => {
346
351
  if (this.abortController?.signal.aborted) return;
347
- await this.openInboundSse(false, this.lastInboundEventId);
352
+ this.startInboundSse(false, this.lastInboundEventId);
348
353
  }, delay);
349
354
  }
350
355
 
356
+ private startInboundSse(
357
+ triedAuth: boolean = false,
358
+ resumeToken?: string,
359
+ ): void {
360
+ void this.openInboundSse(triedAuth, resumeToken).catch(error => {
361
+ if (error instanceof Error && error.name === 'AbortError') {
362
+ return;
363
+ }
364
+ this.onerror?.(error);
365
+ });
366
+ }
367
+
351
368
  // Open optional inbound SSE stream; best-effort and resumable
352
369
  private async openInboundSse(
353
370
  triedAuth: boolean = false,
@@ -448,10 +465,25 @@ export class HttpMCPTransport implements MCPTransport {
448
465
  };
449
466
 
450
467
  this.inboundSseConnection = {
451
- close: () => reader.cancel(),
468
+ close: () => {
469
+ void reader.cancel().catch(error => {
470
+ if (error instanceof Error && error.name === 'AbortError') {
471
+ return;
472
+ }
473
+ this.onerror?.(error);
474
+ });
475
+ },
452
476
  };
453
477
  this.inboundReconnectAttempts = 0;
454
- processEvents();
478
+ void processEvents().catch(error => {
479
+ if (error instanceof Error && error.name === 'AbortError') {
480
+ return;
481
+ }
482
+ this.onerror?.(error);
483
+ if (!this.abortController?.signal.aborted) {
484
+ this.scheduleInboundSseReconnection();
485
+ }
486
+ });
455
487
  } catch (error) {
456
488
  if (error instanceof Error && error.name === 'AbortError') {
457
489
  return;