@ai-sdk/mcp 2.0.18 → 2.0.20
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/CHANGELOG.md +17 -0
- package/dist/index.d.ts +33 -3
- package/dist/index.js +158 -38
- package/dist/index.js.map +1 -1
- package/dist/mcp-stdio/index.d.ts +28 -2
- package/package.json +3 -3
- package/src/index.ts +5 -1
- package/src/tool/mcp-client.ts +161 -8
- package/src/tool/mcp-http-transport.ts +33 -9
- package/src/tool/mcp-sse-transport.ts +17 -2
- package/src/tool/mcp-transport.ts +36 -2
|
@@ -36,6 +36,30 @@ type JSONRPCMessage = z.infer<typeof JSONRPCMessageSchema>;
|
|
|
36
36
|
* Transport interface for MCP (Model Context Protocol) communication.
|
|
37
37
|
* Maps to the `Transport` interface in the MCP spec.
|
|
38
38
|
*/
|
|
39
|
+
type MCPTransportSendOptions = {
|
|
40
|
+
/**
|
|
41
|
+
* Cancels the transport operation for this message.
|
|
42
|
+
*/
|
|
43
|
+
signal?: AbortSignal;
|
|
44
|
+
/**
|
|
45
|
+
* Associates an outgoing message with an incoming request.
|
|
46
|
+
*/
|
|
47
|
+
relatedRequestId?: string | number;
|
|
48
|
+
/**
|
|
49
|
+
* Resumes a previously interrupted request.
|
|
50
|
+
*/
|
|
51
|
+
resumptionToken?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Receives updated resumption tokens from transports that support them.
|
|
54
|
+
*/
|
|
55
|
+
onresumptiontoken?: (token: string) => void;
|
|
56
|
+
};
|
|
57
|
+
type MCPTransportCloseOptions = {
|
|
58
|
+
/**
|
|
59
|
+
* Cancels transport cleanup.
|
|
60
|
+
*/
|
|
61
|
+
signal?: AbortSignal;
|
|
62
|
+
};
|
|
39
63
|
interface MCPTransport {
|
|
40
64
|
/**
|
|
41
65
|
* Initialize and start the transport
|
|
@@ -44,12 +68,14 @@ interface MCPTransport {
|
|
|
44
68
|
/**
|
|
45
69
|
* Send a JSON-RPC message through the transport
|
|
46
70
|
* @param message The JSON-RPC message to send
|
|
71
|
+
* @param options Optional request-scoped cancellation options
|
|
47
72
|
*/
|
|
48
|
-
send(message: JSONRPCMessage): Promise<void>;
|
|
73
|
+
send(message: JSONRPCMessage, options?: MCPTransportSendOptions): Promise<void>;
|
|
49
74
|
/**
|
|
50
75
|
* Clean up and close the transport
|
|
76
|
+
* @param options Optional cancellation options for transport cleanup
|
|
51
77
|
*/
|
|
52
|
-
close(): Promise<void>;
|
|
78
|
+
close(options?: MCPTransportCloseOptions): Promise<void>;
|
|
53
79
|
/**
|
|
54
80
|
* Event handler for transport closure
|
|
55
81
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/mcp",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.20",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"pkce-challenge": "^5.0.1",
|
|
35
35
|
"@ai-sdk/provider": "4.0.4",
|
|
36
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
36
|
+
"@ai-sdk/provider-utils": "5.0.16"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@types/node": "22.19.19",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"typescript": "5.8.3",
|
|
42
42
|
"vitest": "^4.1.6",
|
|
43
43
|
"zod": "3.25.76",
|
|
44
|
-
"@ai-sdk/test-server": "2.0.
|
|
44
|
+
"@ai-sdk/test-server": "2.0.1",
|
|
45
45
|
"@vercel/ai-tsconfig": "0.0.0"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
package/src/index.ts
CHANGED
|
@@ -49,7 +49,11 @@ export type {
|
|
|
49
49
|
OAuthClientMetadata,
|
|
50
50
|
OAuthTokens,
|
|
51
51
|
} from './tool/oauth-types';
|
|
52
|
-
export type {
|
|
52
|
+
export type {
|
|
53
|
+
MCPTransport,
|
|
54
|
+
MCPTransportCloseOptions,
|
|
55
|
+
MCPTransportSendOptions,
|
|
56
|
+
} from './tool/mcp-transport';
|
|
53
57
|
|
|
54
58
|
/**
|
|
55
59
|
* @deprecated Use `createMCPClient` instead. Will be removed in a future version.
|
package/src/tool/mcp-client.ts
CHANGED
|
@@ -16,6 +16,7 @@ import type { z } from 'zod/v4';
|
|
|
16
16
|
import { MCPClientError } from '../error/mcp-client-error';
|
|
17
17
|
import type {
|
|
18
18
|
JSONRPCError,
|
|
19
|
+
JSONRPCMessage,
|
|
19
20
|
JSONRPCNotification,
|
|
20
21
|
JSONRPCRequest,
|
|
21
22
|
JSONRPCResponse,
|
|
@@ -144,6 +145,56 @@ function prepareMaxRetries(maxRetries: number | undefined): number {
|
|
|
144
145
|
return maxRetries;
|
|
145
146
|
}
|
|
146
147
|
|
|
148
|
+
function getEffectiveTimeout({
|
|
149
|
+
timeout,
|
|
150
|
+
maxTotalTimeout,
|
|
151
|
+
}: RequestOptions): number | undefined {
|
|
152
|
+
if (timeout == null) {
|
|
153
|
+
return maxTotalTimeout;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (maxTotalTimeout == null) {
|
|
157
|
+
return timeout;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return Math.min(timeout, maxTotalTimeout);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function waitForAbort<T>(
|
|
164
|
+
promise: Promise<T>,
|
|
165
|
+
signal: AbortSignal | undefined,
|
|
166
|
+
): Promise<T> {
|
|
167
|
+
if (signal == null) {
|
|
168
|
+
return promise;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return new Promise((resolve, reject) => {
|
|
172
|
+
const cleanup = () => {
|
|
173
|
+
signal.removeEventListener('abort', onAbort);
|
|
174
|
+
};
|
|
175
|
+
const onAbort = () => {
|
|
176
|
+
cleanup();
|
|
177
|
+
reject(signal.reason);
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
if (signal.aborted) {
|
|
181
|
+
onAbort();
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
186
|
+
promise
|
|
187
|
+
.then(value => {
|
|
188
|
+
cleanup();
|
|
189
|
+
resolve(value);
|
|
190
|
+
})
|
|
191
|
+
.catch(error => {
|
|
192
|
+
cleanup();
|
|
193
|
+
reject(error);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
147
198
|
function mcpToModelOutput({
|
|
148
199
|
output,
|
|
149
200
|
}: {
|
|
@@ -179,6 +230,10 @@ function mcpToModelOutput({
|
|
|
179
230
|
export interface MCPClientConfig {
|
|
180
231
|
/** Transport configuration for connecting to the MCP server */
|
|
181
232
|
transport: MCPTransportConfig | MCPTransport;
|
|
233
|
+
/**
|
|
234
|
+
* Options that bound or cancel transport startup and the initialize request.
|
|
235
|
+
*/
|
|
236
|
+
initializationOptions?: RequestOptions;
|
|
182
237
|
/** Optional callback for uncaught errors */
|
|
183
238
|
onUncaughtError?: (error: unknown) => void;
|
|
184
239
|
/**
|
|
@@ -338,6 +393,7 @@ class DefaultMCPClient implements MCPClient {
|
|
|
338
393
|
private clientInfo: ClientConfiguration;
|
|
339
394
|
private clientCapabilities: ClientCapabilities;
|
|
340
395
|
private initialInitializeResult?: InitializeResult;
|
|
396
|
+
private initializationOptions?: RequestOptions;
|
|
341
397
|
private requestMessageId = 0;
|
|
342
398
|
private responseHandlers: Map<
|
|
343
399
|
number,
|
|
@@ -365,11 +421,13 @@ class DefaultMCPClient implements MCPClient {
|
|
|
365
421
|
maxRetries,
|
|
366
422
|
capabilities,
|
|
367
423
|
initialInitializeResult,
|
|
424
|
+
initializationOptions,
|
|
368
425
|
}: MCPClientConfig) {
|
|
369
426
|
this.onUncaughtError = onUncaughtError;
|
|
370
427
|
this.maxRetries = prepareMaxRetries(maxRetries);
|
|
371
428
|
this.clientCapabilities = capabilities ?? {};
|
|
372
429
|
this.initialInitializeResult = initialInitializeResult;
|
|
430
|
+
this.initializationOptions = initializationOptions;
|
|
373
431
|
|
|
374
432
|
if (isCustomMcpTransport(transportConfig)) {
|
|
375
433
|
this.transport = transportConfig;
|
|
@@ -415,9 +473,34 @@ class DefaultMCPClient implements MCPClient {
|
|
|
415
473
|
}
|
|
416
474
|
|
|
417
475
|
async init(): Promise<this> {
|
|
476
|
+
const externalSignal = this.initializationOptions?.signal;
|
|
477
|
+
const timeout = this.initializationOptions
|
|
478
|
+
? getEffectiveTimeout(this.initializationOptions)
|
|
479
|
+
: undefined;
|
|
480
|
+
const timeoutController =
|
|
481
|
+
timeout == null ? undefined : new AbortController();
|
|
482
|
+
const signal =
|
|
483
|
+
externalSignal == null
|
|
484
|
+
? timeoutController?.signal
|
|
485
|
+
: timeoutController == null
|
|
486
|
+
? externalSignal
|
|
487
|
+
: AbortSignal.any([externalSignal, timeoutController.signal]);
|
|
488
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
489
|
+
let timeoutError: MCPClientError | undefined;
|
|
490
|
+
|
|
491
|
+
if (timeout != null) {
|
|
492
|
+
timeoutId = setTimeout(() => {
|
|
493
|
+
timeoutError = new MCPClientError({
|
|
494
|
+
message: `MCP client initialization timed out after ${timeout}ms`,
|
|
495
|
+
});
|
|
496
|
+
timeoutController?.abort(timeoutError);
|
|
497
|
+
}, timeout);
|
|
498
|
+
}
|
|
499
|
+
|
|
418
500
|
try {
|
|
419
|
-
await this.transport.start();
|
|
420
501
|
this.isClosed = false;
|
|
502
|
+
signal?.throwIfAborted();
|
|
503
|
+
await waitForAbort(this.transport.start(), signal);
|
|
421
504
|
|
|
422
505
|
if (this.initialInitializeResult) {
|
|
423
506
|
const result = InitializeResultSchema.parse(
|
|
@@ -437,6 +520,7 @@ class DefaultMCPClient implements MCPClient {
|
|
|
437
520
|
},
|
|
438
521
|
},
|
|
439
522
|
resultSchema: InitializeResultSchema,
|
|
523
|
+
options: { signal },
|
|
440
524
|
});
|
|
441
525
|
|
|
442
526
|
if (result === undefined) {
|
|
@@ -448,14 +532,36 @@ class DefaultMCPClient implements MCPClient {
|
|
|
448
532
|
this.applyInitializeResult(result);
|
|
449
533
|
|
|
450
534
|
// Complete initialization handshake:
|
|
451
|
-
await this.notification(
|
|
452
|
-
|
|
453
|
-
|
|
535
|
+
await this.notification(
|
|
536
|
+
{
|
|
537
|
+
method: 'notifications/initialized',
|
|
538
|
+
},
|
|
539
|
+
{ signal },
|
|
540
|
+
);
|
|
454
541
|
|
|
455
542
|
return this;
|
|
456
543
|
} catch (error) {
|
|
457
|
-
|
|
544
|
+
try {
|
|
545
|
+
await waitForAbort(this.transport.close({ signal }), signal);
|
|
546
|
+
} catch {}
|
|
547
|
+
this.onClose();
|
|
548
|
+
|
|
549
|
+
if (timeoutError != null) {
|
|
550
|
+
throw timeoutError;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if (externalSignal?.aborted) {
|
|
554
|
+
throw new MCPClientError({
|
|
555
|
+
message: 'MCP client initialization was aborted',
|
|
556
|
+
cause: externalSignal.reason,
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
|
|
458
560
|
throw error;
|
|
561
|
+
} finally {
|
|
562
|
+
if (timeoutId != null) {
|
|
563
|
+
clearTimeout(timeoutId);
|
|
564
|
+
}
|
|
459
565
|
}
|
|
460
566
|
}
|
|
461
567
|
|
|
@@ -483,6 +589,16 @@ class DefaultMCPClient implements MCPClient {
|
|
|
483
589
|
this.onClose();
|
|
484
590
|
}
|
|
485
591
|
|
|
592
|
+
private send(
|
|
593
|
+
message: JSONRPCMessage,
|
|
594
|
+
signal: AbortSignal | undefined,
|
|
595
|
+
): Promise<void> {
|
|
596
|
+
return this.transport.send(
|
|
597
|
+
message,
|
|
598
|
+
signal == null ? undefined : { signal },
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
|
|
486
602
|
private assertCapability(method: string): void {
|
|
487
603
|
switch (method) {
|
|
488
604
|
case 'initialize':
|
|
@@ -548,6 +664,17 @@ class DefaultMCPClient implements MCPClient {
|
|
|
548
664
|
|
|
549
665
|
const signal = options?.signal;
|
|
550
666
|
signal?.throwIfAborted();
|
|
667
|
+
const timeout =
|
|
668
|
+
options == null ? undefined : getEffectiveTimeout(options);
|
|
669
|
+
const timeoutController =
|
|
670
|
+
timeout == null ? undefined : new AbortController();
|
|
671
|
+
const transportSignal =
|
|
672
|
+
signal == null
|
|
673
|
+
? timeoutController?.signal
|
|
674
|
+
: timeoutController == null
|
|
675
|
+
? signal
|
|
676
|
+
: AbortSignal.any([signal, timeoutController.signal]);
|
|
677
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
551
678
|
|
|
552
679
|
const messageId = this.requestMessageId++;
|
|
553
680
|
const jsonrpcRequest: JSONRPCRequest = {
|
|
@@ -568,6 +695,9 @@ class DefaultMCPClient implements MCPClient {
|
|
|
568
695
|
const cleanup = () => {
|
|
569
696
|
this.responseHandlers.delete(messageId);
|
|
570
697
|
signal?.removeEventListener('abort', onAbort);
|
|
698
|
+
if (timeoutId != null) {
|
|
699
|
+
clearTimeout(timeoutId);
|
|
700
|
+
}
|
|
571
701
|
};
|
|
572
702
|
|
|
573
703
|
const rejectAndCleanup = (error: unknown) => {
|
|
@@ -580,6 +710,14 @@ class DefaultMCPClient implements MCPClient {
|
|
|
580
710
|
rejectWithAbortError();
|
|
581
711
|
};
|
|
582
712
|
|
|
713
|
+
const onTimeout = () => {
|
|
714
|
+
const error = new MCPClientError({
|
|
715
|
+
message: `Request timed out after ${timeout}ms`,
|
|
716
|
+
});
|
|
717
|
+
timeoutController?.abort(error);
|
|
718
|
+
rejectAndCleanup(error);
|
|
719
|
+
};
|
|
720
|
+
|
|
583
721
|
this.responseHandlers.set(messageId, response => {
|
|
584
722
|
if (signal?.aborted) {
|
|
585
723
|
cleanup();
|
|
@@ -605,7 +743,16 @@ class DefaultMCPClient implements MCPClient {
|
|
|
605
743
|
|
|
606
744
|
signal?.addEventListener('abort', onAbort, { once: true });
|
|
607
745
|
|
|
608
|
-
|
|
746
|
+
if (timeout != null) {
|
|
747
|
+
timeoutId = setTimeout(onTimeout, timeout);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
const sendPromise =
|
|
751
|
+
transportSignal == null
|
|
752
|
+
? this.transport.send(jsonrpcRequest)
|
|
753
|
+
: this.send(jsonrpcRequest, transportSignal);
|
|
754
|
+
|
|
755
|
+
sendPromise.catch(error => {
|
|
609
756
|
rejectAndCleanup(error);
|
|
610
757
|
});
|
|
611
758
|
});
|
|
@@ -778,12 +925,18 @@ class DefaultMCPClient implements MCPClient {
|
|
|
778
925
|
});
|
|
779
926
|
}
|
|
780
927
|
|
|
781
|
-
private async notification(
|
|
928
|
+
private async notification(
|
|
929
|
+
notification: Notification,
|
|
930
|
+
options?: { signal?: AbortSignal },
|
|
931
|
+
): Promise<void> {
|
|
782
932
|
const jsonrpcNotification: JSONRPCNotification = {
|
|
783
933
|
...notification,
|
|
784
934
|
jsonrpc: '2.0',
|
|
785
935
|
};
|
|
786
|
-
await
|
|
936
|
+
await waitForAbort(
|
|
937
|
+
this.send(jsonrpcNotification, options?.signal),
|
|
938
|
+
options?.signal,
|
|
939
|
+
);
|
|
787
940
|
}
|
|
788
941
|
|
|
789
942
|
/**
|
|
@@ -189,30 +189,45 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
189
189
|
this.startInboundSse();
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
-
async close(): Promise<void> {
|
|
192
|
+
async close(options?: { signal?: AbortSignal }): Promise<void> {
|
|
193
193
|
this.inboundSseConnection?.close();
|
|
194
|
+
this.abortController?.abort();
|
|
195
|
+
|
|
194
196
|
try {
|
|
195
197
|
if (
|
|
196
198
|
this.sessionId &&
|
|
197
199
|
this.terminateSessionOnClose &&
|
|
198
|
-
this.abortController
|
|
199
|
-
!this.abortController.signal.aborted
|
|
200
|
+
this.abortController
|
|
200
201
|
) {
|
|
202
|
+
options?.signal?.throwIfAborted();
|
|
201
203
|
const headers = await this.commonHeaders({ base: {} });
|
|
204
|
+
options?.signal?.throwIfAborted();
|
|
202
205
|
await this.fetchFn(this.url.href, {
|
|
203
206
|
method: 'DELETE',
|
|
204
207
|
headers,
|
|
205
|
-
signal:
|
|
208
|
+
signal: options?.signal,
|
|
206
209
|
redirect: this.redirectMode,
|
|
207
210
|
}).catch(() => undefined);
|
|
208
211
|
}
|
|
209
212
|
} catch {}
|
|
210
213
|
|
|
211
|
-
this.abortController?.abort();
|
|
212
214
|
this.onclose?.();
|
|
213
215
|
}
|
|
214
216
|
|
|
215
|
-
async send(
|
|
217
|
+
async send(
|
|
218
|
+
message: JSONRPCMessage,
|
|
219
|
+
options?: { signal?: AbortSignal },
|
|
220
|
+
): Promise<void> {
|
|
221
|
+
options?.signal?.throwIfAborted();
|
|
222
|
+
|
|
223
|
+
const transportSignal = this.abortController?.signal;
|
|
224
|
+
const requestSignal =
|
|
225
|
+
options?.signal == null
|
|
226
|
+
? transportSignal
|
|
227
|
+
: transportSignal == null
|
|
228
|
+
? options.signal
|
|
229
|
+
: AbortSignal.any([transportSignal, options.signal]);
|
|
230
|
+
|
|
216
231
|
const attempt = async (triedAuth: boolean = false): Promise<void> => {
|
|
217
232
|
try {
|
|
218
233
|
const isInitializeRequest =
|
|
@@ -232,7 +247,7 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
232
247
|
method: 'POST',
|
|
233
248
|
headers,
|
|
234
249
|
body: JSON.stringify(message),
|
|
235
|
-
signal:
|
|
250
|
+
signal: requestSignal,
|
|
236
251
|
redirect: this.redirectMode,
|
|
237
252
|
} satisfies RequestInit;
|
|
238
253
|
|
|
@@ -348,7 +363,10 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
348
363
|
}
|
|
349
364
|
}
|
|
350
365
|
} catch (error) {
|
|
351
|
-
if (
|
|
366
|
+
if (
|
|
367
|
+
options?.signal?.aborted ||
|
|
368
|
+
(error instanceof Error && error.name === 'AbortError')
|
|
369
|
+
) {
|
|
352
370
|
return;
|
|
353
371
|
}
|
|
354
372
|
this.onerror?.(error);
|
|
@@ -356,7 +374,10 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
356
374
|
};
|
|
357
375
|
|
|
358
376
|
void processEvents().catch(error => {
|
|
359
|
-
if (
|
|
377
|
+
if (
|
|
378
|
+
options?.signal?.aborted ||
|
|
379
|
+
(error instanceof Error && error.name === 'AbortError')
|
|
380
|
+
) {
|
|
360
381
|
return;
|
|
361
382
|
}
|
|
362
383
|
this.onerror?.(error);
|
|
@@ -372,6 +393,9 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
372
393
|
this.onerror?.(error);
|
|
373
394
|
throw error;
|
|
374
395
|
} catch (error) {
|
|
396
|
+
if (options?.signal?.aborted) {
|
|
397
|
+
throw error;
|
|
398
|
+
}
|
|
375
399
|
this.onerror?.(error);
|
|
376
400
|
throw error;
|
|
377
401
|
}
|
|
@@ -236,7 +236,12 @@ export class SseMCPTransport implements MCPTransport {
|
|
|
236
236
|
this.onclose?.();
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
-
async send(
|
|
239
|
+
async send(
|
|
240
|
+
message: JSONRPCMessage,
|
|
241
|
+
options?: { signal?: AbortSignal },
|
|
242
|
+
): Promise<void> {
|
|
243
|
+
options?.signal?.throwIfAborted();
|
|
244
|
+
|
|
240
245
|
if (!this.endpoint || !this.connected) {
|
|
241
246
|
throw new MCPClientError({
|
|
242
247
|
message: 'MCP SSE Transport Error: Not connected',
|
|
@@ -244,6 +249,13 @@ export class SseMCPTransport implements MCPTransport {
|
|
|
244
249
|
}
|
|
245
250
|
|
|
246
251
|
const endpoint = this.endpoint as URL;
|
|
252
|
+
const transportSignal = this.abortController?.signal;
|
|
253
|
+
const requestSignal =
|
|
254
|
+
options?.signal == null
|
|
255
|
+
? transportSignal
|
|
256
|
+
: transportSignal == null
|
|
257
|
+
? options.signal
|
|
258
|
+
: AbortSignal.any([transportSignal, options.signal]);
|
|
247
259
|
|
|
248
260
|
const attempt = async (triedAuth: boolean = false): Promise<void> => {
|
|
249
261
|
try {
|
|
@@ -254,7 +266,7 @@ export class SseMCPTransport implements MCPTransport {
|
|
|
254
266
|
method: 'POST',
|
|
255
267
|
headers,
|
|
256
268
|
body: JSON.stringify(message),
|
|
257
|
-
signal:
|
|
269
|
+
signal: requestSignal,
|
|
258
270
|
redirect: this.redirectMode,
|
|
259
271
|
};
|
|
260
272
|
|
|
@@ -289,6 +301,9 @@ export class SseMCPTransport implements MCPTransport {
|
|
|
289
301
|
return;
|
|
290
302
|
}
|
|
291
303
|
} catch (error) {
|
|
304
|
+
if (options?.signal?.aborted) {
|
|
305
|
+
throw error;
|
|
306
|
+
}
|
|
292
307
|
this.onerror?.(error);
|
|
293
308
|
return;
|
|
294
309
|
}
|
|
@@ -9,6 +9,35 @@ import type { OAuthClientProvider } from './oauth';
|
|
|
9
9
|
* Transport interface for MCP (Model Context Protocol) communication.
|
|
10
10
|
* Maps to the `Transport` interface in the MCP spec.
|
|
11
11
|
*/
|
|
12
|
+
export type MCPTransportSendOptions = {
|
|
13
|
+
/**
|
|
14
|
+
* Cancels the transport operation for this message.
|
|
15
|
+
*/
|
|
16
|
+
signal?: AbortSignal;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Associates an outgoing message with an incoming request.
|
|
20
|
+
*/
|
|
21
|
+
relatedRequestId?: string | number;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Resumes a previously interrupted request.
|
|
25
|
+
*/
|
|
26
|
+
resumptionToken?: string;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Receives updated resumption tokens from transports that support them.
|
|
30
|
+
*/
|
|
31
|
+
onresumptiontoken?: (token: string) => void;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type MCPTransportCloseOptions = {
|
|
35
|
+
/**
|
|
36
|
+
* Cancels transport cleanup.
|
|
37
|
+
*/
|
|
38
|
+
signal?: AbortSignal;
|
|
39
|
+
};
|
|
40
|
+
|
|
12
41
|
export interface MCPTransport {
|
|
13
42
|
/**
|
|
14
43
|
* Initialize and start the transport
|
|
@@ -18,13 +47,18 @@ export interface MCPTransport {
|
|
|
18
47
|
/**
|
|
19
48
|
* Send a JSON-RPC message through the transport
|
|
20
49
|
* @param message The JSON-RPC message to send
|
|
50
|
+
* @param options Optional request-scoped cancellation options
|
|
21
51
|
*/
|
|
22
|
-
send(
|
|
52
|
+
send(
|
|
53
|
+
message: JSONRPCMessage,
|
|
54
|
+
options?: MCPTransportSendOptions,
|
|
55
|
+
): Promise<void>;
|
|
23
56
|
|
|
24
57
|
/**
|
|
25
58
|
* Clean up and close the transport
|
|
59
|
+
* @param options Optional cancellation options for transport cleanup
|
|
26
60
|
*/
|
|
27
|
-
close(): Promise<void>;
|
|
61
|
+
close(options?: MCPTransportCloseOptions): Promise<void>;
|
|
28
62
|
|
|
29
63
|
/**
|
|
30
64
|
* Event handler for transport closure
|