@librechat/agents 3.0.66 → 3.0.68
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/common/enum.cjs +3 -1
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/main.cjs +14 -7
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/messages/tools.cjs +2 -2
- package/dist/cjs/messages/tools.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +1 -1
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/{ToolSearchRegex.cjs → ToolSearch.cjs} +318 -63
- package/dist/cjs/tools/ToolSearch.cjs.map +1 -0
- package/dist/esm/common/enum.mjs +3 -1
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/main.mjs +1 -1
- package/dist/esm/messages/tools.mjs +2 -2
- package/dist/esm/messages/tools.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +1 -1
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/{ToolSearchRegex.mjs → ToolSearch.mjs} +311 -63
- package/dist/esm/tools/ToolSearch.mjs.map +1 -0
- package/dist/types/common/enum.d.ts +4 -2
- package/dist/types/index.d.ts +1 -1
- package/dist/types/tools/ToolSearch.d.ts +133 -0
- package/dist/types/types/tools.d.ts +8 -2
- package/package.json +2 -2
- package/src/common/enum.ts +3 -1
- package/src/index.ts +1 -1
- package/src/messages/__tests__/tools.test.ts +21 -21
- package/src/messages/tools.ts +2 -2
- package/src/scripts/programmatic_exec_agent.ts +4 -4
- package/src/scripts/{tool_search_regex.ts → tool_search.ts} +5 -5
- package/src/tools/ToolNode.ts +1 -1
- package/src/tools/{ToolSearchRegex.ts → ToolSearch.ts} +390 -69
- package/src/tools/__tests__/{ToolSearchRegex.integration.test.ts → ToolSearch.integration.test.ts} +6 -6
- package/src/tools/__tests__/ToolSearch.test.ts +734 -0
- package/src/types/tools.ts +9 -2
- package/dist/cjs/tools/ToolSearchRegex.cjs.map +0 -1
- package/dist/esm/tools/ToolSearchRegex.mjs.map +0 -1
- package/dist/types/tools/ToolSearchRegex.d.ts +0 -80
- package/src/tools/__tests__/ToolSearchRegex.test.ts +0 -232
|
@@ -7,7 +7,7 @@ import { tool } from '@langchain/core/tools';
|
|
|
7
7
|
import { getCodeBaseURL } from './CodeExecutor.mjs';
|
|
8
8
|
import { EnvVar, Constants } from '../common/enum.mjs';
|
|
9
9
|
|
|
10
|
-
// src/tools/
|
|
10
|
+
// src/tools/ToolSearch.ts
|
|
11
11
|
config();
|
|
12
12
|
/** Maximum allowed regex pattern length */
|
|
13
13
|
const MAX_PATTERN_LENGTH = 200;
|
|
@@ -15,26 +15,91 @@ const MAX_PATTERN_LENGTH = 200;
|
|
|
15
15
|
const MAX_REGEX_COMPLEXITY = 5;
|
|
16
16
|
/** Default search timeout in milliseconds */
|
|
17
17
|
const SEARCH_TIMEOUT = 5000;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
.
|
|
26
|
-
.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Creates the Zod schema with dynamic query description based on mode.
|
|
20
|
+
* @param mode - The search mode determining query interpretation
|
|
21
|
+
* @returns Zod schema for tool search parameters
|
|
22
|
+
*/
|
|
23
|
+
function createToolSearchSchema(mode) {
|
|
24
|
+
const queryDescription = mode === 'local'
|
|
25
|
+
? 'Search term to find in tool names and descriptions. Case-insensitive substring matching. Optional if mcp_server is provided.'
|
|
26
|
+
: 'Regex pattern to search tool names and descriptions. Optional if mcp_server is provided.';
|
|
27
|
+
return z.object({
|
|
28
|
+
query: z
|
|
29
|
+
.string()
|
|
30
|
+
.max(MAX_PATTERN_LENGTH)
|
|
31
|
+
.optional()
|
|
32
|
+
.default('')
|
|
33
|
+
.describe(queryDescription),
|
|
34
|
+
fields: z
|
|
35
|
+
.array(z.enum(['name', 'description', 'parameters']))
|
|
36
|
+
.optional()
|
|
37
|
+
.default(['name', 'description'])
|
|
38
|
+
.describe('Which fields to search. Default: name and description'),
|
|
39
|
+
max_results: z
|
|
40
|
+
.number()
|
|
41
|
+
.int()
|
|
42
|
+
.min(1)
|
|
43
|
+
.max(50)
|
|
44
|
+
.optional()
|
|
45
|
+
.default(10)
|
|
46
|
+
.describe('Maximum number of matching tools to return'),
|
|
47
|
+
mcp_server: z
|
|
48
|
+
.union([z.string(), z.array(z.string())])
|
|
49
|
+
.optional()
|
|
50
|
+
.describe('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.'),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Extracts the MCP server name from a tool name.
|
|
55
|
+
* MCP tools follow the pattern: toolName_mcp_serverName
|
|
56
|
+
* @param toolName - The full tool name
|
|
57
|
+
* @returns The server name if it's an MCP tool, undefined otherwise
|
|
58
|
+
*/
|
|
59
|
+
function extractMcpServerName(toolName) {
|
|
60
|
+
const delimiterIndex = toolName.indexOf(Constants.MCP_DELIMITER);
|
|
61
|
+
if (delimiterIndex === -1) {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
return toolName.substring(delimiterIndex + Constants.MCP_DELIMITER.length);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Checks if a tool belongs to a specific MCP server.
|
|
68
|
+
* @param toolName - The full tool name
|
|
69
|
+
* @param serverName - The server name to match
|
|
70
|
+
* @returns True if the tool belongs to the specified server
|
|
71
|
+
*/
|
|
72
|
+
function isFromMcpServer(toolName, serverName) {
|
|
73
|
+
const toolServer = extractMcpServerName(toolName);
|
|
74
|
+
return toolServer === serverName;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Checks if a tool belongs to any of the specified MCP servers.
|
|
78
|
+
* @param toolName - The full tool name
|
|
79
|
+
* @param serverNames - Array of server names to match
|
|
80
|
+
* @returns True if the tool belongs to any of the specified servers
|
|
81
|
+
*/
|
|
82
|
+
function isFromAnyMcpServer(toolName, serverNames) {
|
|
83
|
+
const toolServer = extractMcpServerName(toolName);
|
|
84
|
+
if (toolServer === undefined) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
return serverNames.includes(toolServer);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Normalizes server filter input to always be an array.
|
|
91
|
+
* @param serverFilter - String, array of strings, or undefined
|
|
92
|
+
* @returns Array of server names (empty if none specified)
|
|
93
|
+
*/
|
|
94
|
+
function normalizeServerFilter(serverFilter) {
|
|
95
|
+
if (serverFilter === undefined) {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
if (typeof serverFilter === 'string') {
|
|
99
|
+
return serverFilter === '' ? [] : [serverFilter];
|
|
100
|
+
}
|
|
101
|
+
return serverFilter.filter((s) => s !== '');
|
|
102
|
+
}
|
|
38
103
|
/**
|
|
39
104
|
* Escapes special regex characters in a string to use as a literal pattern.
|
|
40
105
|
* @param pattern - The string to escape
|
|
@@ -145,6 +210,67 @@ function simplifyParametersForSearch(parameters) {
|
|
|
145
210
|
}
|
|
146
211
|
return { type: parameters.type };
|
|
147
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Performs safe local substring search without regex.
|
|
215
|
+
* Uses case-insensitive String.includes() for complete safety against ReDoS.
|
|
216
|
+
* @param tools - Array of tool metadata to search
|
|
217
|
+
* @param query - The search term (treated as literal substring)
|
|
218
|
+
* @param fields - Which fields to search
|
|
219
|
+
* @param maxResults - Maximum results to return
|
|
220
|
+
* @returns Search response with matching tools
|
|
221
|
+
*/
|
|
222
|
+
function performLocalSearch(tools, query, fields, maxResults) {
|
|
223
|
+
const lowerQuery = query.toLowerCase();
|
|
224
|
+
const results = [];
|
|
225
|
+
for (const tool of tools) {
|
|
226
|
+
let bestScore = 0;
|
|
227
|
+
let matchedField = '';
|
|
228
|
+
let snippet = '';
|
|
229
|
+
if (fields.includes('name')) {
|
|
230
|
+
const lowerName = tool.name.toLowerCase();
|
|
231
|
+
if (lowerName.includes(lowerQuery)) {
|
|
232
|
+
const isExactMatch = lowerName === lowerQuery;
|
|
233
|
+
const startsWithQuery = lowerName.startsWith(lowerQuery);
|
|
234
|
+
bestScore = isExactMatch ? 1.0 : startsWithQuery ? 0.95 : 0.85;
|
|
235
|
+
matchedField = 'name';
|
|
236
|
+
snippet = tool.name;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if (fields.includes('description') && tool.description) {
|
|
240
|
+
const lowerDesc = tool.description.toLowerCase();
|
|
241
|
+
if (lowerDesc.includes(lowerQuery) && bestScore === 0) {
|
|
242
|
+
bestScore = 0.7;
|
|
243
|
+
matchedField = 'description';
|
|
244
|
+
snippet = tool.description.substring(0, 100);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (fields.includes('parameters') && tool.parameters?.properties) {
|
|
248
|
+
const paramNames = Object.keys(tool.parameters.properties)
|
|
249
|
+
.join(' ')
|
|
250
|
+
.toLowerCase();
|
|
251
|
+
if (paramNames.includes(lowerQuery) && bestScore === 0) {
|
|
252
|
+
bestScore = 0.55;
|
|
253
|
+
matchedField = 'parameters';
|
|
254
|
+
snippet = Object.keys(tool.parameters.properties).join(' ');
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (bestScore > 0) {
|
|
258
|
+
results.push({
|
|
259
|
+
tool_name: tool.name,
|
|
260
|
+
match_score: bestScore,
|
|
261
|
+
matched_field: matchedField,
|
|
262
|
+
snippet,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
results.sort((a, b) => b.match_score - a.match_score);
|
|
267
|
+
const topResults = results.slice(0, maxResults);
|
|
268
|
+
return {
|
|
269
|
+
tool_references: topResults,
|
|
270
|
+
total_tools_searched: tools.length,
|
|
271
|
+
pattern_used: query,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
148
274
|
/**
|
|
149
275
|
* Generates the JavaScript search script to be executed in the sandbox.
|
|
150
276
|
* Uses plain JavaScript for maximum compatibility with the Code API.
|
|
@@ -268,11 +394,73 @@ function formatSearchResults(searchResponse) {
|
|
|
268
394
|
return response;
|
|
269
395
|
}
|
|
270
396
|
/**
|
|
271
|
-
*
|
|
397
|
+
* Extracts the base tool name (without MCP server suffix) from a full tool name.
|
|
398
|
+
* @param toolName - The full tool name
|
|
399
|
+
* @returns The base tool name without server suffix
|
|
400
|
+
*/
|
|
401
|
+
function getBaseToolName(toolName) {
|
|
402
|
+
const delimiterIndex = toolName.indexOf(Constants.MCP_DELIMITER);
|
|
403
|
+
if (delimiterIndex === -1) {
|
|
404
|
+
return toolName;
|
|
405
|
+
}
|
|
406
|
+
return toolName.substring(0, delimiterIndex);
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Formats a server listing response when listing all tools from MCP server(s).
|
|
410
|
+
* Provides a cohesive view of all tools grouped by server.
|
|
411
|
+
* NOTE: This is a PREVIEW only - tools are NOT discovered/loaded.
|
|
412
|
+
* @param tools - Array of tool metadata from the server(s)
|
|
413
|
+
* @param serverNames - The MCP server name(s)
|
|
414
|
+
* @returns Formatted string showing all tools from the server(s)
|
|
415
|
+
*/
|
|
416
|
+
function formatServerListing(tools, serverNames) {
|
|
417
|
+
const servers = Array.isArray(serverNames) ? serverNames : [serverNames];
|
|
418
|
+
if (tools.length === 0) {
|
|
419
|
+
return `No tools found from MCP server(s): ${servers.join(', ')}.`;
|
|
420
|
+
}
|
|
421
|
+
const toolsByServer = new Map();
|
|
422
|
+
for (const tool of tools) {
|
|
423
|
+
const server = extractMcpServerName(tool.name) ?? 'unknown';
|
|
424
|
+
const existing = toolsByServer.get(server) ?? [];
|
|
425
|
+
existing.push(tool);
|
|
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';
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
response += `\n_To use a tool, search for it by name (e.g., query: "${getBaseToolName(tools[0]?.name ?? 'tool_name')}") to load it._`;
|
|
452
|
+
return response;
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Creates a Tool Search tool for discovering tools from a large registry.
|
|
272
456
|
*
|
|
273
457
|
* This tool enables AI agents to dynamically discover tools from a large library
|
|
274
458
|
* without loading all tool definitions into the LLM context window. The agent
|
|
275
|
-
* can search for relevant tools on-demand
|
|
459
|
+
* can search for relevant tools on-demand.
|
|
460
|
+
*
|
|
461
|
+
* **Modes:**
|
|
462
|
+
* - `code_interpreter` (default): Uses external sandbox for regex search. Safer for complex patterns.
|
|
463
|
+
* - `local`: Uses safe substring matching locally. No network call, faster, completely safe from ReDoS.
|
|
276
464
|
*
|
|
277
465
|
* The tool registry can be provided either:
|
|
278
466
|
* 1. At initialization time via params.toolRegistry
|
|
@@ -282,30 +470,50 @@ function formatSearchResults(searchResponse) {
|
|
|
282
470
|
* @returns A LangChain DynamicStructuredTool for tool searching
|
|
283
471
|
*
|
|
284
472
|
* @example
|
|
285
|
-
* // Option 1:
|
|
286
|
-
* const tool =
|
|
287
|
-
* await tool.invoke({ query: 'expense' });
|
|
473
|
+
* // Option 1: Code interpreter mode (regex via sandbox)
|
|
474
|
+
* const tool = createToolSearch({ apiKey, toolRegistry });
|
|
475
|
+
* await tool.invoke({ query: 'expense.*report' });
|
|
288
476
|
*
|
|
289
477
|
* @example
|
|
290
|
-
* // Option 2:
|
|
291
|
-
* const tool =
|
|
292
|
-
* await tool.invoke(
|
|
293
|
-
* { query: 'expense' },
|
|
294
|
-
* { configurable: { toolRegistry, onlyDeferred: true } }
|
|
295
|
-
* );
|
|
478
|
+
* // Option 2: Local mode (safe substring search, no API key needed)
|
|
479
|
+
* const tool = createToolSearch({ mode: 'local', toolRegistry });
|
|
480
|
+
* await tool.invoke({ query: 'expense' });
|
|
296
481
|
*/
|
|
297
|
-
function
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
482
|
+
function createToolSearch(initParams = {}) {
|
|
483
|
+
const mode = initParams.mode ?? 'code_interpreter';
|
|
484
|
+
const defaultOnlyDeferred = initParams.onlyDeferred ?? true;
|
|
485
|
+
const schema = createToolSearchSchema(mode);
|
|
486
|
+
const apiKey = mode === 'code_interpreter'
|
|
487
|
+
? (initParams[EnvVar.CODE_API_KEY] ??
|
|
488
|
+
initParams.apiKey ??
|
|
489
|
+
getEnvironmentVariable(EnvVar.CODE_API_KEY) ??
|
|
490
|
+
'')
|
|
491
|
+
: '';
|
|
492
|
+
if (mode === 'code_interpreter' && !apiKey) {
|
|
493
|
+
throw new Error('No API key provided for tool search in code_interpreter mode. Use mode: "local" to search without an API key.');
|
|
304
494
|
}
|
|
305
495
|
const baseEndpoint = initParams.baseUrl ?? getCodeBaseURL();
|
|
306
496
|
const EXEC_ENDPOINT = `${baseEndpoint}/exec`;
|
|
307
|
-
const
|
|
308
|
-
|
|
497
|
+
const mcpInstructions = `
|
|
498
|
+
|
|
499
|
+
MCP Server Tools:
|
|
500
|
+
- Tools from MCP servers follow the naming convention: toolName${Constants.MCP_DELIMITER}serverName
|
|
501
|
+
- Example: "get_weather${Constants.MCP_DELIMITER}weather-api" is the "get_weather" tool from the "weather-api" server
|
|
502
|
+
- 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`;
|
|
504
|
+
const description = mode === 'local'
|
|
505
|
+
? `
|
|
506
|
+
Searches through available tools to find ones matching your search term.
|
|
507
|
+
|
|
508
|
+
Usage:
|
|
509
|
+
- Provide a search term to find in tool names and descriptions.
|
|
510
|
+
- Uses case-insensitive substring matching (fast and safe).
|
|
511
|
+
- Use this when you need to discover tools for a specific task.
|
|
512
|
+
- Results include tool names, match quality scores, and snippets showing where the match occurred.
|
|
513
|
+
- Higher scores (0.95+) indicate name matches, medium scores (0.70+) indicate description matches.
|
|
514
|
+
${mcpInstructions}
|
|
515
|
+
`.trim()
|
|
516
|
+
: `
|
|
309
517
|
Searches through available tools to find ones matching your query pattern.
|
|
310
518
|
|
|
311
519
|
Usage:
|
|
@@ -313,30 +521,26 @@ Usage:
|
|
|
313
521
|
- Use this when you need to discover tools for a specific task.
|
|
314
522
|
- Results include tool names, match quality scores, and snippets showing where the match occurred.
|
|
315
523
|
- Higher scores (0.9+) indicate name matches, medium scores (0.7+) indicate description matches.
|
|
524
|
+
${mcpInstructions}
|
|
316
525
|
`.trim();
|
|
317
526
|
return tool(async (params, config) => {
|
|
318
|
-
const { query, fields = ['name', 'description'], max_results = 10, } = params;
|
|
319
|
-
|
|
320
|
-
const { toolRegistry: paramToolRegistry, onlyDeferred: paramOnlyDeferred, } = config.toolCall ?? {};
|
|
321
|
-
const { safe: sanitizedPattern, wasEscaped } = sanitizeRegex(query);
|
|
322
|
-
let warningMessage = '';
|
|
323
|
-
if (wasEscaped) {
|
|
324
|
-
warningMessage =
|
|
325
|
-
'Note: The provided pattern was converted to a literal search for safety.\n\n';
|
|
326
|
-
}
|
|
327
|
-
// Priority: ToolNode injection (via config.toolCall) > initialization params
|
|
527
|
+
const { query, fields = ['name', 'description'], max_results = 10, mcp_server, } = params;
|
|
528
|
+
const { toolRegistry: paramToolRegistry, onlyDeferred: paramOnlyDeferred, mcpServer: paramMcpServer, } = config.toolCall ?? {};
|
|
328
529
|
const toolRegistry = paramToolRegistry ?? initParams.toolRegistry;
|
|
329
530
|
const onlyDeferred = paramOnlyDeferred !== undefined
|
|
330
531
|
? paramOnlyDeferred
|
|
331
532
|
: defaultOnlyDeferred;
|
|
533
|
+
const rawServerFilter = mcp_server ?? paramMcpServer ?? initParams.mcpServer;
|
|
534
|
+
const serverFilters = normalizeServerFilter(rawServerFilter);
|
|
535
|
+
const hasServerFilter = serverFilters.length > 0;
|
|
332
536
|
if (toolRegistry == null) {
|
|
333
537
|
return [
|
|
334
|
-
|
|
538
|
+
'Error: No tool registry provided. Configure toolRegistry at agent level or initialization.',
|
|
335
539
|
{
|
|
336
540
|
tool_references: [],
|
|
337
541
|
metadata: {
|
|
338
542
|
total_searched: 0,
|
|
339
|
-
pattern:
|
|
543
|
+
pattern: query,
|
|
340
544
|
error: 'No tool registry provided',
|
|
341
545
|
},
|
|
342
546
|
},
|
|
@@ -345,8 +549,12 @@ Usage:
|
|
|
345
549
|
const toolsArray = Array.from(toolRegistry.values());
|
|
346
550
|
const deferredTools = toolsArray
|
|
347
551
|
.filter((lcTool) => {
|
|
348
|
-
if (onlyDeferred === true) {
|
|
349
|
-
return
|
|
552
|
+
if (onlyDeferred === true && lcTool.defer_loading !== true) {
|
|
553
|
+
return false;
|
|
554
|
+
}
|
|
555
|
+
if (hasServerFilter &&
|
|
556
|
+
!isFromAnyMcpServer(lcTool.name, serverFilters)) {
|
|
557
|
+
return false;
|
|
350
558
|
}
|
|
351
559
|
return true;
|
|
352
560
|
})
|
|
@@ -356,17 +564,57 @@ Usage:
|
|
|
356
564
|
parameters: simplifyParametersForSearch(lcTool.parameters),
|
|
357
565
|
}));
|
|
358
566
|
if (deferredTools.length === 0) {
|
|
567
|
+
const serverMsg = hasServerFilter
|
|
568
|
+
? ` from MCP server(s): ${serverFilters.join(', ')}`
|
|
569
|
+
: '';
|
|
359
570
|
return [
|
|
360
|
-
|
|
571
|
+
`No tools available to search${serverMsg}. The tool registry is empty or no matching deferred tools are registered.`,
|
|
361
572
|
{
|
|
362
573
|
tool_references: [],
|
|
363
574
|
metadata: {
|
|
364
575
|
total_searched: 0,
|
|
365
|
-
pattern:
|
|
576
|
+
pattern: query,
|
|
577
|
+
mcp_server: serverFilters,
|
|
578
|
+
},
|
|
579
|
+
},
|
|
580
|
+
];
|
|
581
|
+
}
|
|
582
|
+
const isServerListing = hasServerFilter && query === '';
|
|
583
|
+
if (isServerListing) {
|
|
584
|
+
const formattedOutput = formatServerListing(deferredTools, serverFilters);
|
|
585
|
+
return [
|
|
586
|
+
formattedOutput,
|
|
587
|
+
{
|
|
588
|
+
tool_references: [],
|
|
589
|
+
metadata: {
|
|
590
|
+
total_available: deferredTools.length,
|
|
591
|
+
mcp_server: serverFilters,
|
|
592
|
+
listing_mode: true,
|
|
366
593
|
},
|
|
367
594
|
},
|
|
368
595
|
];
|
|
369
596
|
}
|
|
597
|
+
if (mode === 'local') {
|
|
598
|
+
const searchResponse = performLocalSearch(deferredTools, query, fields, max_results);
|
|
599
|
+
const formattedOutput = formatSearchResults(searchResponse);
|
|
600
|
+
return [
|
|
601
|
+
formattedOutput,
|
|
602
|
+
{
|
|
603
|
+
tool_references: searchResponse.tool_references,
|
|
604
|
+
metadata: {
|
|
605
|
+
total_searched: searchResponse.total_tools_searched,
|
|
606
|
+
pattern: searchResponse.pattern_used,
|
|
607
|
+
mcp_server: serverFilters.length > 0 ? serverFilters : undefined,
|
|
608
|
+
},
|
|
609
|
+
},
|
|
610
|
+
];
|
|
611
|
+
}
|
|
612
|
+
const { safe: sanitizedPattern, wasEscaped } = sanitizeRegex(query);
|
|
613
|
+
let warningMessage = '';
|
|
614
|
+
if (wasEscaped) {
|
|
615
|
+
warningMessage =
|
|
616
|
+
'Note: The provided pattern was converted to a literal search for safety.\n\n';
|
|
617
|
+
}
|
|
370
618
|
const searchScript = generateSearchScript(deferredTools, fields, max_results, sanitizedPattern);
|
|
371
619
|
const postData = {
|
|
372
620
|
lang: 'js',
|
|
@@ -393,7 +641,7 @@ Usage:
|
|
|
393
641
|
const result = await response.json();
|
|
394
642
|
if (result.stderr && result.stderr.trim()) {
|
|
395
643
|
// eslint-disable-next-line no-console
|
|
396
|
-
console.warn('[
|
|
644
|
+
console.warn('[ToolSearch] stderr:', result.stderr);
|
|
397
645
|
}
|
|
398
646
|
if (!result.stdout || !result.stdout.trim()) {
|
|
399
647
|
return [
|
|
@@ -422,7 +670,7 @@ Usage:
|
|
|
422
670
|
}
|
|
423
671
|
catch (error) {
|
|
424
672
|
// eslint-disable-next-line no-console
|
|
425
|
-
console.error('[
|
|
673
|
+
console.error('[ToolSearch] Error:', error);
|
|
426
674
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
427
675
|
return [
|
|
428
676
|
`Tool search failed: ${errorMessage}\n\nSuggestion: Try a simpler search pattern or search for specific tool names.`,
|
|
@@ -437,12 +685,12 @@ Usage:
|
|
|
437
685
|
];
|
|
438
686
|
}
|
|
439
687
|
}, {
|
|
440
|
-
name: Constants.
|
|
688
|
+
name: Constants.TOOL_SEARCH,
|
|
441
689
|
description,
|
|
442
|
-
schema
|
|
690
|
+
schema,
|
|
443
691
|
responseFormat: Constants.CONTENT_AND_ARTIFACT,
|
|
444
692
|
});
|
|
445
693
|
}
|
|
446
694
|
|
|
447
|
-
export { countNestedGroups,
|
|
448
|
-
//# sourceMappingURL=
|
|
695
|
+
export { countNestedGroups, createToolSearch, escapeRegexSpecialChars, extractMcpServerName, formatServerListing, getBaseToolName, hasNestedQuantifiers, isDangerousPattern, isFromAnyMcpServer, isFromMcpServer, normalizeServerFilter, performLocalSearch, sanitizeRegex };
|
|
696
|
+
//# sourceMappingURL=ToolSearch.mjs.map
|
|
@@ -0,0 +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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"}
|
|
@@ -114,11 +114,13 @@ export declare enum Callback {
|
|
|
114
114
|
export declare enum Constants {
|
|
115
115
|
OFFICIAL_CODE_BASEURL = "https://api.librechat.ai/v1",
|
|
116
116
|
EXECUTE_CODE = "execute_code",
|
|
117
|
-
|
|
117
|
+
TOOL_SEARCH = "tool_search",
|
|
118
118
|
PROGRAMMATIC_TOOL_CALLING = "run_tools_with_code",
|
|
119
119
|
WEB_SEARCH = "web_search",
|
|
120
120
|
CONTENT_AND_ARTIFACT = "content_and_artifact",
|
|
121
|
-
LC_TRANSFER_TO_ = "lc_transfer_to_"
|
|
121
|
+
LC_TRANSFER_TO_ = "lc_transfer_to_",
|
|
122
|
+
/** Delimiter for MCP tools: toolName_mcp_serverName */
|
|
123
|
+
MCP_DELIMITER = "_mcp_"
|
|
122
124
|
}
|
|
123
125
|
export declare enum TitleMethod {
|
|
124
126
|
STRUCTURED = "structured",
|
package/dist/types/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export * from './graphs';
|
|
|
7
7
|
export * from './tools/Calculator';
|
|
8
8
|
export * from './tools/CodeExecutor';
|
|
9
9
|
export * from './tools/ProgrammaticToolCalling';
|
|
10
|
-
export * from './tools/
|
|
10
|
+
export * from './tools/ToolSearch';
|
|
11
11
|
export * from './tools/handlers';
|
|
12
12
|
export * from './tools/search';
|
|
13
13
|
export * from './common';
|