@librechat/agents 3.0.68 → 3.0.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/main.cjs +1 -0
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/tools/ToolSearch.cjs +68 -47
- package/dist/cjs/tools/ToolSearch.cjs.map +1 -1
- package/dist/esm/main.mjs +1 -1
- package/dist/esm/tools/ToolSearch.mjs +68 -48
- package/dist/esm/tools/ToolSearch.mjs.map +1 -1
- package/dist/types/tools/ToolSearch.d.ts +10 -4
- package/package.json +1 -1
- package/src/tools/ToolSearch.ts +88 -55
- package/src/tools/__tests__/ToolSearch.test.ts +121 -26
package/dist/cjs/main.cjs
CHANGED
|
@@ -92,6 +92,7 @@ exports.createToolSearch = ToolSearch.createToolSearch;
|
|
|
92
92
|
exports.escapeRegexSpecialChars = ToolSearch.escapeRegexSpecialChars;
|
|
93
93
|
exports.extractMcpServerName = ToolSearch.extractMcpServerName;
|
|
94
94
|
exports.formatServerListing = ToolSearch.formatServerListing;
|
|
95
|
+
exports.getAvailableMcpServers = ToolSearch.getAvailableMcpServers;
|
|
95
96
|
exports.getBaseToolName = ToolSearch.getBaseToolName;
|
|
96
97
|
exports.hasNestedQuantifiers = ToolSearch.hasNestedQuantifiers;
|
|
97
98
|
exports.isDangerousPattern = ToolSearch.isDangerousPattern;
|
package/dist/cjs/main.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"main.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -102,6 +102,28 @@ function normalizeServerFilter(serverFilter) {
|
|
|
102
102
|
}
|
|
103
103
|
return serverFilter.filter((s) => s !== '');
|
|
104
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Extracts all unique MCP server names from a tool registry.
|
|
107
|
+
* @param toolRegistry - The tool registry to scan
|
|
108
|
+
* @param onlyDeferred - If true, only considers deferred tools
|
|
109
|
+
* @returns Array of unique server names, sorted alphabetically
|
|
110
|
+
*/
|
|
111
|
+
function getAvailableMcpServers(toolRegistry, onlyDeferred = true) {
|
|
112
|
+
if (!toolRegistry) {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
const servers = new Set();
|
|
116
|
+
for (const [, toolDef] of toolRegistry) {
|
|
117
|
+
if (onlyDeferred && toolDef.defer_loading !== true) {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const server = extractMcpServerName(toolDef.name);
|
|
121
|
+
if (server !== undefined && server !== '') {
|
|
122
|
+
servers.add(server);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return Array.from(servers).sort();
|
|
126
|
+
}
|
|
105
127
|
/**
|
|
106
128
|
* Escapes special regex characters in a string to use as a literal pattern.
|
|
107
129
|
* @param pattern - The string to escape
|
|
@@ -376,24 +398,24 @@ function parseSearchResults(stdout) {
|
|
|
376
398
|
return parsed;
|
|
377
399
|
}
|
|
378
400
|
/**
|
|
379
|
-
* Formats search results
|
|
401
|
+
* Formats search results as structured JSON for efficient parsing.
|
|
380
402
|
* @param searchResponse - The parsed search response
|
|
381
|
-
* @returns
|
|
403
|
+
* @returns JSON string with search results
|
|
382
404
|
*/
|
|
383
405
|
function formatSearchResults(searchResponse) {
|
|
384
406
|
const { tool_references, total_tools_searched, pattern_used } = searchResponse;
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
return
|
|
407
|
+
const output = {
|
|
408
|
+
found: tool_references.length,
|
|
409
|
+
tools: tool_references.map((ref) => ({
|
|
410
|
+
name: ref.tool_name,
|
|
411
|
+
score: Number(ref.match_score.toFixed(2)),
|
|
412
|
+
matched_in: ref.matched_field,
|
|
413
|
+
snippet: ref.snippet,
|
|
414
|
+
})),
|
|
415
|
+
total_searched: total_tools_searched,
|
|
416
|
+
query: pattern_used,
|
|
417
|
+
};
|
|
418
|
+
return JSON.stringify(output, null, 2);
|
|
397
419
|
}
|
|
398
420
|
/**
|
|
399
421
|
* Extracts the base tool name (without MCP server suffix) from a full tool name.
|
|
@@ -408,50 +430,44 @@ function getBaseToolName(toolName) {
|
|
|
408
430
|
return toolName.substring(0, delimiterIndex);
|
|
409
431
|
}
|
|
410
432
|
/**
|
|
411
|
-
* Formats a server listing response
|
|
412
|
-
* Provides a cohesive view of all tools grouped by server.
|
|
433
|
+
* Formats a server listing response as structured JSON.
|
|
413
434
|
* NOTE: This is a PREVIEW only - tools are NOT discovered/loaded.
|
|
414
435
|
* @param tools - Array of tool metadata from the server(s)
|
|
415
436
|
* @param serverNames - The MCP server name(s)
|
|
416
|
-
* @returns
|
|
437
|
+
* @returns JSON string showing all tools grouped by server
|
|
417
438
|
*/
|
|
418
439
|
function formatServerListing(tools, serverNames) {
|
|
419
440
|
const servers = Array.isArray(serverNames) ? serverNames : [serverNames];
|
|
420
441
|
if (tools.length === 0) {
|
|
421
|
-
return
|
|
442
|
+
return JSON.stringify({
|
|
443
|
+
listing_mode: true,
|
|
444
|
+
servers,
|
|
445
|
+
total_tools: 0,
|
|
446
|
+
tools_by_server: {},
|
|
447
|
+
hint: 'No tools found from the specified MCP server(s).',
|
|
448
|
+
}, null, 2);
|
|
422
449
|
}
|
|
423
|
-
const toolsByServer =
|
|
450
|
+
const toolsByServer = {};
|
|
424
451
|
for (const tool of tools) {
|
|
425
452
|
const server = extractMcpServerName(tool.name) ?? 'unknown';
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
toolsByServer.set(server, existing);
|
|
429
|
-
}
|
|
430
|
-
let response = servers.length === 1
|
|
431
|
-
? `## Tools from MCP server: ${servers[0]}\n\n`
|
|
432
|
-
: `## Tools from MCP servers: ${servers.join(', ')}\n\n`;
|
|
433
|
-
response += `Found ${tools.length} tool(s) (preview only - not yet loaded):\n\n`;
|
|
434
|
-
for (const [server, serverTools] of toolsByServer) {
|
|
435
|
-
if (servers.length > 1) {
|
|
436
|
-
response += `### ${server}\n\n`;
|
|
437
|
-
}
|
|
438
|
-
for (const tool of serverTools) {
|
|
439
|
-
const baseName = getBaseToolName(tool.name);
|
|
440
|
-
response += `- **${baseName}**`;
|
|
441
|
-
if (tool.description) {
|
|
442
|
-
const shortDesc = tool.description.length > 80
|
|
443
|
-
? tool.description.substring(0, 77) + '...'
|
|
444
|
-
: tool.description;
|
|
445
|
-
response += `: ${shortDesc}`;
|
|
446
|
-
}
|
|
447
|
-
response += '\n';
|
|
448
|
-
}
|
|
449
|
-
if (servers.length > 1) {
|
|
450
|
-
response += '\n';
|
|
453
|
+
if (!(server in toolsByServer)) {
|
|
454
|
+
toolsByServer[server] = [];
|
|
451
455
|
}
|
|
456
|
+
toolsByServer[server].push({
|
|
457
|
+
name: getBaseToolName(tool.name),
|
|
458
|
+
description: tool.description.length > 100
|
|
459
|
+
? tool.description.substring(0, 97) + '...'
|
|
460
|
+
: tool.description,
|
|
461
|
+
});
|
|
452
462
|
}
|
|
453
|
-
|
|
454
|
-
|
|
463
|
+
const output = {
|
|
464
|
+
listing_mode: true,
|
|
465
|
+
servers,
|
|
466
|
+
total_tools: tools.length,
|
|
467
|
+
tools_by_server: toolsByServer,
|
|
468
|
+
hint: `To use a tool, search for it by name (e.g., query: "${getBaseToolName(tools[0]?.name ?? 'tool_name')}") to load it.`,
|
|
469
|
+
};
|
|
470
|
+
return JSON.stringify(output, null, 2);
|
|
455
471
|
}
|
|
456
472
|
/**
|
|
457
473
|
* Creates a Tool Search tool for discovering tools from a large registry.
|
|
@@ -496,13 +512,17 @@ function createToolSearch(initParams = {}) {
|
|
|
496
512
|
}
|
|
497
513
|
const baseEndpoint = initParams.baseUrl ?? CodeExecutor.getCodeBaseURL();
|
|
498
514
|
const EXEC_ENDPOINT = `${baseEndpoint}/exec`;
|
|
515
|
+
const availableServers = getAvailableMcpServers(initParams.toolRegistry, defaultOnlyDeferred);
|
|
516
|
+
const serverListText = availableServers.length > 0
|
|
517
|
+
? `\n- Available MCP servers: ${availableServers.join(', ')}`
|
|
518
|
+
: '';
|
|
499
519
|
const mcpInstructions = `
|
|
500
520
|
|
|
501
521
|
MCP Server Tools:
|
|
502
522
|
- Tools from MCP servers follow the naming convention: toolName${_enum.Constants.MCP_DELIMITER}serverName
|
|
503
523
|
- Example: "get_weather${_enum.Constants.MCP_DELIMITER}weather-api" is the "get_weather" tool from the "weather-api" server
|
|
504
524
|
- Use mcp_server parameter to filter by server (e.g., mcp_server: "weather-api")
|
|
505
|
-
- If mcp_server is provided without a query, lists ALL tools from that server`;
|
|
525
|
+
- If mcp_server is provided without a query, lists ALL tools from that server${serverListText}`;
|
|
506
526
|
const description = mode === 'local'
|
|
507
527
|
? `
|
|
508
528
|
Searches through available tools to find ones matching your search term.
|
|
@@ -699,6 +719,7 @@ exports.createToolSearch = createToolSearch;
|
|
|
699
719
|
exports.escapeRegexSpecialChars = escapeRegexSpecialChars;
|
|
700
720
|
exports.extractMcpServerName = extractMcpServerName;
|
|
701
721
|
exports.formatServerListing = formatServerListing;
|
|
722
|
+
exports.getAvailableMcpServers = getAvailableMcpServers;
|
|
702
723
|
exports.getBaseToolName = getBaseToolName;
|
|
703
724
|
exports.hasNestedQuantifiers = hasNestedQuantifiers;
|
|
704
725
|
exports.isDangerousPattern = isDangerousPattern;
|
|
@@ -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 * 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 into a human-readable string.\n * @param searchResponse - The parsed search response\n * @returns Formatted string for LLM consumption\n */\nfunction formatSearchResults(searchResponse: t.ToolSearchResponse): string {\n const { tool_references, total_tools_searched, pattern_used } =\n searchResponse;\n\n if (tool_references.length === 0) {\n return `No tools matched the pattern \"${pattern_used}\".\\nTotal tools searched: ${total_tools_searched}`;\n }\n\n let response = `Found ${tool_references.length} matching tools:\\n\\n`;\n\n for (const ref of tool_references) {\n response += `- ${ref.tool_name} (score: ${ref.match_score.toFixed(2)})\\n`;\n response += ` Matched in: ${ref.matched_field}\\n`;\n response += ` Snippet: ${ref.snippet}\\n\\n`;\n }\n\n response += `Total tools searched: ${total_tools_searched}\\n`;\n response += `Pattern used: ${pattern_used}`;\n\n return response;\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 * Formats a server listing response when listing all tools from MCP server(s).\n * Provides a cohesive view of all tools grouped by server.\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 Formatted string showing all tools from the server(s)\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 `No tools found from MCP server(s): ${servers.join(', ')}.`;\n }\n\n const toolsByServer = new Map<string, t.ToolMetadata[]>();\n for (const tool of tools) {\n const server = extractMcpServerName(tool.name) ?? 'unknown';\n const existing = toolsByServer.get(server) ?? [];\n existing.push(tool);\n toolsByServer.set(server, existing);\n }\n\n let response =\n servers.length === 1\n ? `## Tools from MCP server: ${servers[0]}\\n\\n`\n : `## Tools from MCP servers: ${servers.join(', ')}\\n\\n`;\n\n response += `Found ${tools.length} tool(s) (preview only - not yet loaded):\\n\\n`;\n\n for (const [server, serverTools] of toolsByServer) {\n if (servers.length > 1) {\n response += `### ${server}\\n\\n`;\n }\n for (const tool of serverTools) {\n const baseName = getBaseToolName(tool.name);\n response += `- **${baseName}**`;\n if (tool.description) {\n const shortDesc =\n tool.description.length > 80\n ? tool.description.substring(0, 77) + '...'\n : tool.description;\n response += `: ${shortDesc}`;\n }\n response += '\\n';\n }\n if (servers.length > 1) {\n response += '\\n';\n }\n }\n\n response += `\\n_To use a tool, search for it by name (e.g., query: \"${getBaseToolName(tools[0]?.name ?? 'tool_name')}\") to load it._`;\n\n return response;\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 mcpInstructions = `\n\nMCP Server Tools:\n- Tools from MCP servers follow the naming convention: toolName${Constants.MCP_DELIMITER}serverName\n- Example: \"get_weather${Constants.MCP_DELIMITER}weather-api\" is the \"get_weather\" tool from the \"weather-api\" server\n- Use mcp_server parameter to filter by server (e.g., mcp_server: \"weather-api\")\n- If mcp_server is provided without a query, lists ALL tools from that server`;\n\n const description =\n mode === 'local'\n ? `\nSearches through available tools to find ones matching your search term.\n\nUsage:\n- Provide a search term to find in tool names and descriptions.\n- Uses case-insensitive substring matching (fast and safe).\n- Use this when you need to discover tools for a specific task.\n- Results include tool names, match quality scores, and snippets showing where the match occurred.\n- Higher scores (0.95+) indicate name matches, medium scores (0.70+) indicate description matches.\n${mcpInstructions}\n`.trim()\n : `\nSearches through available tools to find ones matching your query pattern.\n\nUsage:\n- Provide a regex pattern to search tool names and descriptions.\n- Use this when you need to discover tools for a specific task.\n- Results include tool names, match quality scores, and snippets showing where the match occurred.\n- Higher scores (0.9+) indicate name matches, medium scores (0.7+) indicate description matches.\n${mcpInstructions}\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 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 { 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 * 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 availableServers = getAvailableMcpServers(\n initParams.toolRegistry,\n defaultOnlyDeferred\n );\n\n const serverListText =\n availableServers.length > 0\n ? `\\n- Available MCP servers: ${availableServers.join(', ')}`\n : '';\n\n const mcpInstructions = `\n\nMCP Server Tools:\n- Tools from MCP servers follow the naming convention: toolName${Constants.MCP_DELIMITER}serverName\n- Example: \"get_weather${Constants.MCP_DELIMITER}weather-api\" is the \"get_weather\" tool from the \"weather-api\" server\n- Use mcp_server parameter to filter by server (e.g., mcp_server: \"weather-api\")\n- If mcp_server is provided without a query, lists ALL tools from that server${serverListText}`;\n\n const description =\n mode === 'local'\n ? `\nSearches through available tools to find ones matching your search term.\n\nUsage:\n- Provide a search term to find in tool names and descriptions.\n- Uses case-insensitive substring matching (fast and safe).\n- Use this when you need to discover tools for a specific task.\n- Results include tool names, match quality scores, and snippets showing where the match occurred.\n- Higher scores (0.95+) indicate name matches, medium scores (0.70+) indicate description matches.\n${mcpInstructions}\n`.trim()\n : `\nSearches through available tools to find ones matching your query pattern.\n\nUsage:\n- Provide a regex pattern to search tool names and descriptions.\n- Use this when you need to discover tools for a specific task.\n- Results include tool names, match quality scores, and snippets showing where the match occurred.\n- Higher scores (0.9+) indicate name matches, medium scores (0.7+) indicate description matches.\n${mcpInstructions}\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 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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"}
|
package/dist/esm/main.mjs
CHANGED
|
@@ -14,7 +14,7 @@ export { MultiAgentGraph } from './graphs/MultiAgentGraph.mjs';
|
|
|
14
14
|
export { Calculator } from './tools/Calculator.mjs';
|
|
15
15
|
export { createCodeExecutionTool, getCodeBaseURL, imageExtRegex } from './tools/CodeExecutor.mjs';
|
|
16
16
|
export { createProgrammaticToolCallingTool, executeTools, extractUsedToolNames, fetchSessionFiles, filterToolsByUsage, formatCompletedResponse, makeRequest, normalizeToPythonIdentifier, unwrapToolResponse } from './tools/ProgrammaticToolCalling.mjs';
|
|
17
|
-
export { countNestedGroups, createToolSearch, escapeRegexSpecialChars, extractMcpServerName, formatServerListing, getBaseToolName, hasNestedQuantifiers, isDangerousPattern, isFromAnyMcpServer, isFromMcpServer, normalizeServerFilter, performLocalSearch, sanitizeRegex } from './tools/ToolSearch.mjs';
|
|
17
|
+
export { countNestedGroups, createToolSearch, escapeRegexSpecialChars, extractMcpServerName, formatServerListing, getAvailableMcpServers, getBaseToolName, hasNestedQuantifiers, isDangerousPattern, isFromAnyMcpServer, isFromMcpServer, normalizeServerFilter, performLocalSearch, sanitizeRegex } from './tools/ToolSearch.mjs';
|
|
18
18
|
export { handleServerToolResult, handleToolCallChunks, handleToolCalls, toolResultTypes } from './tools/handlers.mjs';
|
|
19
19
|
export { createSearchTool } from './tools/search/tool.mjs';
|
|
20
20
|
export { Callback, CommonEvents, Constants, ContentTypes, EnvVar, GraphEvents, GraphNodeActions, GraphNodeKeys, Providers, StepTypes, TitleMethod, ToolCallTypes } from './common/enum.mjs';
|
|
@@ -100,6 +100,28 @@ function normalizeServerFilter(serverFilter) {
|
|
|
100
100
|
}
|
|
101
101
|
return serverFilter.filter((s) => s !== '');
|
|
102
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Extracts all unique MCP server names from a tool registry.
|
|
105
|
+
* @param toolRegistry - The tool registry to scan
|
|
106
|
+
* @param onlyDeferred - If true, only considers deferred tools
|
|
107
|
+
* @returns Array of unique server names, sorted alphabetically
|
|
108
|
+
*/
|
|
109
|
+
function getAvailableMcpServers(toolRegistry, onlyDeferred = true) {
|
|
110
|
+
if (!toolRegistry) {
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
const servers = new Set();
|
|
114
|
+
for (const [, toolDef] of toolRegistry) {
|
|
115
|
+
if (onlyDeferred && toolDef.defer_loading !== true) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const server = extractMcpServerName(toolDef.name);
|
|
119
|
+
if (server !== undefined && server !== '') {
|
|
120
|
+
servers.add(server);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return Array.from(servers).sort();
|
|
124
|
+
}
|
|
103
125
|
/**
|
|
104
126
|
* Escapes special regex characters in a string to use as a literal pattern.
|
|
105
127
|
* @param pattern - The string to escape
|
|
@@ -374,24 +396,24 @@ function parseSearchResults(stdout) {
|
|
|
374
396
|
return parsed;
|
|
375
397
|
}
|
|
376
398
|
/**
|
|
377
|
-
* Formats search results
|
|
399
|
+
* Formats search results as structured JSON for efficient parsing.
|
|
378
400
|
* @param searchResponse - The parsed search response
|
|
379
|
-
* @returns
|
|
401
|
+
* @returns JSON string with search results
|
|
380
402
|
*/
|
|
381
403
|
function formatSearchResults(searchResponse) {
|
|
382
404
|
const { tool_references, total_tools_searched, pattern_used } = searchResponse;
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
return
|
|
405
|
+
const output = {
|
|
406
|
+
found: tool_references.length,
|
|
407
|
+
tools: tool_references.map((ref) => ({
|
|
408
|
+
name: ref.tool_name,
|
|
409
|
+
score: Number(ref.match_score.toFixed(2)),
|
|
410
|
+
matched_in: ref.matched_field,
|
|
411
|
+
snippet: ref.snippet,
|
|
412
|
+
})),
|
|
413
|
+
total_searched: total_tools_searched,
|
|
414
|
+
query: pattern_used,
|
|
415
|
+
};
|
|
416
|
+
return JSON.stringify(output, null, 2);
|
|
395
417
|
}
|
|
396
418
|
/**
|
|
397
419
|
* Extracts the base tool name (without MCP server suffix) from a full tool name.
|
|
@@ -406,50 +428,44 @@ function getBaseToolName(toolName) {
|
|
|
406
428
|
return toolName.substring(0, delimiterIndex);
|
|
407
429
|
}
|
|
408
430
|
/**
|
|
409
|
-
* Formats a server listing response
|
|
410
|
-
* Provides a cohesive view of all tools grouped by server.
|
|
431
|
+
* Formats a server listing response as structured JSON.
|
|
411
432
|
* NOTE: This is a PREVIEW only - tools are NOT discovered/loaded.
|
|
412
433
|
* @param tools - Array of tool metadata from the server(s)
|
|
413
434
|
* @param serverNames - The MCP server name(s)
|
|
414
|
-
* @returns
|
|
435
|
+
* @returns JSON string showing all tools grouped by server
|
|
415
436
|
*/
|
|
416
437
|
function formatServerListing(tools, serverNames) {
|
|
417
438
|
const servers = Array.isArray(serverNames) ? serverNames : [serverNames];
|
|
418
439
|
if (tools.length === 0) {
|
|
419
|
-
return
|
|
440
|
+
return JSON.stringify({
|
|
441
|
+
listing_mode: true,
|
|
442
|
+
servers,
|
|
443
|
+
total_tools: 0,
|
|
444
|
+
tools_by_server: {},
|
|
445
|
+
hint: 'No tools found from the specified MCP server(s).',
|
|
446
|
+
}, null, 2);
|
|
420
447
|
}
|
|
421
|
-
const toolsByServer =
|
|
448
|
+
const toolsByServer = {};
|
|
422
449
|
for (const tool of tools) {
|
|
423
450
|
const server = extractMcpServerName(tool.name) ?? 'unknown';
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
toolsByServer.set(server, existing);
|
|
427
|
-
}
|
|
428
|
-
let response = servers.length === 1
|
|
429
|
-
? `## Tools from MCP server: ${servers[0]}\n\n`
|
|
430
|
-
: `## Tools from MCP servers: ${servers.join(', ')}\n\n`;
|
|
431
|
-
response += `Found ${tools.length} tool(s) (preview only - not yet loaded):\n\n`;
|
|
432
|
-
for (const [server, serverTools] of toolsByServer) {
|
|
433
|
-
if (servers.length > 1) {
|
|
434
|
-
response += `### ${server}\n\n`;
|
|
435
|
-
}
|
|
436
|
-
for (const tool of serverTools) {
|
|
437
|
-
const baseName = getBaseToolName(tool.name);
|
|
438
|
-
response += `- **${baseName}**`;
|
|
439
|
-
if (tool.description) {
|
|
440
|
-
const shortDesc = tool.description.length > 80
|
|
441
|
-
? tool.description.substring(0, 77) + '...'
|
|
442
|
-
: tool.description;
|
|
443
|
-
response += `: ${shortDesc}`;
|
|
444
|
-
}
|
|
445
|
-
response += '\n';
|
|
446
|
-
}
|
|
447
|
-
if (servers.length > 1) {
|
|
448
|
-
response += '\n';
|
|
451
|
+
if (!(server in toolsByServer)) {
|
|
452
|
+
toolsByServer[server] = [];
|
|
449
453
|
}
|
|
454
|
+
toolsByServer[server].push({
|
|
455
|
+
name: getBaseToolName(tool.name),
|
|
456
|
+
description: tool.description.length > 100
|
|
457
|
+
? tool.description.substring(0, 97) + '...'
|
|
458
|
+
: tool.description,
|
|
459
|
+
});
|
|
450
460
|
}
|
|
451
|
-
|
|
452
|
-
|
|
461
|
+
const output = {
|
|
462
|
+
listing_mode: true,
|
|
463
|
+
servers,
|
|
464
|
+
total_tools: tools.length,
|
|
465
|
+
tools_by_server: toolsByServer,
|
|
466
|
+
hint: `To use a tool, search for it by name (e.g., query: "${getBaseToolName(tools[0]?.name ?? 'tool_name')}") to load it.`,
|
|
467
|
+
};
|
|
468
|
+
return JSON.stringify(output, null, 2);
|
|
453
469
|
}
|
|
454
470
|
/**
|
|
455
471
|
* Creates a Tool Search tool for discovering tools from a large registry.
|
|
@@ -494,13 +510,17 @@ function createToolSearch(initParams = {}) {
|
|
|
494
510
|
}
|
|
495
511
|
const baseEndpoint = initParams.baseUrl ?? getCodeBaseURL();
|
|
496
512
|
const EXEC_ENDPOINT = `${baseEndpoint}/exec`;
|
|
513
|
+
const availableServers = getAvailableMcpServers(initParams.toolRegistry, defaultOnlyDeferred);
|
|
514
|
+
const serverListText = availableServers.length > 0
|
|
515
|
+
? `\n- Available MCP servers: ${availableServers.join(', ')}`
|
|
516
|
+
: '';
|
|
497
517
|
const mcpInstructions = `
|
|
498
518
|
|
|
499
519
|
MCP Server Tools:
|
|
500
520
|
- Tools from MCP servers follow the naming convention: toolName${Constants.MCP_DELIMITER}serverName
|
|
501
521
|
- Example: "get_weather${Constants.MCP_DELIMITER}weather-api" is the "get_weather" tool from the "weather-api" server
|
|
502
522
|
- Use mcp_server parameter to filter by server (e.g., mcp_server: "weather-api")
|
|
503
|
-
- If mcp_server is provided without a query, lists ALL tools from that server`;
|
|
523
|
+
- If mcp_server is provided without a query, lists ALL tools from that server${serverListText}`;
|
|
504
524
|
const description = mode === 'local'
|
|
505
525
|
? `
|
|
506
526
|
Searches through available tools to find ones matching your search term.
|
|
@@ -692,5 +712,5 @@ ${mcpInstructions}
|
|
|
692
712
|
});
|
|
693
713
|
}
|
|
694
714
|
|
|
695
|
-
export { countNestedGroups, createToolSearch, escapeRegexSpecialChars, extractMcpServerName, formatServerListing, getBaseToolName, hasNestedQuantifiers, isDangerousPattern, isFromAnyMcpServer, isFromMcpServer, normalizeServerFilter, performLocalSearch, sanitizeRegex };
|
|
715
|
+
export { countNestedGroups, createToolSearch, escapeRegexSpecialChars, extractMcpServerName, formatServerListing, getAvailableMcpServers, getBaseToolName, hasNestedQuantifiers, isDangerousPattern, isFromAnyMcpServer, isFromMcpServer, normalizeServerFilter, performLocalSearch, sanitizeRegex };
|
|
696
716
|
//# sourceMappingURL=ToolSearch.mjs.map
|
|
@@ -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 * 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 into a human-readable string.\n * @param searchResponse - The parsed search response\n * @returns Formatted string for LLM consumption\n */\nfunction formatSearchResults(searchResponse: t.ToolSearchResponse): string {\n const { tool_references, total_tools_searched, pattern_used } =\n searchResponse;\n\n if (tool_references.length === 0) {\n return `No tools matched the pattern \"${pattern_used}\".\\nTotal tools searched: ${total_tools_searched}`;\n }\n\n let response = `Found ${tool_references.length} matching tools:\\n\\n`;\n\n for (const ref of tool_references) {\n response += `- ${ref.tool_name} (score: ${ref.match_score.toFixed(2)})\\n`;\n response += ` Matched in: ${ref.matched_field}\\n`;\n response += ` Snippet: ${ref.snippet}\\n\\n`;\n }\n\n response += `Total tools searched: ${total_tools_searched}\\n`;\n response += `Pattern used: ${pattern_used}`;\n\n return response;\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 * Formats a server listing response when listing all tools from MCP server(s).\n * Provides a cohesive view of all tools grouped by server.\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 Formatted string showing all tools from the server(s)\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 `No tools found from MCP server(s): ${servers.join(', ')}.`;\n }\n\n const toolsByServer = new Map<string, t.ToolMetadata[]>();\n for (const tool of tools) {\n const server = extractMcpServerName(tool.name) ?? 'unknown';\n const existing = toolsByServer.get(server) ?? [];\n existing.push(tool);\n toolsByServer.set(server, existing);\n }\n\n let response =\n servers.length === 1\n ? `## Tools from MCP server: ${servers[0]}\\n\\n`\n : `## Tools from MCP servers: ${servers.join(', ')}\\n\\n`;\n\n response += `Found ${tools.length} tool(s) (preview only - not yet loaded):\\n\\n`;\n\n for (const [server, serverTools] of toolsByServer) {\n if (servers.length > 1) {\n response += `### ${server}\\n\\n`;\n }\n for (const tool of serverTools) {\n const baseName = getBaseToolName(tool.name);\n response += `- **${baseName}**`;\n if (tool.description) {\n const shortDesc =\n tool.description.length > 80\n ? tool.description.substring(0, 77) + '...'\n : tool.description;\n response += `: ${shortDesc}`;\n }\n response += '\\n';\n }\n if (servers.length > 1) {\n response += '\\n';\n }\n }\n\n response += `\\n_To use a tool, search for it by name (e.g., query: \"${getBaseToolName(tools[0]?.name ?? 'tool_name')}\") to load it._`;\n\n return response;\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 mcpInstructions = `\n\nMCP Server Tools:\n- Tools from MCP servers follow the naming convention: toolName${Constants.MCP_DELIMITER}serverName\n- Example: \"get_weather${Constants.MCP_DELIMITER}weather-api\" is the \"get_weather\" tool from the \"weather-api\" server\n- Use mcp_server parameter to filter by server (e.g., mcp_server: \"weather-api\")\n- If mcp_server is provided without a query, lists ALL tools from that server`;\n\n const description =\n mode === 'local'\n ? `\nSearches through available tools to find ones matching your search term.\n\nUsage:\n- Provide a search term to find in tool names and descriptions.\n- Uses case-insensitive substring matching (fast and safe).\n- Use this when you need to discover tools for a specific task.\n- Results include tool names, match quality scores, and snippets showing where the match occurred.\n- Higher scores (0.95+) indicate name matches, medium scores (0.70+) indicate description matches.\n${mcpInstructions}\n`.trim()\n : `\nSearches through available tools to find ones matching your query pattern.\n\nUsage:\n- Provide a regex pattern to search tool names and descriptions.\n- Use this when you need to discover tools for a specific task.\n- Results include tool names, match quality scores, and snippets showing where the match occurred.\n- Higher scores (0.9+) indicate name matches, medium scores (0.7+) indicate description matches.\n${mcpInstructions}\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 getBaseToolName,\n formatServerListing,\n sanitizeRegex,\n escapeRegexSpecialChars,\n isDangerousPattern,\n countNestedGroups,\n hasNestedQuantifiers,\n};\n"],"names":[],"mappings":"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"}
|
|
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 * 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 availableServers = getAvailableMcpServers(\n initParams.toolRegistry,\n defaultOnlyDeferred\n );\n\n const serverListText =\n availableServers.length > 0\n ? `\\n- Available MCP servers: ${availableServers.join(', ')}`\n : '';\n\n const mcpInstructions = `\n\nMCP Server Tools:\n- Tools from MCP servers follow the naming convention: toolName${Constants.MCP_DELIMITER}serverName\n- Example: \"get_weather${Constants.MCP_DELIMITER}weather-api\" is the \"get_weather\" tool from the \"weather-api\" server\n- Use mcp_server parameter to filter by server (e.g., mcp_server: \"weather-api\")\n- If mcp_server is provided without a query, lists ALL tools from that server${serverListText}`;\n\n const description =\n mode === 'local'\n ? `\nSearches through available tools to find ones matching your search term.\n\nUsage:\n- Provide a search term to find in tool names and descriptions.\n- Uses case-insensitive substring matching (fast and safe).\n- Use this when you need to discover tools for a specific task.\n- Results include tool names, match quality scores, and snippets showing where the match occurred.\n- Higher scores (0.95+) indicate name matches, medium scores (0.70+) indicate description matches.\n${mcpInstructions}\n`.trim()\n : `\nSearches through available tools to find ones matching your query pattern.\n\nUsage:\n- Provide a regex pattern to search tool names and descriptions.\n- Use this when you need to discover tools for a specific task.\n- Results include tool names, match quality scores, and snippets showing where the match occurred.\n- Higher scores (0.9+) indicate name matches, medium scores (0.7+) indicate description matches.\n${mcpInstructions}\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 getBaseToolName,\n formatServerListing,\n sanitizeRegex,\n escapeRegexSpecialChars,\n isDangerousPattern,\n countNestedGroups,\n hasNestedQuantifiers,\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -41,6 +41,13 @@ declare function isFromAnyMcpServer(toolName: string, serverNames: string[]): bo
|
|
|
41
41
|
* @returns Array of server names (empty if none specified)
|
|
42
42
|
*/
|
|
43
43
|
declare function normalizeServerFilter(serverFilter: string | string[] | undefined): string[];
|
|
44
|
+
/**
|
|
45
|
+
* Extracts all unique MCP server names from a tool registry.
|
|
46
|
+
* @param toolRegistry - The tool registry to scan
|
|
47
|
+
* @param onlyDeferred - If true, only considers deferred tools
|
|
48
|
+
* @returns Array of unique server names, sorted alphabetically
|
|
49
|
+
*/
|
|
50
|
+
declare function getAvailableMcpServers(toolRegistry: t.LCToolRegistry | undefined, onlyDeferred?: boolean): string[];
|
|
44
51
|
/**
|
|
45
52
|
* Escapes special regex characters in a string to use as a literal pattern.
|
|
46
53
|
* @param pattern - The string to escape
|
|
@@ -93,12 +100,11 @@ declare function performLocalSearch(tools: t.ToolMetadata[], query: string, fiel
|
|
|
93
100
|
*/
|
|
94
101
|
declare function getBaseToolName(toolName: string): string;
|
|
95
102
|
/**
|
|
96
|
-
* Formats a server listing response
|
|
97
|
-
* Provides a cohesive view of all tools grouped by server.
|
|
103
|
+
* Formats a server listing response as structured JSON.
|
|
98
104
|
* NOTE: This is a PREVIEW only - tools are NOT discovered/loaded.
|
|
99
105
|
* @param tools - Array of tool metadata from the server(s)
|
|
100
106
|
* @param serverNames - The MCP server name(s)
|
|
101
|
-
* @returns
|
|
107
|
+
* @returns JSON string showing all tools grouped by server
|
|
102
108
|
*/
|
|
103
109
|
declare function formatServerListing(tools: t.ToolMetadata[], serverNames: string | string[]): string;
|
|
104
110
|
/**
|
|
@@ -130,4 +136,4 @@ declare function formatServerListing(tools: t.ToolMetadata[], serverNames: strin
|
|
|
130
136
|
* await tool.invoke({ query: 'expense' });
|
|
131
137
|
*/
|
|
132
138
|
declare function createToolSearch(initParams?: t.ToolSearchParams): DynamicStructuredTool<ReturnType<typeof createToolSearchSchema>>;
|
|
133
|
-
export { createToolSearch, performLocalSearch, extractMcpServerName, isFromMcpServer, isFromAnyMcpServer, normalizeServerFilter, getBaseToolName, formatServerListing, sanitizeRegex, escapeRegexSpecialChars, isDangerousPattern, countNestedGroups, hasNestedQuantifiers, };
|
|
139
|
+
export { createToolSearch, performLocalSearch, extractMcpServerName, isFromMcpServer, isFromAnyMcpServer, normalizeServerFilter, getAvailableMcpServers, getBaseToolName, formatServerListing, sanitizeRegex, escapeRegexSpecialChars, isDangerousPattern, countNestedGroups, hasNestedQuantifiers, };
|
package/package.json
CHANGED
package/src/tools/ToolSearch.ts
CHANGED
|
@@ -126,6 +126,34 @@ function normalizeServerFilter(
|
|
|
126
126
|
return serverFilter.filter((s) => s !== '');
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Extracts all unique MCP server names from a tool registry.
|
|
131
|
+
* @param toolRegistry - The tool registry to scan
|
|
132
|
+
* @param onlyDeferred - If true, only considers deferred tools
|
|
133
|
+
* @returns Array of unique server names, sorted alphabetically
|
|
134
|
+
*/
|
|
135
|
+
function getAvailableMcpServers(
|
|
136
|
+
toolRegistry: t.LCToolRegistry | undefined,
|
|
137
|
+
onlyDeferred: boolean = true
|
|
138
|
+
): string[] {
|
|
139
|
+
if (!toolRegistry) {
|
|
140
|
+
return [];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const servers = new Set<string>();
|
|
144
|
+
for (const [, toolDef] of toolRegistry) {
|
|
145
|
+
if (onlyDeferred && toolDef.defer_loading !== true) {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const server = extractMcpServerName(toolDef.name);
|
|
149
|
+
if (server !== undefined && server !== '') {
|
|
150
|
+
servers.add(server);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return Array.from(servers).sort();
|
|
155
|
+
}
|
|
156
|
+
|
|
129
157
|
/**
|
|
130
158
|
* Escapes special regex characters in a string to use as a literal pattern.
|
|
131
159
|
* @param pattern - The string to escape
|
|
@@ -437,30 +465,27 @@ function parseSearchResults(stdout: string): t.ToolSearchResponse {
|
|
|
437
465
|
}
|
|
438
466
|
|
|
439
467
|
/**
|
|
440
|
-
* Formats search results
|
|
468
|
+
* Formats search results as structured JSON for efficient parsing.
|
|
441
469
|
* @param searchResponse - The parsed search response
|
|
442
|
-
* @returns
|
|
470
|
+
* @returns JSON string with search results
|
|
443
471
|
*/
|
|
444
472
|
function formatSearchResults(searchResponse: t.ToolSearchResponse): string {
|
|
445
473
|
const { tool_references, total_tools_searched, pattern_used } =
|
|
446
474
|
searchResponse;
|
|
447
475
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
response += `Total tools searched: ${total_tools_searched}\n`;
|
|
461
|
-
response += `Pattern used: ${pattern_used}`;
|
|
476
|
+
const output = {
|
|
477
|
+
found: tool_references.length,
|
|
478
|
+
tools: tool_references.map((ref) => ({
|
|
479
|
+
name: ref.tool_name,
|
|
480
|
+
score: Number(ref.match_score.toFixed(2)),
|
|
481
|
+
matched_in: ref.matched_field,
|
|
482
|
+
snippet: ref.snippet,
|
|
483
|
+
})),
|
|
484
|
+
total_searched: total_tools_searched,
|
|
485
|
+
query: pattern_used,
|
|
486
|
+
};
|
|
462
487
|
|
|
463
|
-
return
|
|
488
|
+
return JSON.stringify(output, null, 2);
|
|
464
489
|
}
|
|
465
490
|
|
|
466
491
|
/**
|
|
@@ -477,12 +502,11 @@ function getBaseToolName(toolName: string): string {
|
|
|
477
502
|
}
|
|
478
503
|
|
|
479
504
|
/**
|
|
480
|
-
* Formats a server listing response
|
|
481
|
-
* Provides a cohesive view of all tools grouped by server.
|
|
505
|
+
* Formats a server listing response as structured JSON.
|
|
482
506
|
* NOTE: This is a PREVIEW only - tools are NOT discovered/loaded.
|
|
483
507
|
* @param tools - Array of tool metadata from the server(s)
|
|
484
508
|
* @param serverNames - The MCP server name(s)
|
|
485
|
-
* @returns
|
|
509
|
+
* @returns JSON string showing all tools grouped by server
|
|
486
510
|
*/
|
|
487
511
|
function formatServerListing(
|
|
488
512
|
tools: t.ToolMetadata[],
|
|
@@ -491,48 +515,46 @@ function formatServerListing(
|
|
|
491
515
|
const servers = Array.isArray(serverNames) ? serverNames : [serverNames];
|
|
492
516
|
|
|
493
517
|
if (tools.length === 0) {
|
|
494
|
-
return
|
|
518
|
+
return JSON.stringify(
|
|
519
|
+
{
|
|
520
|
+
listing_mode: true,
|
|
521
|
+
servers,
|
|
522
|
+
total_tools: 0,
|
|
523
|
+
tools_by_server: {},
|
|
524
|
+
hint: 'No tools found from the specified MCP server(s).',
|
|
525
|
+
},
|
|
526
|
+
null,
|
|
527
|
+
2
|
|
528
|
+
);
|
|
495
529
|
}
|
|
496
530
|
|
|
497
|
-
const toolsByServer
|
|
531
|
+
const toolsByServer: Record<
|
|
532
|
+
string,
|
|
533
|
+
Array<{ name: string; description: string }>
|
|
534
|
+
> = {};
|
|
498
535
|
for (const tool of tools) {
|
|
499
536
|
const server = extractMcpServerName(tool.name) ?? 'unknown';
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
toolsByServer.set(server, existing);
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
let response =
|
|
506
|
-
servers.length === 1
|
|
507
|
-
? `## Tools from MCP server: ${servers[0]}\n\n`
|
|
508
|
-
: `## Tools from MCP servers: ${servers.join(', ')}\n\n`;
|
|
509
|
-
|
|
510
|
-
response += `Found ${tools.length} tool(s) (preview only - not yet loaded):\n\n`;
|
|
511
|
-
|
|
512
|
-
for (const [server, serverTools] of toolsByServer) {
|
|
513
|
-
if (servers.length > 1) {
|
|
514
|
-
response += `### ${server}\n\n`;
|
|
515
|
-
}
|
|
516
|
-
for (const tool of serverTools) {
|
|
517
|
-
const baseName = getBaseToolName(tool.name);
|
|
518
|
-
response += `- **${baseName}**`;
|
|
519
|
-
if (tool.description) {
|
|
520
|
-
const shortDesc =
|
|
521
|
-
tool.description.length > 80
|
|
522
|
-
? tool.description.substring(0, 77) + '...'
|
|
523
|
-
: tool.description;
|
|
524
|
-
response += `: ${shortDesc}`;
|
|
525
|
-
}
|
|
526
|
-
response += '\n';
|
|
527
|
-
}
|
|
528
|
-
if (servers.length > 1) {
|
|
529
|
-
response += '\n';
|
|
537
|
+
if (!(server in toolsByServer)) {
|
|
538
|
+
toolsByServer[server] = [];
|
|
530
539
|
}
|
|
540
|
+
toolsByServer[server].push({
|
|
541
|
+
name: getBaseToolName(tool.name),
|
|
542
|
+
description:
|
|
543
|
+
tool.description.length > 100
|
|
544
|
+
? tool.description.substring(0, 97) + '...'
|
|
545
|
+
: tool.description,
|
|
546
|
+
});
|
|
531
547
|
}
|
|
532
548
|
|
|
533
|
-
|
|
549
|
+
const output = {
|
|
550
|
+
listing_mode: true,
|
|
551
|
+
servers,
|
|
552
|
+
total_tools: tools.length,
|
|
553
|
+
tools_by_server: toolsByServer,
|
|
554
|
+
hint: `To use a tool, search for it by name (e.g., query: "${getBaseToolName(tools[0]?.name ?? 'tool_name')}") to load it.`,
|
|
555
|
+
};
|
|
534
556
|
|
|
535
|
-
return
|
|
557
|
+
return JSON.stringify(output, null, 2);
|
|
536
558
|
}
|
|
537
559
|
|
|
538
560
|
/**
|
|
@@ -587,13 +609,23 @@ function createToolSearch(
|
|
|
587
609
|
const baseEndpoint = initParams.baseUrl ?? getCodeBaseURL();
|
|
588
610
|
const EXEC_ENDPOINT = `${baseEndpoint}/exec`;
|
|
589
611
|
|
|
612
|
+
const availableServers = getAvailableMcpServers(
|
|
613
|
+
initParams.toolRegistry,
|
|
614
|
+
defaultOnlyDeferred
|
|
615
|
+
);
|
|
616
|
+
|
|
617
|
+
const serverListText =
|
|
618
|
+
availableServers.length > 0
|
|
619
|
+
? `\n- Available MCP servers: ${availableServers.join(', ')}`
|
|
620
|
+
: '';
|
|
621
|
+
|
|
590
622
|
const mcpInstructions = `
|
|
591
623
|
|
|
592
624
|
MCP Server Tools:
|
|
593
625
|
- Tools from MCP servers follow the naming convention: toolName${Constants.MCP_DELIMITER}serverName
|
|
594
626
|
- Example: "get_weather${Constants.MCP_DELIMITER}weather-api" is the "get_weather" tool from the "weather-api" server
|
|
595
627
|
- Use mcp_server parameter to filter by server (e.g., mcp_server: "weather-api")
|
|
596
|
-
- If mcp_server is provided without a query, lists ALL tools from that server`;
|
|
628
|
+
- If mcp_server is provided without a query, lists ALL tools from that server${serverListText}`;
|
|
597
629
|
|
|
598
630
|
const description =
|
|
599
631
|
mode === 'local'
|
|
@@ -846,6 +878,7 @@ export {
|
|
|
846
878
|
isFromMcpServer,
|
|
847
879
|
isFromAnyMcpServer,
|
|
848
880
|
normalizeServerFilter,
|
|
881
|
+
getAvailableMcpServers,
|
|
849
882
|
getBaseToolName,
|
|
850
883
|
formatServerListing,
|
|
851
884
|
sanitizeRegex,
|
|
@@ -15,10 +15,11 @@ import {
|
|
|
15
15
|
isFromMcpServer,
|
|
16
16
|
isFromAnyMcpServer,
|
|
17
17
|
normalizeServerFilter,
|
|
18
|
+
getAvailableMcpServers,
|
|
18
19
|
getBaseToolName,
|
|
19
20
|
formatServerListing,
|
|
20
21
|
} from '../ToolSearch';
|
|
21
|
-
import type { ToolMetadata } from '@/types';
|
|
22
|
+
import type { ToolMetadata, LCToolRegistry } from '@/types';
|
|
22
23
|
|
|
23
24
|
describe('ToolSearch', () => {
|
|
24
25
|
describe('escapeRegexSpecialChars', () => {
|
|
@@ -561,6 +562,94 @@ describe('ToolSearch', () => {
|
|
|
561
562
|
});
|
|
562
563
|
});
|
|
563
564
|
|
|
565
|
+
describe('getAvailableMcpServers', () => {
|
|
566
|
+
const createRegistry = (): LCToolRegistry => {
|
|
567
|
+
const registry: LCToolRegistry = new Map();
|
|
568
|
+
registry.set('get_weather_mcp_weather-api', {
|
|
569
|
+
name: 'get_weather_mcp_weather-api',
|
|
570
|
+
description: 'Get weather',
|
|
571
|
+
defer_loading: true,
|
|
572
|
+
});
|
|
573
|
+
registry.set('get_forecast_mcp_weather-api', {
|
|
574
|
+
name: 'get_forecast_mcp_weather-api',
|
|
575
|
+
description: 'Get forecast',
|
|
576
|
+
defer_loading: true,
|
|
577
|
+
});
|
|
578
|
+
registry.set('send_email_mcp_gmail', {
|
|
579
|
+
name: 'send_email_mcp_gmail',
|
|
580
|
+
description: 'Send email',
|
|
581
|
+
defer_loading: true,
|
|
582
|
+
});
|
|
583
|
+
registry.set('read_inbox_mcp_gmail', {
|
|
584
|
+
name: 'read_inbox_mcp_gmail',
|
|
585
|
+
description: 'Read inbox',
|
|
586
|
+
defer_loading: true,
|
|
587
|
+
});
|
|
588
|
+
registry.set('post_message_mcp_slack', {
|
|
589
|
+
name: 'post_message_mcp_slack',
|
|
590
|
+
description: 'Post to Slack',
|
|
591
|
+
defer_loading: true,
|
|
592
|
+
});
|
|
593
|
+
registry.set('regular_tool', {
|
|
594
|
+
name: 'regular_tool',
|
|
595
|
+
description: 'Not an MCP tool',
|
|
596
|
+
defer_loading: true,
|
|
597
|
+
});
|
|
598
|
+
registry.set('non_deferred_mcp_special', {
|
|
599
|
+
name: 'non_deferred_mcp_special',
|
|
600
|
+
description: 'Not deferred',
|
|
601
|
+
defer_loading: false,
|
|
602
|
+
});
|
|
603
|
+
return registry;
|
|
604
|
+
};
|
|
605
|
+
|
|
606
|
+
it('extracts unique server names from registry', () => {
|
|
607
|
+
const registry = createRegistry();
|
|
608
|
+
const servers = getAvailableMcpServers(registry, true);
|
|
609
|
+
|
|
610
|
+
expect(servers).toEqual(['gmail', 'slack', 'weather-api']);
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
it('returns servers sorted alphabetically', () => {
|
|
614
|
+
const registry = createRegistry();
|
|
615
|
+
const servers = getAvailableMcpServers(registry, true);
|
|
616
|
+
|
|
617
|
+
expect(servers).toEqual([...servers].sort());
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
it('excludes non-MCP tools', () => {
|
|
621
|
+
const registry = createRegistry();
|
|
622
|
+
const servers = getAvailableMcpServers(registry, true);
|
|
623
|
+
|
|
624
|
+
expect(servers).not.toContain('regular_tool');
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
it('respects onlyDeferred flag', () => {
|
|
628
|
+
const registry = createRegistry();
|
|
629
|
+
|
|
630
|
+
const deferredOnly = getAvailableMcpServers(registry, true);
|
|
631
|
+
expect(deferredOnly).not.toContain('special');
|
|
632
|
+
|
|
633
|
+
const allTools = getAvailableMcpServers(registry, false);
|
|
634
|
+
expect(allTools).toContain('special');
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
it('returns empty array for undefined registry', () => {
|
|
638
|
+
expect(getAvailableMcpServers(undefined, true)).toEqual([]);
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
it('returns empty array for registry with no MCP tools', () => {
|
|
642
|
+
const registry: LCToolRegistry = new Map();
|
|
643
|
+
registry.set('tool1', {
|
|
644
|
+
name: 'tool1',
|
|
645
|
+
description: 'Regular tool',
|
|
646
|
+
defer_loading: true,
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
expect(getAvailableMcpServers(registry, true)).toEqual([]);
|
|
650
|
+
});
|
|
651
|
+
});
|
|
652
|
+
|
|
564
653
|
describe('performLocalSearch with MCP tools', () => {
|
|
565
654
|
const mcpTools: ToolMetadata[] = [
|
|
566
655
|
{
|
|
@@ -643,34 +732,41 @@ describe('ToolSearch', () => {
|
|
|
643
732
|
},
|
|
644
733
|
];
|
|
645
734
|
|
|
646
|
-
it('
|
|
735
|
+
it('returns valid JSON with tool listing', () => {
|
|
647
736
|
const result = formatServerListing(serverTools, 'weather-api');
|
|
737
|
+
const parsed = JSON.parse(result);
|
|
648
738
|
|
|
649
|
-
expect(
|
|
650
|
-
expect(
|
|
651
|
-
expect(
|
|
652
|
-
expect(
|
|
653
|
-
expect(result).toContain('preview only');
|
|
739
|
+
expect(parsed.listing_mode).toBe(true);
|
|
740
|
+
expect(parsed.servers).toEqual(['weather-api']);
|
|
741
|
+
expect(parsed.total_tools).toBe(2);
|
|
742
|
+
expect(parsed.tools_by_server['weather-api']).toHaveLength(2);
|
|
654
743
|
});
|
|
655
744
|
|
|
656
745
|
it('includes hint to search for specific tool to load it', () => {
|
|
657
746
|
const result = formatServerListing(serverTools, 'weather-api');
|
|
747
|
+
const parsed = JSON.parse(result);
|
|
658
748
|
|
|
659
|
-
expect(
|
|
749
|
+
expect(parsed.hint).toContain('To use a tool, search for it by name');
|
|
660
750
|
});
|
|
661
751
|
|
|
662
752
|
it('uses base tool name (without MCP suffix) in display', () => {
|
|
663
753
|
const result = formatServerListing(serverTools, 'weather-api');
|
|
754
|
+
const parsed = JSON.parse(result);
|
|
664
755
|
|
|
665
|
-
|
|
666
|
-
|
|
756
|
+
const toolNames = parsed.tools_by_server['weather-api'].map(
|
|
757
|
+
(t: { name: string }) => t.name
|
|
758
|
+
);
|
|
759
|
+
expect(toolNames).toContain('get_weather');
|
|
760
|
+
expect(toolNames).not.toContain('get_weather_mcp_weather-api');
|
|
667
761
|
});
|
|
668
762
|
|
|
669
763
|
it('handles empty tools array', () => {
|
|
670
764
|
const result = formatServerListing([], 'empty-server');
|
|
765
|
+
const parsed = JSON.parse(result);
|
|
671
766
|
|
|
672
|
-
expect(
|
|
673
|
-
expect(
|
|
767
|
+
expect(parsed.total_tools).toBe(0);
|
|
768
|
+
expect(parsed.servers).toContain('empty-server');
|
|
769
|
+
expect(parsed.hint).toContain('No tools found');
|
|
674
770
|
});
|
|
675
771
|
|
|
676
772
|
it('truncates long descriptions', () => {
|
|
@@ -678,17 +774,17 @@ describe('ToolSearch', () => {
|
|
|
678
774
|
{
|
|
679
775
|
name: 'long_tool_mcp_server',
|
|
680
776
|
description:
|
|
681
|
-
'This is a very long description that exceeds
|
|
777
|
+
'This is a very long description that exceeds 100 characters and should be truncated to keep the listing compact and readable for the LLM.',
|
|
682
778
|
parameters: undefined,
|
|
683
779
|
},
|
|
684
780
|
];
|
|
685
781
|
|
|
686
782
|
const result = formatServerListing(toolsWithLongDesc, 'server');
|
|
783
|
+
const parsed = JSON.parse(result);
|
|
687
784
|
|
|
688
|
-
|
|
689
|
-
expect(
|
|
690
|
-
|
|
691
|
-
);
|
|
785
|
+
const toolDesc = parsed.tools_by_server['server'][0].description;
|
|
786
|
+
expect(toolDesc).toContain('...');
|
|
787
|
+
expect(toolDesc.length).toBeLessThanOrEqual(100);
|
|
692
788
|
});
|
|
693
789
|
|
|
694
790
|
it('handles multiple servers with grouped output', () => {
|
|
@@ -714,21 +810,20 @@ describe('ToolSearch', () => {
|
|
|
714
810
|
'weather-api',
|
|
715
811
|
'gmail',
|
|
716
812
|
]);
|
|
813
|
+
const parsed = JSON.parse(result);
|
|
717
814
|
|
|
718
|
-
expect(
|
|
719
|
-
expect(
|
|
720
|
-
expect(
|
|
721
|
-
expect(
|
|
722
|
-
expect(result).toContain('get_weather');
|
|
723
|
-
expect(result).toContain('send_email');
|
|
724
|
-
expect(result).toContain('read_inbox');
|
|
815
|
+
expect(parsed.servers).toEqual(['weather-api', 'gmail']);
|
|
816
|
+
expect(parsed.total_tools).toBe(3);
|
|
817
|
+
expect(parsed.tools_by_server['weather-api']).toHaveLength(1);
|
|
818
|
+
expect(parsed.tools_by_server['gmail']).toHaveLength(2);
|
|
725
819
|
});
|
|
726
820
|
|
|
727
821
|
it('accepts single server as array', () => {
|
|
728
822
|
const result = formatServerListing(serverTools, ['weather-api']);
|
|
823
|
+
const parsed = JSON.parse(result);
|
|
729
824
|
|
|
730
|
-
expect(
|
|
731
|
-
expect(
|
|
825
|
+
expect(parsed.servers).toEqual(['weather-api']);
|
|
826
|
+
expect(parsed.tools_by_server['weather-api']).toBeDefined();
|
|
732
827
|
});
|
|
733
828
|
});
|
|
734
829
|
});
|