@ai-sdk/mcp 1.0.65 → 1.0.67
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 +13 -0
- package/dist/index.d.mts +33 -3
- package/dist/index.d.ts +33 -3
- package/dist/index.js +158 -38
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +158 -38
- package/dist/index.mjs.map +1 -1
- package/dist/mcp-stdio/index.d.mts +28 -2
- package/dist/mcp-stdio/index.d.ts +28 -2
- package/package.json +2 -2
- package/src/index.ts +5 -1
- package/src/tool/mcp-client.ts +161 -8
- package/src/tool/mcp-http-transport.ts +33 -12
- 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
|
*/
|
|
@@ -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": "1.0.
|
|
3
|
+
"version": "1.0.67",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"pkce-challenge": "^5.0.0",
|
|
36
36
|
"@ai-sdk/provider": "3.0.14",
|
|
37
|
-
"@ai-sdk/provider-utils": "4.0.
|
|
37
|
+
"@ai-sdk/provider-utils": "4.0.42"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/node": "20.17.24",
|
package/src/index.ts
CHANGED
|
@@ -34,7 +34,11 @@ export type {
|
|
|
34
34
|
OAuthClientMetadata,
|
|
35
35
|
OAuthTokens,
|
|
36
36
|
} from './tool/oauth-types';
|
|
37
|
-
export type {
|
|
37
|
+
export type {
|
|
38
|
+
MCPTransport,
|
|
39
|
+
MCPTransportCloseOptions,
|
|
40
|
+
MCPTransportSendOptions,
|
|
41
|
+
} from './tool/mcp-transport';
|
|
38
42
|
|
|
39
43
|
/**
|
|
40
44
|
* @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,
|
|
@@ -142,6 +143,56 @@ function prepareMaxRetries(maxRetries: number | undefined): number {
|
|
|
142
143
|
return maxRetries;
|
|
143
144
|
}
|
|
144
145
|
|
|
146
|
+
function getEffectiveTimeout({
|
|
147
|
+
timeout,
|
|
148
|
+
maxTotalTimeout,
|
|
149
|
+
}: RequestOptions): number | undefined {
|
|
150
|
+
if (timeout == null) {
|
|
151
|
+
return maxTotalTimeout;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (maxTotalTimeout == null) {
|
|
155
|
+
return timeout;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return Math.min(timeout, maxTotalTimeout);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function waitForAbort<T>(
|
|
162
|
+
promise: Promise<T>,
|
|
163
|
+
signal: AbortSignal | undefined,
|
|
164
|
+
): Promise<T> {
|
|
165
|
+
if (signal == null) {
|
|
166
|
+
return promise;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return new Promise((resolve, reject) => {
|
|
170
|
+
const cleanup = () => {
|
|
171
|
+
signal.removeEventListener('abort', onAbort);
|
|
172
|
+
};
|
|
173
|
+
const onAbort = () => {
|
|
174
|
+
cleanup();
|
|
175
|
+
reject(signal.reason);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
if (signal.aborted) {
|
|
179
|
+
onAbort();
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
184
|
+
promise
|
|
185
|
+
.then(value => {
|
|
186
|
+
cleanup();
|
|
187
|
+
resolve(value);
|
|
188
|
+
})
|
|
189
|
+
.catch(error => {
|
|
190
|
+
cleanup();
|
|
191
|
+
reject(error);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
145
196
|
function mcpToModelOutput({
|
|
146
197
|
output,
|
|
147
198
|
}: {
|
|
@@ -177,6 +228,10 @@ function mcpToModelOutput({
|
|
|
177
228
|
export interface MCPClientConfig {
|
|
178
229
|
/** Transport configuration for connecting to the MCP server */
|
|
179
230
|
transport: MCPTransportConfig | MCPTransport;
|
|
231
|
+
/**
|
|
232
|
+
* Options that bound or cancel transport startup and the initialize request.
|
|
233
|
+
*/
|
|
234
|
+
initializationOptions?: RequestOptions;
|
|
180
235
|
/** Optional callback for uncaught errors */
|
|
181
236
|
onUncaughtError?: (error: unknown) => void;
|
|
182
237
|
/**
|
|
@@ -314,6 +369,7 @@ class DefaultMCPClient implements MCPClient {
|
|
|
314
369
|
private maxRetries: number;
|
|
315
370
|
private clientInfo: ClientConfiguration;
|
|
316
371
|
private clientCapabilities: ClientCapabilities;
|
|
372
|
+
private initializationOptions?: RequestOptions;
|
|
317
373
|
private requestMessageId = 0;
|
|
318
374
|
private responseHandlers: Map<
|
|
319
375
|
number,
|
|
@@ -335,10 +391,12 @@ class DefaultMCPClient implements MCPClient {
|
|
|
335
391
|
onUncaughtError,
|
|
336
392
|
maxRetries,
|
|
337
393
|
capabilities,
|
|
394
|
+
initializationOptions,
|
|
338
395
|
}: MCPClientConfig) {
|
|
339
396
|
this.onUncaughtError = onUncaughtError;
|
|
340
397
|
this.maxRetries = prepareMaxRetries(maxRetries);
|
|
341
398
|
this.clientCapabilities = capabilities ?? {};
|
|
399
|
+
this.initializationOptions = initializationOptions;
|
|
342
400
|
|
|
343
401
|
if (isCustomMcpTransport(transportConfig)) {
|
|
344
402
|
this.transport = transportConfig;
|
|
@@ -380,9 +438,34 @@ class DefaultMCPClient implements MCPClient {
|
|
|
380
438
|
}
|
|
381
439
|
|
|
382
440
|
async init(): Promise<this> {
|
|
441
|
+
const externalSignal = this.initializationOptions?.signal;
|
|
442
|
+
const timeout = this.initializationOptions
|
|
443
|
+
? getEffectiveTimeout(this.initializationOptions)
|
|
444
|
+
: undefined;
|
|
445
|
+
const timeoutController =
|
|
446
|
+
timeout == null ? undefined : new AbortController();
|
|
447
|
+
const signal =
|
|
448
|
+
externalSignal == null
|
|
449
|
+
? timeoutController?.signal
|
|
450
|
+
: timeoutController == null
|
|
451
|
+
? externalSignal
|
|
452
|
+
: AbortSignal.any([externalSignal, timeoutController.signal]);
|
|
453
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
454
|
+
let timeoutError: MCPClientError | undefined;
|
|
455
|
+
|
|
456
|
+
if (timeout != null) {
|
|
457
|
+
timeoutId = setTimeout(() => {
|
|
458
|
+
timeoutError = new MCPClientError({
|
|
459
|
+
message: `MCP client initialization timed out after ${timeout}ms`,
|
|
460
|
+
});
|
|
461
|
+
timeoutController?.abort(timeoutError);
|
|
462
|
+
}, timeout);
|
|
463
|
+
}
|
|
464
|
+
|
|
383
465
|
try {
|
|
384
|
-
await this.transport.start();
|
|
385
466
|
this.isClosed = false;
|
|
467
|
+
signal?.throwIfAborted();
|
|
468
|
+
await waitForAbort(this.transport.start(), signal);
|
|
386
469
|
|
|
387
470
|
const result = await this.request({
|
|
388
471
|
request: {
|
|
@@ -394,6 +477,7 @@ class DefaultMCPClient implements MCPClient {
|
|
|
394
477
|
},
|
|
395
478
|
},
|
|
396
479
|
resultSchema: InitializeResultSchema,
|
|
480
|
+
options: { signal },
|
|
397
481
|
});
|
|
398
482
|
|
|
399
483
|
if (result === undefined) {
|
|
@@ -418,14 +502,36 @@ class DefaultMCPClient implements MCPClient {
|
|
|
418
502
|
this._serverInstructions = result.instructions;
|
|
419
503
|
|
|
420
504
|
// Complete initialization handshake:
|
|
421
|
-
await this.notification(
|
|
422
|
-
|
|
423
|
-
|
|
505
|
+
await this.notification(
|
|
506
|
+
{
|
|
507
|
+
method: 'notifications/initialized',
|
|
508
|
+
},
|
|
509
|
+
{ signal },
|
|
510
|
+
);
|
|
424
511
|
|
|
425
512
|
return this;
|
|
426
513
|
} catch (error) {
|
|
427
|
-
|
|
514
|
+
try {
|
|
515
|
+
await waitForAbort(this.transport.close({ signal }), signal);
|
|
516
|
+
} catch {}
|
|
517
|
+
this.onClose();
|
|
518
|
+
|
|
519
|
+
if (timeoutError != null) {
|
|
520
|
+
throw timeoutError;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
if (externalSignal?.aborted) {
|
|
524
|
+
throw new MCPClientError({
|
|
525
|
+
message: 'MCP client initialization was aborted',
|
|
526
|
+
cause: externalSignal.reason,
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
|
|
428
530
|
throw error;
|
|
531
|
+
} finally {
|
|
532
|
+
if (timeoutId != null) {
|
|
533
|
+
clearTimeout(timeoutId);
|
|
534
|
+
}
|
|
429
535
|
}
|
|
430
536
|
}
|
|
431
537
|
|
|
@@ -435,6 +541,16 @@ class DefaultMCPClient implements MCPClient {
|
|
|
435
541
|
this.onClose();
|
|
436
542
|
}
|
|
437
543
|
|
|
544
|
+
private send(
|
|
545
|
+
message: JSONRPCMessage,
|
|
546
|
+
signal: AbortSignal | undefined,
|
|
547
|
+
): Promise<void> {
|
|
548
|
+
return this.transport.send(
|
|
549
|
+
message,
|
|
550
|
+
signal == null ? undefined : { signal },
|
|
551
|
+
);
|
|
552
|
+
}
|
|
553
|
+
|
|
438
554
|
private assertCapability(method: string): void {
|
|
439
555
|
switch (method) {
|
|
440
556
|
case 'initialize':
|
|
@@ -500,6 +616,17 @@ class DefaultMCPClient implements MCPClient {
|
|
|
500
616
|
|
|
501
617
|
const signal = options?.signal;
|
|
502
618
|
signal?.throwIfAborted();
|
|
619
|
+
const timeout =
|
|
620
|
+
options == null ? undefined : getEffectiveTimeout(options);
|
|
621
|
+
const timeoutController =
|
|
622
|
+
timeout == null ? undefined : new AbortController();
|
|
623
|
+
const transportSignal =
|
|
624
|
+
signal == null
|
|
625
|
+
? timeoutController?.signal
|
|
626
|
+
: timeoutController == null
|
|
627
|
+
? signal
|
|
628
|
+
: AbortSignal.any([signal, timeoutController.signal]);
|
|
629
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
503
630
|
|
|
504
631
|
const messageId = this.requestMessageId++;
|
|
505
632
|
const jsonrpcRequest: JSONRPCRequest = {
|
|
@@ -520,6 +647,9 @@ class DefaultMCPClient implements MCPClient {
|
|
|
520
647
|
const cleanup = () => {
|
|
521
648
|
this.responseHandlers.delete(messageId);
|
|
522
649
|
signal?.removeEventListener('abort', onAbort);
|
|
650
|
+
if (timeoutId != null) {
|
|
651
|
+
clearTimeout(timeoutId);
|
|
652
|
+
}
|
|
523
653
|
};
|
|
524
654
|
|
|
525
655
|
const rejectAndCleanup = (error: unknown) => {
|
|
@@ -532,6 +662,14 @@ class DefaultMCPClient implements MCPClient {
|
|
|
532
662
|
rejectWithAbortError();
|
|
533
663
|
};
|
|
534
664
|
|
|
665
|
+
const onTimeout = () => {
|
|
666
|
+
const error = new MCPClientError({
|
|
667
|
+
message: `Request timed out after ${timeout}ms`,
|
|
668
|
+
});
|
|
669
|
+
timeoutController?.abort(error);
|
|
670
|
+
rejectAndCleanup(error);
|
|
671
|
+
};
|
|
672
|
+
|
|
535
673
|
this.responseHandlers.set(messageId, response => {
|
|
536
674
|
if (signal?.aborted) {
|
|
537
675
|
cleanup();
|
|
@@ -557,7 +695,16 @@ class DefaultMCPClient implements MCPClient {
|
|
|
557
695
|
|
|
558
696
|
signal?.addEventListener('abort', onAbort, { once: true });
|
|
559
697
|
|
|
560
|
-
|
|
698
|
+
if (timeout != null) {
|
|
699
|
+
timeoutId = setTimeout(onTimeout, timeout);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
const sendPromise =
|
|
703
|
+
transportSignal == null
|
|
704
|
+
? this.transport.send(jsonrpcRequest)
|
|
705
|
+
: this.send(jsonrpcRequest, transportSignal);
|
|
706
|
+
|
|
707
|
+
sendPromise.catch(error => {
|
|
561
708
|
rejectAndCleanup(error);
|
|
562
709
|
});
|
|
563
710
|
});
|
|
@@ -732,12 +879,18 @@ class DefaultMCPClient implements MCPClient {
|
|
|
732
879
|
});
|
|
733
880
|
}
|
|
734
881
|
|
|
735
|
-
private async notification(
|
|
882
|
+
private async notification(
|
|
883
|
+
notification: Notification,
|
|
884
|
+
options?: { signal?: AbortSignal },
|
|
885
|
+
): Promise<void> {
|
|
736
886
|
const jsonrpcNotification: JSONRPCNotification = {
|
|
737
887
|
...notification,
|
|
738
888
|
jsonrpc: '2.0',
|
|
739
889
|
};
|
|
740
|
-
await
|
|
890
|
+
await waitForAbort(
|
|
891
|
+
this.send(jsonrpcNotification, options?.signal),
|
|
892
|
+
options?.signal,
|
|
893
|
+
);
|
|
741
894
|
}
|
|
742
895
|
|
|
743
896
|
/**
|
|
@@ -143,29 +143,41 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
143
143
|
this.startInboundSse();
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
async close(): Promise<void> {
|
|
146
|
+
async close(options?: { signal?: AbortSignal }): Promise<void> {
|
|
147
147
|
this.inboundSseConnection?.close();
|
|
148
|
+
this.abortController?.abort();
|
|
149
|
+
|
|
148
150
|
try {
|
|
149
|
-
if (
|
|
150
|
-
|
|
151
|
-
this.abortController &&
|
|
152
|
-
!this.abortController.signal.aborted
|
|
153
|
-
) {
|
|
151
|
+
if (this.sessionId && this.abortController) {
|
|
152
|
+
options?.signal?.throwIfAborted();
|
|
154
153
|
const headers = await this.commonHeaders({});
|
|
154
|
+
options?.signal?.throwIfAborted();
|
|
155
155
|
await this.fetchFn(this.url.href, {
|
|
156
156
|
method: 'DELETE',
|
|
157
157
|
headers,
|
|
158
|
-
signal:
|
|
158
|
+
signal: options?.signal,
|
|
159
159
|
redirect: this.redirectMode,
|
|
160
160
|
}).catch(() => undefined);
|
|
161
161
|
}
|
|
162
162
|
} catch {}
|
|
163
163
|
|
|
164
|
-
this.abortController?.abort();
|
|
165
164
|
this.onclose?.();
|
|
166
165
|
}
|
|
167
166
|
|
|
168
|
-
async send(
|
|
167
|
+
async send(
|
|
168
|
+
message: JSONRPCMessage,
|
|
169
|
+
options?: { signal?: AbortSignal },
|
|
170
|
+
): Promise<void> {
|
|
171
|
+
options?.signal?.throwIfAborted();
|
|
172
|
+
|
|
173
|
+
const transportSignal = this.abortController?.signal;
|
|
174
|
+
const requestSignal =
|
|
175
|
+
options?.signal == null
|
|
176
|
+
? transportSignal
|
|
177
|
+
: transportSignal == null
|
|
178
|
+
? options.signal
|
|
179
|
+
: AbortSignal.any([transportSignal, options.signal]);
|
|
180
|
+
|
|
169
181
|
const attempt = async (triedAuth: boolean = false): Promise<void> => {
|
|
170
182
|
try {
|
|
171
183
|
const headers = await this.commonHeaders({
|
|
@@ -177,7 +189,7 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
177
189
|
method: 'POST',
|
|
178
190
|
headers,
|
|
179
191
|
body: JSON.stringify(message),
|
|
180
|
-
signal:
|
|
192
|
+
signal: requestSignal,
|
|
181
193
|
redirect: this.redirectMode,
|
|
182
194
|
} satisfies RequestInit;
|
|
183
195
|
|
|
@@ -290,7 +302,10 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
290
302
|
}
|
|
291
303
|
}
|
|
292
304
|
} catch (error) {
|
|
293
|
-
if (
|
|
305
|
+
if (
|
|
306
|
+
options?.signal?.aborted ||
|
|
307
|
+
(error instanceof Error && error.name === 'AbortError')
|
|
308
|
+
) {
|
|
294
309
|
return;
|
|
295
310
|
}
|
|
296
311
|
this.onerror?.(error);
|
|
@@ -298,7 +313,10 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
298
313
|
};
|
|
299
314
|
|
|
300
315
|
void processEvents().catch(error => {
|
|
301
|
-
if (
|
|
316
|
+
if (
|
|
317
|
+
options?.signal?.aborted ||
|
|
318
|
+
(error instanceof Error && error.name === 'AbortError')
|
|
319
|
+
) {
|
|
302
320
|
return;
|
|
303
321
|
}
|
|
304
322
|
this.onerror?.(error);
|
|
@@ -314,6 +332,9 @@ export class HttpMCPTransport implements MCPTransport {
|
|
|
314
332
|
this.onerror?.(error);
|
|
315
333
|
throw error;
|
|
316
334
|
} catch (error) {
|
|
335
|
+
if (options?.signal?.aborted) {
|
|
336
|
+
throw error;
|
|
337
|
+
}
|
|
317
338
|
this.onerror?.(error);
|
|
318
339
|
throw error;
|
|
319
340
|
}
|
|
@@ -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
|