@librechat/agents 3.1.30 → 3.1.32
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/messages/prune.cjs +1 -1
- package/dist/cjs/messages/prune.cjs.map +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +36 -53
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +5 -7
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/ToolSearch.cjs +49 -27
- package/dist/cjs/tools/ToolSearch.cjs.map +1 -1
- package/dist/esm/messages/prune.mjs +1 -1
- package/dist/esm/messages/prune.mjs.map +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +36 -53
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +5 -7
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/ToolSearch.mjs +49 -27
- package/dist/esm/tools/ToolSearch.mjs.map +1 -1
- package/dist/types/tools/ToolSearch.d.ts +6 -4
- package/dist/types/types/tools.d.ts +4 -0
- package/package.json +1 -1
- package/src/messages/prune.ts +1 -1
- package/src/specs/thinking-prune.test.ts +172 -51
- package/src/tools/ProgrammaticToolCalling.ts +40 -52
- package/src/tools/ToolNode.ts +4 -5
- package/src/tools/ToolSearch.ts +64 -30
- package/src/tools/__tests__/ToolSearch.test.ts +16 -3
- package/src/types/tools.ts +5 -0
package/src/tools/ToolSearch.ts
CHANGED
|
@@ -38,6 +38,17 @@ export const ToolSearchToolName = Constants.TOOL_SEARCH;
|
|
|
38
38
|
export const ToolSearchToolDescription =
|
|
39
39
|
'Searches deferred tools using BM25 ranking. Multi-word queries supported. Use mcp_server param to filter by server.';
|
|
40
40
|
|
|
41
|
+
const QUERY_DESCRIPTION_LOCAL =
|
|
42
|
+
'Search term to find in tool names and descriptions. Case-insensitive substring matching. Optional if mcp_server is provided.';
|
|
43
|
+
const QUERY_DESCRIPTION_REGEX =
|
|
44
|
+
'Regex pattern to search tool names and descriptions. Optional if mcp_server is provided.';
|
|
45
|
+
const FIELDS_DESCRIPTION =
|
|
46
|
+
'Which fields to search. Default: name and description';
|
|
47
|
+
const MAX_RESULTS_DESCRIPTION = 'Maximum number of matching tools to return';
|
|
48
|
+
const DEFAULT_MAX_RESULTS = 5;
|
|
49
|
+
const MCP_SERVER_DESCRIPTION =
|
|
50
|
+
'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
|
+
|
|
41
52
|
export const ToolSearchToolSchema = {
|
|
42
53
|
type: 'object',
|
|
43
54
|
properties: {
|
|
@@ -45,26 +56,24 @@ export const ToolSearchToolSchema = {
|
|
|
45
56
|
type: 'string',
|
|
46
57
|
maxLength: MAX_PATTERN_LENGTH,
|
|
47
58
|
default: '',
|
|
48
|
-
description:
|
|
49
|
-
'Search term to find in tool names and descriptions. Case-insensitive substring matching. Optional if mcp_server is provided.',
|
|
59
|
+
description: QUERY_DESCRIPTION_LOCAL,
|
|
50
60
|
},
|
|
51
61
|
fields: {
|
|
52
62
|
type: 'array',
|
|
53
63
|
items: { type: 'string', enum: ['name', 'description', 'parameters'] },
|
|
54
64
|
default: ['name', 'description'],
|
|
55
|
-
description:
|
|
65
|
+
description: FIELDS_DESCRIPTION,
|
|
56
66
|
},
|
|
57
67
|
max_results: {
|
|
58
68
|
type: 'integer',
|
|
59
69
|
minimum: 1,
|
|
60
70
|
maximum: 50,
|
|
61
|
-
default:
|
|
62
|
-
description:
|
|
71
|
+
default: DEFAULT_MAX_RESULTS,
|
|
72
|
+
description: MAX_RESULTS_DESCRIPTION,
|
|
63
73
|
},
|
|
64
74
|
mcp_server: {
|
|
65
75
|
oneOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }],
|
|
66
|
-
description:
|
|
67
|
-
'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.',
|
|
76
|
+
description: MCP_SERVER_DESCRIPTION,
|
|
68
77
|
},
|
|
69
78
|
},
|
|
70
79
|
required: [],
|
|
@@ -104,9 +113,7 @@ interface ToolSearchParams {
|
|
|
104
113
|
*/
|
|
105
114
|
function createToolSearchSchema(mode: t.ToolSearchMode): ToolSearchSchema {
|
|
106
115
|
const queryDescription =
|
|
107
|
-
mode === 'local'
|
|
108
|
-
? 'Search term to find in tool names and descriptions. Case-insensitive substring matching. Optional if mcp_server is provided.'
|
|
109
|
-
: 'Regex pattern to search tool names and descriptions. Optional if mcp_server is provided.';
|
|
116
|
+
mode === 'local' ? QUERY_DESCRIPTION_LOCAL : QUERY_DESCRIPTION_REGEX;
|
|
110
117
|
|
|
111
118
|
return {
|
|
112
119
|
type: 'object',
|
|
@@ -121,22 +128,21 @@ function createToolSearchSchema(mode: t.ToolSearchMode): ToolSearchSchema {
|
|
|
121
128
|
type: 'array',
|
|
122
129
|
items: { type: 'string', enum: ['name', 'description', 'parameters'] },
|
|
123
130
|
default: ['name', 'description'],
|
|
124
|
-
description:
|
|
131
|
+
description: FIELDS_DESCRIPTION,
|
|
125
132
|
},
|
|
126
133
|
max_results: {
|
|
127
134
|
type: 'integer',
|
|
128
135
|
minimum: 1,
|
|
129
136
|
maximum: 50,
|
|
130
|
-
default:
|
|
131
|
-
description:
|
|
137
|
+
default: DEFAULT_MAX_RESULTS,
|
|
138
|
+
description: MAX_RESULTS_DESCRIPTION,
|
|
132
139
|
},
|
|
133
140
|
mcp_server: {
|
|
134
141
|
oneOf: [
|
|
135
142
|
{ type: 'string' },
|
|
136
143
|
{ type: 'array', items: { type: 'string' } },
|
|
137
144
|
],
|
|
138
|
-
description:
|
|
139
|
-
'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.',
|
|
145
|
+
description: MCP_SERVER_DESCRIPTION,
|
|
140
146
|
},
|
|
141
147
|
},
|
|
142
148
|
required: [],
|
|
@@ -446,8 +452,9 @@ function findMatchedField(
|
|
|
446
452
|
/**
|
|
447
453
|
* Performs BM25-based search for better relevance ranking.
|
|
448
454
|
* Uses Okapi BM25 algorithm for term frequency and document length normalization.
|
|
455
|
+
* If query is empty, returns all tools (up to maxResults) sorted alphabetically.
|
|
449
456
|
* @param tools - Array of tool metadata to search
|
|
450
|
-
* @param query - The search query
|
|
457
|
+
* @param query - The search query (empty returns all tools)
|
|
451
458
|
* @param fields - Which fields to search
|
|
452
459
|
* @param maxResults - Maximum results to return
|
|
453
460
|
* @returns Search response with matching tools ranked by BM25 score
|
|
@@ -458,25 +465,36 @@ function performLocalSearch(
|
|
|
458
465
|
fields: string[],
|
|
459
466
|
maxResults: number
|
|
460
467
|
): t.ToolSearchResponse {
|
|
461
|
-
if (tools.length === 0
|
|
468
|
+
if (tools.length === 0) {
|
|
462
469
|
return {
|
|
463
470
|
tool_references: [],
|
|
464
|
-
total_tools_searched:
|
|
471
|
+
total_tools_searched: 0,
|
|
465
472
|
pattern_used: query,
|
|
466
473
|
};
|
|
467
474
|
}
|
|
468
475
|
|
|
469
|
-
const documents = tools.map((tool) => createToolDocument(tool, fields));
|
|
470
476
|
const queryTokens = tokenize(query);
|
|
471
477
|
|
|
472
478
|
if (queryTokens.length === 0) {
|
|
479
|
+
const allTools = tools
|
|
480
|
+
.slice()
|
|
481
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
482
|
+
.slice(0, maxResults)
|
|
483
|
+
.map((tool) => ({
|
|
484
|
+
tool_name: tool.name,
|
|
485
|
+
match_score: 1.0,
|
|
486
|
+
matched_field: 'name',
|
|
487
|
+
snippet: tool.description.substring(0, 100) || tool.name,
|
|
488
|
+
}));
|
|
489
|
+
|
|
473
490
|
return {
|
|
474
|
-
tool_references:
|
|
491
|
+
tool_references: allTools,
|
|
475
492
|
total_tools_searched: tools.length,
|
|
476
493
|
pattern_used: query,
|
|
477
494
|
};
|
|
478
495
|
}
|
|
479
496
|
|
|
497
|
+
const documents = tools.map((tool) => createToolDocument(tool, fields));
|
|
480
498
|
const scores = BM25(documents, queryTokens, { k1: 1.5, b: 0.75 }) as number[];
|
|
481
499
|
|
|
482
500
|
const maxScore = Math.max(...scores.filter((s) => s > 0), 1);
|
|
@@ -492,7 +510,6 @@ function performLocalSearch(
|
|
|
492
510
|
);
|
|
493
511
|
let normalizedScore = Math.min(scores[i] / maxScore, 1.0);
|
|
494
512
|
|
|
495
|
-
// Boost score for exact base name match
|
|
496
513
|
const baseName = getBaseToolName(tools[i].name).toLowerCase();
|
|
497
514
|
if (baseName === queryLower) {
|
|
498
515
|
normalizedScore = 1.0;
|
|
@@ -631,16 +648,21 @@ function parseSearchResults(stdout: string): t.ToolSearchResponse {
|
|
|
631
648
|
/**
|
|
632
649
|
* Formats search results as structured JSON for efficient parsing.
|
|
633
650
|
* @param searchResponse - The parsed search response
|
|
651
|
+
* @param nameFormat - Whether to show 'full' names (tool_mcp_server) or 'base' names (tool only)
|
|
634
652
|
* @returns JSON string with search results
|
|
635
653
|
*/
|
|
636
|
-
function formatSearchResults(
|
|
654
|
+
function formatSearchResults(
|
|
655
|
+
searchResponse: t.ToolSearchResponse,
|
|
656
|
+
nameFormat: t.McpNameFormat = 'full'
|
|
657
|
+
): string {
|
|
637
658
|
const { tool_references, total_tools_searched, pattern_used } =
|
|
638
659
|
searchResponse;
|
|
660
|
+
const useFullName = nameFormat === 'full';
|
|
639
661
|
|
|
640
662
|
const output = {
|
|
641
663
|
found: tool_references.length,
|
|
642
664
|
tools: tool_references.map((ref) => ({
|
|
643
|
-
name: ref.tool_name,
|
|
665
|
+
name: useFullName ? ref.tool_name : getBaseToolName(ref.tool_name),
|
|
644
666
|
score: Number(ref.match_score.toFixed(2)),
|
|
645
667
|
matched_in: ref.matched_field,
|
|
646
668
|
snippet: ref.snippet,
|
|
@@ -720,13 +742,16 @@ function getDeferredToolsListing(
|
|
|
720
742
|
* NOTE: This is a PREVIEW only - tools are NOT discovered/loaded.
|
|
721
743
|
* @param tools - Array of tool metadata from the server(s)
|
|
722
744
|
* @param serverNames - The MCP server name(s)
|
|
745
|
+
* @param nameFormat - Whether to show 'full' names (tool_mcp_server) or 'base' names (tool only)
|
|
723
746
|
* @returns JSON string showing all tools grouped by server
|
|
724
747
|
*/
|
|
725
748
|
function formatServerListing(
|
|
726
749
|
tools: t.ToolMetadata[],
|
|
727
|
-
serverNames: string | string[]
|
|
750
|
+
serverNames: string | string[],
|
|
751
|
+
nameFormat: t.McpNameFormat = 'full'
|
|
728
752
|
): string {
|
|
729
753
|
const servers = Array.isArray(serverNames) ? serverNames : [serverNames];
|
|
754
|
+
const useFullName = nameFormat === 'full';
|
|
730
755
|
|
|
731
756
|
if (tools.length === 0) {
|
|
732
757
|
return JSON.stringify(
|
|
@@ -752,7 +777,7 @@ function formatServerListing(
|
|
|
752
777
|
toolsByServer[server] = [];
|
|
753
778
|
}
|
|
754
779
|
toolsByServer[server].push({
|
|
755
|
-
name: getBaseToolName(tool.name),
|
|
780
|
+
name: useFullName ? tool.name : getBaseToolName(tool.name),
|
|
756
781
|
description:
|
|
757
782
|
tool.description.length > 100
|
|
758
783
|
? tool.description.substring(0, 97) + '...'
|
|
@@ -760,12 +785,16 @@ function formatServerListing(
|
|
|
760
785
|
});
|
|
761
786
|
}
|
|
762
787
|
|
|
788
|
+
const exampleToolName = useFullName
|
|
789
|
+
? (tools[0]?.name ?? 'tool_name')
|
|
790
|
+
: getBaseToolName(tools[0]?.name ?? 'tool_name');
|
|
791
|
+
|
|
763
792
|
const output = {
|
|
764
793
|
listing_mode: true,
|
|
765
794
|
servers,
|
|
766
795
|
total_tools: tools.length,
|
|
767
796
|
tools_by_server: toolsByServer,
|
|
768
|
-
hint: `To use a tool, search for it by name (e.g., query: "${
|
|
797
|
+
hint: `To use a tool, search for it by name (e.g., query: "${exampleToolName}") to load it.`,
|
|
769
798
|
};
|
|
770
799
|
|
|
771
800
|
return JSON.stringify(output, null, 2);
|
|
@@ -804,6 +833,7 @@ function createToolSearch(
|
|
|
804
833
|
): DynamicStructuredTool {
|
|
805
834
|
const mode: t.ToolSearchMode = initParams.mode ?? 'code_interpreter';
|
|
806
835
|
const defaultOnlyDeferred = initParams.onlyDeferred ?? true;
|
|
836
|
+
const mcpNameFormat: t.McpNameFormat = initParams.mcpNameFormat ?? 'full';
|
|
807
837
|
const schema = createToolSearchSchema(mode);
|
|
808
838
|
|
|
809
839
|
const apiKey: string =
|
|
@@ -861,7 +891,7 @@ ${mcpNote}${toolsListSection}
|
|
|
861
891
|
const {
|
|
862
892
|
query = '',
|
|
863
893
|
fields = ['name', 'description'],
|
|
864
|
-
max_results =
|
|
894
|
+
max_results = DEFAULT_MAX_RESULTS,
|
|
865
895
|
mcp_server,
|
|
866
896
|
} = params;
|
|
867
897
|
|
|
@@ -937,7 +967,8 @@ ${mcpNote}${toolsListSection}
|
|
|
937
967
|
if (isServerListing) {
|
|
938
968
|
const formattedOutput = formatServerListing(
|
|
939
969
|
deferredTools,
|
|
940
|
-
serverFilters
|
|
970
|
+
serverFilters,
|
|
971
|
+
mcpNameFormat
|
|
941
972
|
);
|
|
942
973
|
|
|
943
974
|
return [
|
|
@@ -960,7 +991,10 @@ ${mcpNote}${toolsListSection}
|
|
|
960
991
|
fields,
|
|
961
992
|
max_results
|
|
962
993
|
);
|
|
963
|
-
const formattedOutput = formatSearchResults(
|
|
994
|
+
const formattedOutput = formatSearchResults(
|
|
995
|
+
searchResponse,
|
|
996
|
+
mcpNameFormat
|
|
997
|
+
);
|
|
964
998
|
|
|
965
999
|
return [
|
|
966
1000
|
formattedOutput,
|
|
@@ -1036,7 +1070,7 @@ ${mcpNote}${toolsListSection}
|
|
|
1036
1070
|
}
|
|
1037
1071
|
|
|
1038
1072
|
const searchResponse = parseSearchResults(result.stdout);
|
|
1039
|
-
const formattedOutput = `${warningMessage}${formatSearchResults(searchResponse)}`;
|
|
1073
|
+
const formattedOutput = `${warningMessage}${formatSearchResults(searchResponse, mcpNameFormat)}`;
|
|
1040
1074
|
|
|
1041
1075
|
return [
|
|
1042
1076
|
formattedOutput,
|
|
@@ -426,8 +426,10 @@ describe('ToolSearch', () => {
|
|
|
426
426
|
it('handles empty query gracefully', () => {
|
|
427
427
|
const result = performLocalSearch(mockTools, '', ['name'], 10);
|
|
428
428
|
|
|
429
|
-
//
|
|
430
|
-
expect(result.tool_references.length).toBe(
|
|
429
|
+
// Empty queries return all tools (up to maxResults) sorted alphabetically
|
|
430
|
+
expect(result.tool_references.length).toBe(
|
|
431
|
+
Math.min(mockTools.length, 10)
|
|
432
|
+
);
|
|
431
433
|
expect(result.total_tools_searched).toBe(mockTools.length);
|
|
432
434
|
});
|
|
433
435
|
|
|
@@ -923,10 +925,21 @@ describe('ToolSearch', () => {
|
|
|
923
925
|
expect(parsed.hint).toContain('To use a tool, search for it by name');
|
|
924
926
|
});
|
|
925
927
|
|
|
926
|
-
it('uses
|
|
928
|
+
it('uses full tool name by default', () => {
|
|
927
929
|
const result = formatServerListing(serverTools, 'weather-api');
|
|
928
930
|
const parsed = JSON.parse(result);
|
|
929
931
|
|
|
932
|
+
const toolNames = parsed.tools_by_server['weather-api'].map(
|
|
933
|
+
(t: { name: string }) => t.name
|
|
934
|
+
);
|
|
935
|
+
expect(toolNames).toContain('get_weather_mcp_weather-api');
|
|
936
|
+
expect(toolNames).not.toContain('get_weather');
|
|
937
|
+
});
|
|
938
|
+
|
|
939
|
+
it('uses base tool name when mcpNameFormat is "base"', () => {
|
|
940
|
+
const result = formatServerListing(serverTools, 'weather-api', 'base');
|
|
941
|
+
const parsed = JSON.parse(result);
|
|
942
|
+
|
|
930
943
|
const toolNames = parsed.tools_by_server['weather-api'].map(
|
|
931
944
|
(t: { name: string }) => t.name
|
|
932
945
|
);
|
package/src/types/tools.ts
CHANGED
|
@@ -193,6 +193,9 @@ export type ProgrammaticCache = { toolMap: ToolMap; toolDefs: LCTool[] };
|
|
|
193
193
|
/** Search mode: code_interpreter uses external sandbox, local uses safe substring matching */
|
|
194
194
|
export type ToolSearchMode = 'code_interpreter' | 'local';
|
|
195
195
|
|
|
196
|
+
/** Format for MCP tool names in search results */
|
|
197
|
+
export type McpNameFormat = 'full' | 'base';
|
|
198
|
+
|
|
196
199
|
/** Parameters for creating a Tool Search tool */
|
|
197
200
|
export type ToolSearchParams = {
|
|
198
201
|
apiKey?: string;
|
|
@@ -203,6 +206,8 @@ export type ToolSearchParams = {
|
|
|
203
206
|
mode?: ToolSearchMode;
|
|
204
207
|
/** Filter tools to only those from specific MCP server(s). Can be a single name or array of names. */
|
|
205
208
|
mcpServer?: string | string[];
|
|
209
|
+
/** Format for MCP tool names: 'full' (tool_mcp_server) or 'base' (tool only). Default: 'full' */
|
|
210
|
+
mcpNameFormat?: McpNameFormat;
|
|
206
211
|
[key: string]: unknown;
|
|
207
212
|
};
|
|
208
213
|
|