@ai-sdk/mcp 2.0.3 → 2.0.5
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 +16 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +79 -4
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/tool/mcp-client.ts +122 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/mcp",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"pkce-challenge": "^5.0.1",
|
|
35
|
-
"@ai-sdk/provider": "4.0.
|
|
36
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
35
|
+
"@ai-sdk/provider": "4.0.1",
|
|
36
|
+
"@ai-sdk/provider-utils": "5.0.3"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@types/node": "22.19.19",
|
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,
|
|
@@ -66,6 +67,82 @@ import {
|
|
|
66
67
|
type InitializeResult,
|
|
67
68
|
} from './types';
|
|
68
69
|
const CLIENT_VERSION = '1.0.0';
|
|
70
|
+
const DEFAULT_MAX_TOOL_CALL_RETRIES = 0;
|
|
71
|
+
|
|
72
|
+
const DEFAULT_RETRY_ERROR_CODES = [
|
|
73
|
+
'ConnectionRefused',
|
|
74
|
+
'ConnectionClosed',
|
|
75
|
+
'FailedToOpenSocket',
|
|
76
|
+
'ECONNRESET',
|
|
77
|
+
'ECONNREFUSED',
|
|
78
|
+
'ETIMEDOUT',
|
|
79
|
+
'EPIPE',
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
function getErrorStatusCode(error: unknown): number | undefined {
|
|
83
|
+
if (
|
|
84
|
+
error != null &&
|
|
85
|
+
typeof error === 'object' &&
|
|
86
|
+
'statusCode' in error &&
|
|
87
|
+
typeof error.statusCode === 'number'
|
|
88
|
+
) {
|
|
89
|
+
return error.statusCode;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function getStringErrorCode(error: unknown): string | undefined {
|
|
96
|
+
if (
|
|
97
|
+
error != null &&
|
|
98
|
+
typeof error === 'object' &&
|
|
99
|
+
'code' in error &&
|
|
100
|
+
typeof error.code === 'string'
|
|
101
|
+
) {
|
|
102
|
+
return error.code;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function isRetryableMCPToolCallError(error: unknown): boolean {
|
|
109
|
+
const statusCode = getErrorStatusCode(error);
|
|
110
|
+
if (statusCode != null) {
|
|
111
|
+
return (
|
|
112
|
+
statusCode === 408 ||
|
|
113
|
+
statusCode === 409 ||
|
|
114
|
+
statusCode === 429 ||
|
|
115
|
+
statusCode >= 500
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (MCPClientError.isInstance(error) && error.code != null) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const errorCode = getStringErrorCode(error);
|
|
124
|
+
return errorCode != null && DEFAULT_RETRY_ERROR_CODES.includes(errorCode);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function prepareMaxRetries(maxRetries: number | undefined): number {
|
|
128
|
+
if (maxRetries == null) {
|
|
129
|
+
return DEFAULT_MAX_TOOL_CALL_RETRIES;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (!Number.isInteger(maxRetries)) {
|
|
133
|
+
throw new MCPClientError({
|
|
134
|
+
message: 'maxRetries must be an integer',
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (maxRetries < 0) {
|
|
139
|
+
throw new MCPClientError({
|
|
140
|
+
message: 'maxRetries must be >= 0',
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return maxRetries;
|
|
145
|
+
}
|
|
69
146
|
|
|
70
147
|
function mcpToModelOutput({
|
|
71
148
|
output,
|
|
@@ -104,6 +181,15 @@ export interface MCPClientConfig {
|
|
|
104
181
|
transport: MCPTransportConfig | MCPTransport;
|
|
105
182
|
/** Optional callback for uncaught errors */
|
|
106
183
|
onUncaughtError?: (error: unknown) => void;
|
|
184
|
+
/**
|
|
185
|
+
* Maximum number of retries for transient MCP tool call failures.
|
|
186
|
+
*
|
|
187
|
+
* Set to 0 to disable retries. Retries only apply to tools/call requests.
|
|
188
|
+
* JSON-RPC application errors, such as invalid params, are not retried.
|
|
189
|
+
*
|
|
190
|
+
* @default 0
|
|
191
|
+
*/
|
|
192
|
+
maxRetries?: number;
|
|
107
193
|
/**
|
|
108
194
|
* Initialize result from a previous MCP session. When provided, the client
|
|
109
195
|
* starts the transport and reuses this metadata without sending a new
|
|
@@ -248,6 +334,7 @@ export interface MCPClient {
|
|
|
248
334
|
class DefaultMCPClient implements MCPClient {
|
|
249
335
|
private transport: MCPTransport;
|
|
250
336
|
private onUncaughtError?: (error: unknown) => void;
|
|
337
|
+
private maxRetries: number;
|
|
251
338
|
private clientInfo: ClientConfiguration;
|
|
252
339
|
private clientCapabilities: ClientCapabilities;
|
|
253
340
|
private initialInitializeResult?: InitializeResult;
|
|
@@ -275,10 +362,12 @@ class DefaultMCPClient implements MCPClient {
|
|
|
275
362
|
clientName = name ?? 'ai-sdk-mcp-client',
|
|
276
363
|
version = CLIENT_VERSION,
|
|
277
364
|
onUncaughtError,
|
|
365
|
+
maxRetries,
|
|
278
366
|
capabilities,
|
|
279
367
|
initialInitializeResult,
|
|
280
368
|
}: MCPClientConfig) {
|
|
281
369
|
this.onUncaughtError = onUncaughtError;
|
|
370
|
+
this.maxRetries = prepareMaxRetries(maxRetries);
|
|
282
371
|
this.clientCapabilities = capabilities ?? {};
|
|
283
372
|
this.initialInitializeResult = initialInitializeResult;
|
|
284
373
|
|
|
@@ -518,6 +607,29 @@ class DefaultMCPClient implements MCPClient {
|
|
|
518
607
|
});
|
|
519
608
|
}
|
|
520
609
|
|
|
610
|
+
private async callToolWithRetry({
|
|
611
|
+
options,
|
|
612
|
+
execute,
|
|
613
|
+
}: {
|
|
614
|
+
options?: RequestOptions;
|
|
615
|
+
execute: () => Promise<CallToolResult>;
|
|
616
|
+
}): Promise<CallToolResult> {
|
|
617
|
+
if (this.maxRetries === 0) {
|
|
618
|
+
return execute();
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
return retryWithExponentialBackoff({
|
|
622
|
+
maxRetries: this.maxRetries,
|
|
623
|
+
abortSignal: options?.signal,
|
|
624
|
+
shouldRetry: isRetryableMCPToolCallError,
|
|
625
|
+
createRetryError: ({ message, errors }) =>
|
|
626
|
+
new MCPClientError({
|
|
627
|
+
message,
|
|
628
|
+
cause: errors[errors.length - 1],
|
|
629
|
+
}),
|
|
630
|
+
})(execute);
|
|
631
|
+
}
|
|
632
|
+
|
|
521
633
|
async callTool({
|
|
522
634
|
name,
|
|
523
635
|
arguments: args = {},
|
|
@@ -528,10 +640,17 @@ class DefaultMCPClient implements MCPClient {
|
|
|
528
640
|
options?: RequestOptions;
|
|
529
641
|
}): Promise<CallToolResult> {
|
|
530
642
|
try {
|
|
531
|
-
return this.
|
|
532
|
-
request: { method: 'tools/call', params: { name, arguments: args } },
|
|
533
|
-
resultSchema: CallToolResultSchema,
|
|
643
|
+
return this.callToolWithRetry({
|
|
534
644
|
options,
|
|
645
|
+
execute: () =>
|
|
646
|
+
this.request({
|
|
647
|
+
request: {
|
|
648
|
+
method: 'tools/call',
|
|
649
|
+
params: { name, arguments: args },
|
|
650
|
+
},
|
|
651
|
+
resultSchema: CallToolResultSchema,
|
|
652
|
+
options,
|
|
653
|
+
}),
|
|
535
654
|
});
|
|
536
655
|
} catch (error) {
|
|
537
656
|
throw error;
|