@librechat/agents 3.0.71 → 3.0.72

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.
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var zod = require('zod');
4
+ var BM25 = require('okapibm25');
4
5
  var dotenv = require('dotenv');
5
6
  var fetch = require('node-fetch');
6
7
  var httpsProxyAgent = require('https-proxy-agent');
@@ -235,54 +236,116 @@ function simplifyParametersForSearch(parameters) {
235
236
  return { type: parameters.type };
236
237
  }
237
238
  /**
238
- * Performs safe local substring search without regex.
239
- * Uses case-insensitive String.includes() for complete safety against ReDoS.
240
- * @param tools - Array of tool metadata to search
241
- * @param query - The search term (treated as literal substring)
242
- * @param fields - Which fields to search
243
- * @param maxResults - Maximum results to return
244
- * @returns Search response with matching tools
239
+ * Tokenizes a string into lowercase words for BM25.
240
+ * @param text - The text to tokenize
241
+ * @returns Array of lowercase tokens
245
242
  */
246
- function performLocalSearch(tools, query, fields, maxResults) {
247
- const lowerQuery = query.toLowerCase();
248
- const results = [];
249
- for (const tool of tools) {
250
- let bestScore = 0;
251
- let matchedField = '';
252
- let snippet = '';
253
- if (fields.includes('name')) {
254
- const lowerName = tool.name.toLowerCase();
255
- if (lowerName.includes(lowerQuery)) {
256
- const isExactMatch = lowerName === lowerQuery;
257
- const startsWithQuery = lowerName.startsWith(lowerQuery);
258
- bestScore = isExactMatch ? 1.0 : startsWithQuery ? 0.95 : 0.85;
259
- matchedField = 'name';
260
- snippet = tool.name;
243
+ function tokenize(text) {
244
+ return text
245
+ .toLowerCase()
246
+ .replace(/[^a-z0-9_]/g, ' ')
247
+ .split(/\s+/)
248
+ .filter((token) => token.length > 0);
249
+ }
250
+ /**
251
+ * Creates a searchable document string from tool metadata.
252
+ * @param tool - The tool metadata
253
+ * @param fields - Which fields to include
254
+ * @returns Combined document string for BM25
255
+ */
256
+ function createToolDocument(tool, fields) {
257
+ const parts = [];
258
+ if (fields.includes('name')) {
259
+ const baseName = tool.name.replace(/_/g, ' ');
260
+ parts.push(baseName, baseName);
261
+ }
262
+ if (fields.includes('description') && tool.description) {
263
+ parts.push(tool.description);
264
+ }
265
+ if (fields.includes('parameters') && tool.parameters?.properties) {
266
+ const paramNames = Object.keys(tool.parameters.properties).join(' ');
267
+ parts.push(paramNames);
268
+ }
269
+ return parts.join(' ');
270
+ }
271
+ /**
272
+ * Determines which field had the best match for a query.
273
+ * @param tool - The tool to check
274
+ * @param queryTokens - Tokenized query
275
+ * @param fields - Fields to check
276
+ * @returns The matched field and a snippet
277
+ */
278
+ function findMatchedField(tool, queryTokens, fields) {
279
+ if (fields.includes('name')) {
280
+ const nameLower = tool.name.toLowerCase();
281
+ for (const token of queryTokens) {
282
+ if (nameLower.includes(token)) {
283
+ return { field: 'name', snippet: tool.name };
261
284
  }
262
285
  }
263
- if (fields.includes('description') && tool.description) {
264
- const lowerDesc = tool.description.toLowerCase();
265
- if (lowerDesc.includes(lowerQuery) && bestScore === 0) {
266
- bestScore = 0.7;
267
- matchedField = 'description';
268
- snippet = tool.description.substring(0, 100);
286
+ }
287
+ if (fields.includes('description') && tool.description) {
288
+ const descLower = tool.description.toLowerCase();
289
+ for (const token of queryTokens) {
290
+ if (descLower.includes(token)) {
291
+ return {
292
+ field: 'description',
293
+ snippet: tool.description.substring(0, 100),
294
+ };
269
295
  }
270
296
  }
271
- if (fields.includes('parameters') && tool.parameters?.properties) {
272
- const paramNames = Object.keys(tool.parameters.properties)
273
- .join(' ')
274
- .toLowerCase();
275
- if (paramNames.includes(lowerQuery) && bestScore === 0) {
276
- bestScore = 0.55;
277
- matchedField = 'parameters';
278
- snippet = Object.keys(tool.parameters.properties).join(' ');
297
+ }
298
+ if (fields.includes('parameters') && tool.parameters?.properties) {
299
+ const paramNames = Object.keys(tool.parameters.properties);
300
+ const paramLower = paramNames.join(' ').toLowerCase();
301
+ for (const token of queryTokens) {
302
+ if (paramLower.includes(token)) {
303
+ return { field: 'parameters', snippet: paramNames.join(', ') };
279
304
  }
280
305
  }
281
- if (bestScore > 0) {
306
+ }
307
+ const fallbackSnippet = tool.description
308
+ ? tool.description.substring(0, 100)
309
+ : tool.name;
310
+ return { field: 'unknown', snippet: fallbackSnippet };
311
+ }
312
+ /**
313
+ * Performs BM25-based search for better relevance ranking.
314
+ * Uses Okapi BM25 algorithm for term frequency and document length normalization.
315
+ * @param tools - Array of tool metadata to search
316
+ * @param query - The search query
317
+ * @param fields - Which fields to search
318
+ * @param maxResults - Maximum results to return
319
+ * @returns Search response with matching tools ranked by BM25 score
320
+ */
321
+ function performLocalSearch(tools, query, fields, maxResults) {
322
+ if (tools.length === 0 || !query.trim()) {
323
+ return {
324
+ tool_references: [],
325
+ total_tools_searched: tools.length,
326
+ pattern_used: query,
327
+ };
328
+ }
329
+ const documents = tools.map((tool) => createToolDocument(tool, fields));
330
+ const queryTokens = tokenize(query);
331
+ if (queryTokens.length === 0) {
332
+ return {
333
+ tool_references: [],
334
+ total_tools_searched: tools.length,
335
+ pattern_used: query,
336
+ };
337
+ }
338
+ const scores = BM25(documents, queryTokens, { k1: 1.5, b: 0.75 });
339
+ const maxScore = Math.max(...scores.filter((s) => s > 0), 1);
340
+ const results = [];
341
+ for (let i = 0; i < tools.length; i++) {
342
+ if (scores[i] > 0) {
343
+ const { field, snippet } = findMatchedField(tools[i], queryTokens, fields);
344
+ const normalizedScore = Math.min(scores[i] / maxScore, 1.0);
282
345
  results.push({
283
- tool_name: tool.name,
284
- match_score: bestScore,
285
- matched_field: matchedField,
346
+ tool_name: tools[i].name,
347
+ match_score: normalizedScore,
348
+ matched_field: field,
286
349
  snippet,
287
350
  });
288
351
  }
@@ -565,7 +628,7 @@ ${deferredToolsListing}`
565
628
  : '';
566
629
  const description = mode === 'local'
567
630
  ? `
568
- Searches deferred tools by name/description. Case-insensitive substring matching.
631
+ Searches deferred tools using BM25 ranking. Multi-word queries supported.
569
632
  ${mcpNote}${toolsListSection}
570
633
  `.trim()
571
634
  : `
@@ -1 +1 @@
1
- {"version":3,"file":"ToolSearch.cjs","sources":["../../../src/tools/ToolSearch.ts"],"sourcesContent":["// src/tools/ToolSearch.ts\nimport { z } from 'zod';\nimport { config } from 'dotenv';\nimport fetch, { RequestInit } from 'node-fetch';\nimport { HttpsProxyAgent } from 'https-proxy-agent';\nimport { getEnvironmentVariable } from '@langchain/core/utils/env';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport type * as t from '@/types';\nimport { getCodeBaseURL } from './CodeExecutor';\nimport { EnvVar, Constants } from '@/common';\n\nconfig();\n\n/** Maximum allowed regex pattern length */\nconst MAX_PATTERN_LENGTH = 200;\n\n/** Maximum allowed regex nesting depth */\nconst MAX_REGEX_COMPLEXITY = 5;\n\n/** Default search timeout in milliseconds */\nconst SEARCH_TIMEOUT = 5000;\n\n/** Zod schema type for tool search parameters */\ntype ToolSearchSchema = z.ZodObject<{\n query: z.ZodDefault<z.ZodOptional<z.ZodString>>;\n fields: z.ZodDefault<\n z.ZodOptional<z.ZodArray<z.ZodEnum<['name', 'description', 'parameters']>>>\n >;\n max_results: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;\n mcp_server: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString>]>>;\n}>;\n\n/**\n * Creates the Zod schema with dynamic query description based on mode.\n * @param mode - The search mode determining query interpretation\n * @returns Zod schema for tool search parameters\n */\nfunction createToolSearchSchema(mode: t.ToolSearchMode): ToolSearchSchema {\n const queryDescription =\n mode === 'local'\n ? 'Search term to find in tool names and descriptions. Case-insensitive substring matching. Optional if mcp_server is provided.'\n : 'Regex pattern to search tool names and descriptions. Optional if mcp_server is provided.';\n\n return z.object({\n query: z\n .string()\n .max(MAX_PATTERN_LENGTH)\n .optional()\n .default('')\n .describe(queryDescription),\n fields: z\n .array(z.enum(['name', 'description', 'parameters']))\n .optional()\n .default(['name', 'description'])\n .describe('Which fields to search. Default: name and description'),\n max_results: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .default(10)\n .describe('Maximum number of matching tools to return'),\n mcp_server: z\n .union([z.string(), z.array(z.string())])\n .optional()\n .describe(\n 'Filter to tools from specific MCP server(s). Can be a single server name or array of names. If provided without a query, lists all tools from those servers.'\n ),\n });\n}\n\n/**\n * Extracts the MCP server name from a tool name.\n * MCP tools follow the pattern: toolName_mcp_serverName\n * @param toolName - The full tool name\n * @returns The server name if it's an MCP tool, undefined otherwise\n */\nfunction extractMcpServerName(toolName: string): string | undefined {\n const delimiterIndex = toolName.indexOf(Constants.MCP_DELIMITER);\n if (delimiterIndex === -1) {\n return undefined;\n }\n return toolName.substring(delimiterIndex + Constants.MCP_DELIMITER.length);\n}\n\n/**\n * Checks if a tool belongs to a specific MCP server.\n * @param toolName - The full tool name\n * @param serverName - The server name to match\n * @returns True if the tool belongs to the specified server\n */\nfunction isFromMcpServer(toolName: string, serverName: string): boolean {\n const toolServer = extractMcpServerName(toolName);\n return toolServer === serverName;\n}\n\n/**\n * Checks if a tool belongs to any of the specified MCP servers.\n * @param toolName - The full tool name\n * @param serverNames - Array of server names to match\n * @returns True if the tool belongs to any of the specified servers\n */\nfunction isFromAnyMcpServer(toolName: string, serverNames: string[]): boolean {\n const toolServer = extractMcpServerName(toolName);\n if (toolServer === undefined) {\n return false;\n }\n return serverNames.includes(toolServer);\n}\n\n/**\n * Normalizes server filter input to always be an array.\n * @param serverFilter - String, array of strings, or undefined\n * @returns Array of server names (empty if none specified)\n */\nfunction normalizeServerFilter(\n serverFilter: string | string[] | undefined\n): string[] {\n if (serverFilter === undefined) {\n return [];\n }\n if (typeof serverFilter === 'string') {\n return serverFilter === '' ? [] : [serverFilter];\n }\n return serverFilter.filter((s) => s !== '');\n}\n\n/**\n * Extracts all unique MCP server names from a tool registry.\n * @param toolRegistry - The tool registry to scan\n * @param onlyDeferred - If true, only considers deferred tools\n * @returns Array of unique server names, sorted alphabetically\n */\nfunction getAvailableMcpServers(\n toolRegistry: t.LCToolRegistry | undefined,\n onlyDeferred: boolean = true\n): string[] {\n if (!toolRegistry) {\n return [];\n }\n\n const servers = new Set<string>();\n for (const [, toolDef] of toolRegistry) {\n if (onlyDeferred && toolDef.defer_loading !== true) {\n continue;\n }\n const server = extractMcpServerName(toolDef.name);\n if (server !== undefined && server !== '') {\n servers.add(server);\n }\n }\n\n return Array.from(servers).sort();\n}\n\n/**\n * Escapes special regex characters in a string to use as a literal pattern.\n * @param pattern - The string to escape\n * @returns The escaped string safe for use in a RegExp\n */\nfunction escapeRegexSpecialChars(pattern: string): string {\n return pattern.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Counts the maximum nesting depth of groups in a regex pattern.\n * @param pattern - The regex pattern to analyze\n * @returns The maximum nesting depth\n */\nfunction countNestedGroups(pattern: string): number {\n let maxDepth = 0;\n let currentDepth = 0;\n\n for (let i = 0; i < pattern.length; i++) {\n if (pattern[i] === '(' && (i === 0 || pattern[i - 1] !== '\\\\')) {\n currentDepth++;\n maxDepth = Math.max(maxDepth, currentDepth);\n } else if (pattern[i] === ')' && (i === 0 || pattern[i - 1] !== '\\\\')) {\n currentDepth = Math.max(0, currentDepth - 1);\n }\n }\n\n return maxDepth;\n}\n\n/**\n * Detects nested quantifiers that can cause catastrophic backtracking.\n * Patterns like (a+)+, (a*)*, (a+)*, etc.\n * @param pattern - The regex pattern to check\n * @returns True if nested quantifiers are detected\n */\nfunction hasNestedQuantifiers(pattern: string): boolean {\n const nestedQuantifierPattern = /\\([^)]*[+*][^)]*\\)[+*?]/;\n return nestedQuantifierPattern.test(pattern);\n}\n\n/**\n * Checks if a regex pattern contains potentially dangerous constructs.\n * @param pattern - The regex pattern to validate\n * @returns True if the pattern is dangerous\n */\nfunction isDangerousPattern(pattern: string): boolean {\n if (hasNestedQuantifiers(pattern)) {\n return true;\n }\n\n if (countNestedGroups(pattern) > MAX_REGEX_COMPLEXITY) {\n return true;\n }\n\n const dangerousPatterns = [\n /\\.\\{1000,\\}/, // Excessive wildcards\n /\\(\\?=\\.\\{100,\\}\\)/, // Runaway lookaheads\n /\\([^)]*\\|\\s*\\){20,}/, // Excessive alternation (rough check)\n /\\(\\.\\*\\)\\+/, // (.*)+\n /\\(\\.\\+\\)\\+/, // (.+)+\n /\\(\\.\\*\\)\\*/, // (.*)*\n /\\(\\.\\+\\)\\*/, // (.+)*\n ];\n\n for (const dangerous of dangerousPatterns) {\n if (dangerous.test(pattern)) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Sanitizes a regex pattern for safe execution.\n * If the pattern is dangerous, it will be escaped to a literal string search.\n * @param pattern - The regex pattern to sanitize\n * @returns Object containing the safe pattern and whether it was escaped\n */\nfunction sanitizeRegex(pattern: string): { safe: string; wasEscaped: boolean } {\n if (isDangerousPattern(pattern)) {\n return {\n safe: escapeRegexSpecialChars(pattern),\n wasEscaped: true,\n };\n }\n\n try {\n new RegExp(pattern);\n return { safe: pattern, wasEscaped: false };\n } catch {\n return {\n safe: escapeRegexSpecialChars(pattern),\n wasEscaped: true,\n };\n }\n}\n\n/**\n * Simplifies tool parameters for search purposes.\n * Extracts only the essential structure needed for parameter name searching.\n * @param parameters - The tool's JSON schema parameters\n * @returns Simplified parameters object\n */\nfunction simplifyParametersForSearch(\n parameters?: t.JsonSchemaType\n): t.JsonSchemaType | undefined {\n if (!parameters) {\n return undefined;\n }\n\n if (parameters.properties) {\n return {\n type: parameters.type,\n properties: Object.fromEntries(\n Object.entries(parameters.properties).map(([key, value]) => [\n key,\n { type: (value as t.JsonSchemaType).type },\n ])\n ),\n } as t.JsonSchemaType;\n }\n\n return { type: parameters.type };\n}\n\n/**\n * Performs safe local substring search without regex.\n * Uses case-insensitive String.includes() for complete safety against ReDoS.\n * @param tools - Array of tool metadata to search\n * @param query - The search term (treated as literal substring)\n * @param fields - Which fields to search\n * @param maxResults - Maximum results to return\n * @returns Search response with matching tools\n */\nfunction performLocalSearch(\n tools: t.ToolMetadata[],\n query: string,\n fields: string[],\n maxResults: number\n): t.ToolSearchResponse {\n const lowerQuery = query.toLowerCase();\n const results: t.ToolSearchResult[] = [];\n\n for (const tool of tools) {\n let bestScore = 0;\n let matchedField = '';\n let snippet = '';\n\n if (fields.includes('name')) {\n const lowerName = tool.name.toLowerCase();\n if (lowerName.includes(lowerQuery)) {\n const isExactMatch = lowerName === lowerQuery;\n const startsWithQuery = lowerName.startsWith(lowerQuery);\n bestScore = isExactMatch ? 1.0 : startsWithQuery ? 0.95 : 0.85;\n matchedField = 'name';\n snippet = tool.name;\n }\n }\n\n if (fields.includes('description') && tool.description) {\n const lowerDesc = tool.description.toLowerCase();\n if (lowerDesc.includes(lowerQuery) && bestScore === 0) {\n bestScore = 0.7;\n matchedField = 'description';\n snippet = tool.description.substring(0, 100);\n }\n }\n\n if (fields.includes('parameters') && tool.parameters?.properties) {\n const paramNames = Object.keys(tool.parameters.properties)\n .join(' ')\n .toLowerCase();\n if (paramNames.includes(lowerQuery) && bestScore === 0) {\n bestScore = 0.55;\n matchedField = 'parameters';\n snippet = Object.keys(tool.parameters.properties).join(' ');\n }\n }\n\n if (bestScore > 0) {\n results.push({\n tool_name: tool.name,\n match_score: bestScore,\n matched_field: matchedField,\n snippet,\n });\n }\n }\n\n results.sort((a, b) => b.match_score - a.match_score);\n const topResults = results.slice(0, maxResults);\n\n return {\n tool_references: topResults,\n total_tools_searched: tools.length,\n pattern_used: query,\n };\n}\n\n/**\n * Generates the JavaScript search script to be executed in the sandbox.\n * Uses plain JavaScript for maximum compatibility with the Code API.\n * @param deferredTools - Array of tool metadata to search through\n * @param fields - Which fields to search\n * @param maxResults - Maximum number of results to return\n * @param sanitizedPattern - The sanitized regex pattern\n * @returns The JavaScript code string\n */\nfunction generateSearchScript(\n deferredTools: t.ToolMetadata[],\n fields: string[],\n maxResults: number,\n sanitizedPattern: string\n): string {\n const lines = [\n '// Tool definitions (injected)',\n 'var tools = ' + JSON.stringify(deferredTools) + ';',\n 'var searchFields = ' + JSON.stringify(fields) + ';',\n 'var maxResults = ' + maxResults + ';',\n 'var pattern = ' + JSON.stringify(sanitizedPattern) + ';',\n '',\n '// Compile regex (pattern is sanitized client-side)',\n 'var regex;',\n 'try {',\n ' regex = new RegExp(pattern, \\'i\\');',\n '} catch (e) {',\n ' regex = new RegExp(pattern.replace(/[.*+?^${}()[\\\\]\\\\\\\\|]/g, \"\\\\\\\\$&\"), \"i\");',\n '}',\n '',\n '// Search logic',\n 'var results = [];',\n '',\n 'for (var j = 0; j < tools.length; j++) {',\n ' var tool = tools[j];',\n ' var bestScore = 0;',\n ' var matchedField = \\'\\';',\n ' var snippet = \\'\\';',\n '',\n ' // Search name (highest priority)',\n ' if (searchFields.indexOf(\\'name\\') >= 0 && regex.test(tool.name)) {',\n ' bestScore = 0.95;',\n ' matchedField = \\'name\\';',\n ' snippet = tool.name;',\n ' }',\n '',\n ' // Search description (medium priority)',\n ' if (searchFields.indexOf(\\'description\\') >= 0 && tool.description && regex.test(tool.description)) {',\n ' if (bestScore === 0) {',\n ' bestScore = 0.75;',\n ' matchedField = \\'description\\';',\n ' snippet = tool.description.substring(0, 100);',\n ' }',\n ' }',\n '',\n ' // Search parameter names (lower priority)',\n ' if (searchFields.indexOf(\\'parameters\\') >= 0 && tool.parameters && tool.parameters.properties) {',\n ' var paramNames = Object.keys(tool.parameters.properties).join(\\' \\');',\n ' if (regex.test(paramNames)) {',\n ' if (bestScore === 0) {',\n ' bestScore = 0.60;',\n ' matchedField = \\'parameters\\';',\n ' snippet = paramNames;',\n ' }',\n ' }',\n ' }',\n '',\n ' if (bestScore > 0) {',\n ' results.push({',\n ' tool_name: tool.name,',\n ' match_score: bestScore,',\n ' matched_field: matchedField,',\n ' snippet: snippet',\n ' });',\n ' }',\n '}',\n '',\n '// Sort by score (descending) and limit results',\n 'results.sort(function(a, b) { return b.match_score - a.match_score; });',\n 'var topResults = results.slice(0, maxResults);',\n '',\n '// Output as JSON',\n 'console.log(JSON.stringify({',\n ' tool_references: topResults.map(function(r) {',\n ' return {',\n ' tool_name: r.tool_name,',\n ' match_score: r.match_score,',\n ' matched_field: r.matched_field,',\n ' snippet: r.snippet',\n ' };',\n ' }),',\n ' total_tools_searched: tools.length,',\n ' pattern_used: pattern',\n '}));',\n ];\n return lines.join('\\n');\n}\n\n/**\n * Parses the search results from stdout JSON.\n * @param stdout - The stdout string containing JSON results\n * @returns Parsed search response\n */\nfunction parseSearchResults(stdout: string): t.ToolSearchResponse {\n const jsonMatch = stdout.trim();\n const parsed = JSON.parse(jsonMatch) as t.ToolSearchResponse;\n return parsed;\n}\n\n/**\n * Formats search results as structured JSON for efficient parsing.\n * @param searchResponse - The parsed search response\n * @returns JSON string with search results\n */\nfunction formatSearchResults(searchResponse: t.ToolSearchResponse): string {\n const { tool_references, total_tools_searched, pattern_used } =\n searchResponse;\n\n const output = {\n found: tool_references.length,\n tools: tool_references.map((ref) => ({\n name: ref.tool_name,\n score: Number(ref.match_score.toFixed(2)),\n matched_in: ref.matched_field,\n snippet: ref.snippet,\n })),\n total_searched: total_tools_searched,\n query: pattern_used,\n };\n\n return JSON.stringify(output, null, 2);\n}\n\n/**\n * Extracts the base tool name (without MCP server suffix) from a full tool name.\n * @param toolName - The full tool name\n * @returns The base tool name without server suffix\n */\nfunction getBaseToolName(toolName: string): string {\n const delimiterIndex = toolName.indexOf(Constants.MCP_DELIMITER);\n if (delimiterIndex === -1) {\n return toolName;\n }\n return toolName.substring(0, delimiterIndex);\n}\n\n/**\n * Generates a compact listing of deferred tools grouped by server.\n * Format: \"server: tool1, tool2, tool3\"\n * Non-MCP tools are grouped under \"other\".\n * @param toolRegistry - The tool registry\n * @param onlyDeferred - Whether to only include deferred tools\n * @returns Formatted string with tools grouped by server\n */\nfunction getDeferredToolsListing(\n toolRegistry: t.LCToolRegistry | undefined,\n onlyDeferred: boolean\n): string {\n if (!toolRegistry) {\n return '';\n }\n\n const toolsByServer: Record<string, string[]> = {};\n\n for (const lcTool of toolRegistry.values()) {\n if (onlyDeferred && lcTool.defer_loading !== true) {\n continue;\n }\n\n const toolName = lcTool.name;\n const serverName = extractMcpServerName(toolName) ?? 'other';\n const baseName = getBaseToolName(toolName);\n\n if (!(serverName in toolsByServer)) {\n toolsByServer[serverName] = [];\n }\n toolsByServer[serverName].push(baseName);\n }\n\n const serverNames = Object.keys(toolsByServer).sort((a, b) => {\n if (a === 'other') return 1;\n if (b === 'other') return -1;\n return a.localeCompare(b);\n });\n\n if (serverNames.length === 0) {\n return '';\n }\n\n const lines = serverNames.map(\n (server) => `${server}: ${toolsByServer[server].join(', ')}`\n );\n\n return lines.join('\\n');\n}\n\n/**\n * Formats a server listing response as structured JSON.\n * NOTE: This is a PREVIEW only - tools are NOT discovered/loaded.\n * @param tools - Array of tool metadata from the server(s)\n * @param serverNames - The MCP server name(s)\n * @returns JSON string showing all tools grouped by server\n */\nfunction formatServerListing(\n tools: t.ToolMetadata[],\n serverNames: string | string[]\n): string {\n const servers = Array.isArray(serverNames) ? serverNames : [serverNames];\n\n if (tools.length === 0) {\n return JSON.stringify(\n {\n listing_mode: true,\n servers,\n total_tools: 0,\n tools_by_server: {},\n hint: 'No tools found from the specified MCP server(s).',\n },\n null,\n 2\n );\n }\n\n const toolsByServer: Record<\n string,\n Array<{ name: string; description: string }>\n > = {};\n for (const tool of tools) {\n const server = extractMcpServerName(tool.name) ?? 'unknown';\n if (!(server in toolsByServer)) {\n toolsByServer[server] = [];\n }\n toolsByServer[server].push({\n name: getBaseToolName(tool.name),\n description:\n tool.description.length > 100\n ? tool.description.substring(0, 97) + '...'\n : tool.description,\n });\n }\n\n const output = {\n listing_mode: true,\n servers,\n total_tools: tools.length,\n tools_by_server: toolsByServer,\n hint: `To use a tool, search for it by name (e.g., query: \"${getBaseToolName(tools[0]?.name ?? 'tool_name')}\") to load it.`,\n };\n\n return JSON.stringify(output, null, 2);\n}\n\n/**\n * Creates a Tool Search tool for discovering tools from a large registry.\n *\n * This tool enables AI agents to dynamically discover tools from a large library\n * without loading all tool definitions into the LLM context window. The agent\n * can search for relevant tools on-demand.\n *\n * **Modes:**\n * - `code_interpreter` (default): Uses external sandbox for regex search. Safer for complex patterns.\n * - `local`: Uses safe substring matching locally. No network call, faster, completely safe from ReDoS.\n *\n * The tool registry can be provided either:\n * 1. At initialization time via params.toolRegistry\n * 2. At runtime via config.configurable.toolRegistry when invoking\n *\n * @param params - Configuration parameters for the tool (toolRegistry is optional)\n * @returns A LangChain DynamicStructuredTool for tool searching\n *\n * @example\n * // Option 1: Code interpreter mode (regex via sandbox)\n * const tool = createToolSearch({ apiKey, toolRegistry });\n * await tool.invoke({ query: 'expense.*report' });\n *\n * @example\n * // Option 2: Local mode (safe substring search, no API key needed)\n * const tool = createToolSearch({ mode: 'local', toolRegistry });\n * await tool.invoke({ query: 'expense' });\n */\nfunction createToolSearch(\n initParams: t.ToolSearchParams = {}\n): DynamicStructuredTool<ReturnType<typeof createToolSearchSchema>> {\n const mode: t.ToolSearchMode = initParams.mode ?? 'code_interpreter';\n const defaultOnlyDeferred = initParams.onlyDeferred ?? true;\n const schema = createToolSearchSchema(mode);\n\n const apiKey: string =\n mode === 'code_interpreter'\n ? ((initParams[EnvVar.CODE_API_KEY] as string | undefined) ??\n initParams.apiKey ??\n getEnvironmentVariable(EnvVar.CODE_API_KEY) ??\n '')\n : '';\n\n if (mode === 'code_interpreter' && !apiKey) {\n throw new Error(\n 'No API key provided for tool search in code_interpreter mode. Use mode: \"local\" to search without an API key.'\n );\n }\n\n const baseEndpoint = initParams.baseUrl ?? getCodeBaseURL();\n const EXEC_ENDPOINT = `${baseEndpoint}/exec`;\n\n const deferredToolsListing = getDeferredToolsListing(\n initParams.toolRegistry,\n defaultOnlyDeferred\n );\n\n const toolsListSection =\n deferredToolsListing.length > 0\n ? `\n\nDeferred tools (search to load):\n${deferredToolsListing}`\n : '';\n\n const mcpNote =\n deferredToolsListing.includes(Constants.MCP_DELIMITER) ||\n deferredToolsListing.split('\\n').some((line) => !line.startsWith('other:'))\n ? `\n- MCP tools use format: toolName${Constants.MCP_DELIMITER}serverName\n- Use mcp_server param to filter by server`\n : '';\n\n const description =\n mode === 'local'\n ? `\nSearches deferred tools by name/description. Case-insensitive substring matching.\n${mcpNote}${toolsListSection}\n`.trim()\n : `\nSearches deferred tools by regex pattern.\n${mcpNote}${toolsListSection}\n`.trim();\n\n return tool<typeof schema>(\n async (params, config) => {\n const {\n query,\n fields = ['name', 'description'],\n max_results = 10,\n mcp_server,\n } = params;\n\n const {\n toolRegistry: paramToolRegistry,\n onlyDeferred: paramOnlyDeferred,\n mcpServer: paramMcpServer,\n } = config.toolCall ?? {};\n\n const toolRegistry = paramToolRegistry ?? initParams.toolRegistry;\n const onlyDeferred =\n paramOnlyDeferred !== undefined\n ? paramOnlyDeferred\n : defaultOnlyDeferred;\n const rawServerFilter =\n mcp_server ?? paramMcpServer ?? initParams.mcpServer;\n const serverFilters = normalizeServerFilter(rawServerFilter);\n const hasServerFilter = serverFilters.length > 0;\n\n if (toolRegistry == null) {\n return [\n 'Error: No tool registry provided. Configure toolRegistry at agent level or initialization.',\n {\n tool_references: [],\n metadata: {\n total_searched: 0,\n pattern: query,\n error: 'No tool registry provided',\n },\n },\n ];\n }\n\n const toolsArray: t.LCTool[] = Array.from(toolRegistry.values());\n const deferredTools: t.ToolMetadata[] = toolsArray\n .filter((lcTool) => {\n if (onlyDeferred === true && lcTool.defer_loading !== true) {\n return false;\n }\n if (\n hasServerFilter &&\n !isFromAnyMcpServer(lcTool.name, serverFilters)\n ) {\n return false;\n }\n return true;\n })\n .map((lcTool) => ({\n name: lcTool.name,\n description: lcTool.description ?? '',\n parameters: simplifyParametersForSearch(lcTool.parameters),\n }));\n\n if (deferredTools.length === 0) {\n const serverMsg = hasServerFilter\n ? ` from MCP server(s): ${serverFilters.join(', ')}`\n : '';\n return [\n `No tools available to search${serverMsg}. The tool registry is empty or no matching deferred tools are registered.`,\n {\n tool_references: [],\n metadata: {\n total_searched: 0,\n pattern: query,\n mcp_server: serverFilters,\n },\n },\n ];\n }\n\n const isServerListing = hasServerFilter && query === '';\n\n if (isServerListing) {\n const formattedOutput = formatServerListing(\n deferredTools,\n serverFilters\n );\n\n return [\n formattedOutput,\n {\n tool_references: [],\n metadata: {\n total_available: deferredTools.length,\n mcp_server: serverFilters,\n listing_mode: true,\n },\n },\n ];\n }\n\n if (mode === 'local') {\n const searchResponse = performLocalSearch(\n deferredTools,\n query,\n fields,\n max_results\n );\n const formattedOutput = formatSearchResults(searchResponse);\n\n return [\n formattedOutput,\n {\n tool_references: searchResponse.tool_references,\n metadata: {\n total_searched: searchResponse.total_tools_searched,\n pattern: searchResponse.pattern_used,\n mcp_server: serverFilters.length > 0 ? serverFilters : undefined,\n },\n },\n ];\n }\n\n const { safe: sanitizedPattern, wasEscaped } = sanitizeRegex(query);\n let warningMessage = '';\n if (wasEscaped) {\n warningMessage =\n 'Note: The provided pattern was converted to a literal search for safety.\\n\\n';\n }\n\n const searchScript = generateSearchScript(\n deferredTools,\n fields,\n max_results,\n sanitizedPattern\n );\n\n const postData = {\n lang: 'js',\n code: searchScript,\n timeout: SEARCH_TIMEOUT,\n };\n\n try {\n const fetchOptions: RequestInit = {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'LibreChat/1.0',\n 'X-API-Key': apiKey,\n },\n body: JSON.stringify(postData),\n };\n\n if (process.env.PROXY != null && process.env.PROXY !== '') {\n fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);\n }\n\n const response = await fetch(EXEC_ENDPOINT, fetchOptions);\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const result: t.ExecuteResult = await response.json();\n\n if (result.stderr && result.stderr.trim()) {\n // eslint-disable-next-line no-console\n console.warn('[ToolSearch] stderr:', result.stderr);\n }\n\n if (!result.stdout || !result.stdout.trim()) {\n return [\n `${warningMessage}No tools matched the pattern \"${sanitizedPattern}\".\\nTotal tools searched: ${deferredTools.length}`,\n {\n tool_references: [],\n metadata: {\n total_searched: deferredTools.length,\n pattern: sanitizedPattern,\n },\n },\n ];\n }\n\n const searchResponse = parseSearchResults(result.stdout);\n const formattedOutput = `${warningMessage}${formatSearchResults(searchResponse)}`;\n\n return [\n formattedOutput,\n {\n tool_references: searchResponse.tool_references,\n metadata: {\n total_searched: searchResponse.total_tools_searched,\n pattern: searchResponse.pattern_used,\n },\n },\n ];\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('[ToolSearch] Error:', error);\n\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n return [\n `Tool search failed: ${errorMessage}\\n\\nSuggestion: Try a simpler search pattern or search for specific tool names.`,\n {\n tool_references: [],\n metadata: {\n total_searched: 0,\n pattern: sanitizedPattern,\n error: errorMessage,\n },\n },\n ];\n }\n },\n {\n name: Constants.TOOL_SEARCH,\n description,\n schema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport {\n createToolSearch,\n performLocalSearch,\n extractMcpServerName,\n isFromMcpServer,\n isFromAnyMcpServer,\n normalizeServerFilter,\n getAvailableMcpServers,\n getDeferredToolsListing,\n getBaseToolName,\n formatServerListing,\n sanitizeRegex,\n escapeRegexSpecialChars,\n isDangerousPattern,\n countNestedGroups,\n hasNestedQuantifiers,\n};\n"],"names":["config","z","Constants","EnvVar","getEnvironmentVariable","getCodeBaseURL","tool","HttpsProxyAgent"],"mappings":"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"}
1
+ {"version":3,"file":"ToolSearch.cjs","sources":["../../../src/tools/ToolSearch.ts"],"sourcesContent":["// src/tools/ToolSearch.ts\nimport { z } from 'zod';\nimport BM25 from 'okapibm25';\nimport { config } from 'dotenv';\nimport fetch, { RequestInit } from 'node-fetch';\nimport { HttpsProxyAgent } from 'https-proxy-agent';\nimport { getEnvironmentVariable } from '@langchain/core/utils/env';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport type * as t from '@/types';\nimport { getCodeBaseURL } from './CodeExecutor';\nimport { EnvVar, Constants } from '@/common';\n\nconfig();\n\n/** Maximum allowed regex pattern length */\nconst MAX_PATTERN_LENGTH = 200;\n\n/** Maximum allowed regex nesting depth */\nconst MAX_REGEX_COMPLEXITY = 5;\n\n/** Default search timeout in milliseconds */\nconst SEARCH_TIMEOUT = 5000;\n\n/** Zod schema type for tool search parameters */\ntype ToolSearchSchema = z.ZodObject<{\n query: z.ZodDefault<z.ZodOptional<z.ZodString>>;\n fields: z.ZodDefault<\n z.ZodOptional<z.ZodArray<z.ZodEnum<['name', 'description', 'parameters']>>>\n >;\n max_results: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;\n mcp_server: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString>]>>;\n}>;\n\n/**\n * Creates the Zod schema with dynamic query description based on mode.\n * @param mode - The search mode determining query interpretation\n * @returns Zod schema for tool search parameters\n */\nfunction createToolSearchSchema(mode: t.ToolSearchMode): ToolSearchSchema {\n const queryDescription =\n mode === 'local'\n ? 'Search term to find in tool names and descriptions. Case-insensitive substring matching. Optional if mcp_server is provided.'\n : 'Regex pattern to search tool names and descriptions. Optional if mcp_server is provided.';\n\n return z.object({\n query: z\n .string()\n .max(MAX_PATTERN_LENGTH)\n .optional()\n .default('')\n .describe(queryDescription),\n fields: z\n .array(z.enum(['name', 'description', 'parameters']))\n .optional()\n .default(['name', 'description'])\n .describe('Which fields to search. Default: name and description'),\n max_results: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .default(10)\n .describe('Maximum number of matching tools to return'),\n mcp_server: z\n .union([z.string(), z.array(z.string())])\n .optional()\n .describe(\n 'Filter to tools from specific MCP server(s). Can be a single server name or array of names. If provided without a query, lists all tools from those servers.'\n ),\n });\n}\n\n/**\n * Extracts the MCP server name from a tool name.\n * MCP tools follow the pattern: toolName_mcp_serverName\n * @param toolName - The full tool name\n * @returns The server name if it's an MCP tool, undefined otherwise\n */\nfunction extractMcpServerName(toolName: string): string | undefined {\n const delimiterIndex = toolName.indexOf(Constants.MCP_DELIMITER);\n if (delimiterIndex === -1) {\n return undefined;\n }\n return toolName.substring(delimiterIndex + Constants.MCP_DELIMITER.length);\n}\n\n/**\n * Checks if a tool belongs to a specific MCP server.\n * @param toolName - The full tool name\n * @param serverName - The server name to match\n * @returns True if the tool belongs to the specified server\n */\nfunction isFromMcpServer(toolName: string, serverName: string): boolean {\n const toolServer = extractMcpServerName(toolName);\n return toolServer === serverName;\n}\n\n/**\n * Checks if a tool belongs to any of the specified MCP servers.\n * @param toolName - The full tool name\n * @param serverNames - Array of server names to match\n * @returns True if the tool belongs to any of the specified servers\n */\nfunction isFromAnyMcpServer(toolName: string, serverNames: string[]): boolean {\n const toolServer = extractMcpServerName(toolName);\n if (toolServer === undefined) {\n return false;\n }\n return serverNames.includes(toolServer);\n}\n\n/**\n * Normalizes server filter input to always be an array.\n * @param serverFilter - String, array of strings, or undefined\n * @returns Array of server names (empty if none specified)\n */\nfunction normalizeServerFilter(\n serverFilter: string | string[] | undefined\n): string[] {\n if (serverFilter === undefined) {\n return [];\n }\n if (typeof serverFilter === 'string') {\n return serverFilter === '' ? [] : [serverFilter];\n }\n return serverFilter.filter((s) => s !== '');\n}\n\n/**\n * Extracts all unique MCP server names from a tool registry.\n * @param toolRegistry - The tool registry to scan\n * @param onlyDeferred - If true, only considers deferred tools\n * @returns Array of unique server names, sorted alphabetically\n */\nfunction getAvailableMcpServers(\n toolRegistry: t.LCToolRegistry | undefined,\n onlyDeferred: boolean = true\n): string[] {\n if (!toolRegistry) {\n return [];\n }\n\n const servers = new Set<string>();\n for (const [, toolDef] of toolRegistry) {\n if (onlyDeferred && toolDef.defer_loading !== true) {\n continue;\n }\n const server = extractMcpServerName(toolDef.name);\n if (server !== undefined && server !== '') {\n servers.add(server);\n }\n }\n\n return Array.from(servers).sort();\n}\n\n/**\n * Escapes special regex characters in a string to use as a literal pattern.\n * @param pattern - The string to escape\n * @returns The escaped string safe for use in a RegExp\n */\nfunction escapeRegexSpecialChars(pattern: string): string {\n return pattern.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Counts the maximum nesting depth of groups in a regex pattern.\n * @param pattern - The regex pattern to analyze\n * @returns The maximum nesting depth\n */\nfunction countNestedGroups(pattern: string): number {\n let maxDepth = 0;\n let currentDepth = 0;\n\n for (let i = 0; i < pattern.length; i++) {\n if (pattern[i] === '(' && (i === 0 || pattern[i - 1] !== '\\\\')) {\n currentDepth++;\n maxDepth = Math.max(maxDepth, currentDepth);\n } else if (pattern[i] === ')' && (i === 0 || pattern[i - 1] !== '\\\\')) {\n currentDepth = Math.max(0, currentDepth - 1);\n }\n }\n\n return maxDepth;\n}\n\n/**\n * Detects nested quantifiers that can cause catastrophic backtracking.\n * Patterns like (a+)+, (a*)*, (a+)*, etc.\n * @param pattern - The regex pattern to check\n * @returns True if nested quantifiers are detected\n */\nfunction hasNestedQuantifiers(pattern: string): boolean {\n const nestedQuantifierPattern = /\\([^)]*[+*][^)]*\\)[+*?]/;\n return nestedQuantifierPattern.test(pattern);\n}\n\n/**\n * Checks if a regex pattern contains potentially dangerous constructs.\n * @param pattern - The regex pattern to validate\n * @returns True if the pattern is dangerous\n */\nfunction isDangerousPattern(pattern: string): boolean {\n if (hasNestedQuantifiers(pattern)) {\n return true;\n }\n\n if (countNestedGroups(pattern) > MAX_REGEX_COMPLEXITY) {\n return true;\n }\n\n const dangerousPatterns = [\n /\\.\\{1000,\\}/, // Excessive wildcards\n /\\(\\?=\\.\\{100,\\}\\)/, // Runaway lookaheads\n /\\([^)]*\\|\\s*\\){20,}/, // Excessive alternation (rough check)\n /\\(\\.\\*\\)\\+/, // (.*)+\n /\\(\\.\\+\\)\\+/, // (.+)+\n /\\(\\.\\*\\)\\*/, // (.*)*\n /\\(\\.\\+\\)\\*/, // (.+)*\n ];\n\n for (const dangerous of dangerousPatterns) {\n if (dangerous.test(pattern)) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Sanitizes a regex pattern for safe execution.\n * If the pattern is dangerous, it will be escaped to a literal string search.\n * @param pattern - The regex pattern to sanitize\n * @returns Object containing the safe pattern and whether it was escaped\n */\nfunction sanitizeRegex(pattern: string): { safe: string; wasEscaped: boolean } {\n if (isDangerousPattern(pattern)) {\n return {\n safe: escapeRegexSpecialChars(pattern),\n wasEscaped: true,\n };\n }\n\n try {\n new RegExp(pattern);\n return { safe: pattern, wasEscaped: false };\n } catch {\n return {\n safe: escapeRegexSpecialChars(pattern),\n wasEscaped: true,\n };\n }\n}\n\n/**\n * Simplifies tool parameters for search purposes.\n * Extracts only the essential structure needed for parameter name searching.\n * @param parameters - The tool's JSON schema parameters\n * @returns Simplified parameters object\n */\nfunction simplifyParametersForSearch(\n parameters?: t.JsonSchemaType\n): t.JsonSchemaType | undefined {\n if (!parameters) {\n return undefined;\n }\n\n if (parameters.properties) {\n return {\n type: parameters.type,\n properties: Object.fromEntries(\n Object.entries(parameters.properties).map(([key, value]) => [\n key,\n { type: (value as t.JsonSchemaType).type },\n ])\n ),\n } as t.JsonSchemaType;\n }\n\n return { type: parameters.type };\n}\n\n/**\n * Tokenizes a string into lowercase words for BM25.\n * @param text - The text to tokenize\n * @returns Array of lowercase tokens\n */\nfunction tokenize(text: string): string[] {\n return text\n .toLowerCase()\n .replace(/[^a-z0-9_]/g, ' ')\n .split(/\\s+/)\n .filter((token) => token.length > 0);\n}\n\n/**\n * Creates a searchable document string from tool metadata.\n * @param tool - The tool metadata\n * @param fields - Which fields to include\n * @returns Combined document string for BM25\n */\nfunction createToolDocument(tool: t.ToolMetadata, fields: string[]): string {\n const parts: string[] = [];\n\n if (fields.includes('name')) {\n const baseName = tool.name.replace(/_/g, ' ');\n parts.push(baseName, baseName);\n }\n\n if (fields.includes('description') && tool.description) {\n parts.push(tool.description);\n }\n\n if (fields.includes('parameters') && tool.parameters?.properties) {\n const paramNames = Object.keys(tool.parameters.properties).join(' ');\n parts.push(paramNames);\n }\n\n return parts.join(' ');\n}\n\n/**\n * Determines which field had the best match for a query.\n * @param tool - The tool to check\n * @param queryTokens - Tokenized query\n * @param fields - Fields to check\n * @returns The matched field and a snippet\n */\nfunction findMatchedField(\n tool: t.ToolMetadata,\n queryTokens: string[],\n fields: string[]\n): { field: string; snippet: string } {\n if (fields.includes('name')) {\n const nameLower = tool.name.toLowerCase();\n for (const token of queryTokens) {\n if (nameLower.includes(token)) {\n return { field: 'name', snippet: tool.name };\n }\n }\n }\n\n if (fields.includes('description') && tool.description) {\n const descLower = tool.description.toLowerCase();\n for (const token of queryTokens) {\n if (descLower.includes(token)) {\n return {\n field: 'description',\n snippet: tool.description.substring(0, 100),\n };\n }\n }\n }\n\n if (fields.includes('parameters') && tool.parameters?.properties) {\n const paramNames = Object.keys(tool.parameters.properties);\n const paramLower = paramNames.join(' ').toLowerCase();\n for (const token of queryTokens) {\n if (paramLower.includes(token)) {\n return { field: 'parameters', snippet: paramNames.join(', ') };\n }\n }\n }\n\n const fallbackSnippet = tool.description\n ? tool.description.substring(0, 100)\n : tool.name;\n return { field: 'unknown', snippet: fallbackSnippet };\n}\n\n/**\n * Performs BM25-based search for better relevance ranking.\n * Uses Okapi BM25 algorithm for term frequency and document length normalization.\n * @param tools - Array of tool metadata to search\n * @param query - The search query\n * @param fields - Which fields to search\n * @param maxResults - Maximum results to return\n * @returns Search response with matching tools ranked by BM25 score\n */\nfunction performLocalSearch(\n tools: t.ToolMetadata[],\n query: string,\n fields: string[],\n maxResults: number\n): t.ToolSearchResponse {\n if (tools.length === 0 || !query.trim()) {\n return {\n tool_references: [],\n total_tools_searched: tools.length,\n pattern_used: query,\n };\n }\n\n const documents = tools.map((tool) => createToolDocument(tool, fields));\n const queryTokens = tokenize(query);\n\n if (queryTokens.length === 0) {\n return {\n tool_references: [],\n total_tools_searched: tools.length,\n pattern_used: query,\n };\n }\n\n const scores = BM25(documents, queryTokens, { k1: 1.5, b: 0.75 }) as number[];\n\n const maxScore = Math.max(...scores.filter((s) => s > 0), 1);\n\n const results: t.ToolSearchResult[] = [];\n for (let i = 0; i < tools.length; i++) {\n if (scores[i] > 0) {\n const { field, snippet } = findMatchedField(\n tools[i],\n queryTokens,\n fields\n );\n const normalizedScore = Math.min(scores[i] / maxScore, 1.0);\n\n results.push({\n tool_name: tools[i].name,\n match_score: normalizedScore,\n matched_field: field,\n snippet,\n });\n }\n }\n\n results.sort((a, b) => b.match_score - a.match_score);\n const topResults = results.slice(0, maxResults);\n\n return {\n tool_references: topResults,\n total_tools_searched: tools.length,\n pattern_used: query,\n };\n}\n\n/**\n * Generates the JavaScript search script to be executed in the sandbox.\n * Uses plain JavaScript for maximum compatibility with the Code API.\n * @param deferredTools - Array of tool metadata to search through\n * @param fields - Which fields to search\n * @param maxResults - Maximum number of results to return\n * @param sanitizedPattern - The sanitized regex pattern\n * @returns The JavaScript code string\n */\nfunction generateSearchScript(\n deferredTools: t.ToolMetadata[],\n fields: string[],\n maxResults: number,\n sanitizedPattern: string\n): string {\n const lines = [\n '// Tool definitions (injected)',\n 'var tools = ' + JSON.stringify(deferredTools) + ';',\n 'var searchFields = ' + JSON.stringify(fields) + ';',\n 'var maxResults = ' + maxResults + ';',\n 'var pattern = ' + JSON.stringify(sanitizedPattern) + ';',\n '',\n '// Compile regex (pattern is sanitized client-side)',\n 'var regex;',\n 'try {',\n ' regex = new RegExp(pattern, \\'i\\');',\n '} catch (e) {',\n ' regex = new RegExp(pattern.replace(/[.*+?^${}()[\\\\]\\\\\\\\|]/g, \"\\\\\\\\$&\"), \"i\");',\n '}',\n '',\n '// Search logic',\n 'var results = [];',\n '',\n 'for (var j = 0; j < tools.length; j++) {',\n ' var tool = tools[j];',\n ' var bestScore = 0;',\n ' var matchedField = \\'\\';',\n ' var snippet = \\'\\';',\n '',\n ' // Search name (highest priority)',\n ' if (searchFields.indexOf(\\'name\\') >= 0 && regex.test(tool.name)) {',\n ' bestScore = 0.95;',\n ' matchedField = \\'name\\';',\n ' snippet = tool.name;',\n ' }',\n '',\n ' // Search description (medium priority)',\n ' if (searchFields.indexOf(\\'description\\') >= 0 && tool.description && regex.test(tool.description)) {',\n ' if (bestScore === 0) {',\n ' bestScore = 0.75;',\n ' matchedField = \\'description\\';',\n ' snippet = tool.description.substring(0, 100);',\n ' }',\n ' }',\n '',\n ' // Search parameter names (lower priority)',\n ' if (searchFields.indexOf(\\'parameters\\') >= 0 && tool.parameters && tool.parameters.properties) {',\n ' var paramNames = Object.keys(tool.parameters.properties).join(\\' \\');',\n ' if (regex.test(paramNames)) {',\n ' if (bestScore === 0) {',\n ' bestScore = 0.60;',\n ' matchedField = \\'parameters\\';',\n ' snippet = paramNames;',\n ' }',\n ' }',\n ' }',\n '',\n ' if (bestScore > 0) {',\n ' results.push({',\n ' tool_name: tool.name,',\n ' match_score: bestScore,',\n ' matched_field: matchedField,',\n ' snippet: snippet',\n ' });',\n ' }',\n '}',\n '',\n '// Sort by score (descending) and limit results',\n 'results.sort(function(a, b) { return b.match_score - a.match_score; });',\n 'var topResults = results.slice(0, maxResults);',\n '',\n '// Output as JSON',\n 'console.log(JSON.stringify({',\n ' tool_references: topResults.map(function(r) {',\n ' return {',\n ' tool_name: r.tool_name,',\n ' match_score: r.match_score,',\n ' matched_field: r.matched_field,',\n ' snippet: r.snippet',\n ' };',\n ' }),',\n ' total_tools_searched: tools.length,',\n ' pattern_used: pattern',\n '}));',\n ];\n return lines.join('\\n');\n}\n\n/**\n * Parses the search results from stdout JSON.\n * @param stdout - The stdout string containing JSON results\n * @returns Parsed search response\n */\nfunction parseSearchResults(stdout: string): t.ToolSearchResponse {\n const jsonMatch = stdout.trim();\n const parsed = JSON.parse(jsonMatch) as t.ToolSearchResponse;\n return parsed;\n}\n\n/**\n * Formats search results as structured JSON for efficient parsing.\n * @param searchResponse - The parsed search response\n * @returns JSON string with search results\n */\nfunction formatSearchResults(searchResponse: t.ToolSearchResponse): string {\n const { tool_references, total_tools_searched, pattern_used } =\n searchResponse;\n\n const output = {\n found: tool_references.length,\n tools: tool_references.map((ref) => ({\n name: ref.tool_name,\n score: Number(ref.match_score.toFixed(2)),\n matched_in: ref.matched_field,\n snippet: ref.snippet,\n })),\n total_searched: total_tools_searched,\n query: pattern_used,\n };\n\n return JSON.stringify(output, null, 2);\n}\n\n/**\n * Extracts the base tool name (without MCP server suffix) from a full tool name.\n * @param toolName - The full tool name\n * @returns The base tool name without server suffix\n */\nfunction getBaseToolName(toolName: string): string {\n const delimiterIndex = toolName.indexOf(Constants.MCP_DELIMITER);\n if (delimiterIndex === -1) {\n return toolName;\n }\n return toolName.substring(0, delimiterIndex);\n}\n\n/**\n * Generates a compact listing of deferred tools grouped by server.\n * Format: \"server: tool1, tool2, tool3\"\n * Non-MCP tools are grouped under \"other\".\n * @param toolRegistry - The tool registry\n * @param onlyDeferred - Whether to only include deferred tools\n * @returns Formatted string with tools grouped by server\n */\nfunction getDeferredToolsListing(\n toolRegistry: t.LCToolRegistry | undefined,\n onlyDeferred: boolean\n): string {\n if (!toolRegistry) {\n return '';\n }\n\n const toolsByServer: Record<string, string[]> = {};\n\n for (const lcTool of toolRegistry.values()) {\n if (onlyDeferred && lcTool.defer_loading !== true) {\n continue;\n }\n\n const toolName = lcTool.name;\n const serverName = extractMcpServerName(toolName) ?? 'other';\n const baseName = getBaseToolName(toolName);\n\n if (!(serverName in toolsByServer)) {\n toolsByServer[serverName] = [];\n }\n toolsByServer[serverName].push(baseName);\n }\n\n const serverNames = Object.keys(toolsByServer).sort((a, b) => {\n if (a === 'other') return 1;\n if (b === 'other') return -1;\n return a.localeCompare(b);\n });\n\n if (serverNames.length === 0) {\n return '';\n }\n\n const lines = serverNames.map(\n (server) => `${server}: ${toolsByServer[server].join(', ')}`\n );\n\n return lines.join('\\n');\n}\n\n/**\n * Formats a server listing response as structured JSON.\n * NOTE: This is a PREVIEW only - tools are NOT discovered/loaded.\n * @param tools - Array of tool metadata from the server(s)\n * @param serverNames - The MCP server name(s)\n * @returns JSON string showing all tools grouped by server\n */\nfunction formatServerListing(\n tools: t.ToolMetadata[],\n serverNames: string | string[]\n): string {\n const servers = Array.isArray(serverNames) ? serverNames : [serverNames];\n\n if (tools.length === 0) {\n return JSON.stringify(\n {\n listing_mode: true,\n servers,\n total_tools: 0,\n tools_by_server: {},\n hint: 'No tools found from the specified MCP server(s).',\n },\n null,\n 2\n );\n }\n\n const toolsByServer: Record<\n string,\n Array<{ name: string; description: string }>\n > = {};\n for (const tool of tools) {\n const server = extractMcpServerName(tool.name) ?? 'unknown';\n if (!(server in toolsByServer)) {\n toolsByServer[server] = [];\n }\n toolsByServer[server].push({\n name: getBaseToolName(tool.name),\n description:\n tool.description.length > 100\n ? tool.description.substring(0, 97) + '...'\n : tool.description,\n });\n }\n\n const output = {\n listing_mode: true,\n servers,\n total_tools: tools.length,\n tools_by_server: toolsByServer,\n hint: `To use a tool, search for it by name (e.g., query: \"${getBaseToolName(tools[0]?.name ?? 'tool_name')}\") to load it.`,\n };\n\n return JSON.stringify(output, null, 2);\n}\n\n/**\n * Creates a Tool Search tool for discovering tools from a large registry.\n *\n * This tool enables AI agents to dynamically discover tools from a large library\n * without loading all tool definitions into the LLM context window. The agent\n * can search for relevant tools on-demand.\n *\n * **Modes:**\n * - `code_interpreter` (default): Uses external sandbox for regex search. Safer for complex patterns.\n * - `local`: Uses safe substring matching locally. No network call, faster, completely safe from ReDoS.\n *\n * The tool registry can be provided either:\n * 1. At initialization time via params.toolRegistry\n * 2. At runtime via config.configurable.toolRegistry when invoking\n *\n * @param params - Configuration parameters for the tool (toolRegistry is optional)\n * @returns A LangChain DynamicStructuredTool for tool searching\n *\n * @example\n * // Option 1: Code interpreter mode (regex via sandbox)\n * const tool = createToolSearch({ apiKey, toolRegistry });\n * await tool.invoke({ query: 'expense.*report' });\n *\n * @example\n * // Option 2: Local mode (safe substring search, no API key needed)\n * const tool = createToolSearch({ mode: 'local', toolRegistry });\n * await tool.invoke({ query: 'expense' });\n */\nfunction createToolSearch(\n initParams: t.ToolSearchParams = {}\n): DynamicStructuredTool<ReturnType<typeof createToolSearchSchema>> {\n const mode: t.ToolSearchMode = initParams.mode ?? 'code_interpreter';\n const defaultOnlyDeferred = initParams.onlyDeferred ?? true;\n const schema = createToolSearchSchema(mode);\n\n const apiKey: string =\n mode === 'code_interpreter'\n ? ((initParams[EnvVar.CODE_API_KEY] as string | undefined) ??\n initParams.apiKey ??\n getEnvironmentVariable(EnvVar.CODE_API_KEY) ??\n '')\n : '';\n\n if (mode === 'code_interpreter' && !apiKey) {\n throw new Error(\n 'No API key provided for tool search in code_interpreter mode. Use mode: \"local\" to search without an API key.'\n );\n }\n\n const baseEndpoint = initParams.baseUrl ?? getCodeBaseURL();\n const EXEC_ENDPOINT = `${baseEndpoint}/exec`;\n\n const deferredToolsListing = getDeferredToolsListing(\n initParams.toolRegistry,\n defaultOnlyDeferred\n );\n\n const toolsListSection =\n deferredToolsListing.length > 0\n ? `\n\nDeferred tools (search to load):\n${deferredToolsListing}`\n : '';\n\n const mcpNote =\n deferredToolsListing.includes(Constants.MCP_DELIMITER) ||\n deferredToolsListing.split('\\n').some((line) => !line.startsWith('other:'))\n ? `\n- MCP tools use format: toolName${Constants.MCP_DELIMITER}serverName\n- Use mcp_server param to filter by server`\n : '';\n\n const description =\n mode === 'local'\n ? `\nSearches deferred tools using BM25 ranking. Multi-word queries supported.\n${mcpNote}${toolsListSection}\n`.trim()\n : `\nSearches deferred tools by regex pattern.\n${mcpNote}${toolsListSection}\n`.trim();\n\n return tool<typeof schema>(\n async (params, config) => {\n const {\n query,\n fields = ['name', 'description'],\n max_results = 10,\n mcp_server,\n } = params;\n\n const {\n toolRegistry: paramToolRegistry,\n onlyDeferred: paramOnlyDeferred,\n mcpServer: paramMcpServer,\n } = config.toolCall ?? {};\n\n const toolRegistry = paramToolRegistry ?? initParams.toolRegistry;\n const onlyDeferred =\n paramOnlyDeferred !== undefined\n ? paramOnlyDeferred\n : defaultOnlyDeferred;\n const rawServerFilter =\n mcp_server ?? paramMcpServer ?? initParams.mcpServer;\n const serverFilters = normalizeServerFilter(rawServerFilter);\n const hasServerFilter = serverFilters.length > 0;\n\n if (toolRegistry == null) {\n return [\n 'Error: No tool registry provided. Configure toolRegistry at agent level or initialization.',\n {\n tool_references: [],\n metadata: {\n total_searched: 0,\n pattern: query,\n error: 'No tool registry provided',\n },\n },\n ];\n }\n\n const toolsArray: t.LCTool[] = Array.from(toolRegistry.values());\n const deferredTools: t.ToolMetadata[] = toolsArray\n .filter((lcTool) => {\n if (onlyDeferred === true && lcTool.defer_loading !== true) {\n return false;\n }\n if (\n hasServerFilter &&\n !isFromAnyMcpServer(lcTool.name, serverFilters)\n ) {\n return false;\n }\n return true;\n })\n .map((lcTool) => ({\n name: lcTool.name,\n description: lcTool.description ?? '',\n parameters: simplifyParametersForSearch(lcTool.parameters),\n }));\n\n if (deferredTools.length === 0) {\n const serverMsg = hasServerFilter\n ? ` from MCP server(s): ${serverFilters.join(', ')}`\n : '';\n return [\n `No tools available to search${serverMsg}. The tool registry is empty or no matching deferred tools are registered.`,\n {\n tool_references: [],\n metadata: {\n total_searched: 0,\n pattern: query,\n mcp_server: serverFilters,\n },\n },\n ];\n }\n\n const isServerListing = hasServerFilter && query === '';\n\n if (isServerListing) {\n const formattedOutput = formatServerListing(\n deferredTools,\n serverFilters\n );\n\n return [\n formattedOutput,\n {\n tool_references: [],\n metadata: {\n total_available: deferredTools.length,\n mcp_server: serverFilters,\n listing_mode: true,\n },\n },\n ];\n }\n\n if (mode === 'local') {\n const searchResponse = performLocalSearch(\n deferredTools,\n query,\n fields,\n max_results\n );\n const formattedOutput = formatSearchResults(searchResponse);\n\n return [\n formattedOutput,\n {\n tool_references: searchResponse.tool_references,\n metadata: {\n total_searched: searchResponse.total_tools_searched,\n pattern: searchResponse.pattern_used,\n mcp_server: serverFilters.length > 0 ? serverFilters : undefined,\n },\n },\n ];\n }\n\n const { safe: sanitizedPattern, wasEscaped } = sanitizeRegex(query);\n let warningMessage = '';\n if (wasEscaped) {\n warningMessage =\n 'Note: The provided pattern was converted to a literal search for safety.\\n\\n';\n }\n\n const searchScript = generateSearchScript(\n deferredTools,\n fields,\n max_results,\n sanitizedPattern\n );\n\n const postData = {\n lang: 'js',\n code: searchScript,\n timeout: SEARCH_TIMEOUT,\n };\n\n try {\n const fetchOptions: RequestInit = {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'LibreChat/1.0',\n 'X-API-Key': apiKey,\n },\n body: JSON.stringify(postData),\n };\n\n if (process.env.PROXY != null && process.env.PROXY !== '') {\n fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);\n }\n\n const response = await fetch(EXEC_ENDPOINT, fetchOptions);\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const result: t.ExecuteResult = await response.json();\n\n if (result.stderr && result.stderr.trim()) {\n // eslint-disable-next-line no-console\n console.warn('[ToolSearch] stderr:', result.stderr);\n }\n\n if (!result.stdout || !result.stdout.trim()) {\n return [\n `${warningMessage}No tools matched the pattern \"${sanitizedPattern}\".\\nTotal tools searched: ${deferredTools.length}`,\n {\n tool_references: [],\n metadata: {\n total_searched: deferredTools.length,\n pattern: sanitizedPattern,\n },\n },\n ];\n }\n\n const searchResponse = parseSearchResults(result.stdout);\n const formattedOutput = `${warningMessage}${formatSearchResults(searchResponse)}`;\n\n return [\n formattedOutput,\n {\n tool_references: searchResponse.tool_references,\n metadata: {\n total_searched: searchResponse.total_tools_searched,\n pattern: searchResponse.pattern_used,\n },\n },\n ];\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('[ToolSearch] Error:', error);\n\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n return [\n `Tool search failed: ${errorMessage}\\n\\nSuggestion: Try a simpler search pattern or search for specific tool names.`,\n {\n tool_references: [],\n metadata: {\n total_searched: 0,\n pattern: sanitizedPattern,\n error: errorMessage,\n },\n },\n ];\n }\n },\n {\n name: Constants.TOOL_SEARCH,\n description,\n schema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport {\n createToolSearch,\n performLocalSearch,\n extractMcpServerName,\n isFromMcpServer,\n isFromAnyMcpServer,\n normalizeServerFilter,\n getAvailableMcpServers,\n getDeferredToolsListing,\n getBaseToolName,\n formatServerListing,\n sanitizeRegex,\n escapeRegexSpecialChars,\n isDangerousPattern,\n countNestedGroups,\n hasNestedQuantifiers,\n};\n"],"names":["config","z","Constants","EnvVar","getEnvironmentVariable","getCodeBaseURL","tool","HttpsProxyAgent"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import BM25 from 'okapibm25';
2
3
  import { config } from 'dotenv';
3
4
  import fetch from 'node-fetch';
4
5
  import { HttpsProxyAgent } from 'https-proxy-agent';
@@ -233,54 +234,116 @@ function simplifyParametersForSearch(parameters) {
233
234
  return { type: parameters.type };
234
235
  }
235
236
  /**
236
- * Performs safe local substring search without regex.
237
- * Uses case-insensitive String.includes() for complete safety against ReDoS.
238
- * @param tools - Array of tool metadata to search
239
- * @param query - The search term (treated as literal substring)
240
- * @param fields - Which fields to search
241
- * @param maxResults - Maximum results to return
242
- * @returns Search response with matching tools
237
+ * Tokenizes a string into lowercase words for BM25.
238
+ * @param text - The text to tokenize
239
+ * @returns Array of lowercase tokens
243
240
  */
244
- function performLocalSearch(tools, query, fields, maxResults) {
245
- const lowerQuery = query.toLowerCase();
246
- const results = [];
247
- for (const tool of tools) {
248
- let bestScore = 0;
249
- let matchedField = '';
250
- let snippet = '';
251
- if (fields.includes('name')) {
252
- const lowerName = tool.name.toLowerCase();
253
- if (lowerName.includes(lowerQuery)) {
254
- const isExactMatch = lowerName === lowerQuery;
255
- const startsWithQuery = lowerName.startsWith(lowerQuery);
256
- bestScore = isExactMatch ? 1.0 : startsWithQuery ? 0.95 : 0.85;
257
- matchedField = 'name';
258
- snippet = tool.name;
241
+ function tokenize(text) {
242
+ return text
243
+ .toLowerCase()
244
+ .replace(/[^a-z0-9_]/g, ' ')
245
+ .split(/\s+/)
246
+ .filter((token) => token.length > 0);
247
+ }
248
+ /**
249
+ * Creates a searchable document string from tool metadata.
250
+ * @param tool - The tool metadata
251
+ * @param fields - Which fields to include
252
+ * @returns Combined document string for BM25
253
+ */
254
+ function createToolDocument(tool, fields) {
255
+ const parts = [];
256
+ if (fields.includes('name')) {
257
+ const baseName = tool.name.replace(/_/g, ' ');
258
+ parts.push(baseName, baseName);
259
+ }
260
+ if (fields.includes('description') && tool.description) {
261
+ parts.push(tool.description);
262
+ }
263
+ if (fields.includes('parameters') && tool.parameters?.properties) {
264
+ const paramNames = Object.keys(tool.parameters.properties).join(' ');
265
+ parts.push(paramNames);
266
+ }
267
+ return parts.join(' ');
268
+ }
269
+ /**
270
+ * Determines which field had the best match for a query.
271
+ * @param tool - The tool to check
272
+ * @param queryTokens - Tokenized query
273
+ * @param fields - Fields to check
274
+ * @returns The matched field and a snippet
275
+ */
276
+ function findMatchedField(tool, queryTokens, fields) {
277
+ if (fields.includes('name')) {
278
+ const nameLower = tool.name.toLowerCase();
279
+ for (const token of queryTokens) {
280
+ if (nameLower.includes(token)) {
281
+ return { field: 'name', snippet: tool.name };
259
282
  }
260
283
  }
261
- if (fields.includes('description') && tool.description) {
262
- const lowerDesc = tool.description.toLowerCase();
263
- if (lowerDesc.includes(lowerQuery) && bestScore === 0) {
264
- bestScore = 0.7;
265
- matchedField = 'description';
266
- snippet = tool.description.substring(0, 100);
284
+ }
285
+ if (fields.includes('description') && tool.description) {
286
+ const descLower = tool.description.toLowerCase();
287
+ for (const token of queryTokens) {
288
+ if (descLower.includes(token)) {
289
+ return {
290
+ field: 'description',
291
+ snippet: tool.description.substring(0, 100),
292
+ };
267
293
  }
268
294
  }
269
- if (fields.includes('parameters') && tool.parameters?.properties) {
270
- const paramNames = Object.keys(tool.parameters.properties)
271
- .join(' ')
272
- .toLowerCase();
273
- if (paramNames.includes(lowerQuery) && bestScore === 0) {
274
- bestScore = 0.55;
275
- matchedField = 'parameters';
276
- snippet = Object.keys(tool.parameters.properties).join(' ');
295
+ }
296
+ if (fields.includes('parameters') && tool.parameters?.properties) {
297
+ const paramNames = Object.keys(tool.parameters.properties);
298
+ const paramLower = paramNames.join(' ').toLowerCase();
299
+ for (const token of queryTokens) {
300
+ if (paramLower.includes(token)) {
301
+ return { field: 'parameters', snippet: paramNames.join(', ') };
277
302
  }
278
303
  }
279
- if (bestScore > 0) {
304
+ }
305
+ const fallbackSnippet = tool.description
306
+ ? tool.description.substring(0, 100)
307
+ : tool.name;
308
+ return { field: 'unknown', snippet: fallbackSnippet };
309
+ }
310
+ /**
311
+ * Performs BM25-based search for better relevance ranking.
312
+ * Uses Okapi BM25 algorithm for term frequency and document length normalization.
313
+ * @param tools - Array of tool metadata to search
314
+ * @param query - The search query
315
+ * @param fields - Which fields to search
316
+ * @param maxResults - Maximum results to return
317
+ * @returns Search response with matching tools ranked by BM25 score
318
+ */
319
+ function performLocalSearch(tools, query, fields, maxResults) {
320
+ if (tools.length === 0 || !query.trim()) {
321
+ return {
322
+ tool_references: [],
323
+ total_tools_searched: tools.length,
324
+ pattern_used: query,
325
+ };
326
+ }
327
+ const documents = tools.map((tool) => createToolDocument(tool, fields));
328
+ const queryTokens = tokenize(query);
329
+ if (queryTokens.length === 0) {
330
+ return {
331
+ tool_references: [],
332
+ total_tools_searched: tools.length,
333
+ pattern_used: query,
334
+ };
335
+ }
336
+ const scores = BM25(documents, queryTokens, { k1: 1.5, b: 0.75 });
337
+ const maxScore = Math.max(...scores.filter((s) => s > 0), 1);
338
+ const results = [];
339
+ for (let i = 0; i < tools.length; i++) {
340
+ if (scores[i] > 0) {
341
+ const { field, snippet } = findMatchedField(tools[i], queryTokens, fields);
342
+ const normalizedScore = Math.min(scores[i] / maxScore, 1.0);
280
343
  results.push({
281
- tool_name: tool.name,
282
- match_score: bestScore,
283
- matched_field: matchedField,
344
+ tool_name: tools[i].name,
345
+ match_score: normalizedScore,
346
+ matched_field: field,
284
347
  snippet,
285
348
  });
286
349
  }
@@ -563,7 +626,7 @@ ${deferredToolsListing}`
563
626
  : '';
564
627
  const description = mode === 'local'
565
628
  ? `
566
- Searches deferred tools by name/description. Case-insensitive substring matching.
629
+ Searches deferred tools using BM25 ranking. Multi-word queries supported.
567
630
  ${mcpNote}${toolsListSection}
568
631
  `.trim()
569
632
  : `
@@ -1 +1 @@
1
- {"version":3,"file":"ToolSearch.mjs","sources":["../../../src/tools/ToolSearch.ts"],"sourcesContent":["// src/tools/ToolSearch.ts\nimport { z } from 'zod';\nimport { config } from 'dotenv';\nimport fetch, { RequestInit } from 'node-fetch';\nimport { HttpsProxyAgent } from 'https-proxy-agent';\nimport { getEnvironmentVariable } from '@langchain/core/utils/env';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport type * as t from '@/types';\nimport { getCodeBaseURL } from './CodeExecutor';\nimport { EnvVar, Constants } from '@/common';\n\nconfig();\n\n/** Maximum allowed regex pattern length */\nconst MAX_PATTERN_LENGTH = 200;\n\n/** Maximum allowed regex nesting depth */\nconst MAX_REGEX_COMPLEXITY = 5;\n\n/** Default search timeout in milliseconds */\nconst SEARCH_TIMEOUT = 5000;\n\n/** Zod schema type for tool search parameters */\ntype ToolSearchSchema = z.ZodObject<{\n query: z.ZodDefault<z.ZodOptional<z.ZodString>>;\n fields: z.ZodDefault<\n z.ZodOptional<z.ZodArray<z.ZodEnum<['name', 'description', 'parameters']>>>\n >;\n max_results: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;\n mcp_server: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString>]>>;\n}>;\n\n/**\n * Creates the Zod schema with dynamic query description based on mode.\n * @param mode - The search mode determining query interpretation\n * @returns Zod schema for tool search parameters\n */\nfunction createToolSearchSchema(mode: t.ToolSearchMode): ToolSearchSchema {\n const queryDescription =\n mode === 'local'\n ? 'Search term to find in tool names and descriptions. Case-insensitive substring matching. Optional if mcp_server is provided.'\n : 'Regex pattern to search tool names and descriptions. Optional if mcp_server is provided.';\n\n return z.object({\n query: z\n .string()\n .max(MAX_PATTERN_LENGTH)\n .optional()\n .default('')\n .describe(queryDescription),\n fields: z\n .array(z.enum(['name', 'description', 'parameters']))\n .optional()\n .default(['name', 'description'])\n .describe('Which fields to search. Default: name and description'),\n max_results: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .default(10)\n .describe('Maximum number of matching tools to return'),\n mcp_server: z\n .union([z.string(), z.array(z.string())])\n .optional()\n .describe(\n 'Filter to tools from specific MCP server(s). Can be a single server name or array of names. If provided without a query, lists all tools from those servers.'\n ),\n });\n}\n\n/**\n * Extracts the MCP server name from a tool name.\n * MCP tools follow the pattern: toolName_mcp_serverName\n * @param toolName - The full tool name\n * @returns The server name if it's an MCP tool, undefined otherwise\n */\nfunction extractMcpServerName(toolName: string): string | undefined {\n const delimiterIndex = toolName.indexOf(Constants.MCP_DELIMITER);\n if (delimiterIndex === -1) {\n return undefined;\n }\n return toolName.substring(delimiterIndex + Constants.MCP_DELIMITER.length);\n}\n\n/**\n * Checks if a tool belongs to a specific MCP server.\n * @param toolName - The full tool name\n * @param serverName - The server name to match\n * @returns True if the tool belongs to the specified server\n */\nfunction isFromMcpServer(toolName: string, serverName: string): boolean {\n const toolServer = extractMcpServerName(toolName);\n return toolServer === serverName;\n}\n\n/**\n * Checks if a tool belongs to any of the specified MCP servers.\n * @param toolName - The full tool name\n * @param serverNames - Array of server names to match\n * @returns True if the tool belongs to any of the specified servers\n */\nfunction isFromAnyMcpServer(toolName: string, serverNames: string[]): boolean {\n const toolServer = extractMcpServerName(toolName);\n if (toolServer === undefined) {\n return false;\n }\n return serverNames.includes(toolServer);\n}\n\n/**\n * Normalizes server filter input to always be an array.\n * @param serverFilter - String, array of strings, or undefined\n * @returns Array of server names (empty if none specified)\n */\nfunction normalizeServerFilter(\n serverFilter: string | string[] | undefined\n): string[] {\n if (serverFilter === undefined) {\n return [];\n }\n if (typeof serverFilter === 'string') {\n return serverFilter === '' ? [] : [serverFilter];\n }\n return serverFilter.filter((s) => s !== '');\n}\n\n/**\n * Extracts all unique MCP server names from a tool registry.\n * @param toolRegistry - The tool registry to scan\n * @param onlyDeferred - If true, only considers deferred tools\n * @returns Array of unique server names, sorted alphabetically\n */\nfunction getAvailableMcpServers(\n toolRegistry: t.LCToolRegistry | undefined,\n onlyDeferred: boolean = true\n): string[] {\n if (!toolRegistry) {\n return [];\n }\n\n const servers = new Set<string>();\n for (const [, toolDef] of toolRegistry) {\n if (onlyDeferred && toolDef.defer_loading !== true) {\n continue;\n }\n const server = extractMcpServerName(toolDef.name);\n if (server !== undefined && server !== '') {\n servers.add(server);\n }\n }\n\n return Array.from(servers).sort();\n}\n\n/**\n * Escapes special regex characters in a string to use as a literal pattern.\n * @param pattern - The string to escape\n * @returns The escaped string safe for use in a RegExp\n */\nfunction escapeRegexSpecialChars(pattern: string): string {\n return pattern.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Counts the maximum nesting depth of groups in a regex pattern.\n * @param pattern - The regex pattern to analyze\n * @returns The maximum nesting depth\n */\nfunction countNestedGroups(pattern: string): number {\n let maxDepth = 0;\n let currentDepth = 0;\n\n for (let i = 0; i < pattern.length; i++) {\n if (pattern[i] === '(' && (i === 0 || pattern[i - 1] !== '\\\\')) {\n currentDepth++;\n maxDepth = Math.max(maxDepth, currentDepth);\n } else if (pattern[i] === ')' && (i === 0 || pattern[i - 1] !== '\\\\')) {\n currentDepth = Math.max(0, currentDepth - 1);\n }\n }\n\n return maxDepth;\n}\n\n/**\n * Detects nested quantifiers that can cause catastrophic backtracking.\n * Patterns like (a+)+, (a*)*, (a+)*, etc.\n * @param pattern - The regex pattern to check\n * @returns True if nested quantifiers are detected\n */\nfunction hasNestedQuantifiers(pattern: string): boolean {\n const nestedQuantifierPattern = /\\([^)]*[+*][^)]*\\)[+*?]/;\n return nestedQuantifierPattern.test(pattern);\n}\n\n/**\n * Checks if a regex pattern contains potentially dangerous constructs.\n * @param pattern - The regex pattern to validate\n * @returns True if the pattern is dangerous\n */\nfunction isDangerousPattern(pattern: string): boolean {\n if (hasNestedQuantifiers(pattern)) {\n return true;\n }\n\n if (countNestedGroups(pattern) > MAX_REGEX_COMPLEXITY) {\n return true;\n }\n\n const dangerousPatterns = [\n /\\.\\{1000,\\}/, // Excessive wildcards\n /\\(\\?=\\.\\{100,\\}\\)/, // Runaway lookaheads\n /\\([^)]*\\|\\s*\\){20,}/, // Excessive alternation (rough check)\n /\\(\\.\\*\\)\\+/, // (.*)+\n /\\(\\.\\+\\)\\+/, // (.+)+\n /\\(\\.\\*\\)\\*/, // (.*)*\n /\\(\\.\\+\\)\\*/, // (.+)*\n ];\n\n for (const dangerous of dangerousPatterns) {\n if (dangerous.test(pattern)) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Sanitizes a regex pattern for safe execution.\n * If the pattern is dangerous, it will be escaped to a literal string search.\n * @param pattern - The regex pattern to sanitize\n * @returns Object containing the safe pattern and whether it was escaped\n */\nfunction sanitizeRegex(pattern: string): { safe: string; wasEscaped: boolean } {\n if (isDangerousPattern(pattern)) {\n return {\n safe: escapeRegexSpecialChars(pattern),\n wasEscaped: true,\n };\n }\n\n try {\n new RegExp(pattern);\n return { safe: pattern, wasEscaped: false };\n } catch {\n return {\n safe: escapeRegexSpecialChars(pattern),\n wasEscaped: true,\n };\n }\n}\n\n/**\n * Simplifies tool parameters for search purposes.\n * Extracts only the essential structure needed for parameter name searching.\n * @param parameters - The tool's JSON schema parameters\n * @returns Simplified parameters object\n */\nfunction simplifyParametersForSearch(\n parameters?: t.JsonSchemaType\n): t.JsonSchemaType | undefined {\n if (!parameters) {\n return undefined;\n }\n\n if (parameters.properties) {\n return {\n type: parameters.type,\n properties: Object.fromEntries(\n Object.entries(parameters.properties).map(([key, value]) => [\n key,\n { type: (value as t.JsonSchemaType).type },\n ])\n ),\n } as t.JsonSchemaType;\n }\n\n return { type: parameters.type };\n}\n\n/**\n * Performs safe local substring search without regex.\n * Uses case-insensitive String.includes() for complete safety against ReDoS.\n * @param tools - Array of tool metadata to search\n * @param query - The search term (treated as literal substring)\n * @param fields - Which fields to search\n * @param maxResults - Maximum results to return\n * @returns Search response with matching tools\n */\nfunction performLocalSearch(\n tools: t.ToolMetadata[],\n query: string,\n fields: string[],\n maxResults: number\n): t.ToolSearchResponse {\n const lowerQuery = query.toLowerCase();\n const results: t.ToolSearchResult[] = [];\n\n for (const tool of tools) {\n let bestScore = 0;\n let matchedField = '';\n let snippet = '';\n\n if (fields.includes('name')) {\n const lowerName = tool.name.toLowerCase();\n if (lowerName.includes(lowerQuery)) {\n const isExactMatch = lowerName === lowerQuery;\n const startsWithQuery = lowerName.startsWith(lowerQuery);\n bestScore = isExactMatch ? 1.0 : startsWithQuery ? 0.95 : 0.85;\n matchedField = 'name';\n snippet = tool.name;\n }\n }\n\n if (fields.includes('description') && tool.description) {\n const lowerDesc = tool.description.toLowerCase();\n if (lowerDesc.includes(lowerQuery) && bestScore === 0) {\n bestScore = 0.7;\n matchedField = 'description';\n snippet = tool.description.substring(0, 100);\n }\n }\n\n if (fields.includes('parameters') && tool.parameters?.properties) {\n const paramNames = Object.keys(tool.parameters.properties)\n .join(' ')\n .toLowerCase();\n if (paramNames.includes(lowerQuery) && bestScore === 0) {\n bestScore = 0.55;\n matchedField = 'parameters';\n snippet = Object.keys(tool.parameters.properties).join(' ');\n }\n }\n\n if (bestScore > 0) {\n results.push({\n tool_name: tool.name,\n match_score: bestScore,\n matched_field: matchedField,\n snippet,\n });\n }\n }\n\n results.sort((a, b) => b.match_score - a.match_score);\n const topResults = results.slice(0, maxResults);\n\n return {\n tool_references: topResults,\n total_tools_searched: tools.length,\n pattern_used: query,\n };\n}\n\n/**\n * Generates the JavaScript search script to be executed in the sandbox.\n * Uses plain JavaScript for maximum compatibility with the Code API.\n * @param deferredTools - Array of tool metadata to search through\n * @param fields - Which fields to search\n * @param maxResults - Maximum number of results to return\n * @param sanitizedPattern - The sanitized regex pattern\n * @returns The JavaScript code string\n */\nfunction generateSearchScript(\n deferredTools: t.ToolMetadata[],\n fields: string[],\n maxResults: number,\n sanitizedPattern: string\n): string {\n const lines = [\n '// Tool definitions (injected)',\n 'var tools = ' + JSON.stringify(deferredTools) + ';',\n 'var searchFields = ' + JSON.stringify(fields) + ';',\n 'var maxResults = ' + maxResults + ';',\n 'var pattern = ' + JSON.stringify(sanitizedPattern) + ';',\n '',\n '// Compile regex (pattern is sanitized client-side)',\n 'var regex;',\n 'try {',\n ' regex = new RegExp(pattern, \\'i\\');',\n '} catch (e) {',\n ' regex = new RegExp(pattern.replace(/[.*+?^${}()[\\\\]\\\\\\\\|]/g, \"\\\\\\\\$&\"), \"i\");',\n '}',\n '',\n '// Search logic',\n 'var results = [];',\n '',\n 'for (var j = 0; j < tools.length; j++) {',\n ' var tool = tools[j];',\n ' var bestScore = 0;',\n ' var matchedField = \\'\\';',\n ' var snippet = \\'\\';',\n '',\n ' // Search name (highest priority)',\n ' if (searchFields.indexOf(\\'name\\') >= 0 && regex.test(tool.name)) {',\n ' bestScore = 0.95;',\n ' matchedField = \\'name\\';',\n ' snippet = tool.name;',\n ' }',\n '',\n ' // Search description (medium priority)',\n ' if (searchFields.indexOf(\\'description\\') >= 0 && tool.description && regex.test(tool.description)) {',\n ' if (bestScore === 0) {',\n ' bestScore = 0.75;',\n ' matchedField = \\'description\\';',\n ' snippet = tool.description.substring(0, 100);',\n ' }',\n ' }',\n '',\n ' // Search parameter names (lower priority)',\n ' if (searchFields.indexOf(\\'parameters\\') >= 0 && tool.parameters && tool.parameters.properties) {',\n ' var paramNames = Object.keys(tool.parameters.properties).join(\\' \\');',\n ' if (regex.test(paramNames)) {',\n ' if (bestScore === 0) {',\n ' bestScore = 0.60;',\n ' matchedField = \\'parameters\\';',\n ' snippet = paramNames;',\n ' }',\n ' }',\n ' }',\n '',\n ' if (bestScore > 0) {',\n ' results.push({',\n ' tool_name: tool.name,',\n ' match_score: bestScore,',\n ' matched_field: matchedField,',\n ' snippet: snippet',\n ' });',\n ' }',\n '}',\n '',\n '// Sort by score (descending) and limit results',\n 'results.sort(function(a, b) { return b.match_score - a.match_score; });',\n 'var topResults = results.slice(0, maxResults);',\n '',\n '// Output as JSON',\n 'console.log(JSON.stringify({',\n ' tool_references: topResults.map(function(r) {',\n ' return {',\n ' tool_name: r.tool_name,',\n ' match_score: r.match_score,',\n ' matched_field: r.matched_field,',\n ' snippet: r.snippet',\n ' };',\n ' }),',\n ' total_tools_searched: tools.length,',\n ' pattern_used: pattern',\n '}));',\n ];\n return lines.join('\\n');\n}\n\n/**\n * Parses the search results from stdout JSON.\n * @param stdout - The stdout string containing JSON results\n * @returns Parsed search response\n */\nfunction parseSearchResults(stdout: string): t.ToolSearchResponse {\n const jsonMatch = stdout.trim();\n const parsed = JSON.parse(jsonMatch) as t.ToolSearchResponse;\n return parsed;\n}\n\n/**\n * Formats search results as structured JSON for efficient parsing.\n * @param searchResponse - The parsed search response\n * @returns JSON string with search results\n */\nfunction formatSearchResults(searchResponse: t.ToolSearchResponse): string {\n const { tool_references, total_tools_searched, pattern_used } =\n searchResponse;\n\n const output = {\n found: tool_references.length,\n tools: tool_references.map((ref) => ({\n name: ref.tool_name,\n score: Number(ref.match_score.toFixed(2)),\n matched_in: ref.matched_field,\n snippet: ref.snippet,\n })),\n total_searched: total_tools_searched,\n query: pattern_used,\n };\n\n return JSON.stringify(output, null, 2);\n}\n\n/**\n * Extracts the base tool name (without MCP server suffix) from a full tool name.\n * @param toolName - The full tool name\n * @returns The base tool name without server suffix\n */\nfunction getBaseToolName(toolName: string): string {\n const delimiterIndex = toolName.indexOf(Constants.MCP_DELIMITER);\n if (delimiterIndex === -1) {\n return toolName;\n }\n return toolName.substring(0, delimiterIndex);\n}\n\n/**\n * Generates a compact listing of deferred tools grouped by server.\n * Format: \"server: tool1, tool2, tool3\"\n * Non-MCP tools are grouped under \"other\".\n * @param toolRegistry - The tool registry\n * @param onlyDeferred - Whether to only include deferred tools\n * @returns Formatted string with tools grouped by server\n */\nfunction getDeferredToolsListing(\n toolRegistry: t.LCToolRegistry | undefined,\n onlyDeferred: boolean\n): string {\n if (!toolRegistry) {\n return '';\n }\n\n const toolsByServer: Record<string, string[]> = {};\n\n for (const lcTool of toolRegistry.values()) {\n if (onlyDeferred && lcTool.defer_loading !== true) {\n continue;\n }\n\n const toolName = lcTool.name;\n const serverName = extractMcpServerName(toolName) ?? 'other';\n const baseName = getBaseToolName(toolName);\n\n if (!(serverName in toolsByServer)) {\n toolsByServer[serverName] = [];\n }\n toolsByServer[serverName].push(baseName);\n }\n\n const serverNames = Object.keys(toolsByServer).sort((a, b) => {\n if (a === 'other') return 1;\n if (b === 'other') return -1;\n return a.localeCompare(b);\n });\n\n if (serverNames.length === 0) {\n return '';\n }\n\n const lines = serverNames.map(\n (server) => `${server}: ${toolsByServer[server].join(', ')}`\n );\n\n return lines.join('\\n');\n}\n\n/**\n * Formats a server listing response as structured JSON.\n * NOTE: This is a PREVIEW only - tools are NOT discovered/loaded.\n * @param tools - Array of tool metadata from the server(s)\n * @param serverNames - The MCP server name(s)\n * @returns JSON string showing all tools grouped by server\n */\nfunction formatServerListing(\n tools: t.ToolMetadata[],\n serverNames: string | string[]\n): string {\n const servers = Array.isArray(serverNames) ? serverNames : [serverNames];\n\n if (tools.length === 0) {\n return JSON.stringify(\n {\n listing_mode: true,\n servers,\n total_tools: 0,\n tools_by_server: {},\n hint: 'No tools found from the specified MCP server(s).',\n },\n null,\n 2\n );\n }\n\n const toolsByServer: Record<\n string,\n Array<{ name: string; description: string }>\n > = {};\n for (const tool of tools) {\n const server = extractMcpServerName(tool.name) ?? 'unknown';\n if (!(server in toolsByServer)) {\n toolsByServer[server] = [];\n }\n toolsByServer[server].push({\n name: getBaseToolName(tool.name),\n description:\n tool.description.length > 100\n ? tool.description.substring(0, 97) + '...'\n : tool.description,\n });\n }\n\n const output = {\n listing_mode: true,\n servers,\n total_tools: tools.length,\n tools_by_server: toolsByServer,\n hint: `To use a tool, search for it by name (e.g., query: \"${getBaseToolName(tools[0]?.name ?? 'tool_name')}\") to load it.`,\n };\n\n return JSON.stringify(output, null, 2);\n}\n\n/**\n * Creates a Tool Search tool for discovering tools from a large registry.\n *\n * This tool enables AI agents to dynamically discover tools from a large library\n * without loading all tool definitions into the LLM context window. The agent\n * can search for relevant tools on-demand.\n *\n * **Modes:**\n * - `code_interpreter` (default): Uses external sandbox for regex search. Safer for complex patterns.\n * - `local`: Uses safe substring matching locally. No network call, faster, completely safe from ReDoS.\n *\n * The tool registry can be provided either:\n * 1. At initialization time via params.toolRegistry\n * 2. At runtime via config.configurable.toolRegistry when invoking\n *\n * @param params - Configuration parameters for the tool (toolRegistry is optional)\n * @returns A LangChain DynamicStructuredTool for tool searching\n *\n * @example\n * // Option 1: Code interpreter mode (regex via sandbox)\n * const tool = createToolSearch({ apiKey, toolRegistry });\n * await tool.invoke({ query: 'expense.*report' });\n *\n * @example\n * // Option 2: Local mode (safe substring search, no API key needed)\n * const tool = createToolSearch({ mode: 'local', toolRegistry });\n * await tool.invoke({ query: 'expense' });\n */\nfunction createToolSearch(\n initParams: t.ToolSearchParams = {}\n): DynamicStructuredTool<ReturnType<typeof createToolSearchSchema>> {\n const mode: t.ToolSearchMode = initParams.mode ?? 'code_interpreter';\n const defaultOnlyDeferred = initParams.onlyDeferred ?? true;\n const schema = createToolSearchSchema(mode);\n\n const apiKey: string =\n mode === 'code_interpreter'\n ? ((initParams[EnvVar.CODE_API_KEY] as string | undefined) ??\n initParams.apiKey ??\n getEnvironmentVariable(EnvVar.CODE_API_KEY) ??\n '')\n : '';\n\n if (mode === 'code_interpreter' && !apiKey) {\n throw new Error(\n 'No API key provided for tool search in code_interpreter mode. Use mode: \"local\" to search without an API key.'\n );\n }\n\n const baseEndpoint = initParams.baseUrl ?? getCodeBaseURL();\n const EXEC_ENDPOINT = `${baseEndpoint}/exec`;\n\n const deferredToolsListing = getDeferredToolsListing(\n initParams.toolRegistry,\n defaultOnlyDeferred\n );\n\n const toolsListSection =\n deferredToolsListing.length > 0\n ? `\n\nDeferred tools (search to load):\n${deferredToolsListing}`\n : '';\n\n const mcpNote =\n deferredToolsListing.includes(Constants.MCP_DELIMITER) ||\n deferredToolsListing.split('\\n').some((line) => !line.startsWith('other:'))\n ? `\n- MCP tools use format: toolName${Constants.MCP_DELIMITER}serverName\n- Use mcp_server param to filter by server`\n : '';\n\n const description =\n mode === 'local'\n ? `\nSearches deferred tools by name/description. Case-insensitive substring matching.\n${mcpNote}${toolsListSection}\n`.trim()\n : `\nSearches deferred tools by regex pattern.\n${mcpNote}${toolsListSection}\n`.trim();\n\n return tool<typeof schema>(\n async (params, config) => {\n const {\n query,\n fields = ['name', 'description'],\n max_results = 10,\n mcp_server,\n } = params;\n\n const {\n toolRegistry: paramToolRegistry,\n onlyDeferred: paramOnlyDeferred,\n mcpServer: paramMcpServer,\n } = config.toolCall ?? {};\n\n const toolRegistry = paramToolRegistry ?? initParams.toolRegistry;\n const onlyDeferred =\n paramOnlyDeferred !== undefined\n ? paramOnlyDeferred\n : defaultOnlyDeferred;\n const rawServerFilter =\n mcp_server ?? paramMcpServer ?? initParams.mcpServer;\n const serverFilters = normalizeServerFilter(rawServerFilter);\n const hasServerFilter = serverFilters.length > 0;\n\n if (toolRegistry == null) {\n return [\n 'Error: No tool registry provided. Configure toolRegistry at agent level or initialization.',\n {\n tool_references: [],\n metadata: {\n total_searched: 0,\n pattern: query,\n error: 'No tool registry provided',\n },\n },\n ];\n }\n\n const toolsArray: t.LCTool[] = Array.from(toolRegistry.values());\n const deferredTools: t.ToolMetadata[] = toolsArray\n .filter((lcTool) => {\n if (onlyDeferred === true && lcTool.defer_loading !== true) {\n return false;\n }\n if (\n hasServerFilter &&\n !isFromAnyMcpServer(lcTool.name, serverFilters)\n ) {\n return false;\n }\n return true;\n })\n .map((lcTool) => ({\n name: lcTool.name,\n description: lcTool.description ?? '',\n parameters: simplifyParametersForSearch(lcTool.parameters),\n }));\n\n if (deferredTools.length === 0) {\n const serverMsg = hasServerFilter\n ? ` from MCP server(s): ${serverFilters.join(', ')}`\n : '';\n return [\n `No tools available to search${serverMsg}. The tool registry is empty or no matching deferred tools are registered.`,\n {\n tool_references: [],\n metadata: {\n total_searched: 0,\n pattern: query,\n mcp_server: serverFilters,\n },\n },\n ];\n }\n\n const isServerListing = hasServerFilter && query === '';\n\n if (isServerListing) {\n const formattedOutput = formatServerListing(\n deferredTools,\n serverFilters\n );\n\n return [\n formattedOutput,\n {\n tool_references: [],\n metadata: {\n total_available: deferredTools.length,\n mcp_server: serverFilters,\n listing_mode: true,\n },\n },\n ];\n }\n\n if (mode === 'local') {\n const searchResponse = performLocalSearch(\n deferredTools,\n query,\n fields,\n max_results\n );\n const formattedOutput = formatSearchResults(searchResponse);\n\n return [\n formattedOutput,\n {\n tool_references: searchResponse.tool_references,\n metadata: {\n total_searched: searchResponse.total_tools_searched,\n pattern: searchResponse.pattern_used,\n mcp_server: serverFilters.length > 0 ? serverFilters : undefined,\n },\n },\n ];\n }\n\n const { safe: sanitizedPattern, wasEscaped } = sanitizeRegex(query);\n let warningMessage = '';\n if (wasEscaped) {\n warningMessage =\n 'Note: The provided pattern was converted to a literal search for safety.\\n\\n';\n }\n\n const searchScript = generateSearchScript(\n deferredTools,\n fields,\n max_results,\n sanitizedPattern\n );\n\n const postData = {\n lang: 'js',\n code: searchScript,\n timeout: SEARCH_TIMEOUT,\n };\n\n try {\n const fetchOptions: RequestInit = {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'LibreChat/1.0',\n 'X-API-Key': apiKey,\n },\n body: JSON.stringify(postData),\n };\n\n if (process.env.PROXY != null && process.env.PROXY !== '') {\n fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);\n }\n\n const response = await fetch(EXEC_ENDPOINT, fetchOptions);\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const result: t.ExecuteResult = await response.json();\n\n if (result.stderr && result.stderr.trim()) {\n // eslint-disable-next-line no-console\n console.warn('[ToolSearch] stderr:', result.stderr);\n }\n\n if (!result.stdout || !result.stdout.trim()) {\n return [\n `${warningMessage}No tools matched the pattern \"${sanitizedPattern}\".\\nTotal tools searched: ${deferredTools.length}`,\n {\n tool_references: [],\n metadata: {\n total_searched: deferredTools.length,\n pattern: sanitizedPattern,\n },\n },\n ];\n }\n\n const searchResponse = parseSearchResults(result.stdout);\n const formattedOutput = `${warningMessage}${formatSearchResults(searchResponse)}`;\n\n return [\n formattedOutput,\n {\n tool_references: searchResponse.tool_references,\n metadata: {\n total_searched: searchResponse.total_tools_searched,\n pattern: searchResponse.pattern_used,\n },\n },\n ];\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('[ToolSearch] Error:', error);\n\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n return [\n `Tool search failed: ${errorMessage}\\n\\nSuggestion: Try a simpler search pattern or search for specific tool names.`,\n {\n tool_references: [],\n metadata: {\n total_searched: 0,\n pattern: sanitizedPattern,\n error: errorMessage,\n },\n },\n ];\n }\n },\n {\n name: Constants.TOOL_SEARCH,\n description,\n schema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport {\n createToolSearch,\n performLocalSearch,\n extractMcpServerName,\n isFromMcpServer,\n isFromAnyMcpServer,\n normalizeServerFilter,\n getAvailableMcpServers,\n getDeferredToolsListing,\n getBaseToolName,\n formatServerListing,\n sanitizeRegex,\n escapeRegexSpecialChars,\n isDangerousPattern,\n countNestedGroups,\n hasNestedQuantifiers,\n};\n"],"names":[],"mappings":";;;;;;;;;AAAA;AAWA,MAAM,EAAE;AAER;AACA,MAAM,kBAAkB,GAAG,GAAG;AAE9B;AACA,MAAM,oBAAoB,GAAG,CAAC;AAE9B;AACA,MAAM,cAAc,GAAG,IAAI;AAY3B;;;;AAIG;AACH,SAAS,sBAAsB,CAAC,IAAsB,EAAA;AACpD,IAAA,MAAM,gBAAgB,GACpB,IAAI,KAAK;AACP,UAAE;UACA,0FAA0F;IAEhG,OAAO,CAAC,CAAC,MAAM,CAAC;AACd,QAAA,KAAK,EAAE;AACJ,aAAA,MAAM;aACN,GAAG,CAAC,kBAAkB;AACtB,aAAA,QAAQ;aACR,OAAO,CAAC,EAAE;aACV,QAAQ,CAAC,gBAAgB,CAAC;AAC7B,QAAA,MAAM,EAAE;AACL,aAAA,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,aAAa,EAAE,YAAY,CAAC,CAAC;AACnD,aAAA,QAAQ;AACR,aAAA,OAAO,CAAC,CAAC,MAAM,EAAE,aAAa,CAAC;aAC/B,QAAQ,CAAC,uDAAuD,CAAC;AACpE,QAAA,WAAW,EAAE;AACV,aAAA,MAAM;AACN,aAAA,GAAG;aACH,GAAG,CAAC,CAAC;aACL,GAAG,CAAC,EAAE;AACN,aAAA,QAAQ;aACR,OAAO,CAAC,EAAE;aACV,QAAQ,CAAC,4CAA4C,CAAC;AACzD,QAAA,UAAU,EAAE;AACT,aAAA,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;AACvC,aAAA,QAAQ;aACR,QAAQ,CACP,8JAA8J,CAC/J;AACJ,KAAA,CAAC;AACJ;AAEA;;;;;AAKG;AACH,SAAS,oBAAoB,CAAC,QAAgB,EAAA;IAC5C,MAAM,cAAc,GAAG,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,aAAa,CAAC;AAChE,IAAA,IAAI,cAAc,KAAK,EAAE,EAAE;AACzB,QAAA,OAAO,SAAS;;AAElB,IAAA,OAAO,QAAQ,CAAC,SAAS,CAAC,cAAc,GAAG,SAAS,CAAC,aAAa,CAAC,MAAM,CAAC;AAC5E;AAEA;;;;;AAKG;AACH,SAAS,eAAe,CAAC,QAAgB,EAAE,UAAkB,EAAA;AAC3D,IAAA,MAAM,UAAU,GAAG,oBAAoB,CAAC,QAAQ,CAAC;IACjD,OAAO,UAAU,KAAK,UAAU;AAClC;AAEA;;;;;AAKG;AACH,SAAS,kBAAkB,CAAC,QAAgB,EAAE,WAAqB,EAAA;AACjE,IAAA,MAAM,UAAU,GAAG,oBAAoB,CAAC,QAAQ,CAAC;AACjD,IAAA,IAAI,UAAU,KAAK,SAAS,EAAE;AAC5B,QAAA,OAAO,KAAK;;AAEd,IAAA,OAAO,WAAW,CAAC,QAAQ,CAAC,UAAU,CAAC;AACzC;AAEA;;;;AAIG;AACH,SAAS,qBAAqB,CAC5B,YAA2C,EAAA;AAE3C,IAAA,IAAI,YAAY,KAAK,SAAS,EAAE;AAC9B,QAAA,OAAO,EAAE;;AAEX,IAAA,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE;AACpC,QAAA,OAAO,YAAY,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,YAAY,CAAC;;AAElD,IAAA,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;AAC7C;AAEA;;;;;AAKG;AACH,SAAS,sBAAsB,CAC7B,YAA0C,EAC1C,eAAwB,IAAI,EAAA;IAE5B,IAAI,CAAC,YAAY,EAAE;AACjB,QAAA,OAAO,EAAE;;AAGX,IAAA,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU;IACjC,KAAK,MAAM,GAAG,OAAO,CAAC,IAAI,YAAY,EAAE;QACtC,IAAI,YAAY,IAAI,OAAO,CAAC,aAAa,KAAK,IAAI,EAAE;YAClD;;QAEF,MAAM,MAAM,GAAG,oBAAoB,CAAC,OAAO,CAAC,IAAI,CAAC;QACjD,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,EAAE,EAAE;AACzC,YAAA,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;;;IAIvB,OAAO,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE;AACnC;AAEA;;;;AAIG;AACH,SAAS,uBAAuB,CAAC,OAAe,EAAA;IAC9C,OAAO,OAAO,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC;AACvD;AAEA;;;;AAIG;AACH,SAAS,iBAAiB,CAAC,OAAe,EAAA;IACxC,IAAI,QAAQ,GAAG,CAAC;IAChB,IAAI,YAAY,GAAG,CAAC;AAEpB,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;QACvC,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,EAAE;AAC9D,YAAA,YAAY,EAAE;YACd,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,YAAY,CAAC;;aACtC,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,EAAE;YACrE,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,YAAY,GAAG,CAAC,CAAC;;;AAIhD,IAAA,OAAO,QAAQ;AACjB;AAEA;;;;;AAKG;AACH,SAAS,oBAAoB,CAAC,OAAe,EAAA;IAC3C,MAAM,uBAAuB,GAAG,yBAAyB;AACzD,IAAA,OAAO,uBAAuB,CAAC,IAAI,CAAC,OAAO,CAAC;AAC9C;AAEA;;;;AAIG;AACH,SAAS,kBAAkB,CAAC,OAAe,EAAA;AACzC,IAAA,IAAI,oBAAoB,CAAC,OAAO,CAAC,EAAE;AACjC,QAAA,OAAO,IAAI;;AAGb,IAAA,IAAI,iBAAiB,CAAC,OAAO,CAAC,GAAG,oBAAoB,EAAE;AACrD,QAAA,OAAO,IAAI;;AAGb,IAAA,MAAM,iBAAiB,GAAG;AACxB,QAAA,aAAa;AACb,QAAA,mBAAmB;AACnB,QAAA,qBAAqB;AACrB,QAAA,YAAY;AACZ,QAAA,YAAY;AACZ,QAAA,YAAY;AACZ,QAAA,YAAY;KACb;AAED,IAAA,KAAK,MAAM,SAAS,IAAI,iBAAiB,EAAE;AACzC,QAAA,IAAI,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;AAC3B,YAAA,OAAO,IAAI;;;AAIf,IAAA,OAAO,KAAK;AACd;AAEA;;;;;AAKG;AACH,SAAS,aAAa,CAAC,OAAe,EAAA;AACpC,IAAA,IAAI,kBAAkB,CAAC,OAAO,CAAC,EAAE;QAC/B,OAAO;AACL,YAAA,IAAI,EAAE,uBAAuB,CAAC,OAAO,CAAC;AACtC,YAAA,UAAU,EAAE,IAAI;SACjB;;AAGH,IAAA,IAAI;AACF,QAAA,IAAI,MAAM,CAAC,OAAO,CAAC;QACnB,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE;;AAC3C,IAAA,MAAM;QACN,OAAO;AACL,YAAA,IAAI,EAAE,uBAAuB,CAAC,OAAO,CAAC;AACtC,YAAA,UAAU,EAAE,IAAI;SACjB;;AAEL;AAEA;;;;;AAKG;AACH,SAAS,2BAA2B,CAClC,UAA6B,EAAA;IAE7B,IAAI,CAAC,UAAU,EAAE;AACf,QAAA,OAAO,SAAS;;AAGlB,IAAA,IAAI,UAAU,CAAC,UAAU,EAAE;QACzB,OAAO;YACL,IAAI,EAAE,UAAU,CAAC,IAAI;YACrB,UAAU,EAAE,MAAM,CAAC,WAAW,CAC5B,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,KAAK;gBAC1D,GAAG;AACH,gBAAA,EAAE,IAAI,EAAG,KAA0B,CAAC,IAAI,EAAE;AAC3C,aAAA,CAAC,CACH;SACkB;;AAGvB,IAAA,OAAO,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE;AAClC;AAEA;;;;;;;;AAQG;AACH,SAAS,kBAAkB,CACzB,KAAuB,EACvB,KAAa,EACb,MAAgB,EAChB,UAAkB,EAAA;AAElB,IAAA,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,EAAE;IACtC,MAAM,OAAO,GAAyB,EAAE;AAExC,IAAA,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;QACxB,IAAI,SAAS,GAAG,CAAC;QACjB,IAAI,YAAY,GAAG,EAAE;QACrB,IAAI,OAAO,GAAG,EAAE;AAEhB,QAAA,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE;YAC3B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;AACzC,YAAA,IAAI,SAAS,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE;AAClC,gBAAA,MAAM,YAAY,GAAG,SAAS,KAAK,UAAU;gBAC7C,MAAM,eAAe,GAAG,SAAS,CAAC,UAAU,CAAC,UAAU,CAAC;AACxD,gBAAA,SAAS,GAAG,YAAY,GAAG,GAAG,GAAG,eAAe,GAAG,IAAI,GAAG,IAAI;gBAC9D,YAAY,GAAG,MAAM;AACrB,gBAAA,OAAO,GAAG,IAAI,CAAC,IAAI;;;QAIvB,IAAI,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,IAAI,CAAC,WAAW,EAAE;YACtD,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,WAAW,EAAE;YAChD,IAAI,SAAS,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,SAAS,KAAK,CAAC,EAAE;gBACrD,SAAS,GAAG,GAAG;gBACf,YAAY,GAAG,aAAa;gBAC5B,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC;;;AAIhD,QAAA,IAAI,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC,UAAU,EAAE,UAAU,EAAE;YAChE,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU;iBACtD,IAAI,CAAC,GAAG;AACR,iBAAA,WAAW,EAAE;YAChB,IAAI,UAAU,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,SAAS,KAAK,CAAC,EAAE;gBACtD,SAAS,GAAG,IAAI;gBAChB,YAAY,GAAG,YAAY;AAC3B,gBAAA,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;;;AAI/D,QAAA,IAAI,SAAS,GAAG,CAAC,EAAE;YACjB,OAAO,CAAC,IAAI,CAAC;gBACX,SAAS,EAAE,IAAI,CAAC,IAAI;AACpB,gBAAA,WAAW,EAAE,SAAS;AACtB,gBAAA,aAAa,EAAE,YAAY;gBAC3B,OAAO;AACR,aAAA,CAAC;;;AAIN,IAAA,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,WAAW,CAAC;IACrD,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC;IAE/C,OAAO;AACL,QAAA,eAAe,EAAE,UAAU;QAC3B,oBAAoB,EAAE,KAAK,CAAC,MAAM;AAClC,QAAA,YAAY,EAAE,KAAK;KACpB;AACH;AAEA;;;;;;;;AAQG;AACH,SAAS,oBAAoB,CAC3B,aAA+B,EAC/B,MAAgB,EAChB,UAAkB,EAClB,gBAAwB,EAAA;AAExB,IAAA,MAAM,KAAK,GAAG;QACZ,gCAAgC;QAChC,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,GAAG,GAAG;QACpD,qBAAqB,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,GAAG;QACpD,mBAAmB,GAAG,UAAU,GAAG,GAAG;QACtC,gBAAgB,GAAG,IAAI,CAAC,SAAS,CAAC,gBAAgB,CAAC,GAAG,GAAG;QACzD,EAAE;QACF,qDAAqD;QACrD,YAAY;QACZ,OAAO;QACP,uCAAuC;QACvC,eAAe;QACf,iFAAiF;QACjF,GAAG;QACH,EAAE;QACF,iBAAiB;QACjB,mBAAmB;QACnB,EAAE;QACF,0CAA0C;QAC1C,wBAAwB;QACxB,sBAAsB;QACtB,4BAA4B;QAC5B,uBAAuB;QACvB,EAAE;QACF,qCAAqC;QACrC,uEAAuE;QACvE,uBAAuB;QACvB,8BAA8B;QAC9B,0BAA0B;QAC1B,KAAK;QACL,EAAE;QACF,2CAA2C;QAC3C,yGAAyG;QACzG,4BAA4B;QAC5B,yBAAyB;QACzB,uCAAuC;QACvC,qDAAqD;QACrD,OAAO;QACP,KAAK;QACL,EAAE;QACF,8CAA8C;QAC9C,qGAAqG;QACrG,2EAA2E;QAC3E,mCAAmC;QACnC,8BAA8B;QAC9B,2BAA2B;QAC3B,wCAAwC;QACxC,+BAA+B;QAC/B,SAAS;QACT,OAAO;QACP,KAAK;QACL,EAAE;QACF,wBAAwB;QACxB,oBAAoB;QACpB,6BAA6B;QAC7B,+BAA+B;QAC/B,oCAAoC;QACpC,wBAAwB;QACxB,SAAS;QACT,KAAK;QACL,GAAG;QACH,EAAE;QACF,iDAAiD;QACjD,yEAAyE;QACzE,gDAAgD;QAChD,EAAE;QACF,mBAAmB;QACnB,8BAA8B;QAC9B,iDAAiD;QACjD,cAAc;QACd,+BAA+B;QAC/B,mCAAmC;QACnC,uCAAuC;QACvC,0BAA0B;QAC1B,QAAQ;QACR,OAAO;QACP,uCAAuC;QACvC,yBAAyB;QACzB,MAAM;KACP;AACD,IAAA,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;AACzB;AAEA;;;;AAIG;AACH,SAAS,kBAAkB,CAAC,MAAc,EAAA;AACxC,IAAA,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,EAAE;IAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAyB;AAC5D,IAAA,OAAO,MAAM;AACf;AAEA;;;;AAIG;AACH,SAAS,mBAAmB,CAAC,cAAoC,EAAA;IAC/D,MAAM,EAAE,eAAe,EAAE,oBAAoB,EAAE,YAAY,EAAE,GAC3D,cAAc;AAEhB,IAAA,MAAM,MAAM,GAAG;QACb,KAAK,EAAE,eAAe,CAAC,MAAM;QAC7B,KAAK,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM;YACnC,IAAI,EAAE,GAAG,CAAC,SAAS;YACnB,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YACzC,UAAU,EAAE,GAAG,CAAC,aAAa;YAC7B,OAAO,EAAE,GAAG,CAAC,OAAO;AACrB,SAAA,CAAC,CAAC;AACH,QAAA,cAAc,EAAE,oBAAoB;AACpC,QAAA,KAAK,EAAE,YAAY;KACpB;IAED,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;AACxC;AAEA;;;;AAIG;AACH,SAAS,eAAe,CAAC,QAAgB,EAAA;IACvC,MAAM,cAAc,GAAG,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,aAAa,CAAC;AAChE,IAAA,IAAI,cAAc,KAAK,EAAE,EAAE;AACzB,QAAA,OAAO,QAAQ;;IAEjB,OAAO,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,cAAc,CAAC;AAC9C;AAEA;;;;;;;AAOG;AACH,SAAS,uBAAuB,CAC9B,YAA0C,EAC1C,YAAqB,EAAA;IAErB,IAAI,CAAC,YAAY,EAAE;AACjB,QAAA,OAAO,EAAE;;IAGX,MAAM,aAAa,GAA6B,EAAE;IAElD,KAAK,MAAM,MAAM,IAAI,YAAY,CAAC,MAAM,EAAE,EAAE;QAC1C,IAAI,YAAY,IAAI,MAAM,CAAC,aAAa,KAAK,IAAI,EAAE;YACjD;;AAGF,QAAA,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI;QAC5B,MAAM,UAAU,GAAG,oBAAoB,CAAC,QAAQ,CAAC,IAAI,OAAO;AAC5D,QAAA,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,CAAC;AAE1C,QAAA,IAAI,EAAE,UAAU,IAAI,aAAa,CAAC,EAAE;AAClC,YAAA,aAAa,CAAC,UAAU,CAAC,GAAG,EAAE;;QAEhC,aAAa,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC;;AAG1C,IAAA,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAI;QAC3D,IAAI,CAAC,KAAK,OAAO;AAAE,YAAA,OAAO,CAAC;QAC3B,IAAI,CAAC,KAAK,OAAO;YAAE,OAAO,EAAE;AAC5B,QAAA,OAAO,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC;AAC3B,KAAC,CAAC;AAEF,IAAA,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE;AAC5B,QAAA,OAAO,EAAE;;IAGX,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAC3B,CAAC,MAAM,KAAK,CAAA,EAAG,MAAM,CAAK,EAAA,EAAA,aAAa,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAE,CAAA,CAC7D;AAED,IAAA,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;AACzB;AAEA;;;;;;AAMG;AACH,SAAS,mBAAmB,CAC1B,KAAuB,EACvB,WAA8B,EAAA;AAE9B,IAAA,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,GAAG,WAAW,GAAG,CAAC,WAAW,CAAC;AAExE,IAAA,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE;QACtB,OAAO,IAAI,CAAC,SAAS,CACnB;AACE,YAAA,YAAY,EAAE,IAAI;YAClB,OAAO;AACP,YAAA,WAAW,EAAE,CAAC;AACd,YAAA,eAAe,EAAE,EAAE;AACnB,YAAA,IAAI,EAAE,kDAAkD;AACzD,SAAA,EACD,IAAI,EACJ,CAAC,CACF;;IAGH,MAAM,aAAa,GAGf,EAAE;AACN,IAAA,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;QACxB,MAAM,MAAM,GAAG,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,SAAS;AAC3D,QAAA,IAAI,EAAE,MAAM,IAAI,aAAa,CAAC,EAAE;AAC9B,YAAA,aAAa,CAAC,MAAM,CAAC,GAAG,EAAE;;AAE5B,QAAA,aAAa,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC;AACzB,YAAA,IAAI,EAAE,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC;AAChC,YAAA,WAAW,EACT,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG;AACxB,kBAAE,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG;kBACpC,IAAI,CAAC,WAAW;AACvB,SAAA,CAAC;;AAGJ,IAAA,MAAM,MAAM,GAAG;AACb,QAAA,YAAY,EAAE,IAAI;QAClB,OAAO;QACP,WAAW,EAAE,KAAK,CAAC,MAAM;AACzB,QAAA,eAAe,EAAE,aAAa;AAC9B,QAAA,IAAI,EAAE,CAAA,oDAAA,EAAuD,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,IAAI,WAAW,CAAC,CAAgB,cAAA,CAAA;KAC5H;IAED,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;AACxC;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BG;AACH,SAAS,gBAAgB,CACvB,UAAA,GAAiC,EAAE,EAAA;AAEnC,IAAA,MAAM,IAAI,GAAqB,UAAU,CAAC,IAAI,IAAI,kBAAkB;AACpE,IAAA,MAAM,mBAAmB,GAAG,UAAU,CAAC,YAAY,IAAI,IAAI;AAC3D,IAAA,MAAM,MAAM,GAAG,sBAAsB,CAAC,IAAI,CAAC;AAE3C,IAAA,MAAM,MAAM,GACV,IAAI,KAAK;AACP,WAAI,UAAU,CAAC,MAAM,CAAC,YAAY,CAAwB;AACxD,YAAA,UAAU,CAAC,MAAM;AACjB,YAAA,sBAAsB,CAAC,MAAM,CAAC,YAAY,CAAC;AAC3C,YAAA,EAAE;UACF,EAAE;AAER,IAAA,IAAI,IAAI,KAAK,kBAAkB,IAAI,CAAC,MAAM,EAAE;AAC1C,QAAA,MAAM,IAAI,KAAK,CACb,+GAA+G,CAChH;;IAGH,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,IAAI,cAAc,EAAE;AAC3D,IAAA,MAAM,aAAa,GAAG,CAAG,EAAA,YAAY,OAAO;IAE5C,MAAM,oBAAoB,GAAG,uBAAuB,CAClD,UAAU,CAAC,YAAY,EACvB,mBAAmB,CACpB;AAED,IAAA,MAAM,gBAAgB,GACpB,oBAAoB,CAAC,MAAM,GAAG;AAC5B,UAAE;;;AAGN,EAAA,oBAAoB,CAAE;UAChB,EAAE;IAER,MAAM,OAAO,GACX,oBAAoB,CAAC,QAAQ,CAAC,SAAS,CAAC,aAAa,CAAC;QACtD,oBAAoB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;AACxE,UAAE;AAC0B,gCAAA,EAAA,SAAS,CAAC,aAAa,CAAA;AACd,0CAAA;UACnC,EAAE;AAER,IAAA,MAAM,WAAW,GACf,IAAI,KAAK;AACP,UAAE;;AAEN,EAAA,OAAO,GAAG,gBAAgB;AAC3B,CAAA,CAAC,IAAI;AACA,UAAE;;AAEN,EAAA,OAAO,GAAG,gBAAgB;CAC3B,CAAC,IAAI,EAAE;IAEN,OAAO,IAAI,CACT,OAAO,MAAM,EAAE,MAAM,KAAI;AACvB,QAAA,MAAM,EACJ,KAAK,EACL,MAAM,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,EAChC,WAAW,GAAG,EAAE,EAChB,UAAU,GACX,GAAG,MAAM;AAEV,QAAA,MAAM,EACJ,YAAY,EAAE,iBAAiB,EAC/B,YAAY,EAAE,iBAAiB,EAC/B,SAAS,EAAE,cAAc,GAC1B,GAAG,MAAM,CAAC,QAAQ,IAAI,EAAE;AAEzB,QAAA,MAAM,YAAY,GAAG,iBAAiB,IAAI,UAAU,CAAC,YAAY;AACjE,QAAA,MAAM,YAAY,GAChB,iBAAiB,KAAK;AACpB,cAAE;cACA,mBAAmB;QACzB,MAAM,eAAe,GACnB,UAAU,IAAI,cAAc,IAAI,UAAU,CAAC,SAAS;AACtD,QAAA,MAAM,aAAa,GAAG,qBAAqB,CAAC,eAAe,CAAC;AAC5D,QAAA,MAAM,eAAe,GAAG,aAAa,CAAC,MAAM,GAAG,CAAC;AAEhD,QAAA,IAAI,YAAY,IAAI,IAAI,EAAE;YACxB,OAAO;gBACL,4FAA4F;AAC5F,gBAAA;AACE,oBAAA,eAAe,EAAE,EAAE;AACnB,oBAAA,QAAQ,EAAE;AACR,wBAAA,cAAc,EAAE,CAAC;AACjB,wBAAA,OAAO,EAAE,KAAK;AACd,wBAAA,KAAK,EAAE,2BAA2B;AACnC,qBAAA;AACF,iBAAA;aACF;;QAGH,MAAM,UAAU,GAAe,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC;QAChE,MAAM,aAAa,GAAqB;AACrC,aAAA,MAAM,CAAC,CAAC,MAAM,KAAI;YACjB,IAAI,YAAY,KAAK,IAAI,IAAI,MAAM,CAAC,aAAa,KAAK,IAAI,EAAE;AAC1D,gBAAA,OAAO,KAAK;;AAEd,YAAA,IACE,eAAe;gBACf,CAAC,kBAAkB,CAAC,MAAM,CAAC,IAAI,EAAE,aAAa,CAAC,EAC/C;AACA,gBAAA,OAAO,KAAK;;AAEd,YAAA,OAAO,IAAI;AACb,SAAC;AACA,aAAA,GAAG,CAAC,CAAC,MAAM,MAAM;YAChB,IAAI,EAAE,MAAM,CAAC,IAAI;AACjB,YAAA,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,EAAE;AACrC,YAAA,UAAU,EAAE,2BAA2B,CAAC,MAAM,CAAC,UAAU,CAAC;AAC3D,SAAA,CAAC,CAAC;AAEL,QAAA,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE;YAC9B,MAAM,SAAS,GAAG;kBACd,wBAAwB,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAE;kBAClD,EAAE;YACN,OAAO;AACL,gBAAA,CAAA,4BAAA,EAA+B,SAAS,CAA4E,0EAAA,CAAA;AACpH,gBAAA;AACE,oBAAA,eAAe,EAAE,EAAE;AACnB,oBAAA,QAAQ,EAAE;AACR,wBAAA,cAAc,EAAE,CAAC;AACjB,wBAAA,OAAO,EAAE,KAAK;AACd,wBAAA,UAAU,EAAE,aAAa;AAC1B,qBAAA;AACF,iBAAA;aACF;;AAGH,QAAA,MAAM,eAAe,GAAG,eAAe,IAAI,KAAK,KAAK,EAAE;QAEvD,IAAI,eAAe,EAAE;YACnB,MAAM,eAAe,GAAG,mBAAmB,CACzC,aAAa,EACb,aAAa,CACd;YAED,OAAO;gBACL,eAAe;AACf,gBAAA;AACE,oBAAA,eAAe,EAAE,EAAE;AACnB,oBAAA,QAAQ,EAAE;wBACR,eAAe,EAAE,aAAa,CAAC,MAAM;AACrC,wBAAA,UAAU,EAAE,aAAa;AACzB,wBAAA,YAAY,EAAE,IAAI;AACnB,qBAAA;AACF,iBAAA;aACF;;AAGH,QAAA,IAAI,IAAI,KAAK,OAAO,EAAE;AACpB,YAAA,MAAM,cAAc,GAAG,kBAAkB,CACvC,aAAa,EACb,KAAK,EACL,MAAM,EACN,WAAW,CACZ;AACD,YAAA,MAAM,eAAe,GAAG,mBAAmB,CAAC,cAAc,CAAC;YAE3D,OAAO;gBACL,eAAe;AACf,gBAAA;oBACE,eAAe,EAAE,cAAc,CAAC,eAAe;AAC/C,oBAAA,QAAQ,EAAE;wBACR,cAAc,EAAE,cAAc,CAAC,oBAAoB;wBACnD,OAAO,EAAE,cAAc,CAAC,YAAY;AACpC,wBAAA,UAAU,EAAE,aAAa,CAAC,MAAM,GAAG,CAAC,GAAG,aAAa,GAAG,SAAS;AACjE,qBAAA;AACF,iBAAA;aACF;;AAGH,QAAA,MAAM,EAAE,IAAI,EAAE,gBAAgB,EAAE,UAAU,EAAE,GAAG,aAAa,CAAC,KAAK,CAAC;QACnE,IAAI,cAAc,GAAG,EAAE;QACvB,IAAI,UAAU,EAAE;YACd,cAAc;AACZ,gBAAA,8EAA8E;;AAGlF,QAAA,MAAM,YAAY,GAAG,oBAAoB,CACvC,aAAa,EACb,MAAM,EACN,WAAW,EACX,gBAAgB,CACjB;AAED,QAAA,MAAM,QAAQ,GAAG;AACf,YAAA,IAAI,EAAE,IAAI;AACV,YAAA,IAAI,EAAE,YAAY;AAClB,YAAA,OAAO,EAAE,cAAc;SACxB;AAED,QAAA,IAAI;AACF,YAAA,MAAM,YAAY,GAAgB;AAChC,gBAAA,MAAM,EAAE,MAAM;AACd,gBAAA,OAAO,EAAE;AACP,oBAAA,cAAc,EAAE,kBAAkB;AAClC,oBAAA,YAAY,EAAE,eAAe;AAC7B,oBAAA,WAAW,EAAE,MAAM;AACpB,iBAAA;AACD,gBAAA,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;aAC/B;AAED,YAAA,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,EAAE,EAAE;AACzD,gBAAA,YAAY,CAAC,KAAK,GAAG,IAAI,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;;YAG7D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,aAAa,EAAE,YAAY,CAAC;AACzD,YAAA,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE;gBAChB,MAAM,IAAI,KAAK,CAAC,CAAA,oBAAA,EAAuB,QAAQ,CAAC,MAAM,CAAE,CAAA,CAAC;;AAG3D,YAAA,MAAM,MAAM,GAAoB,MAAM,QAAQ,CAAC,IAAI,EAAE;YAErD,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE;;gBAEzC,OAAO,CAAC,IAAI,CAAC,sBAAsB,EAAE,MAAM,CAAC,MAAM,CAAC;;AAGrD,YAAA,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE;gBAC3C,OAAO;AACL,oBAAA,CAAA,EAAG,cAAc,CAAiC,8BAAA,EAAA,gBAAgB,6BAA6B,aAAa,CAAC,MAAM,CAAE,CAAA;AACrH,oBAAA;AACE,wBAAA,eAAe,EAAE,EAAE;AACnB,wBAAA,QAAQ,EAAE;4BACR,cAAc,EAAE,aAAa,CAAC,MAAM;AACpC,4BAAA,OAAO,EAAE,gBAAgB;AAC1B,yBAAA;AACF,qBAAA;iBACF;;YAGH,MAAM,cAAc,GAAG,kBAAkB,CAAC,MAAM,CAAC,MAAM,CAAC;YACxD,MAAM,eAAe,GAAG,CAAA,EAAG,cAAc,CAAA,EAAG,mBAAmB,CAAC,cAAc,CAAC,CAAA,CAAE;YAEjF,OAAO;gBACL,eAAe;AACf,gBAAA;oBACE,eAAe,EAAE,cAAc,CAAC,eAAe;AAC/C,oBAAA,QAAQ,EAAE;wBACR,cAAc,EAAE,cAAc,CAAC,oBAAoB;wBACnD,OAAO,EAAE,cAAc,CAAC,YAAY;AACrC,qBAAA;AACF,iBAAA;aACF;;QACD,OAAO,KAAK,EAAE;;AAEd,YAAA,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,KAAK,CAAC;AAE3C,YAAA,MAAM,YAAY,GAChB,KAAK,YAAY,KAAK,GAAG,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC;YACxD,OAAO;AACL,gBAAA,CAAA,oBAAA,EAAuB,YAAY,CAAiF,+EAAA,CAAA;AACpH,gBAAA;AACE,oBAAA,eAAe,EAAE,EAAE;AACnB,oBAAA,QAAQ,EAAE;AACR,wBAAA,cAAc,EAAE,CAAC;AACjB,wBAAA,OAAO,EAAE,gBAAgB;AACzB,wBAAA,KAAK,EAAE,YAAY;AACpB,qBAAA;AACF,iBAAA;aACF;;AAEL,KAAC,EACD;QACE,IAAI,EAAE,SAAS,CAAC,WAAW;QAC3B,WAAW;QACX,MAAM;QACN,cAAc,EAAE,SAAS,CAAC,oBAAoB;AAC/C,KAAA,CACF;AACH;;;;"}
1
+ {"version":3,"file":"ToolSearch.mjs","sources":["../../../src/tools/ToolSearch.ts"],"sourcesContent":["// src/tools/ToolSearch.ts\nimport { z } from 'zod';\nimport BM25 from 'okapibm25';\nimport { config } from 'dotenv';\nimport fetch, { RequestInit } from 'node-fetch';\nimport { HttpsProxyAgent } from 'https-proxy-agent';\nimport { getEnvironmentVariable } from '@langchain/core/utils/env';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport type * as t from '@/types';\nimport { getCodeBaseURL } from './CodeExecutor';\nimport { EnvVar, Constants } from '@/common';\n\nconfig();\n\n/** Maximum allowed regex pattern length */\nconst MAX_PATTERN_LENGTH = 200;\n\n/** Maximum allowed regex nesting depth */\nconst MAX_REGEX_COMPLEXITY = 5;\n\n/** Default search timeout in milliseconds */\nconst SEARCH_TIMEOUT = 5000;\n\n/** Zod schema type for tool search parameters */\ntype ToolSearchSchema = z.ZodObject<{\n query: z.ZodDefault<z.ZodOptional<z.ZodString>>;\n fields: z.ZodDefault<\n z.ZodOptional<z.ZodArray<z.ZodEnum<['name', 'description', 'parameters']>>>\n >;\n max_results: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;\n mcp_server: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString>]>>;\n}>;\n\n/**\n * Creates the Zod schema with dynamic query description based on mode.\n * @param mode - The search mode determining query interpretation\n * @returns Zod schema for tool search parameters\n */\nfunction createToolSearchSchema(mode: t.ToolSearchMode): ToolSearchSchema {\n const queryDescription =\n mode === 'local'\n ? 'Search term to find in tool names and descriptions. Case-insensitive substring matching. Optional if mcp_server is provided.'\n : 'Regex pattern to search tool names and descriptions. Optional if mcp_server is provided.';\n\n return z.object({\n query: z\n .string()\n .max(MAX_PATTERN_LENGTH)\n .optional()\n .default('')\n .describe(queryDescription),\n fields: z\n .array(z.enum(['name', 'description', 'parameters']))\n .optional()\n .default(['name', 'description'])\n .describe('Which fields to search. Default: name and description'),\n max_results: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .default(10)\n .describe('Maximum number of matching tools to return'),\n mcp_server: z\n .union([z.string(), z.array(z.string())])\n .optional()\n .describe(\n 'Filter to tools from specific MCP server(s). Can be a single server name or array of names. If provided without a query, lists all tools from those servers.'\n ),\n });\n}\n\n/**\n * Extracts the MCP server name from a tool name.\n * MCP tools follow the pattern: toolName_mcp_serverName\n * @param toolName - The full tool name\n * @returns The server name if it's an MCP tool, undefined otherwise\n */\nfunction extractMcpServerName(toolName: string): string | undefined {\n const delimiterIndex = toolName.indexOf(Constants.MCP_DELIMITER);\n if (delimiterIndex === -1) {\n return undefined;\n }\n return toolName.substring(delimiterIndex + Constants.MCP_DELIMITER.length);\n}\n\n/**\n * Checks if a tool belongs to a specific MCP server.\n * @param toolName - The full tool name\n * @param serverName - The server name to match\n * @returns True if the tool belongs to the specified server\n */\nfunction isFromMcpServer(toolName: string, serverName: string): boolean {\n const toolServer = extractMcpServerName(toolName);\n return toolServer === serverName;\n}\n\n/**\n * Checks if a tool belongs to any of the specified MCP servers.\n * @param toolName - The full tool name\n * @param serverNames - Array of server names to match\n * @returns True if the tool belongs to any of the specified servers\n */\nfunction isFromAnyMcpServer(toolName: string, serverNames: string[]): boolean {\n const toolServer = extractMcpServerName(toolName);\n if (toolServer === undefined) {\n return false;\n }\n return serverNames.includes(toolServer);\n}\n\n/**\n * Normalizes server filter input to always be an array.\n * @param serverFilter - String, array of strings, or undefined\n * @returns Array of server names (empty if none specified)\n */\nfunction normalizeServerFilter(\n serverFilter: string | string[] | undefined\n): string[] {\n if (serverFilter === undefined) {\n return [];\n }\n if (typeof serverFilter === 'string') {\n return serverFilter === '' ? [] : [serverFilter];\n }\n return serverFilter.filter((s) => s !== '');\n}\n\n/**\n * Extracts all unique MCP server names from a tool registry.\n * @param toolRegistry - The tool registry to scan\n * @param onlyDeferred - If true, only considers deferred tools\n * @returns Array of unique server names, sorted alphabetically\n */\nfunction getAvailableMcpServers(\n toolRegistry: t.LCToolRegistry | undefined,\n onlyDeferred: boolean = true\n): string[] {\n if (!toolRegistry) {\n return [];\n }\n\n const servers = new Set<string>();\n for (const [, toolDef] of toolRegistry) {\n if (onlyDeferred && toolDef.defer_loading !== true) {\n continue;\n }\n const server = extractMcpServerName(toolDef.name);\n if (server !== undefined && server !== '') {\n servers.add(server);\n }\n }\n\n return Array.from(servers).sort();\n}\n\n/**\n * Escapes special regex characters in a string to use as a literal pattern.\n * @param pattern - The string to escape\n * @returns The escaped string safe for use in a RegExp\n */\nfunction escapeRegexSpecialChars(pattern: string): string {\n return pattern.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Counts the maximum nesting depth of groups in a regex pattern.\n * @param pattern - The regex pattern to analyze\n * @returns The maximum nesting depth\n */\nfunction countNestedGroups(pattern: string): number {\n let maxDepth = 0;\n let currentDepth = 0;\n\n for (let i = 0; i < pattern.length; i++) {\n if (pattern[i] === '(' && (i === 0 || pattern[i - 1] !== '\\\\')) {\n currentDepth++;\n maxDepth = Math.max(maxDepth, currentDepth);\n } else if (pattern[i] === ')' && (i === 0 || pattern[i - 1] !== '\\\\')) {\n currentDepth = Math.max(0, currentDepth - 1);\n }\n }\n\n return maxDepth;\n}\n\n/**\n * Detects nested quantifiers that can cause catastrophic backtracking.\n * Patterns like (a+)+, (a*)*, (a+)*, etc.\n * @param pattern - The regex pattern to check\n * @returns True if nested quantifiers are detected\n */\nfunction hasNestedQuantifiers(pattern: string): boolean {\n const nestedQuantifierPattern = /\\([^)]*[+*][^)]*\\)[+*?]/;\n return nestedQuantifierPattern.test(pattern);\n}\n\n/**\n * Checks if a regex pattern contains potentially dangerous constructs.\n * @param pattern - The regex pattern to validate\n * @returns True if the pattern is dangerous\n */\nfunction isDangerousPattern(pattern: string): boolean {\n if (hasNestedQuantifiers(pattern)) {\n return true;\n }\n\n if (countNestedGroups(pattern) > MAX_REGEX_COMPLEXITY) {\n return true;\n }\n\n const dangerousPatterns = [\n /\\.\\{1000,\\}/, // Excessive wildcards\n /\\(\\?=\\.\\{100,\\}\\)/, // Runaway lookaheads\n /\\([^)]*\\|\\s*\\){20,}/, // Excessive alternation (rough check)\n /\\(\\.\\*\\)\\+/, // (.*)+\n /\\(\\.\\+\\)\\+/, // (.+)+\n /\\(\\.\\*\\)\\*/, // (.*)*\n /\\(\\.\\+\\)\\*/, // (.+)*\n ];\n\n for (const dangerous of dangerousPatterns) {\n if (dangerous.test(pattern)) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Sanitizes a regex pattern for safe execution.\n * If the pattern is dangerous, it will be escaped to a literal string search.\n * @param pattern - The regex pattern to sanitize\n * @returns Object containing the safe pattern and whether it was escaped\n */\nfunction sanitizeRegex(pattern: string): { safe: string; wasEscaped: boolean } {\n if (isDangerousPattern(pattern)) {\n return {\n safe: escapeRegexSpecialChars(pattern),\n wasEscaped: true,\n };\n }\n\n try {\n new RegExp(pattern);\n return { safe: pattern, wasEscaped: false };\n } catch {\n return {\n safe: escapeRegexSpecialChars(pattern),\n wasEscaped: true,\n };\n }\n}\n\n/**\n * Simplifies tool parameters for search purposes.\n * Extracts only the essential structure needed for parameter name searching.\n * @param parameters - The tool's JSON schema parameters\n * @returns Simplified parameters object\n */\nfunction simplifyParametersForSearch(\n parameters?: t.JsonSchemaType\n): t.JsonSchemaType | undefined {\n if (!parameters) {\n return undefined;\n }\n\n if (parameters.properties) {\n return {\n type: parameters.type,\n properties: Object.fromEntries(\n Object.entries(parameters.properties).map(([key, value]) => [\n key,\n { type: (value as t.JsonSchemaType).type },\n ])\n ),\n } as t.JsonSchemaType;\n }\n\n return { type: parameters.type };\n}\n\n/**\n * Tokenizes a string into lowercase words for BM25.\n * @param text - The text to tokenize\n * @returns Array of lowercase tokens\n */\nfunction tokenize(text: string): string[] {\n return text\n .toLowerCase()\n .replace(/[^a-z0-9_]/g, ' ')\n .split(/\\s+/)\n .filter((token) => token.length > 0);\n}\n\n/**\n * Creates a searchable document string from tool metadata.\n * @param tool - The tool metadata\n * @param fields - Which fields to include\n * @returns Combined document string for BM25\n */\nfunction createToolDocument(tool: t.ToolMetadata, fields: string[]): string {\n const parts: string[] = [];\n\n if (fields.includes('name')) {\n const baseName = tool.name.replace(/_/g, ' ');\n parts.push(baseName, baseName);\n }\n\n if (fields.includes('description') && tool.description) {\n parts.push(tool.description);\n }\n\n if (fields.includes('parameters') && tool.parameters?.properties) {\n const paramNames = Object.keys(tool.parameters.properties).join(' ');\n parts.push(paramNames);\n }\n\n return parts.join(' ');\n}\n\n/**\n * Determines which field had the best match for a query.\n * @param tool - The tool to check\n * @param queryTokens - Tokenized query\n * @param fields - Fields to check\n * @returns The matched field and a snippet\n */\nfunction findMatchedField(\n tool: t.ToolMetadata,\n queryTokens: string[],\n fields: string[]\n): { field: string; snippet: string } {\n if (fields.includes('name')) {\n const nameLower = tool.name.toLowerCase();\n for (const token of queryTokens) {\n if (nameLower.includes(token)) {\n return { field: 'name', snippet: tool.name };\n }\n }\n }\n\n if (fields.includes('description') && tool.description) {\n const descLower = tool.description.toLowerCase();\n for (const token of queryTokens) {\n if (descLower.includes(token)) {\n return {\n field: 'description',\n snippet: tool.description.substring(0, 100),\n };\n }\n }\n }\n\n if (fields.includes('parameters') && tool.parameters?.properties) {\n const paramNames = Object.keys(tool.parameters.properties);\n const paramLower = paramNames.join(' ').toLowerCase();\n for (const token of queryTokens) {\n if (paramLower.includes(token)) {\n return { field: 'parameters', snippet: paramNames.join(', ') };\n }\n }\n }\n\n const fallbackSnippet = tool.description\n ? tool.description.substring(0, 100)\n : tool.name;\n return { field: 'unknown', snippet: fallbackSnippet };\n}\n\n/**\n * Performs BM25-based search for better relevance ranking.\n * Uses Okapi BM25 algorithm for term frequency and document length normalization.\n * @param tools - Array of tool metadata to search\n * @param query - The search query\n * @param fields - Which fields to search\n * @param maxResults - Maximum results to return\n * @returns Search response with matching tools ranked by BM25 score\n */\nfunction performLocalSearch(\n tools: t.ToolMetadata[],\n query: string,\n fields: string[],\n maxResults: number\n): t.ToolSearchResponse {\n if (tools.length === 0 || !query.trim()) {\n return {\n tool_references: [],\n total_tools_searched: tools.length,\n pattern_used: query,\n };\n }\n\n const documents = tools.map((tool) => createToolDocument(tool, fields));\n const queryTokens = tokenize(query);\n\n if (queryTokens.length === 0) {\n return {\n tool_references: [],\n total_tools_searched: tools.length,\n pattern_used: query,\n };\n }\n\n const scores = BM25(documents, queryTokens, { k1: 1.5, b: 0.75 }) as number[];\n\n const maxScore = Math.max(...scores.filter((s) => s > 0), 1);\n\n const results: t.ToolSearchResult[] = [];\n for (let i = 0; i < tools.length; i++) {\n if (scores[i] > 0) {\n const { field, snippet } = findMatchedField(\n tools[i],\n queryTokens,\n fields\n );\n const normalizedScore = Math.min(scores[i] / maxScore, 1.0);\n\n results.push({\n tool_name: tools[i].name,\n match_score: normalizedScore,\n matched_field: field,\n snippet,\n });\n }\n }\n\n results.sort((a, b) => b.match_score - a.match_score);\n const topResults = results.slice(0, maxResults);\n\n return {\n tool_references: topResults,\n total_tools_searched: tools.length,\n pattern_used: query,\n };\n}\n\n/**\n * Generates the JavaScript search script to be executed in the sandbox.\n * Uses plain JavaScript for maximum compatibility with the Code API.\n * @param deferredTools - Array of tool metadata to search through\n * @param fields - Which fields to search\n * @param maxResults - Maximum number of results to return\n * @param sanitizedPattern - The sanitized regex pattern\n * @returns The JavaScript code string\n */\nfunction generateSearchScript(\n deferredTools: t.ToolMetadata[],\n fields: string[],\n maxResults: number,\n sanitizedPattern: string\n): string {\n const lines = [\n '// Tool definitions (injected)',\n 'var tools = ' + JSON.stringify(deferredTools) + ';',\n 'var searchFields = ' + JSON.stringify(fields) + ';',\n 'var maxResults = ' + maxResults + ';',\n 'var pattern = ' + JSON.stringify(sanitizedPattern) + ';',\n '',\n '// Compile regex (pattern is sanitized client-side)',\n 'var regex;',\n 'try {',\n ' regex = new RegExp(pattern, \\'i\\');',\n '} catch (e) {',\n ' regex = new RegExp(pattern.replace(/[.*+?^${}()[\\\\]\\\\\\\\|]/g, \"\\\\\\\\$&\"), \"i\");',\n '}',\n '',\n '// Search logic',\n 'var results = [];',\n '',\n 'for (var j = 0; j < tools.length; j++) {',\n ' var tool = tools[j];',\n ' var bestScore = 0;',\n ' var matchedField = \\'\\';',\n ' var snippet = \\'\\';',\n '',\n ' // Search name (highest priority)',\n ' if (searchFields.indexOf(\\'name\\') >= 0 && regex.test(tool.name)) {',\n ' bestScore = 0.95;',\n ' matchedField = \\'name\\';',\n ' snippet = tool.name;',\n ' }',\n '',\n ' // Search description (medium priority)',\n ' if (searchFields.indexOf(\\'description\\') >= 0 && tool.description && regex.test(tool.description)) {',\n ' if (bestScore === 0) {',\n ' bestScore = 0.75;',\n ' matchedField = \\'description\\';',\n ' snippet = tool.description.substring(0, 100);',\n ' }',\n ' }',\n '',\n ' // Search parameter names (lower priority)',\n ' if (searchFields.indexOf(\\'parameters\\') >= 0 && tool.parameters && tool.parameters.properties) {',\n ' var paramNames = Object.keys(tool.parameters.properties).join(\\' \\');',\n ' if (regex.test(paramNames)) {',\n ' if (bestScore === 0) {',\n ' bestScore = 0.60;',\n ' matchedField = \\'parameters\\';',\n ' snippet = paramNames;',\n ' }',\n ' }',\n ' }',\n '',\n ' if (bestScore > 0) {',\n ' results.push({',\n ' tool_name: tool.name,',\n ' match_score: bestScore,',\n ' matched_field: matchedField,',\n ' snippet: snippet',\n ' });',\n ' }',\n '}',\n '',\n '// Sort by score (descending) and limit results',\n 'results.sort(function(a, b) { return b.match_score - a.match_score; });',\n 'var topResults = results.slice(0, maxResults);',\n '',\n '// Output as JSON',\n 'console.log(JSON.stringify({',\n ' tool_references: topResults.map(function(r) {',\n ' return {',\n ' tool_name: r.tool_name,',\n ' match_score: r.match_score,',\n ' matched_field: r.matched_field,',\n ' snippet: r.snippet',\n ' };',\n ' }),',\n ' total_tools_searched: tools.length,',\n ' pattern_used: pattern',\n '}));',\n ];\n return lines.join('\\n');\n}\n\n/**\n * Parses the search results from stdout JSON.\n * @param stdout - The stdout string containing JSON results\n * @returns Parsed search response\n */\nfunction parseSearchResults(stdout: string): t.ToolSearchResponse {\n const jsonMatch = stdout.trim();\n const parsed = JSON.parse(jsonMatch) as t.ToolSearchResponse;\n return parsed;\n}\n\n/**\n * Formats search results as structured JSON for efficient parsing.\n * @param searchResponse - The parsed search response\n * @returns JSON string with search results\n */\nfunction formatSearchResults(searchResponse: t.ToolSearchResponse): string {\n const { tool_references, total_tools_searched, pattern_used } =\n searchResponse;\n\n const output = {\n found: tool_references.length,\n tools: tool_references.map((ref) => ({\n name: ref.tool_name,\n score: Number(ref.match_score.toFixed(2)),\n matched_in: ref.matched_field,\n snippet: ref.snippet,\n })),\n total_searched: total_tools_searched,\n query: pattern_used,\n };\n\n return JSON.stringify(output, null, 2);\n}\n\n/**\n * Extracts the base tool name (without MCP server suffix) from a full tool name.\n * @param toolName - The full tool name\n * @returns The base tool name without server suffix\n */\nfunction getBaseToolName(toolName: string): string {\n const delimiterIndex = toolName.indexOf(Constants.MCP_DELIMITER);\n if (delimiterIndex === -1) {\n return toolName;\n }\n return toolName.substring(0, delimiterIndex);\n}\n\n/**\n * Generates a compact listing of deferred tools grouped by server.\n * Format: \"server: tool1, tool2, tool3\"\n * Non-MCP tools are grouped under \"other\".\n * @param toolRegistry - The tool registry\n * @param onlyDeferred - Whether to only include deferred tools\n * @returns Formatted string with tools grouped by server\n */\nfunction getDeferredToolsListing(\n toolRegistry: t.LCToolRegistry | undefined,\n onlyDeferred: boolean\n): string {\n if (!toolRegistry) {\n return '';\n }\n\n const toolsByServer: Record<string, string[]> = {};\n\n for (const lcTool of toolRegistry.values()) {\n if (onlyDeferred && lcTool.defer_loading !== true) {\n continue;\n }\n\n const toolName = lcTool.name;\n const serverName = extractMcpServerName(toolName) ?? 'other';\n const baseName = getBaseToolName(toolName);\n\n if (!(serverName in toolsByServer)) {\n toolsByServer[serverName] = [];\n }\n toolsByServer[serverName].push(baseName);\n }\n\n const serverNames = Object.keys(toolsByServer).sort((a, b) => {\n if (a === 'other') return 1;\n if (b === 'other') return -1;\n return a.localeCompare(b);\n });\n\n if (serverNames.length === 0) {\n return '';\n }\n\n const lines = serverNames.map(\n (server) => `${server}: ${toolsByServer[server].join(', ')}`\n );\n\n return lines.join('\\n');\n}\n\n/**\n * Formats a server listing response as structured JSON.\n * NOTE: This is a PREVIEW only - tools are NOT discovered/loaded.\n * @param tools - Array of tool metadata from the server(s)\n * @param serverNames - The MCP server name(s)\n * @returns JSON string showing all tools grouped by server\n */\nfunction formatServerListing(\n tools: t.ToolMetadata[],\n serverNames: string | string[]\n): string {\n const servers = Array.isArray(serverNames) ? serverNames : [serverNames];\n\n if (tools.length === 0) {\n return JSON.stringify(\n {\n listing_mode: true,\n servers,\n total_tools: 0,\n tools_by_server: {},\n hint: 'No tools found from the specified MCP server(s).',\n },\n null,\n 2\n );\n }\n\n const toolsByServer: Record<\n string,\n Array<{ name: string; description: string }>\n > = {};\n for (const tool of tools) {\n const server = extractMcpServerName(tool.name) ?? 'unknown';\n if (!(server in toolsByServer)) {\n toolsByServer[server] = [];\n }\n toolsByServer[server].push({\n name: getBaseToolName(tool.name),\n description:\n tool.description.length > 100\n ? tool.description.substring(0, 97) + '...'\n : tool.description,\n });\n }\n\n const output = {\n listing_mode: true,\n servers,\n total_tools: tools.length,\n tools_by_server: toolsByServer,\n hint: `To use a tool, search for it by name (e.g., query: \"${getBaseToolName(tools[0]?.name ?? 'tool_name')}\") to load it.`,\n };\n\n return JSON.stringify(output, null, 2);\n}\n\n/**\n * Creates a Tool Search tool for discovering tools from a large registry.\n *\n * This tool enables AI agents to dynamically discover tools from a large library\n * without loading all tool definitions into the LLM context window. The agent\n * can search for relevant tools on-demand.\n *\n * **Modes:**\n * - `code_interpreter` (default): Uses external sandbox for regex search. Safer for complex patterns.\n * - `local`: Uses safe substring matching locally. No network call, faster, completely safe from ReDoS.\n *\n * The tool registry can be provided either:\n * 1. At initialization time via params.toolRegistry\n * 2. At runtime via config.configurable.toolRegistry when invoking\n *\n * @param params - Configuration parameters for the tool (toolRegistry is optional)\n * @returns A LangChain DynamicStructuredTool for tool searching\n *\n * @example\n * // Option 1: Code interpreter mode (regex via sandbox)\n * const tool = createToolSearch({ apiKey, toolRegistry });\n * await tool.invoke({ query: 'expense.*report' });\n *\n * @example\n * // Option 2: Local mode (safe substring search, no API key needed)\n * const tool = createToolSearch({ mode: 'local', toolRegistry });\n * await tool.invoke({ query: 'expense' });\n */\nfunction createToolSearch(\n initParams: t.ToolSearchParams = {}\n): DynamicStructuredTool<ReturnType<typeof createToolSearchSchema>> {\n const mode: t.ToolSearchMode = initParams.mode ?? 'code_interpreter';\n const defaultOnlyDeferred = initParams.onlyDeferred ?? true;\n const schema = createToolSearchSchema(mode);\n\n const apiKey: string =\n mode === 'code_interpreter'\n ? ((initParams[EnvVar.CODE_API_KEY] as string | undefined) ??\n initParams.apiKey ??\n getEnvironmentVariable(EnvVar.CODE_API_KEY) ??\n '')\n : '';\n\n if (mode === 'code_interpreter' && !apiKey) {\n throw new Error(\n 'No API key provided for tool search in code_interpreter mode. Use mode: \"local\" to search without an API key.'\n );\n }\n\n const baseEndpoint = initParams.baseUrl ?? getCodeBaseURL();\n const EXEC_ENDPOINT = `${baseEndpoint}/exec`;\n\n const deferredToolsListing = getDeferredToolsListing(\n initParams.toolRegistry,\n defaultOnlyDeferred\n );\n\n const toolsListSection =\n deferredToolsListing.length > 0\n ? `\n\nDeferred tools (search to load):\n${deferredToolsListing}`\n : '';\n\n const mcpNote =\n deferredToolsListing.includes(Constants.MCP_DELIMITER) ||\n deferredToolsListing.split('\\n').some((line) => !line.startsWith('other:'))\n ? `\n- MCP tools use format: toolName${Constants.MCP_DELIMITER}serverName\n- Use mcp_server param to filter by server`\n : '';\n\n const description =\n mode === 'local'\n ? `\nSearches deferred tools using BM25 ranking. Multi-word queries supported.\n${mcpNote}${toolsListSection}\n`.trim()\n : `\nSearches deferred tools by regex pattern.\n${mcpNote}${toolsListSection}\n`.trim();\n\n return tool<typeof schema>(\n async (params, config) => {\n const {\n query,\n fields = ['name', 'description'],\n max_results = 10,\n mcp_server,\n } = params;\n\n const {\n toolRegistry: paramToolRegistry,\n onlyDeferred: paramOnlyDeferred,\n mcpServer: paramMcpServer,\n } = config.toolCall ?? {};\n\n const toolRegistry = paramToolRegistry ?? initParams.toolRegistry;\n const onlyDeferred =\n paramOnlyDeferred !== undefined\n ? paramOnlyDeferred\n : defaultOnlyDeferred;\n const rawServerFilter =\n mcp_server ?? paramMcpServer ?? initParams.mcpServer;\n const serverFilters = normalizeServerFilter(rawServerFilter);\n const hasServerFilter = serverFilters.length > 0;\n\n if (toolRegistry == null) {\n return [\n 'Error: No tool registry provided. Configure toolRegistry at agent level or initialization.',\n {\n tool_references: [],\n metadata: {\n total_searched: 0,\n pattern: query,\n error: 'No tool registry provided',\n },\n },\n ];\n }\n\n const toolsArray: t.LCTool[] = Array.from(toolRegistry.values());\n const deferredTools: t.ToolMetadata[] = toolsArray\n .filter((lcTool) => {\n if (onlyDeferred === true && lcTool.defer_loading !== true) {\n return false;\n }\n if (\n hasServerFilter &&\n !isFromAnyMcpServer(lcTool.name, serverFilters)\n ) {\n return false;\n }\n return true;\n })\n .map((lcTool) => ({\n name: lcTool.name,\n description: lcTool.description ?? '',\n parameters: simplifyParametersForSearch(lcTool.parameters),\n }));\n\n if (deferredTools.length === 0) {\n const serverMsg = hasServerFilter\n ? ` from MCP server(s): ${serverFilters.join(', ')}`\n : '';\n return [\n `No tools available to search${serverMsg}. The tool registry is empty or no matching deferred tools are registered.`,\n {\n tool_references: [],\n metadata: {\n total_searched: 0,\n pattern: query,\n mcp_server: serverFilters,\n },\n },\n ];\n }\n\n const isServerListing = hasServerFilter && query === '';\n\n if (isServerListing) {\n const formattedOutput = formatServerListing(\n deferredTools,\n serverFilters\n );\n\n return [\n formattedOutput,\n {\n tool_references: [],\n metadata: {\n total_available: deferredTools.length,\n mcp_server: serverFilters,\n listing_mode: true,\n },\n },\n ];\n }\n\n if (mode === 'local') {\n const searchResponse = performLocalSearch(\n deferredTools,\n query,\n fields,\n max_results\n );\n const formattedOutput = formatSearchResults(searchResponse);\n\n return [\n formattedOutput,\n {\n tool_references: searchResponse.tool_references,\n metadata: {\n total_searched: searchResponse.total_tools_searched,\n pattern: searchResponse.pattern_used,\n mcp_server: serverFilters.length > 0 ? serverFilters : undefined,\n },\n },\n ];\n }\n\n const { safe: sanitizedPattern, wasEscaped } = sanitizeRegex(query);\n let warningMessage = '';\n if (wasEscaped) {\n warningMessage =\n 'Note: The provided pattern was converted to a literal search for safety.\\n\\n';\n }\n\n const searchScript = generateSearchScript(\n deferredTools,\n fields,\n max_results,\n sanitizedPattern\n );\n\n const postData = {\n lang: 'js',\n code: searchScript,\n timeout: SEARCH_TIMEOUT,\n };\n\n try {\n const fetchOptions: RequestInit = {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'LibreChat/1.0',\n 'X-API-Key': apiKey,\n },\n body: JSON.stringify(postData),\n };\n\n if (process.env.PROXY != null && process.env.PROXY !== '') {\n fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);\n }\n\n const response = await fetch(EXEC_ENDPOINT, fetchOptions);\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const result: t.ExecuteResult = await response.json();\n\n if (result.stderr && result.stderr.trim()) {\n // eslint-disable-next-line no-console\n console.warn('[ToolSearch] stderr:', result.stderr);\n }\n\n if (!result.stdout || !result.stdout.trim()) {\n return [\n `${warningMessage}No tools matched the pattern \"${sanitizedPattern}\".\\nTotal tools searched: ${deferredTools.length}`,\n {\n tool_references: [],\n metadata: {\n total_searched: deferredTools.length,\n pattern: sanitizedPattern,\n },\n },\n ];\n }\n\n const searchResponse = parseSearchResults(result.stdout);\n const formattedOutput = `${warningMessage}${formatSearchResults(searchResponse)}`;\n\n return [\n formattedOutput,\n {\n tool_references: searchResponse.tool_references,\n metadata: {\n total_searched: searchResponse.total_tools_searched,\n pattern: searchResponse.pattern_used,\n },\n },\n ];\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('[ToolSearch] Error:', error);\n\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n return [\n `Tool search failed: ${errorMessage}\\n\\nSuggestion: Try a simpler search pattern or search for specific tool names.`,\n {\n tool_references: [],\n metadata: {\n total_searched: 0,\n pattern: sanitizedPattern,\n error: errorMessage,\n },\n },\n ];\n }\n },\n {\n name: Constants.TOOL_SEARCH,\n description,\n schema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport {\n createToolSearch,\n performLocalSearch,\n extractMcpServerName,\n isFromMcpServer,\n isFromAnyMcpServer,\n normalizeServerFilter,\n getAvailableMcpServers,\n getDeferredToolsListing,\n getBaseToolName,\n formatServerListing,\n sanitizeRegex,\n escapeRegexSpecialChars,\n isDangerousPattern,\n countNestedGroups,\n hasNestedQuantifiers,\n};\n"],"names":[],"mappings":"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"}
@@ -84,13 +84,13 @@ declare function sanitizeRegex(pattern: string): {
84
84
  wasEscaped: boolean;
85
85
  };
86
86
  /**
87
- * Performs safe local substring search without regex.
88
- * Uses case-insensitive String.includes() for complete safety against ReDoS.
87
+ * Performs BM25-based search for better relevance ranking.
88
+ * Uses Okapi BM25 algorithm for term frequency and document length normalization.
89
89
  * @param tools - Array of tool metadata to search
90
- * @param query - The search term (treated as literal substring)
90
+ * @param query - The search query
91
91
  * @param fields - Which fields to search
92
92
  * @param maxResults - Maximum results to return
93
- * @returns Search response with matching tools
93
+ * @returns Search response with matching tools ranked by BM25 score
94
94
  */
95
95
  declare function performLocalSearch(tools: t.ToolMetadata[], query: string, fields: string[], maxResults: number): t.ToolSearchResponse;
96
96
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@librechat/agents",
3
- "version": "3.0.71",
3
+ "version": "3.0.72",
4
4
  "main": "./dist/cjs/main.cjs",
5
5
  "module": "./dist/esm/main.mjs",
6
6
  "types": "./dist/types/index.d.ts",
@@ -130,6 +130,7 @@
130
130
  "https-proxy-agent": "^7.0.6",
131
131
  "mathjs": "^15.1.0",
132
132
  "nanoid": "^3.3.7",
133
+ "okapibm25": "^1.4.1",
133
134
  "openai": "5.8.2"
134
135
  },
135
136
  "imports": {
@@ -156,6 +157,7 @@
156
157
  "eslint-plugin-import": "^2.31.0",
157
158
  "husky": "^9.1.7",
158
159
  "jest": "^30.2.0",
160
+ "jest-util": "^30.2.0",
159
161
  "lint-staged": "^15.2.7",
160
162
  "prettier": "^3.6.2",
161
163
  "rollup": "^4.34.6",
@@ -1,5 +1,6 @@
1
1
  // src/tools/ToolSearch.ts
2
2
  import { z } from 'zod';
3
+ import BM25 from 'okapibm25';
3
4
  import { config } from 'dotenv';
4
5
  import fetch, { RequestInit } from 'node-fetch';
5
6
  import { HttpsProxyAgent } from 'https-proxy-agent';
@@ -282,13 +283,101 @@ function simplifyParametersForSearch(
282
283
  }
283
284
 
284
285
  /**
285
- * Performs safe local substring search without regex.
286
- * Uses case-insensitive String.includes() for complete safety against ReDoS.
286
+ * Tokenizes a string into lowercase words for BM25.
287
+ * @param text - The text to tokenize
288
+ * @returns Array of lowercase tokens
289
+ */
290
+ function tokenize(text: string): string[] {
291
+ return text
292
+ .toLowerCase()
293
+ .replace(/[^a-z0-9_]/g, ' ')
294
+ .split(/\s+/)
295
+ .filter((token) => token.length > 0);
296
+ }
297
+
298
+ /**
299
+ * Creates a searchable document string from tool metadata.
300
+ * @param tool - The tool metadata
301
+ * @param fields - Which fields to include
302
+ * @returns Combined document string for BM25
303
+ */
304
+ function createToolDocument(tool: t.ToolMetadata, fields: string[]): string {
305
+ const parts: string[] = [];
306
+
307
+ if (fields.includes('name')) {
308
+ const baseName = tool.name.replace(/_/g, ' ');
309
+ parts.push(baseName, baseName);
310
+ }
311
+
312
+ if (fields.includes('description') && tool.description) {
313
+ parts.push(tool.description);
314
+ }
315
+
316
+ if (fields.includes('parameters') && tool.parameters?.properties) {
317
+ const paramNames = Object.keys(tool.parameters.properties).join(' ');
318
+ parts.push(paramNames);
319
+ }
320
+
321
+ return parts.join(' ');
322
+ }
323
+
324
+ /**
325
+ * Determines which field had the best match for a query.
326
+ * @param tool - The tool to check
327
+ * @param queryTokens - Tokenized query
328
+ * @param fields - Fields to check
329
+ * @returns The matched field and a snippet
330
+ */
331
+ function findMatchedField(
332
+ tool: t.ToolMetadata,
333
+ queryTokens: string[],
334
+ fields: string[]
335
+ ): { field: string; snippet: string } {
336
+ if (fields.includes('name')) {
337
+ const nameLower = tool.name.toLowerCase();
338
+ for (const token of queryTokens) {
339
+ if (nameLower.includes(token)) {
340
+ return { field: 'name', snippet: tool.name };
341
+ }
342
+ }
343
+ }
344
+
345
+ if (fields.includes('description') && tool.description) {
346
+ const descLower = tool.description.toLowerCase();
347
+ for (const token of queryTokens) {
348
+ if (descLower.includes(token)) {
349
+ return {
350
+ field: 'description',
351
+ snippet: tool.description.substring(0, 100),
352
+ };
353
+ }
354
+ }
355
+ }
356
+
357
+ if (fields.includes('parameters') && tool.parameters?.properties) {
358
+ const paramNames = Object.keys(tool.parameters.properties);
359
+ const paramLower = paramNames.join(' ').toLowerCase();
360
+ for (const token of queryTokens) {
361
+ if (paramLower.includes(token)) {
362
+ return { field: 'parameters', snippet: paramNames.join(', ') };
363
+ }
364
+ }
365
+ }
366
+
367
+ const fallbackSnippet = tool.description
368
+ ? tool.description.substring(0, 100)
369
+ : tool.name;
370
+ return { field: 'unknown', snippet: fallbackSnippet };
371
+ }
372
+
373
+ /**
374
+ * Performs BM25-based search for better relevance ranking.
375
+ * Uses Okapi BM25 algorithm for term frequency and document length normalization.
287
376
  * @param tools - Array of tool metadata to search
288
- * @param query - The search term (treated as literal substring)
377
+ * @param query - The search query
289
378
  * @param fields - Which fields to search
290
379
  * @param maxResults - Maximum results to return
291
- * @returns Search response with matching tools
380
+ * @returns Search response with matching tools ranked by BM25 score
292
381
  */
293
382
  function performLocalSearch(
294
383
  tools: t.ToolMetadata[],
@@ -296,50 +385,43 @@ function performLocalSearch(
296
385
  fields: string[],
297
386
  maxResults: number
298
387
  ): t.ToolSearchResponse {
299
- const lowerQuery = query.toLowerCase();
300
- const results: t.ToolSearchResult[] = [];
388
+ if (tools.length === 0 || !query.trim()) {
389
+ return {
390
+ tool_references: [],
391
+ total_tools_searched: tools.length,
392
+ pattern_used: query,
393
+ };
394
+ }
301
395
 
302
- for (const tool of tools) {
303
- let bestScore = 0;
304
- let matchedField = '';
305
- let snippet = '';
306
-
307
- if (fields.includes('name')) {
308
- const lowerName = tool.name.toLowerCase();
309
- if (lowerName.includes(lowerQuery)) {
310
- const isExactMatch = lowerName === lowerQuery;
311
- const startsWithQuery = lowerName.startsWith(lowerQuery);
312
- bestScore = isExactMatch ? 1.0 : startsWithQuery ? 0.95 : 0.85;
313
- matchedField = 'name';
314
- snippet = tool.name;
315
- }
316
- }
396
+ const documents = tools.map((tool) => createToolDocument(tool, fields));
397
+ const queryTokens = tokenize(query);
317
398
 
318
- if (fields.includes('description') && tool.description) {
319
- const lowerDesc = tool.description.toLowerCase();
320
- if (lowerDesc.includes(lowerQuery) && bestScore === 0) {
321
- bestScore = 0.7;
322
- matchedField = 'description';
323
- snippet = tool.description.substring(0, 100);
324
- }
325
- }
399
+ if (queryTokens.length === 0) {
400
+ return {
401
+ tool_references: [],
402
+ total_tools_searched: tools.length,
403
+ pattern_used: query,
404
+ };
405
+ }
326
406
 
327
- if (fields.includes('parameters') && tool.parameters?.properties) {
328
- const paramNames = Object.keys(tool.parameters.properties)
329
- .join(' ')
330
- .toLowerCase();
331
- if (paramNames.includes(lowerQuery) && bestScore === 0) {
332
- bestScore = 0.55;
333
- matchedField = 'parameters';
334
- snippet = Object.keys(tool.parameters.properties).join(' ');
335
- }
336
- }
407
+ const scores = BM25(documents, queryTokens, { k1: 1.5, b: 0.75 }) as number[];
408
+
409
+ const maxScore = Math.max(...scores.filter((s) => s > 0), 1);
410
+
411
+ const results: t.ToolSearchResult[] = [];
412
+ for (let i = 0; i < tools.length; i++) {
413
+ if (scores[i] > 0) {
414
+ const { field, snippet } = findMatchedField(
415
+ tools[i],
416
+ queryTokens,
417
+ fields
418
+ );
419
+ const normalizedScore = Math.min(scores[i] / maxScore, 1.0);
337
420
 
338
- if (bestScore > 0) {
339
421
  results.push({
340
- tool_name: tool.name,
341
- match_score: bestScore,
342
- matched_field: matchedField,
422
+ tool_name: tools[i].name,
423
+ match_score: normalizedScore,
424
+ matched_field: field,
343
425
  snippet,
344
426
  });
345
427
  }
@@ -683,7 +765,7 @@ ${deferredToolsListing}`
683
765
  const description =
684
766
  mode === 'local'
685
767
  ? `
686
- Searches deferred tools by name/description. Case-insensitive substring matching.
768
+ Searches deferred tools using BM25 ranking. Multi-word queries supported.
687
769
  ${mcpNote}${toolsListSection}
688
770
  `.trim()
689
771
  : `
@@ -281,19 +281,21 @@ describe('ToolSearch', () => {
281
281
  ];
282
282
 
283
283
  it('finds tools by exact name match', () => {
284
- const result = performLocalSearch(mockTools, 'get_weather', ['name'], 10);
284
+ // BM25 tokenizes "get weather" from "get_weather"
285
+ const result = performLocalSearch(mockTools, 'get weather', ['name'], 10);
285
286
 
286
- expect(result.tool_references.length).toBe(1);
287
+ expect(result.tool_references.length).toBeGreaterThan(0);
287
288
  expect(result.tool_references[0].tool_name).toBe('get_weather');
288
- expect(result.tool_references[0].match_score).toBe(1.0);
289
+ expect(result.tool_references[0].match_score).toBeGreaterThan(0.5);
289
290
  expect(result.tool_references[0].matched_field).toBe('name');
290
291
  });
291
292
 
292
- it('finds tools by partial name match (starts with)', () => {
293
- const result = performLocalSearch(mockTools, 'get_', ['name'], 10);
293
+ it('finds tools by partial name match', () => {
294
+ // BM25 finds tools containing "get" token
295
+ const result = performLocalSearch(mockTools, 'get', ['name'], 10);
294
296
 
295
297
  expect(result.tool_references.length).toBe(3);
296
- expect(result.tool_references[0].match_score).toBe(0.95);
298
+ expect(result.tool_references[0].match_score).toBeGreaterThan(0);
297
299
  expect(result.tool_references.map((r) => r.tool_name)).toContain(
298
300
  'get_weather'
299
301
  );
@@ -315,7 +317,7 @@ describe('ToolSearch', () => {
315
317
  expect(result.tool_references.map((r) => r.tool_name)).toContain(
316
318
  'calculate_expense_totals'
317
319
  );
318
- expect(result.tool_references[0].match_score).toBe(0.85);
320
+ expect(result.tool_references[0].match_score).toBeGreaterThan(0);
319
321
  });
320
322
 
321
323
  it('performs case-insensitive search', () => {
@@ -346,16 +348,16 @@ describe('ToolSearch', () => {
346
348
  expect(result.tool_references.length).toBe(1);
347
349
  expect(result.tool_references[0].tool_name).toBe('send_email');
348
350
  expect(result.tool_references[0].matched_field).toBe('description');
349
- expect(result.tool_references[0].match_score).toBe(0.7);
351
+ expect(result.tool_references[0].match_score).toBeGreaterThan(0);
350
352
  });
351
353
 
352
354
  it('searches in parameter names', () => {
353
355
  const result = performLocalSearch(mockTools, 'query', ['parameters'], 10);
354
356
 
355
- expect(result.tool_references.length).toBe(1);
357
+ expect(result.tool_references.length).toBeGreaterThan(0);
356
358
  expect(result.tool_references[0].tool_name).toBe('run_database_query');
357
359
  expect(result.tool_references[0].matched_field).toBe('parameters');
358
- expect(result.tool_references[0].match_score).toBe(0.55);
360
+ expect(result.tool_references[0].match_score).toBeGreaterThan(0);
359
361
  });
360
362
 
361
363
  it('prioritizes name matches over description matches', () => {
@@ -424,7 +426,9 @@ describe('ToolSearch', () => {
424
426
  it('handles empty query gracefully', () => {
425
427
  const result = performLocalSearch(mockTools, '', ['name'], 10);
426
428
 
427
- expect(result.tool_references.length).toBe(mockTools.length);
429
+ // BM25 correctly returns no results for empty queries (no terms to match)
430
+ expect(result.tool_references.length).toBe(0);
431
+ expect(result.total_tools_searched).toBe(mockTools.length);
428
432
  });
429
433
 
430
434
  it('includes correct metadata in response', () => {
@@ -793,8 +797,9 @@ describe('ToolSearch', () => {
793
797
  );
794
798
  });
795
799
 
796
- it('can search for tools by MCP delimiter', () => {
797
- const result = performLocalSearch(mcpTools, '_mcp_', ['name'], 10);
800
+ it('can search for tools by MCP keyword', () => {
801
+ // BM25 tokenizes queries, so search for "mcp" to find MCP tools
802
+ const result = performLocalSearch(mcpTools, 'mcp', ['name'], 10);
798
803
 
799
804
  expect(result.tool_references.length).toBe(4);
800
805
  expect(result.tool_references.map((r) => r.tool_name)).not.toContain(