@probelabs/probe 0.6.0-rc206 → 0.6.0-rc208

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.
Files changed (30) hide show
  1. package/bin/binaries/{probe-v0.6.0-rc206-aarch64-apple-darwin.tar.gz → probe-v0.6.0-rc208-aarch64-apple-darwin.tar.gz} +0 -0
  2. package/bin/binaries/probe-v0.6.0-rc208-aarch64-unknown-linux-musl.tar.gz +0 -0
  3. package/bin/binaries/probe-v0.6.0-rc208-x86_64-apple-darwin.tar.gz +0 -0
  4. package/bin/binaries/probe-v0.6.0-rc208-x86_64-pc-windows-msvc.zip +0 -0
  5. package/bin/binaries/probe-v0.6.0-rc208-x86_64-unknown-linux-musl.tar.gz +0 -0
  6. package/build/agent/ProbeAgent.js +144 -2
  7. package/build/agent/bashPermissions.js +88 -7
  8. package/build/agent/index.js +450 -18
  9. package/build/agent/mcp/client.js +234 -4
  10. package/build/agent/mcp/config.js +87 -0
  11. package/build/agent/mcp/xmlBridge.js +15 -5
  12. package/build/agent/simpleTelemetry.js +26 -0
  13. package/build/tools/bash.js +5 -3
  14. package/build/tools/common.js +31 -0
  15. package/cjs/agent/ProbeAgent.cjs +428 -18
  16. package/cjs/agent/simpleTelemetry.cjs +22 -0
  17. package/cjs/index.cjs +450 -18
  18. package/package.json +1 -1
  19. package/src/agent/ProbeAgent.js +144 -2
  20. package/src/agent/bashPermissions.js +88 -7
  21. package/src/agent/mcp/client.js +234 -4
  22. package/src/agent/mcp/config.js +87 -0
  23. package/src/agent/mcp/xmlBridge.js +15 -5
  24. package/src/agent/simpleTelemetry.js +26 -0
  25. package/src/tools/bash.js +5 -3
  26. package/src/tools/common.js +31 -0
  27. package/bin/binaries/probe-v0.6.0-rc206-aarch64-unknown-linux-musl.tar.gz +0 -0
  28. package/bin/binaries/probe-v0.6.0-rc206-x86_64-apple-darwin.tar.gz +0 -0
  29. package/bin/binaries/probe-v0.6.0-rc206-x86_64-pc-windows-msvc.zip +0 -0
  30. package/bin/binaries/probe-v0.6.0-rc206-x86_64-unknown-linux-musl.tar.gz +0 -0
@@ -9059,6 +9059,22 @@ function detectUnrecognizedToolCall(xmlString, validTools) {
9059
9059
  return toolName;
9060
9060
  }
9061
9061
  }
9062
+ const allToolNames = [.../* @__PURE__ */ new Set([...knownToolNames, ...validTools])];
9063
+ for (const toolName of allToolNames) {
9064
+ const escapedToolName = toolName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
9065
+ const wrapperPatterns = [
9066
+ new RegExp(`<tool_name>\\s*${escapedToolName}\\s*</tool_name>`, "i"),
9067
+ new RegExp(`<function>\\s*${escapedToolName}\\s*</function>`, "i"),
9068
+ new RegExp(`<name>\\s*${escapedToolName}\\s*</name>`, "i"),
9069
+ // Also check for tool name immediately after api_call or call opening tag
9070
+ new RegExp(`<(?:api_call|call)[^>]*>[\\s\\S]*?<tool_name>\\s*${escapedToolName}`, "i")
9071
+ ];
9072
+ for (const pattern of wrapperPatterns) {
9073
+ if (pattern.test(xmlString)) {
9074
+ return `wrapped_tool:${toolName}`;
9075
+ }
9076
+ }
9077
+ }
9062
9078
  return null;
9063
9079
  }
9064
9080
  function parseTargets(targets) {
@@ -10585,9 +10601,11 @@ var init_bashPermissions = __esm({
10585
10601
  * @param {boolean} [config.disableDefaultAllow] - Disable default allow list
10586
10602
  * @param {boolean} [config.disableDefaultDeny] - Disable default deny list
10587
10603
  * @param {boolean} [config.debug] - Enable debug logging
10604
+ * @param {Object} [config.tracer] - Optional tracer for telemetry
10588
10605
  */
10589
10606
  constructor(config = {}) {
10590
10607
  this.debug = config.debug || false;
10608
+ this.tracer = config.tracer || null;
10591
10609
  this.allowPatterns = [];
10592
10610
  if (!config.disableDefaultAllow) {
10593
10611
  this.allowPatterns.push(...DEFAULT_ALLOW_PATTERNS);
@@ -10617,6 +10635,24 @@ var init_bashPermissions = __esm({
10617
10635
  if (this.debug) {
10618
10636
  console.log(`[BashPermissions] Total patterns - Allow: ${this.allowPatterns.length}, Deny: ${this.denyPatterns.length}`);
10619
10637
  }
10638
+ this.recordBashEvent("permissions.initialized", {
10639
+ allowPatternCount: this.allowPatterns.length,
10640
+ denyPatternCount: this.denyPatterns.length,
10641
+ hasCustomAllowPatterns: !!(config.allow && config.allow.length > 0),
10642
+ hasCustomDenyPatterns: !!(config.deny && config.deny.length > 0),
10643
+ disableDefaultAllow: !!config.disableDefaultAllow,
10644
+ disableDefaultDeny: !!config.disableDefaultDeny
10645
+ });
10646
+ }
10647
+ /**
10648
+ * Record a bash telemetry event if tracer is available
10649
+ * @param {string} eventType - Event type (e.g., 'permission.checked', 'permission.denied')
10650
+ * @param {Object} data - Event data
10651
+ */
10652
+ recordBashEvent(eventType, data = {}) {
10653
+ if (this.tracer && typeof this.tracer.recordBashEvent === "function") {
10654
+ this.tracer.recordBashEvent(eventType, data);
10655
+ }
10620
10656
  }
10621
10657
  /**
10622
10658
  * Check if a simple command is allowed (complex commands allowed if they match patterns)
@@ -10625,11 +10661,17 @@ var init_bashPermissions = __esm({
10625
10661
  */
10626
10662
  check(command) {
10627
10663
  if (!command || typeof command !== "string") {
10628
- return {
10664
+ const result2 = {
10629
10665
  allowed: false,
10630
10666
  reason: "Invalid or empty command",
10631
10667
  command
10632
10668
  };
10669
+ this.recordBashEvent("permission.denied", {
10670
+ command: String(command),
10671
+ reason: result2.reason,
10672
+ isComplex: false
10673
+ });
10674
+ return result2;
10633
10675
  }
10634
10676
  const commandIsComplex = isComplexCommand(command);
10635
10677
  if (commandIsComplex) {
@@ -10637,18 +10679,31 @@ var init_bashPermissions = __esm({
10637
10679
  }
10638
10680
  const parsed = parseCommand(command);
10639
10681
  if (parsed.error) {
10640
- return {
10682
+ const result2 = {
10641
10683
  allowed: false,
10642
10684
  reason: parsed.error,
10643
10685
  command
10644
10686
  };
10687
+ this.recordBashEvent("permission.denied", {
10688
+ command,
10689
+ reason: result2.reason,
10690
+ isComplex: false,
10691
+ parseError: true
10692
+ });
10693
+ return result2;
10645
10694
  }
10646
10695
  if (!parsed.command) {
10647
- return {
10696
+ const result2 = {
10648
10697
  allowed: false,
10649
10698
  reason: "No valid command found",
10650
10699
  command
10651
10700
  };
10701
+ this.recordBashEvent("permission.denied", {
10702
+ command,
10703
+ reason: result2.reason,
10704
+ isComplex: false
10705
+ });
10706
+ return result2;
10652
10707
  }
10653
10708
  if (this.debug) {
10654
10709
  console.log(`[BashPermissions] Checking simple command: "${command}"`);
@@ -10656,22 +10711,37 @@ var init_bashPermissions = __esm({
10656
10711
  }
10657
10712
  if (matchesAnyPattern(parsed, this.denyPatterns)) {
10658
10713
  const matchedPatterns = this.denyPatterns.filter((pattern) => matchesPattern(parsed, pattern));
10659
- return {
10714
+ const result2 = {
10660
10715
  allowed: false,
10661
10716
  reason: `Command matches deny pattern: ${matchedPatterns[0]}`,
10662
10717
  command,
10663
10718
  parsed,
10664
10719
  matchedPatterns
10665
10720
  };
10721
+ this.recordBashEvent("permission.denied", {
10722
+ command,
10723
+ parsedCommand: parsed.command,
10724
+ reason: "matches_deny_pattern",
10725
+ matchedPattern: matchedPatterns[0],
10726
+ isComplex: false
10727
+ });
10728
+ return result2;
10666
10729
  }
10667
10730
  if (this.allowPatterns.length > 0) {
10668
10731
  if (!matchesAnyPattern(parsed, this.allowPatterns)) {
10669
- return {
10732
+ const result2 = {
10670
10733
  allowed: false,
10671
10734
  reason: "Command not in allow list",
10672
10735
  command,
10673
10736
  parsed
10674
10737
  };
10738
+ this.recordBashEvent("permission.denied", {
10739
+ command,
10740
+ parsedCommand: parsed.command,
10741
+ reason: "not_in_allow_list",
10742
+ isComplex: false
10743
+ });
10744
+ return result2;
10675
10745
  }
10676
10746
  }
10677
10747
  const result = {
@@ -10683,6 +10753,11 @@ var init_bashPermissions = __esm({
10683
10753
  if (this.debug) {
10684
10754
  console.log(`[BashPermissions] ALLOWED - command passed all checks`);
10685
10755
  }
10756
+ this.recordBashEvent("permission.allowed", {
10757
+ command,
10758
+ parsedCommand: parsed.command,
10759
+ isComplex: false
10760
+ });
10686
10761
  return result;
10687
10762
  }
10688
10763
  /**
@@ -10706,13 +10781,20 @@ var init_bashPermissions = __esm({
10706
10781
  if (this.debug) {
10707
10782
  console.log(`[BashPermissions] DENIED - matches complex deny pattern: ${pattern}`);
10708
10783
  }
10709
- return {
10784
+ const result = {
10710
10785
  allowed: false,
10711
10786
  reason: `Command matches deny pattern: ${pattern}`,
10712
10787
  command,
10713
10788
  isComplex: true,
10714
10789
  matchedPatterns: [pattern]
10715
10790
  };
10791
+ this.recordBashEvent("permission.denied", {
10792
+ command,
10793
+ reason: "matches_deny_pattern",
10794
+ matchedPattern: pattern,
10795
+ isComplex: true
10796
+ });
10797
+ return result;
10716
10798
  }
10717
10799
  }
10718
10800
  for (const pattern of complexAllowPatterns) {
@@ -10720,17 +10802,28 @@ var init_bashPermissions = __esm({
10720
10802
  if (this.debug) {
10721
10803
  console.log(`[BashPermissions] ALLOWED - matches complex allow pattern: ${pattern}`);
10722
10804
  }
10723
- return {
10805
+ const result = {
10724
10806
  allowed: true,
10725
10807
  command,
10726
10808
  isComplex: true,
10727
10809
  matchedPattern: pattern
10728
10810
  };
10811
+ this.recordBashEvent("permission.allowed", {
10812
+ command,
10813
+ matchedPattern: pattern,
10814
+ isComplex: true
10815
+ });
10816
+ return result;
10729
10817
  }
10730
10818
  }
10731
10819
  if (this.debug) {
10732
10820
  console.log(`[BashPermissions] DENIED - no matching complex pattern found`);
10733
10821
  }
10822
+ this.recordBashEvent("permission.denied", {
10823
+ command,
10824
+ reason: "no_matching_complex_pattern",
10825
+ isComplex: true
10826
+ });
10734
10827
  return {
10735
10828
  allowed: false,
10736
10829
  reason: 'Complex shell commands require explicit allow patterns (e.g., "cd * && git *")',
@@ -11028,14 +11121,16 @@ var init_bash = __esm({
11028
11121
  bashConfig = {},
11029
11122
  debug = false,
11030
11123
  cwd,
11031
- allowedFolders = []
11124
+ allowedFolders = [],
11125
+ tracer = null
11032
11126
  } = options;
11033
11127
  const permissionChecker = new BashPermissionChecker({
11034
11128
  allow: bashConfig.allow,
11035
11129
  deny: bashConfig.deny,
11036
11130
  disableDefaultAllow: bashConfig.disableDefaultAllow,
11037
11131
  disableDefaultDeny: bashConfig.disableDefaultDeny,
11038
- debug
11132
+ debug,
11133
+ tracer
11039
11134
  });
11040
11135
  const getDefaultWorkingDirectory = () => {
11041
11136
  if (bashConfig.workingDirectory) {
@@ -11665,6 +11760,28 @@ var init_simpleTelemetry = __esm({
11665
11760
  ...data
11666
11761
  });
11667
11762
  }
11763
+ /**
11764
+ * Record MCP (Model Context Protocol) events
11765
+ * Tracks server connections, tool discovery, method filtering, and tool execution
11766
+ */
11767
+ recordMcpEvent(eventType, data = {}) {
11768
+ if (!this.isEnabled()) return;
11769
+ this.addEvent(`mcp.${eventType}`, {
11770
+ "session.id": this.sessionId,
11771
+ ...data
11772
+ });
11773
+ }
11774
+ /**
11775
+ * Record bash tool events
11776
+ * Tracks command permission checks, allowed/denied commands, and execution
11777
+ */
11778
+ recordBashEvent(eventType, data = {}) {
11779
+ if (!this.isEnabled()) return;
11780
+ this.addEvent(`bash.${eventType}`, {
11781
+ "session.id": this.sessionId,
11782
+ ...data
11783
+ });
11784
+ }
11668
11785
  setAttributes(attributes) {
11669
11786
  if (this.telemetry && this.telemetry.enableConsole) {
11670
11787
  console.log("[Attributes]", attributes);
@@ -57891,6 +58008,47 @@ function validateTimeout(value) {
57891
58008
  if (!Number.isFinite(num) || num < 0) return void 0;
57892
58009
  return Math.min(num, MAX_TIMEOUT);
57893
58010
  }
58011
+ function validateMethodFilter(serverConfig, serverName = "unknown") {
58012
+ const result = { allowedMethods: null, blockedMethods: null };
58013
+ const debug = process.env.DEBUG === "1" || process.env.DEBUG_MCP === "1";
58014
+ if (serverConfig.allowedMethods && serverConfig.blockedMethods) {
58015
+ console.error(`[MCP WARN] Server '${serverName}' has both allowedMethods and blockedMethods - using allowedMethods only`);
58016
+ }
58017
+ if (serverConfig.allowedMethods) {
58018
+ if (!Array.isArray(serverConfig.allowedMethods)) {
58019
+ console.error(`[MCP WARN] Server '${serverName}' allowedMethods must be an array, ignoring`);
58020
+ } else {
58021
+ const validMethods = serverConfig.allowedMethods.filter((m) => typeof m === "string" && m.length > 0);
58022
+ if (validMethods.length !== serverConfig.allowedMethods.length) {
58023
+ console.error(`[MCP WARN] Server '${serverName}' allowedMethods contains non-string values, skipping those`);
58024
+ }
58025
+ if (validMethods.length > 0) {
58026
+ result.allowedMethods = validMethods;
58027
+ if (debug) {
58028
+ console.error(`[MCP DEBUG] Server '${serverName}' allowedMethods: ${validMethods.join(", ")}`);
58029
+ }
58030
+ }
58031
+ }
58032
+ return result;
58033
+ }
58034
+ if (serverConfig.blockedMethods) {
58035
+ if (!Array.isArray(serverConfig.blockedMethods)) {
58036
+ console.error(`[MCP WARN] Server '${serverName}' blockedMethods must be an array, ignoring`);
58037
+ } else {
58038
+ const validMethods = serverConfig.blockedMethods.filter((m) => typeof m === "string" && m.length > 0);
58039
+ if (validMethods.length !== serverConfig.blockedMethods.length) {
58040
+ console.error(`[MCP WARN] Server '${serverName}' blockedMethods contains non-string values, skipping those`);
58041
+ }
58042
+ if (validMethods.length > 0) {
58043
+ result.blockedMethods = validMethods;
58044
+ if (debug) {
58045
+ console.error(`[MCP DEBUG] Server '${serverName}' blockedMethods: ${validMethods.join(", ")}`);
58046
+ }
58047
+ }
58048
+ }
58049
+ }
58050
+ return result;
58051
+ }
57894
58052
  function loadMCPConfigurationFromPath(configPath) {
57895
58053
  if (!configPath) {
57896
58054
  throw new Error("Config path is required");
@@ -57984,6 +58142,12 @@ function mergeWithEnvironment(config) {
57984
58142
  console.error(`[MCP WARN] Invalid timeout value for ${normalizedName}: ${value}`);
57985
58143
  }
57986
58144
  break;
58145
+ case "ALLOWLIST":
58146
+ config.mcpServers[normalizedName].allowedMethods = value.split(",").map((m) => m.trim()).filter(Boolean);
58147
+ break;
58148
+ case "BLOCKLIST":
58149
+ config.mcpServers[normalizedName].blockedMethods = value.split(",").map((m) => m.trim()).filter(Boolean);
58150
+ break;
57987
58151
  }
57988
58152
  }
57989
58153
  }
@@ -58034,6 +58198,9 @@ function parseEnabledServers(config) {
58034
58198
  }
58035
58199
  server.timeout = validatedTimeout;
58036
58200
  }
58201
+ const methodFilter = validateMethodFilter(serverConfig, name);
58202
+ server.allowedMethods = methodFilter.allowedMethods;
58203
+ server.blockedMethods = methodFilter.blockedMethods;
58037
58204
  servers.push(server);
58038
58205
  }
58039
58206
  return servers;
@@ -58071,6 +58238,22 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
58071
58238
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
58072
58239
  import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
58073
58240
  import { WebSocketClientTransport } from "@modelcontextprotocol/sdk/client/websocket.js";
58241
+ function isMethodAllowed(methodName, allowedMethods, blockedMethods) {
58242
+ const matchesPattern2 = (name, pattern) => {
58243
+ if (!pattern.includes("*")) {
58244
+ return name === pattern;
58245
+ }
58246
+ const regexPattern = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
58247
+ return new RegExp(`^${regexPattern}$`).test(name);
58248
+ };
58249
+ if (allowedMethods && allowedMethods.length > 0) {
58250
+ return allowedMethods.some((pattern) => matchesPattern2(methodName, pattern));
58251
+ }
58252
+ if (blockedMethods && blockedMethods.length > 0) {
58253
+ return !blockedMethods.some((pattern) => matchesPattern2(methodName, pattern));
58254
+ }
58255
+ return true;
58256
+ }
58074
58257
  function createTransport(serverConfig) {
58075
58258
  const { transport, command, args, url, env } = serverConfig;
58076
58259
  switch (transport) {
@@ -58151,6 +58334,17 @@ var init_client = __esm({
58151
58334
  this.tools = /* @__PURE__ */ new Map();
58152
58335
  this.debug = options.debug || process.env.DEBUG_MCP === "1";
58153
58336
  this.config = null;
58337
+ this.tracer = options.tracer || null;
58338
+ }
58339
+ /**
58340
+ * Record an MCP telemetry event if tracer is available
58341
+ * @param {string} eventType - Event type (e.g., 'server.connect', 'tool.discovered')
58342
+ * @param {Object} data - Event data
58343
+ */
58344
+ recordMcpEvent(eventType, data = {}) {
58345
+ if (this.tracer && typeof this.tracer.recordMcpEvent === "function") {
58346
+ this.tracer.recordMcpEvent(eventType, data);
58347
+ }
58154
58348
  }
58155
58349
  /**
58156
58350
  * Initialize MCP clients from configuration
@@ -58159,10 +58353,20 @@ var init_client = __esm({
58159
58353
  async initialize(config = null) {
58160
58354
  this.config = config || loadMCPConfiguration();
58161
58355
  const servers = parseEnabledServers(this.config);
58356
+ this.recordMcpEvent("initialization.started", {
58357
+ serverCount: servers.length,
58358
+ serverNames: servers.map((s) => s.name)
58359
+ });
58162
58360
  console.error(`[MCP INFO] Found ${servers.length} enabled MCP server${servers.length !== 1 ? "s" : ""}`);
58163
58361
  if (servers.length === 0) {
58164
58362
  console.error("[MCP INFO] No MCP servers configured or enabled");
58165
58363
  console.error("[MCP INFO] 0 MCP tools available");
58364
+ this.recordMcpEvent("initialization.completed", {
58365
+ connected: 0,
58366
+ total: 0,
58367
+ toolCount: 0,
58368
+ tools: []
58369
+ });
58166
58370
  return {
58167
58371
  connected: 0,
58168
58372
  total: 0,
@@ -58199,10 +58403,17 @@ var init_client = __esm({
58199
58403
  console.error(`[MCP DEBUG] - ${toolName}`);
58200
58404
  });
58201
58405
  }
58406
+ const toolNames = Array.from(this.tools.keys());
58407
+ this.recordMcpEvent("initialization.completed", {
58408
+ connected: connectedCount,
58409
+ total: servers.length,
58410
+ toolCount: this.tools.size,
58411
+ tools: toolNames
58412
+ });
58202
58413
  return {
58203
58414
  connected: connectedCount,
58204
58415
  total: servers.length,
58205
- tools: Array.from(this.tools.keys())
58416
+ tools: toolNames
58206
58417
  };
58207
58418
  }
58208
58419
  /**
@@ -58211,6 +58422,12 @@ var init_client = __esm({
58211
58422
  */
58212
58423
  async connectToServer(serverConfig) {
58213
58424
  const { name } = serverConfig;
58425
+ this.recordMcpEvent("server.connecting", {
58426
+ serverName: name,
58427
+ transport: serverConfig.transport,
58428
+ hasAllowedMethods: !!(serverConfig.allowedMethods && serverConfig.allowedMethods.length > 0),
58429
+ hasBlockedMethods: !!(serverConfig.blockedMethods && serverConfig.blockedMethods.length > 0)
58430
+ });
58214
58431
  try {
58215
58432
  if (this.debug) {
58216
58433
  console.error(`[MCP DEBUG] Connecting to ${name} via ${serverConfig.transport}...`);
@@ -58232,27 +58449,92 @@ var init_client = __esm({
58232
58449
  config: serverConfig
58233
58450
  });
58234
58451
  const toolsResponse = await client.listTools();
58235
- const toolCount = toolsResponse?.tools?.length || 0;
58452
+ const totalToolCount = toolsResponse?.tools?.length || 0;
58453
+ let registeredCount = 0;
58454
+ let filteredCount = 0;
58455
+ const registeredTools = [];
58456
+ const filteredTools = [];
58236
58457
  if (toolsResponse && toolsResponse.tools) {
58458
+ const { allowedMethods, blockedMethods } = serverConfig;
58459
+ const allToolNames = toolsResponse.tools.map((t) => t.name);
58460
+ this.recordMcpEvent("tools.discovered", {
58461
+ serverName: name,
58462
+ toolCount: totalToolCount,
58463
+ tools: allToolNames
58464
+ });
58237
58465
  for (const tool4 of toolsResponse.tools) {
58466
+ if (!isMethodAllowed(tool4.name, allowedMethods, blockedMethods)) {
58467
+ filteredCount++;
58468
+ filteredTools.push(tool4.name);
58469
+ if (this.debug) {
58470
+ console.error(`[MCP DEBUG] Filtered out tool: ${tool4.name} (not allowed by method filter)`);
58471
+ }
58472
+ continue;
58473
+ }
58238
58474
  const qualifiedName = `${name}_${tool4.name}`;
58239
58475
  this.tools.set(qualifiedName, {
58240
58476
  ...tool4,
58241
58477
  serverName: name,
58242
58478
  originalName: tool4.name
58243
58479
  });
58480
+ registeredCount++;
58481
+ registeredTools.push(qualifiedName);
58244
58482
  if (this.debug) {
58245
58483
  console.error(`[MCP DEBUG] Registered tool: ${qualifiedName}`);
58246
58484
  }
58247
58485
  }
58486
+ if (filteredCount > 0) {
58487
+ this.recordMcpEvent("tools.filtered", {
58488
+ serverName: name,
58489
+ filteredCount,
58490
+ filteredTools,
58491
+ allowedMethods: allowedMethods || [],
58492
+ blockedMethods: blockedMethods || []
58493
+ });
58494
+ }
58495
+ if (allowedMethods && allowedMethods.length > 0) {
58496
+ const unmatchedPatterns = allowedMethods.filter((pattern) => {
58497
+ return !allToolNames.some((toolName) => isMethodAllowed(toolName, [pattern], null));
58498
+ });
58499
+ if (unmatchedPatterns.length > 0) {
58500
+ console.error(`[MCP WARN] Server '${name}': The following allowedMethods patterns did not match any tools: ${unmatchedPatterns.join(", ")}`);
58501
+ console.error(`[MCP WARN] Available methods from '${name}': ${allToolNames.join(", ")}`);
58502
+ }
58503
+ }
58504
+ if (blockedMethods && blockedMethods.length > 0) {
58505
+ const unmatchedPatterns = blockedMethods.filter((pattern) => {
58506
+ return !allToolNames.some((toolName) => !isMethodAllowed(toolName, null, [pattern]));
58507
+ });
58508
+ if (unmatchedPatterns.length > 0) {
58509
+ console.error(`[MCP WARN] Server '${name}': The following blockedMethods patterns did not match any tools: ${unmatchedPatterns.join(", ")}`);
58510
+ console.error(`[MCP WARN] Available methods from '${name}': ${allToolNames.join(", ")}`);
58511
+ }
58512
+ }
58248
58513
  }
58249
- console.error(`[MCP INFO] Connected to ${name}: ${toolCount} tool${toolCount !== 1 ? "s" : ""} loaded`);
58514
+ if (filteredCount > 0) {
58515
+ console.error(`[MCP INFO] Connected to ${name}: ${registeredCount} tool${registeredCount !== 1 ? "s" : ""} loaded (${filteredCount} filtered out)`);
58516
+ } else {
58517
+ console.error(`[MCP INFO] Connected to ${name}: ${registeredCount} tool${registeredCount !== 1 ? "s" : ""} loaded`);
58518
+ }
58519
+ this.recordMcpEvent("server.connected", {
58520
+ serverName: name,
58521
+ transport: serverConfig.transport,
58522
+ totalToolCount,
58523
+ registeredCount,
58524
+ filteredCount,
58525
+ registeredTools
58526
+ });
58250
58527
  return true;
58251
58528
  } catch (error) {
58252
58529
  console.error(`[MCP ERROR] Error connecting to ${name}:`, error.message);
58253
58530
  if (this.debug) {
58254
58531
  console.error(`[MCP DEBUG] Full error details:`, error);
58255
58532
  }
58533
+ this.recordMcpEvent("server.connection_failed", {
58534
+ serverName: name,
58535
+ transport: serverConfig.transport,
58536
+ error: error.message
58537
+ });
58256
58538
  return false;
58257
58539
  }
58258
58540
  }
@@ -58264,12 +58546,27 @@ var init_client = __esm({
58264
58546
  async callTool(toolName, args) {
58265
58547
  const tool4 = this.tools.get(toolName);
58266
58548
  if (!tool4) {
58549
+ this.recordMcpEvent("tool.call_failed", {
58550
+ toolName,
58551
+ error: "Unknown tool"
58552
+ });
58267
58553
  throw new Error(`Unknown tool: ${toolName}`);
58268
58554
  }
58269
58555
  const clientInfo = this.clients.get(tool4.serverName);
58270
58556
  if (!clientInfo) {
58557
+ this.recordMcpEvent("tool.call_failed", {
58558
+ toolName,
58559
+ serverName: tool4.serverName,
58560
+ error: "Server not connected"
58561
+ });
58271
58562
  throw new Error(`Server ${tool4.serverName} not connected`);
58272
58563
  }
58564
+ const startTime = Date.now();
58565
+ this.recordMcpEvent("tool.call_started", {
58566
+ toolName,
58567
+ serverName: tool4.serverName,
58568
+ originalToolName: tool4.originalName
58569
+ });
58273
58570
  try {
58274
58571
  if (this.debug) {
58275
58572
  console.error(`[MCP DEBUG] Calling ${toolName} with args:`, JSON.stringify(args, null, 2));
@@ -58289,15 +58586,31 @@ var init_client = __esm({
58289
58586
  }),
58290
58587
  timeoutPromise
58291
58588
  ]);
58589
+ const durationMs = Date.now() - startTime;
58292
58590
  if (this.debug) {
58293
58591
  console.error(`[MCP DEBUG] Tool ${toolName} executed successfully`);
58294
58592
  }
58593
+ this.recordMcpEvent("tool.call_completed", {
58594
+ toolName,
58595
+ serverName: tool4.serverName,
58596
+ originalToolName: tool4.originalName,
58597
+ durationMs
58598
+ });
58295
58599
  return result;
58296
58600
  } catch (error) {
58601
+ const durationMs = Date.now() - startTime;
58297
58602
  console.error(`[MCP ERROR] Error calling tool ${toolName}:`, error.message);
58298
58603
  if (this.debug) {
58299
58604
  console.error(`[MCP DEBUG] Full error details:`, error);
58300
58605
  }
58606
+ this.recordMcpEvent("tool.call_failed", {
58607
+ toolName,
58608
+ serverName: tool4.serverName,
58609
+ originalToolName: tool4.originalName,
58610
+ error: error.message,
58611
+ durationMs,
58612
+ isTimeout: error.message.includes("timeout")
58613
+ });
58301
58614
  throw error;
58302
58615
  }
58303
58616
  }
@@ -58342,12 +58655,17 @@ var init_client = __esm({
58342
58655
  */
58343
58656
  async disconnect() {
58344
58657
  const disconnectPromises = [];
58658
+ const serverNames = Array.from(this.clients.keys());
58345
58659
  if (this.clients.size === 0) {
58346
58660
  if (this.debug) {
58347
58661
  console.error("[MCP DEBUG] No MCP clients to disconnect");
58348
58662
  }
58349
58663
  return;
58350
58664
  }
58665
+ this.recordMcpEvent("disconnection.started", {
58666
+ serverCount: this.clients.size,
58667
+ serverNames
58668
+ });
58351
58669
  if (this.debug) {
58352
58670
  console.error(`[MCP DEBUG] Disconnecting from ${this.clients.size} MCP server${this.clients.size !== 1 ? "s" : ""}...`);
58353
58671
  }
@@ -58357,14 +58675,25 @@ var init_client = __esm({
58357
58675
  if (this.debug) {
58358
58676
  console.error(`[MCP DEBUG] Disconnected from ${name}`);
58359
58677
  }
58678
+ this.recordMcpEvent("server.disconnected", {
58679
+ serverName: name
58680
+ });
58360
58681
  }).catch((error) => {
58361
58682
  console.error(`[MCP ERROR] Error disconnecting from ${name}:`, error.message);
58683
+ this.recordMcpEvent("server.disconnect_failed", {
58684
+ serverName: name,
58685
+ error: error.message
58686
+ });
58362
58687
  })
58363
58688
  );
58364
58689
  }
58365
58690
  await Promise.all(disconnectPromises);
58366
58691
  this.clients.clear();
58367
58692
  this.tools.clear();
58693
+ this.recordMcpEvent("disconnection.completed", {
58694
+ serverCount: serverNames.length,
58695
+ serverNames
58696
+ });
58368
58697
  if (this.debug) {
58369
58698
  console.error("[MCP DEBUG] All MCP connections closed");
58370
58699
  }
@@ -58508,6 +58837,7 @@ var init_xmlBridge = __esm({
58508
58837
  MCPXmlBridge = class {
58509
58838
  constructor(options = {}) {
58510
58839
  this.debug = options.debug || false;
58840
+ this.tracer = options.tracer || null;
58511
58841
  this.mcpTools = {};
58512
58842
  this.mcpManager = null;
58513
58843
  this.xmlDefinitions = {};
@@ -58550,7 +58880,7 @@ var init_xmlBridge = __esm({
58550
58880
  if (this.debug) {
58551
58881
  console.error("[MCP DEBUG] Initializing MCP client manager...");
58552
58882
  }
58553
- this.mcpManager = new MCPClientManager({ debug: this.debug });
58883
+ this.mcpManager = new MCPClientManager({ debug: this.debug, tracer: this.tracer });
58554
58884
  const result = await this.mcpManager.initialize(mcpConfigs);
58555
58885
  const vercelTools = this.mcpManager.getVercelTools();
58556
58886
  this.mcpTools = vercelTools;
@@ -58574,11 +58904,15 @@ var init_xmlBridge = __esm({
58574
58904
  }
58575
58905
  }
58576
58906
  /**
58577
- * Get all XML tool definitions for inclusion in system prompt
58907
+ * Get XML tool definitions for inclusion in system prompt
58908
+ * @param {Array<string>|null} filterToolNames - Optional list of tool names to include (if null, include all)
58578
58909
  * @returns {string} Combined XML tool definitions
58579
58910
  */
58580
- getXmlToolDefinitions() {
58581
- return Object.values(this.xmlDefinitions).join("\n\n");
58911
+ getXmlToolDefinitions(filterToolNames = null) {
58912
+ if (filterToolNames === null) {
58913
+ return Object.values(this.xmlDefinitions).join("\n\n");
58914
+ }
58915
+ return Object.entries(this.xmlDefinitions).filter(([name]) => filterToolNames.includes(name)).map(([, def]) => def).join("\n\n");
58582
58916
  }
58583
58917
  /**
58584
58918
  * Get list of MCP tool names
@@ -68676,6 +69010,27 @@ import { EventEmitter as EventEmitter5 } from "events";
68676
69010
  import { existsSync as existsSync6 } from "fs";
68677
69011
  import { readFile as readFile3, stat, readdir as readdir3 } from "fs/promises";
68678
69012
  import { resolve as resolve6, isAbsolute as isAbsolute5, dirname as dirname5, basename, normalize as normalize2, sep as sep5 } from "path";
69013
+ function extractWrappedToolName(wrappedToolError) {
69014
+ if (!wrappedToolError || typeof wrappedToolError !== "string") {
69015
+ return "unknown";
69016
+ }
69017
+ const colonIndex = wrappedToolError.indexOf(":");
69018
+ return colonIndex !== -1 ? wrappedToolError.slice(colonIndex + 1) : "unknown";
69019
+ }
69020
+ function isWrappedToolError(error) {
69021
+ return error && typeof error === "string" && error.startsWith("wrapped_tool:");
69022
+ }
69023
+ function createWrappedToolErrorMessage(wrappedToolName) {
69024
+ return `Your response contained an incorrectly formatted tool call (${wrappedToolName} wrapped in XML tags). This cannot be used.
69025
+
69026
+ Please use the CORRECT format:
69027
+
69028
+ <${wrappedToolName}>
69029
+ Your content here
69030
+ </${wrappedToolName}>
69031
+
69032
+ Do NOT wrap in other tags like <api_call>, <tool_name>, <function>, etc.`;
69033
+ }
68679
69034
  var MAX_TOOL_ITERATIONS, MAX_HISTORY_MESSAGES, MAX_IMAGE_FILE_SIZE, ProbeAgent;
68680
69035
  var init_ProbeAgent = __esm({
68681
69036
  "src/agent/ProbeAgent.js"() {
@@ -70675,6 +71030,9 @@ You are working with a repository located at: ${searchDirectory}
70675
71030
  console.log(`[DEBUG] Schema provided, using extended iteration limit: ${maxIterations} (base: ${baseMaxIterations})`);
70676
71031
  }
70677
71032
  }
71033
+ let lastFormatErrorType = null;
71034
+ let sameFormatErrorCount = 0;
71035
+ const MAX_REPEATED_FORMAT_ERRORS = 3;
70678
71036
  while (currentIteration < maxIterations && !completionAttempted) {
70679
71037
  currentIteration++;
70680
71038
  if (this.cancelled) throw new Error("Request was cancelled by the user");
@@ -70873,7 +71231,22 @@ You are working with a repository located at: ${searchDirectory}
70873
71231
  (msg) => msg.role === "assistant" && msg.content && !(this.mcpBridge ? parseHybridXmlToolCall(msg.content, validTools, this.mcpBridge) : parseXmlToolCallWithThinking(msg.content, validTools))
70874
71232
  );
70875
71233
  if (lastAssistantMessage) {
70876
- finalResult = lastAssistantMessage.content;
71234
+ const prevContent = lastAssistantMessage.content;
71235
+ const wrappedToolError = detectUnrecognizedToolCall(prevContent, validTools);
71236
+ if (isWrappedToolError(wrappedToolError)) {
71237
+ const wrappedToolName = extractWrappedToolName(wrappedToolError);
71238
+ if (this.debug) {
71239
+ console.log(`[DEBUG] Previous response contains wrapped tool '${wrappedToolName}' - rejecting for __PREVIOUS_RESPONSE__`);
71240
+ }
71241
+ currentMessages.push({ role: "assistant", content: assistantResponseContent });
71242
+ currentMessages.push({
71243
+ role: "user",
71244
+ content: createWrappedToolErrorMessage(wrappedToolName)
71245
+ });
71246
+ completionAttempted = false;
71247
+ continue;
71248
+ }
71249
+ finalResult = prevContent;
70877
71250
  if (this.debug) console.log(`[DEBUG] Using previous response as completion: ${finalResult.substring(0, 100)}...`);
70878
71251
  } else {
70879
71252
  finalResult = "Error: No previous response found to use as completion.";
@@ -71144,7 +71517,33 @@ ${errorXml}
71144
71517
  currentMessages.push({ role: "assistant", content: assistantResponseContent });
71145
71518
  const unrecognizedTool = detectUnrecognizedToolCall(assistantResponseContent, validTools);
71146
71519
  let reminderContent;
71147
- if (unrecognizedTool) {
71520
+ if (isWrappedToolError(unrecognizedTool)) {
71521
+ const wrappedToolName = extractWrappedToolName(unrecognizedTool);
71522
+ if (this.debug) {
71523
+ console.log(`[DEBUG] Detected wrapped tool '${wrappedToolName}' in assistant response - wrong XML format.`);
71524
+ }
71525
+ const toolError = new ParameterError(
71526
+ `Tool '${wrappedToolName}' found but in WRONG FORMAT - do not wrap tools in other XML tags.`,
71527
+ {
71528
+ suggestion: `Use the tool tag DIRECTLY without any wrapper:
71529
+
71530
+ CORRECT FORMAT:
71531
+ <${wrappedToolName}>
71532
+ <param>value</param>
71533
+ </${wrappedToolName}>
71534
+
71535
+ WRONG (what you did - do not wrap in other tags):
71536
+ <api_call><tool_name>${wrappedToolName}</tool_name>...</api_call>
71537
+ <function>${wrappedToolName}</function>
71538
+ <call name="${wrappedToolName}">...</call>
71539
+
71540
+ Remove ALL wrapper tags and use <${wrappedToolName}> directly as the outermost tag.`
71541
+ }
71542
+ );
71543
+ reminderContent = `<tool_result>
71544
+ ${formatErrorForAI(toolError)}
71545
+ </tool_result>`;
71546
+ } else if (unrecognizedTool) {
71148
71547
  if (this.debug) {
71149
71548
  console.log(`[DEBUG] Detected unrecognized tool '${unrecognizedTool}' in assistant response.`);
71150
71549
  }
@@ -71155,6 +71554,20 @@ ${errorXml}
71155
71554
  ${formatErrorForAI(toolError)}
71156
71555
  </tool_result>`;
71157
71556
  } else {
71557
+ if (currentIteration >= maxIterations) {
71558
+ let cleanedResponse = assistantResponseContent;
71559
+ cleanedResponse = cleanedResponse.replace(/<thinking>[\s\S]*?<\/thinking>/gi, "").trim();
71560
+ cleanedResponse = cleanedResponse.replace(/<thinking>[\s\S]*$/gi, "").trim();
71561
+ const hasSubstantialContent = cleanedResponse.length > 50 && !cleanedResponse.includes("<api_call>") && !cleanedResponse.includes("<tool_name>") && !cleanedResponse.includes("<function>");
71562
+ if (hasSubstantialContent) {
71563
+ if (this.debug) {
71564
+ console.log(`[DEBUG] Max iterations reached - accepting AI response as final answer (${cleanedResponse.length} chars)`);
71565
+ }
71566
+ finalResult = cleanedResponse;
71567
+ completionAttempted = true;
71568
+ break;
71569
+ }
71570
+ }
71158
71571
  reminderContent = `Please use one of the available tools to help answer the question, or use attempt_completion if you have enough information to provide a final answer.
71159
71572
 
71160
71573
  Remember: Use proper XML format with BOTH opening and closing tags:
@@ -71184,6 +71597,25 @@ Note: <attempt_complete></attempt_complete> reuses your PREVIOUS assistant messa
71184
71597
  console.log(`[DEBUG] No tool call detected in assistant response. Prompting for tool use.`);
71185
71598
  }
71186
71599
  }
71600
+ if (unrecognizedTool) {
71601
+ const isWrapped = isWrappedToolError(unrecognizedTool);
71602
+ const errorCategory = isWrapped ? "wrapped_tool" : unrecognizedTool;
71603
+ if (errorCategory === lastFormatErrorType) {
71604
+ sameFormatErrorCount++;
71605
+ if (sameFormatErrorCount >= MAX_REPEATED_FORMAT_ERRORS) {
71606
+ const errorDesc = isWrapped ? "wrapped tool format" : unrecognizedTool;
71607
+ console.error(`[ERROR] Format error category '${errorCategory}' repeated ${sameFormatErrorCount} times. Breaking loop early to prevent infinite iteration.`);
71608
+ finalResult = `Error: Unable to complete request. The AI model repeatedly used incorrect tool call format (${errorDesc}). Please try rephrasing your question or using a different model.`;
71609
+ break;
71610
+ }
71611
+ } else {
71612
+ lastFormatErrorType = errorCategory;
71613
+ sameFormatErrorCount = 1;
71614
+ }
71615
+ } else {
71616
+ lastFormatErrorType = null;
71617
+ sameFormatErrorCount = 0;
71618
+ }
71187
71619
  }
71188
71620
  if (currentMessages.length > MAX_HISTORY_MESSAGES) {
71189
71621
  const messagesBefore = currentMessages.length;