@ai-sdk/mcp 1.0.56 → 1.0.57
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 +8 -0
- package/dist/index.d.mts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +80 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +81 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/tool/mcp-client.ts +126 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.57",
|
|
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": "
|
|
37
|
-
"@ai-sdk/provider
|
|
36
|
+
"@ai-sdk/provider-utils": "4.0.35",
|
|
37
|
+
"@ai-sdk/provider": "3.0.13"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/node": "20.17.24",
|
package/src/tool/mcp-client.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
asSchema,
|
|
4
4
|
dynamicTool,
|
|
5
5
|
jsonSchema,
|
|
6
|
+
retryWithExponentialBackoff,
|
|
6
7
|
safeParseJSON,
|
|
7
8
|
safeValidateTypes,
|
|
8
9
|
tool,
|
|
@@ -64,6 +65,82 @@ import {
|
|
|
64
65
|
} from './types';
|
|
65
66
|
|
|
66
67
|
const CLIENT_VERSION = '1.0.0';
|
|
68
|
+
const DEFAULT_MAX_TOOL_CALL_RETRIES = 0;
|
|
69
|
+
|
|
70
|
+
const DEFAULT_RETRY_ERROR_CODES = [
|
|
71
|
+
'ConnectionRefused',
|
|
72
|
+
'ConnectionClosed',
|
|
73
|
+
'FailedToOpenSocket',
|
|
74
|
+
'ECONNRESET',
|
|
75
|
+
'ECONNREFUSED',
|
|
76
|
+
'ETIMEDOUT',
|
|
77
|
+
'EPIPE',
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
function getErrorStatusCode(error: unknown): number | undefined {
|
|
81
|
+
if (
|
|
82
|
+
error != null &&
|
|
83
|
+
typeof error === 'object' &&
|
|
84
|
+
'statusCode' in error &&
|
|
85
|
+
typeof error.statusCode === 'number'
|
|
86
|
+
) {
|
|
87
|
+
return error.statusCode;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function getStringErrorCode(error: unknown): string | undefined {
|
|
94
|
+
if (
|
|
95
|
+
error != null &&
|
|
96
|
+
typeof error === 'object' &&
|
|
97
|
+
'code' in error &&
|
|
98
|
+
typeof error.code === 'string'
|
|
99
|
+
) {
|
|
100
|
+
return error.code;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function isRetryableMCPToolCallError(error: unknown): boolean {
|
|
107
|
+
const statusCode = getErrorStatusCode(error);
|
|
108
|
+
if (statusCode != null) {
|
|
109
|
+
return (
|
|
110
|
+
statusCode === 408 ||
|
|
111
|
+
statusCode === 409 ||
|
|
112
|
+
statusCode === 429 ||
|
|
113
|
+
statusCode >= 500
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (MCPClientError.isInstance(error) && error.code != null) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const errorCode = getStringErrorCode(error);
|
|
122
|
+
return errorCode != null && DEFAULT_RETRY_ERROR_CODES.includes(errorCode);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function prepareMaxRetries(maxRetries: number | undefined): number {
|
|
126
|
+
if (maxRetries == null) {
|
|
127
|
+
return DEFAULT_MAX_TOOL_CALL_RETRIES;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (!Number.isInteger(maxRetries)) {
|
|
131
|
+
throw new MCPClientError({
|
|
132
|
+
message: 'maxRetries must be an integer',
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (maxRetries < 0) {
|
|
137
|
+
throw new MCPClientError({
|
|
138
|
+
message: 'maxRetries must be >= 0',
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return maxRetries;
|
|
143
|
+
}
|
|
67
144
|
|
|
68
145
|
function mcpToModelOutput({
|
|
69
146
|
output,
|
|
@@ -102,6 +179,15 @@ export interface MCPClientConfig {
|
|
|
102
179
|
transport: MCPTransportConfig | MCPTransport;
|
|
103
180
|
/** Optional callback for uncaught errors */
|
|
104
181
|
onUncaughtError?: (error: unknown) => void;
|
|
182
|
+
/**
|
|
183
|
+
* Maximum number of retries for transient MCP tool call failures.
|
|
184
|
+
*
|
|
185
|
+
* Set to 0 to disable retries. Retries only apply to tools/call requests.
|
|
186
|
+
* JSON-RPC application errors, such as invalid params, are not retried.
|
|
187
|
+
*
|
|
188
|
+
* @default 0
|
|
189
|
+
*/
|
|
190
|
+
maxRetries?: number;
|
|
105
191
|
/** Optional client name, defaults to 'ai-sdk-mcp-client' */
|
|
106
192
|
clientName?: string;
|
|
107
193
|
/**
|
|
@@ -225,6 +311,7 @@ export interface MCPClient {
|
|
|
225
311
|
class DefaultMCPClient implements MCPClient {
|
|
226
312
|
private transport: MCPTransport;
|
|
227
313
|
private onUncaughtError?: (error: unknown) => void;
|
|
314
|
+
private maxRetries: number;
|
|
228
315
|
private clientInfo: ClientConfiguration;
|
|
229
316
|
private clientCapabilities: ClientCapabilities;
|
|
230
317
|
private requestMessageId = 0;
|
|
@@ -246,9 +333,11 @@ class DefaultMCPClient implements MCPClient {
|
|
|
246
333
|
clientName = name ?? 'ai-sdk-mcp-client',
|
|
247
334
|
version = CLIENT_VERSION,
|
|
248
335
|
onUncaughtError,
|
|
336
|
+
maxRetries,
|
|
249
337
|
capabilities,
|
|
250
338
|
}: MCPClientConfig) {
|
|
251
339
|
this.onUncaughtError = onUncaughtError;
|
|
340
|
+
this.maxRetries = prepareMaxRetries(maxRetries);
|
|
252
341
|
this.clientCapabilities = capabilities ?? {};
|
|
253
342
|
|
|
254
343
|
if (isCustomMcpTransport(transportConfig)) {
|
|
@@ -470,7 +559,30 @@ class DefaultMCPClient implements MCPClient {
|
|
|
470
559
|
});
|
|
471
560
|
}
|
|
472
561
|
|
|
473
|
-
private async
|
|
562
|
+
private async callToolWithRetry({
|
|
563
|
+
options,
|
|
564
|
+
execute,
|
|
565
|
+
}: {
|
|
566
|
+
options?: ToolExecutionOptions;
|
|
567
|
+
execute: () => Promise<CallToolResult>;
|
|
568
|
+
}): Promise<CallToolResult> {
|
|
569
|
+
if (this.maxRetries === 0) {
|
|
570
|
+
return execute();
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return retryWithExponentialBackoff({
|
|
574
|
+
maxRetries: this.maxRetries,
|
|
575
|
+
abortSignal: options?.abortSignal,
|
|
576
|
+
shouldRetry: isRetryableMCPToolCallError,
|
|
577
|
+
createRetryError: ({ message, errors }) =>
|
|
578
|
+
new MCPClientError({
|
|
579
|
+
message,
|
|
580
|
+
cause: errors[errors.length - 1],
|
|
581
|
+
}),
|
|
582
|
+
})(execute);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
async callTool({
|
|
474
586
|
name,
|
|
475
587
|
args,
|
|
476
588
|
options,
|
|
@@ -480,12 +592,19 @@ class DefaultMCPClient implements MCPClient {
|
|
|
480
592
|
options?: ToolExecutionOptions;
|
|
481
593
|
}): Promise<CallToolResult> {
|
|
482
594
|
try {
|
|
483
|
-
return this.
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
595
|
+
return this.callToolWithRetry({
|
|
596
|
+
options,
|
|
597
|
+
execute: () =>
|
|
598
|
+
this.request({
|
|
599
|
+
request: {
|
|
600
|
+
method: 'tools/call',
|
|
601
|
+
params: { name, arguments: args },
|
|
602
|
+
},
|
|
603
|
+
resultSchema: CallToolResultSchema,
|
|
604
|
+
options: {
|
|
605
|
+
signal: options?.abortSignal,
|
|
606
|
+
},
|
|
607
|
+
}),
|
|
489
608
|
});
|
|
490
609
|
} catch (error) {
|
|
491
610
|
throw error;
|