@librechat/agents 3.0.67 → 3.0.69
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 +2 -0
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/main.cjs +7 -0
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/tools/ToolSearch.cjs +202 -8
- package/dist/cjs/tools/ToolSearch.cjs.map +1 -1
- package/dist/esm/common/enum.mjs +2 -0
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/main.mjs +1 -1
- package/dist/esm/tools/ToolSearch.mjs +196 -9
- package/dist/esm/tools/ToolSearch.mjs.map +1 -1
- package/dist/types/common/enum.d.ts +3 -1
- package/dist/types/tools/ToolSearch.d.ts +52 -2
- package/dist/types/types/tools.d.ts +3 -1
- package/package.json +1 -1
- package/src/common/enum.ts +2 -0
- package/src/tools/ToolSearch.ts +248 -9
- package/src/tools/__tests__/ToolSearch.test.ts +384 -2
- package/src/types/tools.ts +3 -1
package/src/tools/ToolSearch.ts
CHANGED
|
@@ -22,11 +22,12 @@ const SEARCH_TIMEOUT = 5000;
|
|
|
22
22
|
|
|
23
23
|
/** Zod schema type for tool search parameters */
|
|
24
24
|
type ToolSearchSchema = z.ZodObject<{
|
|
25
|
-
query: z.ZodString
|
|
25
|
+
query: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
26
26
|
fields: z.ZodDefault<
|
|
27
27
|
z.ZodOptional<z.ZodArray<z.ZodEnum<['name', 'description', 'parameters']>>>
|
|
28
28
|
>;
|
|
29
29
|
max_results: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
30
|
+
mcp_server: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
30
31
|
}>;
|
|
31
32
|
|
|
32
33
|
/**
|
|
@@ -37,11 +38,16 @@ type ToolSearchSchema = z.ZodObject<{
|
|
|
37
38
|
function createToolSearchSchema(mode: t.ToolSearchMode): ToolSearchSchema {
|
|
38
39
|
const queryDescription =
|
|
39
40
|
mode === 'local'
|
|
40
|
-
? 'Search term to find in tool names and descriptions. Case-insensitive substring matching.'
|
|
41
|
-
: 'Regex pattern to search tool names and descriptions.
|
|
41
|
+
? 'Search term to find in tool names and descriptions. Case-insensitive substring matching. Optional if mcp_server is provided.'
|
|
42
|
+
: 'Regex pattern to search tool names and descriptions. Optional if mcp_server is provided.';
|
|
42
43
|
|
|
43
44
|
return z.object({
|
|
44
|
-
query: z
|
|
45
|
+
query: z
|
|
46
|
+
.string()
|
|
47
|
+
.max(MAX_PATTERN_LENGTH)
|
|
48
|
+
.optional()
|
|
49
|
+
.default('')
|
|
50
|
+
.describe(queryDescription),
|
|
45
51
|
fields: z
|
|
46
52
|
.array(z.enum(['name', 'description', 'parameters']))
|
|
47
53
|
.optional()
|
|
@@ -55,9 +61,99 @@ function createToolSearchSchema(mode: t.ToolSearchMode): ToolSearchSchema {
|
|
|
55
61
|
.optional()
|
|
56
62
|
.default(10)
|
|
57
63
|
.describe('Maximum number of matching tools to return'),
|
|
64
|
+
mcp_server: z
|
|
65
|
+
.union([z.string(), z.array(z.string())])
|
|
66
|
+
.optional()
|
|
67
|
+
.describe(
|
|
68
|
+
'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.'
|
|
69
|
+
),
|
|
58
70
|
});
|
|
59
71
|
}
|
|
60
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Extracts the MCP server name from a tool name.
|
|
75
|
+
* MCP tools follow the pattern: toolName_mcp_serverName
|
|
76
|
+
* @param toolName - The full tool name
|
|
77
|
+
* @returns The server name if it's an MCP tool, undefined otherwise
|
|
78
|
+
*/
|
|
79
|
+
function extractMcpServerName(toolName: string): string | undefined {
|
|
80
|
+
const delimiterIndex = toolName.indexOf(Constants.MCP_DELIMITER);
|
|
81
|
+
if (delimiterIndex === -1) {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
return toolName.substring(delimiterIndex + Constants.MCP_DELIMITER.length);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Checks if a tool belongs to a specific MCP server.
|
|
89
|
+
* @param toolName - The full tool name
|
|
90
|
+
* @param serverName - The server name to match
|
|
91
|
+
* @returns True if the tool belongs to the specified server
|
|
92
|
+
*/
|
|
93
|
+
function isFromMcpServer(toolName: string, serverName: string): boolean {
|
|
94
|
+
const toolServer = extractMcpServerName(toolName);
|
|
95
|
+
return toolServer === serverName;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Checks if a tool belongs to any of the specified MCP servers.
|
|
100
|
+
* @param toolName - The full tool name
|
|
101
|
+
* @param serverNames - Array of server names to match
|
|
102
|
+
* @returns True if the tool belongs to any of the specified servers
|
|
103
|
+
*/
|
|
104
|
+
function isFromAnyMcpServer(toolName: string, serverNames: string[]): boolean {
|
|
105
|
+
const toolServer = extractMcpServerName(toolName);
|
|
106
|
+
if (toolServer === undefined) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
return serverNames.includes(toolServer);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Normalizes server filter input to always be an array.
|
|
114
|
+
* @param serverFilter - String, array of strings, or undefined
|
|
115
|
+
* @returns Array of server names (empty if none specified)
|
|
116
|
+
*/
|
|
117
|
+
function normalizeServerFilter(
|
|
118
|
+
serverFilter: string | string[] | undefined
|
|
119
|
+
): string[] {
|
|
120
|
+
if (serverFilter === undefined) {
|
|
121
|
+
return [];
|
|
122
|
+
}
|
|
123
|
+
if (typeof serverFilter === 'string') {
|
|
124
|
+
return serverFilter === '' ? [] : [serverFilter];
|
|
125
|
+
}
|
|
126
|
+
return serverFilter.filter((s) => s !== '');
|
|
127
|
+
}
|
|
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
|
+
|
|
61
157
|
/**
|
|
62
158
|
* Escapes special regex characters in a string to use as a literal pattern.
|
|
63
159
|
* @param pattern - The string to escape
|
|
@@ -395,6 +491,78 @@ function formatSearchResults(searchResponse: t.ToolSearchResponse): string {
|
|
|
395
491
|
return response;
|
|
396
492
|
}
|
|
397
493
|
|
|
494
|
+
/**
|
|
495
|
+
* Extracts the base tool name (without MCP server suffix) from a full tool name.
|
|
496
|
+
* @param toolName - The full tool name
|
|
497
|
+
* @returns The base tool name without server suffix
|
|
498
|
+
*/
|
|
499
|
+
function getBaseToolName(toolName: string): string {
|
|
500
|
+
const delimiterIndex = toolName.indexOf(Constants.MCP_DELIMITER);
|
|
501
|
+
if (delimiterIndex === -1) {
|
|
502
|
+
return toolName;
|
|
503
|
+
}
|
|
504
|
+
return toolName.substring(0, delimiterIndex);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Formats a server listing response when listing all tools from MCP server(s).
|
|
509
|
+
* Provides a cohesive view of all tools grouped by server.
|
|
510
|
+
* NOTE: This is a PREVIEW only - tools are NOT discovered/loaded.
|
|
511
|
+
* @param tools - Array of tool metadata from the server(s)
|
|
512
|
+
* @param serverNames - The MCP server name(s)
|
|
513
|
+
* @returns Formatted string showing all tools from the server(s)
|
|
514
|
+
*/
|
|
515
|
+
function formatServerListing(
|
|
516
|
+
tools: t.ToolMetadata[],
|
|
517
|
+
serverNames: string | string[]
|
|
518
|
+
): string {
|
|
519
|
+
const servers = Array.isArray(serverNames) ? serverNames : [serverNames];
|
|
520
|
+
|
|
521
|
+
if (tools.length === 0) {
|
|
522
|
+
return `No tools found from MCP server(s): ${servers.join(', ')}.`;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
const toolsByServer = new Map<string, t.ToolMetadata[]>();
|
|
526
|
+
for (const tool of tools) {
|
|
527
|
+
const server = extractMcpServerName(tool.name) ?? 'unknown';
|
|
528
|
+
const existing = toolsByServer.get(server) ?? [];
|
|
529
|
+
existing.push(tool);
|
|
530
|
+
toolsByServer.set(server, existing);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
let response =
|
|
534
|
+
servers.length === 1
|
|
535
|
+
? `## Tools from MCP server: ${servers[0]}\n\n`
|
|
536
|
+
: `## Tools from MCP servers: ${servers.join(', ')}\n\n`;
|
|
537
|
+
|
|
538
|
+
response += `Found ${tools.length} tool(s) (preview only - not yet loaded):\n\n`;
|
|
539
|
+
|
|
540
|
+
for (const [server, serverTools] of toolsByServer) {
|
|
541
|
+
if (servers.length > 1) {
|
|
542
|
+
response += `### ${server}\n\n`;
|
|
543
|
+
}
|
|
544
|
+
for (const tool of serverTools) {
|
|
545
|
+
const baseName = getBaseToolName(tool.name);
|
|
546
|
+
response += `- **${baseName}**`;
|
|
547
|
+
if (tool.description) {
|
|
548
|
+
const shortDesc =
|
|
549
|
+
tool.description.length > 80
|
|
550
|
+
? tool.description.substring(0, 77) + '...'
|
|
551
|
+
: tool.description;
|
|
552
|
+
response += `: ${shortDesc}`;
|
|
553
|
+
}
|
|
554
|
+
response += '\n';
|
|
555
|
+
}
|
|
556
|
+
if (servers.length > 1) {
|
|
557
|
+
response += '\n';
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
response += `\n_To use a tool, search for it by name (e.g., query: "${getBaseToolName(tools[0]?.name ?? 'tool_name')}") to load it._`;
|
|
562
|
+
|
|
563
|
+
return response;
|
|
564
|
+
}
|
|
565
|
+
|
|
398
566
|
/**
|
|
399
567
|
* Creates a Tool Search tool for discovering tools from a large registry.
|
|
400
568
|
*
|
|
@@ -447,6 +615,24 @@ function createToolSearch(
|
|
|
447
615
|
const baseEndpoint = initParams.baseUrl ?? getCodeBaseURL();
|
|
448
616
|
const EXEC_ENDPOINT = `${baseEndpoint}/exec`;
|
|
449
617
|
|
|
618
|
+
const availableServers = getAvailableMcpServers(
|
|
619
|
+
initParams.toolRegistry,
|
|
620
|
+
defaultOnlyDeferred
|
|
621
|
+
);
|
|
622
|
+
|
|
623
|
+
const serverListText =
|
|
624
|
+
availableServers.length > 0
|
|
625
|
+
? `\n- Available MCP servers: ${availableServers.join(', ')}`
|
|
626
|
+
: '';
|
|
627
|
+
|
|
628
|
+
const mcpInstructions = `
|
|
629
|
+
|
|
630
|
+
MCP Server Tools:
|
|
631
|
+
- Tools from MCP servers follow the naming convention: toolName${Constants.MCP_DELIMITER}serverName
|
|
632
|
+
- Example: "get_weather${Constants.MCP_DELIMITER}weather-api" is the "get_weather" tool from the "weather-api" server
|
|
633
|
+
- Use mcp_server parameter to filter by server (e.g., mcp_server: "weather-api")
|
|
634
|
+
- If mcp_server is provided without a query, lists ALL tools from that server${serverListText}`;
|
|
635
|
+
|
|
450
636
|
const description =
|
|
451
637
|
mode === 'local'
|
|
452
638
|
? `
|
|
@@ -458,6 +644,7 @@ Usage:
|
|
|
458
644
|
- Use this when you need to discover tools for a specific task.
|
|
459
645
|
- Results include tool names, match quality scores, and snippets showing where the match occurred.
|
|
460
646
|
- Higher scores (0.95+) indicate name matches, medium scores (0.70+) indicate description matches.
|
|
647
|
+
${mcpInstructions}
|
|
461
648
|
`.trim()
|
|
462
649
|
: `
|
|
463
650
|
Searches through available tools to find ones matching your query pattern.
|
|
@@ -467,6 +654,7 @@ Usage:
|
|
|
467
654
|
- Use this when you need to discover tools for a specific task.
|
|
468
655
|
- Results include tool names, match quality scores, and snippets showing where the match occurred.
|
|
469
656
|
- Higher scores (0.9+) indicate name matches, medium scores (0.7+) indicate description matches.
|
|
657
|
+
${mcpInstructions}
|
|
470
658
|
`.trim();
|
|
471
659
|
|
|
472
660
|
return tool<typeof schema>(
|
|
@@ -475,11 +663,13 @@ Usage:
|
|
|
475
663
|
query,
|
|
476
664
|
fields = ['name', 'description'],
|
|
477
665
|
max_results = 10,
|
|
666
|
+
mcp_server,
|
|
478
667
|
} = params;
|
|
479
668
|
|
|
480
669
|
const {
|
|
481
670
|
toolRegistry: paramToolRegistry,
|
|
482
671
|
onlyDeferred: paramOnlyDeferred,
|
|
672
|
+
mcpServer: paramMcpServer,
|
|
483
673
|
} = config.toolCall ?? {};
|
|
484
674
|
|
|
485
675
|
const toolRegistry = paramToolRegistry ?? initParams.toolRegistry;
|
|
@@ -487,6 +677,10 @@ Usage:
|
|
|
487
677
|
paramOnlyDeferred !== undefined
|
|
488
678
|
? paramOnlyDeferred
|
|
489
679
|
: defaultOnlyDeferred;
|
|
680
|
+
const rawServerFilter =
|
|
681
|
+
mcp_server ?? paramMcpServer ?? initParams.mcpServer;
|
|
682
|
+
const serverFilters = normalizeServerFilter(rawServerFilter);
|
|
683
|
+
const hasServerFilter = serverFilters.length > 0;
|
|
490
684
|
|
|
491
685
|
if (toolRegistry == null) {
|
|
492
686
|
return [
|
|
@@ -504,9 +698,18 @@ Usage:
|
|
|
504
698
|
|
|
505
699
|
const toolsArray: t.LCTool[] = Array.from(toolRegistry.values());
|
|
506
700
|
const deferredTools: t.ToolMetadata[] = toolsArray
|
|
507
|
-
.filter((lcTool) =>
|
|
508
|
-
onlyDeferred === true
|
|
509
|
-
|
|
701
|
+
.filter((lcTool) => {
|
|
702
|
+
if (onlyDeferred === true && lcTool.defer_loading !== true) {
|
|
703
|
+
return false;
|
|
704
|
+
}
|
|
705
|
+
if (
|
|
706
|
+
hasServerFilter &&
|
|
707
|
+
!isFromAnyMcpServer(lcTool.name, serverFilters)
|
|
708
|
+
) {
|
|
709
|
+
return false;
|
|
710
|
+
}
|
|
711
|
+
return true;
|
|
712
|
+
})
|
|
510
713
|
.map((lcTool) => ({
|
|
511
714
|
name: lcTool.name,
|
|
512
715
|
description: lcTool.description ?? '',
|
|
@@ -514,11 +717,39 @@ Usage:
|
|
|
514
717
|
}));
|
|
515
718
|
|
|
516
719
|
if (deferredTools.length === 0) {
|
|
720
|
+
const serverMsg = hasServerFilter
|
|
721
|
+
? ` from MCP server(s): ${serverFilters.join(', ')}`
|
|
722
|
+
: '';
|
|
517
723
|
return [
|
|
518
|
-
|
|
724
|
+
`No tools available to search${serverMsg}. The tool registry is empty or no matching deferred tools are registered.`,
|
|
519
725
|
{
|
|
520
726
|
tool_references: [],
|
|
521
|
-
metadata: {
|
|
727
|
+
metadata: {
|
|
728
|
+
total_searched: 0,
|
|
729
|
+
pattern: query,
|
|
730
|
+
mcp_server: serverFilters,
|
|
731
|
+
},
|
|
732
|
+
},
|
|
733
|
+
];
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
const isServerListing = hasServerFilter && query === '';
|
|
737
|
+
|
|
738
|
+
if (isServerListing) {
|
|
739
|
+
const formattedOutput = formatServerListing(
|
|
740
|
+
deferredTools,
|
|
741
|
+
serverFilters
|
|
742
|
+
);
|
|
743
|
+
|
|
744
|
+
return [
|
|
745
|
+
formattedOutput,
|
|
746
|
+
{
|
|
747
|
+
tool_references: [],
|
|
748
|
+
metadata: {
|
|
749
|
+
total_available: deferredTools.length,
|
|
750
|
+
mcp_server: serverFilters,
|
|
751
|
+
listing_mode: true,
|
|
752
|
+
},
|
|
522
753
|
},
|
|
523
754
|
];
|
|
524
755
|
}
|
|
@@ -539,6 +770,7 @@ Usage:
|
|
|
539
770
|
metadata: {
|
|
540
771
|
total_searched: searchResponse.total_tools_searched,
|
|
541
772
|
pattern: searchResponse.pattern_used,
|
|
773
|
+
mcp_server: serverFilters.length > 0 ? serverFilters : undefined,
|
|
542
774
|
},
|
|
543
775
|
},
|
|
544
776
|
];
|
|
@@ -648,6 +880,13 @@ Usage:
|
|
|
648
880
|
export {
|
|
649
881
|
createToolSearch,
|
|
650
882
|
performLocalSearch,
|
|
883
|
+
extractMcpServerName,
|
|
884
|
+
isFromMcpServer,
|
|
885
|
+
isFromAnyMcpServer,
|
|
886
|
+
normalizeServerFilter,
|
|
887
|
+
getAvailableMcpServers,
|
|
888
|
+
getBaseToolName,
|
|
889
|
+
formatServerListing,
|
|
651
890
|
sanitizeRegex,
|
|
652
891
|
escapeRegexSpecialChars,
|
|
653
892
|
isDangerousPattern,
|