@probelabs/probe 0.6.0-rc176 → 0.6.0-rc177
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/build/agent/index.js +28 -2
- package/build/agent/mcp/client.js +6 -3
- package/build/agent/mcp/config.js +50 -2
- package/cjs/agent/ProbeAgent.cjs +31 -6
- package/cjs/index.cjs +31 -6
- package/package.json +1 -1
- package/src/agent/mcp/client.js +6 -3
- package/src/agent/mcp/config.js +50 -2
package/build/agent/index.js
CHANGED
|
@@ -56110,6 +56110,12 @@ import { readFileSync, existsSync as existsSync4, mkdirSync as mkdirSync2, write
|
|
|
56110
56110
|
import { join as join2, dirname as dirname3 } from "path";
|
|
56111
56111
|
import { homedir } from "os";
|
|
56112
56112
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
56113
|
+
function validateTimeout(value) {
|
|
56114
|
+
if (value === void 0 || value === null) return void 0;
|
|
56115
|
+
const num = Number(value);
|
|
56116
|
+
if (!Number.isFinite(num) || num < 0) return void 0;
|
|
56117
|
+
return Math.min(num, MAX_TIMEOUT);
|
|
56118
|
+
}
|
|
56113
56119
|
function loadMCPConfigurationFromPath(configPath) {
|
|
56114
56120
|
if (!configPath) {
|
|
56115
56121
|
throw new Error("Config path is required");
|
|
@@ -56195,6 +56201,14 @@ function mergeWithEnvironment(config) {
|
|
|
56195
56201
|
config.mcpServers[normalizedName].env = { [property2]: value };
|
|
56196
56202
|
}
|
|
56197
56203
|
break;
|
|
56204
|
+
case "TIMEOUT":
|
|
56205
|
+
const validatedTimeout = validateTimeout(value);
|
|
56206
|
+
if (validatedTimeout !== void 0) {
|
|
56207
|
+
config.mcpServers[normalizedName].timeout = validatedTimeout;
|
|
56208
|
+
} else {
|
|
56209
|
+
console.error(`[MCP WARN] Invalid timeout value for ${normalizedName}: ${value}`);
|
|
56210
|
+
}
|
|
56211
|
+
break;
|
|
56198
56212
|
}
|
|
56199
56213
|
}
|
|
56200
56214
|
}
|
|
@@ -56237,16 +56251,26 @@ function parseEnabledServers(config) {
|
|
|
56237
56251
|
continue;
|
|
56238
56252
|
}
|
|
56239
56253
|
}
|
|
56254
|
+
if (server.timeout !== void 0) {
|
|
56255
|
+
const validatedTimeout = validateTimeout(server.timeout);
|
|
56256
|
+
if (validatedTimeout === void 0) {
|
|
56257
|
+
console.error(`[MCP ERROR] Server ${name} has invalid timeout value: ${server.timeout}`);
|
|
56258
|
+
continue;
|
|
56259
|
+
}
|
|
56260
|
+
server.timeout = validatedTimeout;
|
|
56261
|
+
}
|
|
56240
56262
|
servers.push(server);
|
|
56241
56263
|
}
|
|
56242
56264
|
return servers;
|
|
56243
56265
|
}
|
|
56244
|
-
var __filename4, __dirname4, DEFAULT_CONFIG;
|
|
56266
|
+
var __filename4, __dirname4, DEFAULT_TIMEOUT, MAX_TIMEOUT, DEFAULT_CONFIG;
|
|
56245
56267
|
var init_config = __esm({
|
|
56246
56268
|
"src/agent/mcp/config.js"() {
|
|
56247
56269
|
"use strict";
|
|
56248
56270
|
__filename4 = fileURLToPath6(import.meta.url);
|
|
56249
56271
|
__dirname4 = dirname3(__filename4);
|
|
56272
|
+
DEFAULT_TIMEOUT = 3e4;
|
|
56273
|
+
MAX_TIMEOUT = 6e5;
|
|
56250
56274
|
DEFAULT_CONFIG = {
|
|
56251
56275
|
mcpServers: {
|
|
56252
56276
|
// Example probe server configuration
|
|
@@ -56475,7 +56499,9 @@ var init_client = __esm({
|
|
|
56475
56499
|
if (this.debug) {
|
|
56476
56500
|
console.error(`[MCP DEBUG] Calling ${toolName} with args:`, JSON.stringify(args, null, 2));
|
|
56477
56501
|
}
|
|
56478
|
-
const
|
|
56502
|
+
const serverTimeout = clientInfo.config?.timeout;
|
|
56503
|
+
const globalTimeout = this.config?.settings?.timeout ?? DEFAULT_TIMEOUT;
|
|
56504
|
+
const timeout = serverTimeout ?? globalTimeout;
|
|
56479
56505
|
const timeoutPromise = new Promise((_, reject2) => {
|
|
56480
56506
|
setTimeout(() => {
|
|
56481
56507
|
reject2(new Error(`MCP tool call timeout after ${timeout}ms`));
|
|
@@ -7,7 +7,7 @@ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
|
7
7
|
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
8
8
|
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
9
9
|
import { WebSocketClientTransport } from '@modelcontextprotocol/sdk/client/websocket.js';
|
|
10
|
-
import { loadMCPConfiguration, parseEnabledServers } from './config.js';
|
|
10
|
+
import { loadMCPConfiguration, parseEnabledServers, DEFAULT_TIMEOUT } from './config.js';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Create transport based on configuration
|
|
@@ -274,8 +274,11 @@ export class MCPClientManager {
|
|
|
274
274
|
console.error(`[MCP DEBUG] Calling ${toolName} with args:`, JSON.stringify(args, null, 2));
|
|
275
275
|
}
|
|
276
276
|
|
|
277
|
-
// Get timeout
|
|
278
|
-
|
|
277
|
+
// Get timeout: per-server timeout takes priority over global timeout (default 30 seconds)
|
|
278
|
+
// Note: Timeout values are already validated at config load time by parseEnabledServers
|
|
279
|
+
const serverTimeout = clientInfo.config?.timeout;
|
|
280
|
+
const globalTimeout = this.config?.settings?.timeout ?? DEFAULT_TIMEOUT;
|
|
281
|
+
const timeout = serverTimeout ?? globalTimeout;
|
|
279
282
|
|
|
280
283
|
// Create a timeout promise
|
|
281
284
|
const timeoutPromise = new Promise((_, reject) => {
|
|
@@ -11,6 +11,24 @@ import { fileURLToPath } from 'url';
|
|
|
11
11
|
const __filename = fileURLToPath(import.meta.url);
|
|
12
12
|
const __dirname = dirname(__filename);
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Timeout configuration constants
|
|
16
|
+
*/
|
|
17
|
+
export const DEFAULT_TIMEOUT = 30000; // 30 seconds
|
|
18
|
+
export const MAX_TIMEOUT = 600000; // 10 minutes max to prevent resource exhaustion
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Validate and normalize a timeout value
|
|
22
|
+
* @param {*} value - The timeout value to validate
|
|
23
|
+
* @returns {number|undefined} Validated timeout in ms, or undefined if invalid
|
|
24
|
+
*/
|
|
25
|
+
export function validateTimeout(value) {
|
|
26
|
+
if (value === undefined || value === null) return undefined;
|
|
27
|
+
const num = Number(value);
|
|
28
|
+
if (!Number.isFinite(num) || num < 0) return undefined; // Invalid, use fallback
|
|
29
|
+
return Math.min(num, MAX_TIMEOUT); // Cap at max timeout
|
|
30
|
+
}
|
|
31
|
+
|
|
14
32
|
/**
|
|
15
33
|
* Default MCP configuration structure
|
|
16
34
|
*/
|
|
@@ -160,6 +178,15 @@ function mergeWithEnvironment(config) {
|
|
|
160
178
|
config.mcpServers[normalizedName].env = { [property]: value };
|
|
161
179
|
}
|
|
162
180
|
break;
|
|
181
|
+
case 'TIMEOUT':
|
|
182
|
+
// Per-server timeout in milliseconds with validation
|
|
183
|
+
const validatedTimeout = validateTimeout(value);
|
|
184
|
+
if (validatedTimeout !== undefined) {
|
|
185
|
+
config.mcpServers[normalizedName].timeout = validatedTimeout;
|
|
186
|
+
} else {
|
|
187
|
+
console.error(`[MCP WARN] Invalid timeout value for ${normalizedName}: ${value}`);
|
|
188
|
+
}
|
|
189
|
+
break;
|
|
163
190
|
}
|
|
164
191
|
}
|
|
165
192
|
}
|
|
@@ -219,6 +246,16 @@ export function parseEnabledServers(config) {
|
|
|
219
246
|
}
|
|
220
247
|
}
|
|
221
248
|
|
|
249
|
+
// Validate and normalize timeout if present (fail-fast validation)
|
|
250
|
+
if (server.timeout !== undefined) {
|
|
251
|
+
const validatedTimeout = validateTimeout(server.timeout);
|
|
252
|
+
if (validatedTimeout === undefined) {
|
|
253
|
+
console.error(`[MCP ERROR] Server ${name} has invalid timeout value: ${server.timeout}`);
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
server.timeout = validatedTimeout;
|
|
257
|
+
}
|
|
258
|
+
|
|
222
259
|
servers.push(server);
|
|
223
260
|
}
|
|
224
261
|
|
|
@@ -276,9 +313,17 @@ export function createSampleConfig() {
|
|
|
276
313
|
transport: 'websocket',
|
|
277
314
|
enabled: false,
|
|
278
315
|
description: 'Custom WebSocket MCP server'
|
|
316
|
+
},
|
|
317
|
+
'slow-server-example': {
|
|
318
|
+
command: 'node',
|
|
319
|
+
args: ['path/to/slow-server.js'],
|
|
320
|
+
transport: 'stdio',
|
|
321
|
+
enabled: false,
|
|
322
|
+
timeout: 120000,
|
|
323
|
+
description: 'Example server with custom 2-minute timeout (overrides global setting)'
|
|
279
324
|
}
|
|
280
325
|
},
|
|
281
|
-
// Global settings
|
|
326
|
+
// Global settings (apply to all servers unless overridden per-server)
|
|
282
327
|
settings: {
|
|
283
328
|
timeout: 30000,
|
|
284
329
|
retryCount: 3,
|
|
@@ -309,5 +354,8 @@ export default {
|
|
|
309
354
|
loadMCPConfigurationFromPath,
|
|
310
355
|
parseEnabledServers,
|
|
311
356
|
createSampleConfig,
|
|
312
|
-
saveConfig
|
|
357
|
+
saveConfig,
|
|
358
|
+
validateTimeout,
|
|
359
|
+
DEFAULT_TIMEOUT,
|
|
360
|
+
MAX_TIMEOUT
|
|
313
361
|
};
|
package/cjs/agent/ProbeAgent.cjs
CHANGED
|
@@ -5842,7 +5842,6 @@ var init_EventStreamSerde = __esm({
|
|
|
5842
5842
|
const { eventHeader, eventPayload } = memberSchema.getMergedTraits();
|
|
5843
5843
|
if (eventPayload) {
|
|
5844
5844
|
explicitPayloadMember = memberName;
|
|
5845
|
-
break;
|
|
5846
5845
|
} else if (eventHeader) {
|
|
5847
5846
|
const value = event[unionMember][memberName];
|
|
5848
5847
|
let type = "binary";
|
|
@@ -17253,9 +17252,9 @@ var require_dist_cjs49 = __commonJS({
|
|
|
17253
17252
|
expiration: new Date(creds.Expiration),
|
|
17254
17253
|
...creds.AccountId && { accountId: creds.AccountId }
|
|
17255
17254
|
});
|
|
17256
|
-
var
|
|
17255
|
+
var DEFAULT_TIMEOUT2 = 1e3;
|
|
17257
17256
|
var DEFAULT_MAX_RETRIES = 0;
|
|
17258
|
-
var providerConfigFromInit = ({ maxRetries = DEFAULT_MAX_RETRIES, timeout =
|
|
17257
|
+
var providerConfigFromInit = ({ maxRetries = DEFAULT_MAX_RETRIES, timeout = DEFAULT_TIMEOUT2 }) => ({ maxRetries, timeout });
|
|
17259
17258
|
var retry = (toRetry, maxRetries) => {
|
|
17260
17259
|
let promise = toRetry();
|
|
17261
17260
|
for (let i4 = 0; i4 < maxRetries; i4++) {
|
|
@@ -17536,7 +17535,7 @@ For more information, please visit: ` + STATIC_STABILITY_DOC_URL);
|
|
|
17536
17535
|
return fromImdsCredentials(credentialsResponse);
|
|
17537
17536
|
};
|
|
17538
17537
|
exports2.DEFAULT_MAX_RETRIES = DEFAULT_MAX_RETRIES;
|
|
17539
|
-
exports2.DEFAULT_TIMEOUT =
|
|
17538
|
+
exports2.DEFAULT_TIMEOUT = DEFAULT_TIMEOUT2;
|
|
17540
17539
|
exports2.ENV_CMDS_AUTH_TOKEN = ENV_CMDS_AUTH_TOKEN;
|
|
17541
17540
|
exports2.ENV_CMDS_FULL_URI = ENV_CMDS_FULL_URI;
|
|
17542
17541
|
exports2.ENV_CMDS_RELATIVE_URI = ENV_CMDS_RELATIVE_URI;
|
|
@@ -81986,6 +81985,12 @@ You should be:
|
|
|
81986
81985
|
});
|
|
81987
81986
|
|
|
81988
81987
|
// src/agent/mcp/config.js
|
|
81988
|
+
function validateTimeout(value) {
|
|
81989
|
+
if (value === void 0 || value === null) return void 0;
|
|
81990
|
+
const num = Number(value);
|
|
81991
|
+
if (!Number.isFinite(num) || num < 0) return void 0;
|
|
81992
|
+
return Math.min(num, MAX_TIMEOUT);
|
|
81993
|
+
}
|
|
81989
81994
|
function loadMCPConfigurationFromPath(configPath) {
|
|
81990
81995
|
if (!configPath) {
|
|
81991
81996
|
throw new Error("Config path is required");
|
|
@@ -82071,6 +82076,14 @@ function mergeWithEnvironment(config) {
|
|
|
82071
82076
|
config.mcpServers[normalizedName].env = { [property2]: value };
|
|
82072
82077
|
}
|
|
82073
82078
|
break;
|
|
82079
|
+
case "TIMEOUT":
|
|
82080
|
+
const validatedTimeout = validateTimeout(value);
|
|
82081
|
+
if (validatedTimeout !== void 0) {
|
|
82082
|
+
config.mcpServers[normalizedName].timeout = validatedTimeout;
|
|
82083
|
+
} else {
|
|
82084
|
+
console.error(`[MCP WARN] Invalid timeout value for ${normalizedName}: ${value}`);
|
|
82085
|
+
}
|
|
82086
|
+
break;
|
|
82074
82087
|
}
|
|
82075
82088
|
}
|
|
82076
82089
|
}
|
|
@@ -82113,11 +82126,19 @@ function parseEnabledServers(config) {
|
|
|
82113
82126
|
continue;
|
|
82114
82127
|
}
|
|
82115
82128
|
}
|
|
82129
|
+
if (server.timeout !== void 0) {
|
|
82130
|
+
const validatedTimeout = validateTimeout(server.timeout);
|
|
82131
|
+
if (validatedTimeout === void 0) {
|
|
82132
|
+
console.error(`[MCP ERROR] Server ${name14} has invalid timeout value: ${server.timeout}`);
|
|
82133
|
+
continue;
|
|
82134
|
+
}
|
|
82135
|
+
server.timeout = validatedTimeout;
|
|
82136
|
+
}
|
|
82116
82137
|
servers.push(server);
|
|
82117
82138
|
}
|
|
82118
82139
|
return servers;
|
|
82119
82140
|
}
|
|
82120
|
-
var import_fs12, import_path11, import_os3, import_url4, __filename4, __dirname4, DEFAULT_CONFIG;
|
|
82141
|
+
var import_fs12, import_path11, import_os3, import_url4, __filename4, __dirname4, DEFAULT_TIMEOUT, MAX_TIMEOUT, DEFAULT_CONFIG;
|
|
82121
82142
|
var init_config = __esm({
|
|
82122
82143
|
"src/agent/mcp/config.js"() {
|
|
82123
82144
|
"use strict";
|
|
@@ -82127,6 +82148,8 @@ var init_config = __esm({
|
|
|
82127
82148
|
import_url4 = require("url");
|
|
82128
82149
|
__filename4 = (0, import_url4.fileURLToPath)("file:///");
|
|
82129
82150
|
__dirname4 = (0, import_path11.dirname)(__filename4);
|
|
82151
|
+
DEFAULT_TIMEOUT = 3e4;
|
|
82152
|
+
MAX_TIMEOUT = 6e5;
|
|
82130
82153
|
DEFAULT_CONFIG = {
|
|
82131
82154
|
mcpServers: {
|
|
82132
82155
|
// Example probe server configuration
|
|
@@ -82355,7 +82378,9 @@ var init_client2 = __esm({
|
|
|
82355
82378
|
if (this.debug) {
|
|
82356
82379
|
console.error(`[MCP DEBUG] Calling ${toolName} with args:`, JSON.stringify(args, null, 2));
|
|
82357
82380
|
}
|
|
82358
|
-
const
|
|
82381
|
+
const serverTimeout = clientInfo.config?.timeout;
|
|
82382
|
+
const globalTimeout = this.config?.settings?.timeout ?? DEFAULT_TIMEOUT;
|
|
82383
|
+
const timeout = serverTimeout ?? globalTimeout;
|
|
82359
82384
|
const timeoutPromise = new Promise((_, reject2) => {
|
|
82360
82385
|
setTimeout(() => {
|
|
82361
82386
|
reject2(new Error(`MCP tool call timeout after ${timeout}ms`));
|
package/cjs/index.cjs
CHANGED
|
@@ -7369,7 +7369,6 @@ var init_EventStreamSerde = __esm({
|
|
|
7369
7369
|
const { eventHeader, eventPayload } = memberSchema.getMergedTraits();
|
|
7370
7370
|
if (eventPayload) {
|
|
7371
7371
|
explicitPayloadMember = memberName;
|
|
7372
|
-
break;
|
|
7373
7372
|
} else if (eventHeader) {
|
|
7374
7373
|
const value = event[unionMember][memberName];
|
|
7375
7374
|
let type = "binary";
|
|
@@ -18780,9 +18779,9 @@ var require_dist_cjs49 = __commonJS({
|
|
|
18780
18779
|
expiration: new Date(creds.Expiration),
|
|
18781
18780
|
...creds.AccountId && { accountId: creds.AccountId }
|
|
18782
18781
|
});
|
|
18783
|
-
var
|
|
18782
|
+
var DEFAULT_TIMEOUT2 = 1e3;
|
|
18784
18783
|
var DEFAULT_MAX_RETRIES = 0;
|
|
18785
|
-
var providerConfigFromInit = ({ maxRetries = DEFAULT_MAX_RETRIES, timeout =
|
|
18784
|
+
var providerConfigFromInit = ({ maxRetries = DEFAULT_MAX_RETRIES, timeout = DEFAULT_TIMEOUT2 }) => ({ maxRetries, timeout });
|
|
18786
18785
|
var retry = (toRetry, maxRetries) => {
|
|
18787
18786
|
let promise = toRetry();
|
|
18788
18787
|
for (let i4 = 0; i4 < maxRetries; i4++) {
|
|
@@ -19063,7 +19062,7 @@ For more information, please visit: ` + STATIC_STABILITY_DOC_URL);
|
|
|
19063
19062
|
return fromImdsCredentials(credentialsResponse);
|
|
19064
19063
|
};
|
|
19065
19064
|
exports2.DEFAULT_MAX_RETRIES = DEFAULT_MAX_RETRIES;
|
|
19066
|
-
exports2.DEFAULT_TIMEOUT =
|
|
19065
|
+
exports2.DEFAULT_TIMEOUT = DEFAULT_TIMEOUT2;
|
|
19067
19066
|
exports2.ENV_CMDS_AUTH_TOKEN = ENV_CMDS_AUTH_TOKEN;
|
|
19068
19067
|
exports2.ENV_CMDS_FULL_URI = ENV_CMDS_FULL_URI;
|
|
19069
19068
|
exports2.ENV_CMDS_RELATIVE_URI = ENV_CMDS_RELATIVE_URI;
|
|
@@ -79717,6 +79716,12 @@ You should be:
|
|
|
79717
79716
|
});
|
|
79718
79717
|
|
|
79719
79718
|
// src/agent/mcp/config.js
|
|
79719
|
+
function validateTimeout(value) {
|
|
79720
|
+
if (value === void 0 || value === null) return void 0;
|
|
79721
|
+
const num = Number(value);
|
|
79722
|
+
if (!Number.isFinite(num) || num < 0) return void 0;
|
|
79723
|
+
return Math.min(num, MAX_TIMEOUT);
|
|
79724
|
+
}
|
|
79720
79725
|
function loadMCPConfigurationFromPath(configPath) {
|
|
79721
79726
|
if (!configPath) {
|
|
79722
79727
|
throw new Error("Config path is required");
|
|
@@ -79802,6 +79807,14 @@ function mergeWithEnvironment(config) {
|
|
|
79802
79807
|
config.mcpServers[normalizedName].env = { [property2]: value };
|
|
79803
79808
|
}
|
|
79804
79809
|
break;
|
|
79810
|
+
case "TIMEOUT":
|
|
79811
|
+
const validatedTimeout = validateTimeout(value);
|
|
79812
|
+
if (validatedTimeout !== void 0) {
|
|
79813
|
+
config.mcpServers[normalizedName].timeout = validatedTimeout;
|
|
79814
|
+
} else {
|
|
79815
|
+
console.error(`[MCP WARN] Invalid timeout value for ${normalizedName}: ${value}`);
|
|
79816
|
+
}
|
|
79817
|
+
break;
|
|
79805
79818
|
}
|
|
79806
79819
|
}
|
|
79807
79820
|
}
|
|
@@ -79844,11 +79857,19 @@ function parseEnabledServers(config) {
|
|
|
79844
79857
|
continue;
|
|
79845
79858
|
}
|
|
79846
79859
|
}
|
|
79860
|
+
if (server.timeout !== void 0) {
|
|
79861
|
+
const validatedTimeout = validateTimeout(server.timeout);
|
|
79862
|
+
if (validatedTimeout === void 0) {
|
|
79863
|
+
console.error(`[MCP ERROR] Server ${name14} has invalid timeout value: ${server.timeout}`);
|
|
79864
|
+
continue;
|
|
79865
|
+
}
|
|
79866
|
+
server.timeout = validatedTimeout;
|
|
79867
|
+
}
|
|
79847
79868
|
servers.push(server);
|
|
79848
79869
|
}
|
|
79849
79870
|
return servers;
|
|
79850
79871
|
}
|
|
79851
|
-
var import_fs7, import_path6, import_os3, import_url4, __filename4, __dirname4, DEFAULT_CONFIG;
|
|
79872
|
+
var import_fs7, import_path6, import_os3, import_url4, __filename4, __dirname4, DEFAULT_TIMEOUT, MAX_TIMEOUT, DEFAULT_CONFIG;
|
|
79852
79873
|
var init_config = __esm({
|
|
79853
79874
|
"src/agent/mcp/config.js"() {
|
|
79854
79875
|
"use strict";
|
|
@@ -79858,6 +79879,8 @@ var init_config = __esm({
|
|
|
79858
79879
|
import_url4 = require("url");
|
|
79859
79880
|
__filename4 = (0, import_url4.fileURLToPath)("file:///");
|
|
79860
79881
|
__dirname4 = (0, import_path6.dirname)(__filename4);
|
|
79882
|
+
DEFAULT_TIMEOUT = 3e4;
|
|
79883
|
+
MAX_TIMEOUT = 6e5;
|
|
79861
79884
|
DEFAULT_CONFIG = {
|
|
79862
79885
|
mcpServers: {
|
|
79863
79886
|
// Example probe server configuration
|
|
@@ -80086,7 +80109,9 @@ var init_client2 = __esm({
|
|
|
80086
80109
|
if (this.debug) {
|
|
80087
80110
|
console.error(`[MCP DEBUG] Calling ${toolName} with args:`, JSON.stringify(args, null, 2));
|
|
80088
80111
|
}
|
|
80089
|
-
const
|
|
80112
|
+
const serverTimeout = clientInfo.config?.timeout;
|
|
80113
|
+
const globalTimeout = this.config?.settings?.timeout ?? DEFAULT_TIMEOUT;
|
|
80114
|
+
const timeout = serverTimeout ?? globalTimeout;
|
|
80090
80115
|
const timeoutPromise = new Promise((_, reject2) => {
|
|
80091
80116
|
setTimeout(() => {
|
|
80092
80117
|
reject2(new Error(`MCP tool call timeout after ${timeout}ms`));
|
package/package.json
CHANGED
package/src/agent/mcp/client.js
CHANGED
|
@@ -7,7 +7,7 @@ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
|
7
7
|
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
8
8
|
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
9
9
|
import { WebSocketClientTransport } from '@modelcontextprotocol/sdk/client/websocket.js';
|
|
10
|
-
import { loadMCPConfiguration, parseEnabledServers } from './config.js';
|
|
10
|
+
import { loadMCPConfiguration, parseEnabledServers, DEFAULT_TIMEOUT } from './config.js';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Create transport based on configuration
|
|
@@ -274,8 +274,11 @@ export class MCPClientManager {
|
|
|
274
274
|
console.error(`[MCP DEBUG] Calling ${toolName} with args:`, JSON.stringify(args, null, 2));
|
|
275
275
|
}
|
|
276
276
|
|
|
277
|
-
// Get timeout
|
|
278
|
-
|
|
277
|
+
// Get timeout: per-server timeout takes priority over global timeout (default 30 seconds)
|
|
278
|
+
// Note: Timeout values are already validated at config load time by parseEnabledServers
|
|
279
|
+
const serverTimeout = clientInfo.config?.timeout;
|
|
280
|
+
const globalTimeout = this.config?.settings?.timeout ?? DEFAULT_TIMEOUT;
|
|
281
|
+
const timeout = serverTimeout ?? globalTimeout;
|
|
279
282
|
|
|
280
283
|
// Create a timeout promise
|
|
281
284
|
const timeoutPromise = new Promise((_, reject) => {
|
package/src/agent/mcp/config.js
CHANGED
|
@@ -11,6 +11,24 @@ import { fileURLToPath } from 'url';
|
|
|
11
11
|
const __filename = fileURLToPath(import.meta.url);
|
|
12
12
|
const __dirname = dirname(__filename);
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Timeout configuration constants
|
|
16
|
+
*/
|
|
17
|
+
export const DEFAULT_TIMEOUT = 30000; // 30 seconds
|
|
18
|
+
export const MAX_TIMEOUT = 600000; // 10 minutes max to prevent resource exhaustion
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Validate and normalize a timeout value
|
|
22
|
+
* @param {*} value - The timeout value to validate
|
|
23
|
+
* @returns {number|undefined} Validated timeout in ms, or undefined if invalid
|
|
24
|
+
*/
|
|
25
|
+
export function validateTimeout(value) {
|
|
26
|
+
if (value === undefined || value === null) return undefined;
|
|
27
|
+
const num = Number(value);
|
|
28
|
+
if (!Number.isFinite(num) || num < 0) return undefined; // Invalid, use fallback
|
|
29
|
+
return Math.min(num, MAX_TIMEOUT); // Cap at max timeout
|
|
30
|
+
}
|
|
31
|
+
|
|
14
32
|
/**
|
|
15
33
|
* Default MCP configuration structure
|
|
16
34
|
*/
|
|
@@ -160,6 +178,15 @@ function mergeWithEnvironment(config) {
|
|
|
160
178
|
config.mcpServers[normalizedName].env = { [property]: value };
|
|
161
179
|
}
|
|
162
180
|
break;
|
|
181
|
+
case 'TIMEOUT':
|
|
182
|
+
// Per-server timeout in milliseconds with validation
|
|
183
|
+
const validatedTimeout = validateTimeout(value);
|
|
184
|
+
if (validatedTimeout !== undefined) {
|
|
185
|
+
config.mcpServers[normalizedName].timeout = validatedTimeout;
|
|
186
|
+
} else {
|
|
187
|
+
console.error(`[MCP WARN] Invalid timeout value for ${normalizedName}: ${value}`);
|
|
188
|
+
}
|
|
189
|
+
break;
|
|
163
190
|
}
|
|
164
191
|
}
|
|
165
192
|
}
|
|
@@ -219,6 +246,16 @@ export function parseEnabledServers(config) {
|
|
|
219
246
|
}
|
|
220
247
|
}
|
|
221
248
|
|
|
249
|
+
// Validate and normalize timeout if present (fail-fast validation)
|
|
250
|
+
if (server.timeout !== undefined) {
|
|
251
|
+
const validatedTimeout = validateTimeout(server.timeout);
|
|
252
|
+
if (validatedTimeout === undefined) {
|
|
253
|
+
console.error(`[MCP ERROR] Server ${name} has invalid timeout value: ${server.timeout}`);
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
server.timeout = validatedTimeout;
|
|
257
|
+
}
|
|
258
|
+
|
|
222
259
|
servers.push(server);
|
|
223
260
|
}
|
|
224
261
|
|
|
@@ -276,9 +313,17 @@ export function createSampleConfig() {
|
|
|
276
313
|
transport: 'websocket',
|
|
277
314
|
enabled: false,
|
|
278
315
|
description: 'Custom WebSocket MCP server'
|
|
316
|
+
},
|
|
317
|
+
'slow-server-example': {
|
|
318
|
+
command: 'node',
|
|
319
|
+
args: ['path/to/slow-server.js'],
|
|
320
|
+
transport: 'stdio',
|
|
321
|
+
enabled: false,
|
|
322
|
+
timeout: 120000,
|
|
323
|
+
description: 'Example server with custom 2-minute timeout (overrides global setting)'
|
|
279
324
|
}
|
|
280
325
|
},
|
|
281
|
-
// Global settings
|
|
326
|
+
// Global settings (apply to all servers unless overridden per-server)
|
|
282
327
|
settings: {
|
|
283
328
|
timeout: 30000,
|
|
284
329
|
retryCount: 3,
|
|
@@ -309,5 +354,8 @@ export default {
|
|
|
309
354
|
loadMCPConfigurationFromPath,
|
|
310
355
|
parseEnabledServers,
|
|
311
356
|
createSampleConfig,
|
|
312
|
-
saveConfig
|
|
357
|
+
saveConfig,
|
|
358
|
+
validateTimeout,
|
|
359
|
+
DEFAULT_TIMEOUT,
|
|
360
|
+
MAX_TIMEOUT
|
|
313
361
|
};
|