@probelabs/probe 0.6.0-rc175 → 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.
@@ -8323,7 +8323,7 @@ var init_vercel = __esm({
8323
8323
  path: searchPath,
8324
8324
  cwd: options.cwd,
8325
8325
  // Working directory for resolving relative paths
8326
- allowTests: allow_tests,
8326
+ allowTests: allow_tests ?? true,
8327
8327
  exact,
8328
8328
  json: false,
8329
8329
  maxTokens: effectiveMaxTokens,
@@ -8368,7 +8368,7 @@ var init_vercel = __esm({
8368
8368
  cwd: options.cwd,
8369
8369
  // Working directory for resolving relative paths
8370
8370
  language,
8371
- allow_tests,
8371
+ allowTests: allow_tests ?? true,
8372
8372
  json: false
8373
8373
  });
8374
8374
  return results;
@@ -8414,7 +8414,7 @@ var init_vercel = __esm({
8414
8414
  extractOptions = {
8415
8415
  inputFile: tempFilePath,
8416
8416
  cwd: effectiveCwd,
8417
- allowTests: allow_tests,
8417
+ allowTests: allow_tests ?? true,
8418
8418
  contextLines: context_lines,
8419
8419
  format: effectiveFormat
8420
8420
  };
@@ -8427,7 +8427,7 @@ var init_vercel = __esm({
8427
8427
  extractOptions = {
8428
8428
  files,
8429
8429
  cwd: effectiveCwd,
8430
- allowTests: allow_tests,
8430
+ allowTests: allow_tests ?? true,
8431
8431
  contextLines: context_lines,
8432
8432
  format: effectiveFormat
8433
8433
  };
@@ -10460,6 +10460,17 @@ var init_simpleTelemetry = __esm({
10460
10460
  console.log("[Event]", name, attributes);
10461
10461
  }
10462
10462
  }
10463
+ /**
10464
+ * Record a generic event (used by completionPrompt and other features)
10465
+ */
10466
+ // visor-disable: SimpleAppTracer uses this.sessionId because it's a per-session instance. AppTracer extracts from attributes because it's a singleton managing multiple sessions. Different architectures require different approaches.
10467
+ recordEvent(name, attributes = {}) {
10468
+ if (!this.isEnabled()) return;
10469
+ this.addEvent(name, {
10470
+ "session.id": this.sessionId,
10471
+ ...attributes
10472
+ });
10473
+ }
10463
10474
  /**
10464
10475
  * Record delegation events
10465
10476
  */
@@ -56099,6 +56110,12 @@ import { readFileSync, existsSync as existsSync4, mkdirSync as mkdirSync2, write
56099
56110
  import { join as join2, dirname as dirname3 } from "path";
56100
56111
  import { homedir } from "os";
56101
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
+ }
56102
56119
  function loadMCPConfigurationFromPath(configPath) {
56103
56120
  if (!configPath) {
56104
56121
  throw new Error("Config path is required");
@@ -56184,6 +56201,14 @@ function mergeWithEnvironment(config) {
56184
56201
  config.mcpServers[normalizedName].env = { [property2]: value };
56185
56202
  }
56186
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;
56187
56212
  }
56188
56213
  }
56189
56214
  }
@@ -56226,16 +56251,26 @@ function parseEnabledServers(config) {
56226
56251
  continue;
56227
56252
  }
56228
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
+ }
56229
56262
  servers.push(server);
56230
56263
  }
56231
56264
  return servers;
56232
56265
  }
56233
- var __filename4, __dirname4, DEFAULT_CONFIG;
56266
+ var __filename4, __dirname4, DEFAULT_TIMEOUT, MAX_TIMEOUT, DEFAULT_CONFIG;
56234
56267
  var init_config = __esm({
56235
56268
  "src/agent/mcp/config.js"() {
56236
56269
  "use strict";
56237
56270
  __filename4 = fileURLToPath6(import.meta.url);
56238
56271
  __dirname4 = dirname3(__filename4);
56272
+ DEFAULT_TIMEOUT = 3e4;
56273
+ MAX_TIMEOUT = 6e5;
56239
56274
  DEFAULT_CONFIG = {
56240
56275
  mcpServers: {
56241
56276
  // Example probe server configuration
@@ -56464,7 +56499,9 @@ var init_client = __esm({
56464
56499
  if (this.debug) {
56465
56500
  console.error(`[MCP DEBUG] Calling ${toolName} with args:`, JSON.stringify(args, null, 2));
56466
56501
  }
56467
- const timeout = this.config?.settings?.timeout || 3e4;
56502
+ const serverTimeout = clientInfo.config?.timeout;
56503
+ const globalTimeout = this.config?.settings?.timeout ?? DEFAULT_TIMEOUT;
56504
+ const timeout = serverTimeout ?? globalTimeout;
56468
56505
  const timeoutPromise = new Promise((_, reject2) => {
56469
56506
  setTimeout(() => {
56470
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 from config (default 30 seconds)
278
- const timeout = this.config?.settings?.timeout || 30000;
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
  };
@@ -170,6 +170,19 @@ export class SimpleAppTracer {
170
170
  }
171
171
  }
172
172
 
173
+ /**
174
+ * Record a generic event (used by completionPrompt and other features)
175
+ */
176
+ // visor-disable: SimpleAppTracer uses this.sessionId because it's a per-session instance. AppTracer extracts from attributes because it's a singleton managing multiple sessions. Different architectures require different approaches.
177
+ recordEvent(name, attributes = {}) {
178
+ if (!this.isEnabled()) return;
179
+
180
+ this.addEvent(name, {
181
+ 'session.id': this.sessionId,
182
+ ...attributes
183
+ });
184
+ }
185
+
173
186
  /**
174
187
  * Record delegation events
175
188
  */
@@ -22,7 +22,7 @@ export function createSearchTool(options = {}) {
22
22
  query: searchQuery,
23
23
  path,
24
24
  cwd, // Working directory for resolving relative paths
25
- allow_tests,
25
+ allowTests: allow_tests ?? true,
26
26
  exact,
27
27
  json: false,
28
28
  maxResults,
@@ -54,7 +54,7 @@ export function createQueryTool(options = {}) {
54
54
  path,
55
55
  cwd, // Working directory for resolving relative paths
56
56
  language,
57
- allow_tests,
57
+ allowTests: allow_tests ?? true,
58
58
  json: false
59
59
  });
60
60
 
@@ -83,7 +83,7 @@ export function createExtractTool(options = {}) {
83
83
  const results = await extract({
84
84
  files,
85
85
  cwd, // Working directory for resolving relative paths
86
- allowTests: allow_tests,
86
+ allowTests: allow_tests ?? true,
87
87
  contextLines: context_lines,
88
88
  format
89
89
  });
@@ -50,7 +50,7 @@ export const searchTool = (options = {}) => {
50
50
  query: searchQuery,
51
51
  path: searchPath,
52
52
  cwd: options.cwd, // Working directory for resolving relative paths
53
- allowTests: allow_tests,
53
+ allowTests: allow_tests ?? true,
54
54
  exact,
55
55
  json: false,
56
56
  maxTokens: effectiveMaxTokens,
@@ -110,7 +110,7 @@ export const queryTool = (options = {}) => {
110
110
  path: queryPath,
111
111
  cwd: options.cwd, // Working directory for resolving relative paths
112
112
  language,
113
- allow_tests,
113
+ allowTests: allow_tests ?? true,
114
114
  json: false
115
115
  });
116
116
 
@@ -179,7 +179,7 @@ export const extractTool = (options = {}) => {
179
179
  extractOptions = {
180
180
  inputFile: tempFilePath,
181
181
  cwd: effectiveCwd,
182
- allowTests: allow_tests,
182
+ allowTests: allow_tests ?? true,
183
183
  contextLines: context_lines,
184
184
  format: effectiveFormat
185
185
  };
@@ -198,7 +198,7 @@ export const extractTool = (options = {}) => {
198
198
  extractOptions = {
199
199
  files,
200
200
  cwd: effectiveCwd,
201
- allowTests: allow_tests,
201
+ allowTests: allow_tests ?? true,
202
202
  contextLines: context_lines,
203
203
  format: effectiveFormat
204
204
  };
@@ -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 DEFAULT_TIMEOUT = 1e3;
17255
+ var DEFAULT_TIMEOUT2 = 1e3;
17257
17256
  var DEFAULT_MAX_RETRIES = 0;
17258
- var providerConfigFromInit = ({ maxRetries = DEFAULT_MAX_RETRIES, timeout = DEFAULT_TIMEOUT }) => ({ maxRetries, 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 = 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;
@@ -34427,7 +34426,7 @@ var init_vercel = __esm({
34427
34426
  path: searchPath,
34428
34427
  cwd: options.cwd,
34429
34428
  // Working directory for resolving relative paths
34430
- allowTests: allow_tests,
34429
+ allowTests: allow_tests ?? true,
34431
34430
  exact,
34432
34431
  json: false,
34433
34432
  maxTokens: effectiveMaxTokens,
@@ -34472,7 +34471,7 @@ var init_vercel = __esm({
34472
34471
  cwd: options.cwd,
34473
34472
  // Working directory for resolving relative paths
34474
34473
  language,
34475
- allow_tests,
34474
+ allowTests: allow_tests ?? true,
34476
34475
  json: false
34477
34476
  });
34478
34477
  return results;
@@ -34518,7 +34517,7 @@ var init_vercel = __esm({
34518
34517
  extractOptions = {
34519
34518
  inputFile: tempFilePath,
34520
34519
  cwd: effectiveCwd,
34521
- allowTests: allow_tests,
34520
+ allowTests: allow_tests ?? true,
34522
34521
  contextLines: context_lines,
34523
34522
  format: effectiveFormat
34524
34523
  };
@@ -34531,7 +34530,7 @@ var init_vercel = __esm({
34531
34530
  extractOptions = {
34532
34531
  files,
34533
34532
  cwd: effectiveCwd,
34534
- allowTests: allow_tests,
34533
+ allowTests: allow_tests ?? true,
34535
34534
  contextLines: context_lines,
34536
34535
  format: effectiveFormat
34537
34536
  };
@@ -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 timeout = this.config?.settings?.timeout || 3e4;
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`));
@@ -164,6 +164,17 @@ var SimpleAppTracer = class {
164
164
  console.log("[Event]", name, attributes);
165
165
  }
166
166
  }
167
+ /**
168
+ * Record a generic event (used by completionPrompt and other features)
169
+ */
170
+ // visor-disable: SimpleAppTracer uses this.sessionId because it's a per-session instance. AppTracer extracts from attributes because it's a singleton managing multiple sessions. Different architectures require different approaches.
171
+ recordEvent(name, attributes = {}) {
172
+ if (!this.isEnabled()) return;
173
+ this.addEvent(name, {
174
+ "session.id": this.sessionId,
175
+ ...attributes
176
+ });
177
+ }
167
178
  /**
168
179
  * Record delegation events
169
180
  */
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 DEFAULT_TIMEOUT = 1e3;
18782
+ var DEFAULT_TIMEOUT2 = 1e3;
18784
18783
  var DEFAULT_MAX_RETRIES = 0;
18785
- var providerConfigFromInit = ({ maxRetries = DEFAULT_MAX_RETRIES, timeout = DEFAULT_TIMEOUT }) => ({ maxRetries, 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 = 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 timeout = this.config?.settings?.timeout || 3e4;
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`));
@@ -85849,7 +85874,7 @@ var init_vercel = __esm({
85849
85874
  path: searchPath,
85850
85875
  cwd: options.cwd,
85851
85876
  // Working directory for resolving relative paths
85852
- allowTests: allow_tests,
85877
+ allowTests: allow_tests ?? true,
85853
85878
  exact,
85854
85879
  json: false,
85855
85880
  maxTokens: effectiveMaxTokens,
@@ -85894,7 +85919,7 @@ var init_vercel = __esm({
85894
85919
  cwd: options.cwd,
85895
85920
  // Working directory for resolving relative paths
85896
85921
  language,
85897
- allow_tests,
85922
+ allowTests: allow_tests ?? true,
85898
85923
  json: false
85899
85924
  });
85900
85925
  return results;
@@ -85940,7 +85965,7 @@ var init_vercel = __esm({
85940
85965
  extractOptions = {
85941
85966
  inputFile: tempFilePath,
85942
85967
  cwd: effectiveCwd,
85943
- allowTests: allow_tests,
85968
+ allowTests: allow_tests ?? true,
85944
85969
  contextLines: context_lines,
85945
85970
  format: effectiveFormat
85946
85971
  };
@@ -85953,7 +85978,7 @@ var init_vercel = __esm({
85953
85978
  extractOptions = {
85954
85979
  files,
85955
85980
  cwd: effectiveCwd,
85956
- allowTests: allow_tests,
85981
+ allowTests: allow_tests ?? true,
85957
85982
  contextLines: context_lines,
85958
85983
  format: effectiveFormat
85959
85984
  };
@@ -87613,7 +87638,7 @@ function createSearchTool(options = {}) {
87613
87638
  path: path9,
87614
87639
  cwd,
87615
87640
  // Working directory for resolving relative paths
87616
- allow_tests,
87641
+ allowTests: allow_tests ?? true,
87617
87642
  exact,
87618
87643
  json: false,
87619
87644
  maxResults,
@@ -87642,7 +87667,7 @@ function createQueryTool(options = {}) {
87642
87667
  cwd,
87643
87668
  // Working directory for resolving relative paths
87644
87669
  language,
87645
- allow_tests,
87670
+ allowTests: allow_tests ?? true,
87646
87671
  json: false
87647
87672
  });
87648
87673
  return results;
@@ -87666,7 +87691,7 @@ function createExtractTool(options = {}) {
87666
87691
  files,
87667
87692
  cwd,
87668
87693
  // Working directory for resolving relative paths
87669
- allowTests: allow_tests,
87694
+ allowTests: allow_tests ?? true,
87670
87695
  contextLines: context_lines,
87671
87696
  format: format2
87672
87697
  });
@@ -88137,6 +88162,17 @@ var init_simpleTelemetry = __esm({
88137
88162
  console.log("[Event]", name14, attributes);
88138
88163
  }
88139
88164
  }
88165
+ /**
88166
+ * Record a generic event (used by completionPrompt and other features)
88167
+ */
88168
+ // visor-disable: SimpleAppTracer uses this.sessionId because it's a per-session instance. AppTracer extracts from attributes because it's a singleton managing multiple sessions. Different architectures require different approaches.
88169
+ recordEvent(name14, attributes = {}) {
88170
+ if (!this.isEnabled()) return;
88171
+ this.addEvent(name14, {
88172
+ "session.id": this.sessionId,
88173
+ ...attributes
88174
+ });
88175
+ }
88140
88176
  /**
88141
88177
  * Record delegation events
88142
88178
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@probelabs/probe",
3
- "version": "0.6.0-rc175",
3
+ "version": "0.6.0-rc177",
4
4
  "description": "Node.js wrapper for the probe code search tool",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",
@@ -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 from config (default 30 seconds)
278
- const timeout = this.config?.settings?.timeout || 30000;
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
  };
@@ -170,6 +170,19 @@ export class SimpleAppTracer {
170
170
  }
171
171
  }
172
172
 
173
+ /**
174
+ * Record a generic event (used by completionPrompt and other features)
175
+ */
176
+ // visor-disable: SimpleAppTracer uses this.sessionId because it's a per-session instance. AppTracer extracts from attributes because it's a singleton managing multiple sessions. Different architectures require different approaches.
177
+ recordEvent(name, attributes = {}) {
178
+ if (!this.isEnabled()) return;
179
+
180
+ this.addEvent(name, {
181
+ 'session.id': this.sessionId,
182
+ ...attributes
183
+ });
184
+ }
185
+
173
186
  /**
174
187
  * Record delegation events
175
188
  */
@@ -22,7 +22,7 @@ export function createSearchTool(options = {}) {
22
22
  query: searchQuery,
23
23
  path,
24
24
  cwd, // Working directory for resolving relative paths
25
- allow_tests,
25
+ allowTests: allow_tests ?? true,
26
26
  exact,
27
27
  json: false,
28
28
  maxResults,
@@ -54,7 +54,7 @@ export function createQueryTool(options = {}) {
54
54
  path,
55
55
  cwd, // Working directory for resolving relative paths
56
56
  language,
57
- allow_tests,
57
+ allowTests: allow_tests ?? true,
58
58
  json: false
59
59
  });
60
60
 
@@ -83,7 +83,7 @@ export function createExtractTool(options = {}) {
83
83
  const results = await extract({
84
84
  files,
85
85
  cwd, // Working directory for resolving relative paths
86
- allowTests: allow_tests,
86
+ allowTests: allow_tests ?? true,
87
87
  contextLines: context_lines,
88
88
  format
89
89
  });
@@ -50,7 +50,7 @@ export const searchTool = (options = {}) => {
50
50
  query: searchQuery,
51
51
  path: searchPath,
52
52
  cwd: options.cwd, // Working directory for resolving relative paths
53
- allowTests: allow_tests,
53
+ allowTests: allow_tests ?? true,
54
54
  exact,
55
55
  json: false,
56
56
  maxTokens: effectiveMaxTokens,
@@ -110,7 +110,7 @@ export const queryTool = (options = {}) => {
110
110
  path: queryPath,
111
111
  cwd: options.cwd, // Working directory for resolving relative paths
112
112
  language,
113
- allow_tests,
113
+ allowTests: allow_tests ?? true,
114
114
  json: false
115
115
  });
116
116
 
@@ -179,7 +179,7 @@ export const extractTool = (options = {}) => {
179
179
  extractOptions = {
180
180
  inputFile: tempFilePath,
181
181
  cwd: effectiveCwd,
182
- allowTests: allow_tests,
182
+ allowTests: allow_tests ?? true,
183
183
  contextLines: context_lines,
184
184
  format: effectiveFormat
185
185
  };
@@ -198,7 +198,7 @@ export const extractTool = (options = {}) => {
198
198
  extractOptions = {
199
199
  files,
200
200
  cwd: effectiveCwd,
201
- allowTests: allow_tests,
201
+ allowTests: allow_tests ?? true,
202
202
  contextLines: context_lines,
203
203
  format: effectiveFormat
204
204
  };