@mcp-ts/sdk 1.5.1 → 1.5.3
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/README.md +89 -27
- package/dist/adapters/agui-adapter.d.mts +1 -1
- package/dist/adapters/agui-adapter.d.ts +1 -1
- package/dist/adapters/agui-adapter.js +50 -10
- package/dist/adapters/agui-adapter.js.map +1 -1
- package/dist/adapters/agui-adapter.mjs +50 -10
- package/dist/adapters/agui-adapter.mjs.map +1 -1
- package/dist/adapters/agui-middleware.d.mts +5 -1
- package/dist/adapters/agui-middleware.d.ts +5 -1
- package/dist/adapters/agui-middleware.js +116 -49
- package/dist/adapters/agui-middleware.js.map +1 -1
- package/dist/adapters/agui-middleware.mjs +117 -50
- package/dist/adapters/agui-middleware.mjs.map +1 -1
- package/dist/adapters/ai-adapter.d.mts +1 -1
- package/dist/adapters/ai-adapter.d.ts +1 -1
- package/dist/adapters/ai-adapter.js +49 -9
- package/dist/adapters/ai-adapter.js.map +1 -1
- package/dist/adapters/ai-adapter.mjs +49 -9
- package/dist/adapters/ai-adapter.mjs.map +1 -1
- package/dist/adapters/langchain-adapter.d.mts +1 -1
- package/dist/adapters/langchain-adapter.d.ts +1 -1
- package/dist/adapters/langchain-adapter.js +49 -9
- package/dist/adapters/langchain-adapter.js.map +1 -1
- package/dist/adapters/langchain-adapter.mjs +49 -9
- package/dist/adapters/langchain-adapter.mjs.map +1 -1
- package/dist/client/react.d.mts +10 -0
- package/dist/client/react.d.ts +10 -0
- package/dist/client/react.js +23 -0
- package/dist/client/react.js.map +1 -1
- package/dist/client/react.mjs +23 -0
- package/dist/client/react.mjs.map +1 -1
- package/dist/client/vue.d.mts +10 -0
- package/dist/client/vue.d.ts +10 -0
- package/dist/client/vue.js +17 -0
- package/dist/client/vue.js.map +1 -1
- package/dist/client/vue.mjs +17 -0
- package/dist/client/vue.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +123 -28
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +123 -28
- package/dist/index.mjs.map +1 -1
- package/dist/shared/index.d.mts +2 -2
- package/dist/shared/index.d.ts +2 -2
- package/dist/shared/index.js +123 -28
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/index.mjs +123 -28
- package/dist/shared/index.mjs.map +1 -1
- package/dist/{tool-router-XnWVxPzv.d.mts → tool-router-DK0RJblO.d.mts} +3 -0
- package/dist/{tool-router-Bo8qZbsD.d.ts → tool-router-DsKhRmJm.d.ts} +3 -0
- package/package.json +3 -3
- package/src/adapters/agui-adapter.ts +7 -7
- package/src/adapters/agui-middleware.ts +163 -59
- package/src/adapters/ai-adapter.ts +5 -5
- package/src/adapters/langchain-adapter.ts +5 -5
- package/src/client/react/use-mcp.ts +48 -0
- package/src/client/vue/use-mcp.ts +42 -0
- package/src/shared/meta-tools.ts +73 -15
- package/src/shared/tool-index.ts +85 -12
- package/src/shared/tool-router.ts +8 -7
package/src/shared/meta-tools.ts
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import type { Tool, CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
19
19
|
import type { ToolRouter } from './tool-router.js';
|
|
20
|
+
import type { IndexedTool } from './tool-index.js';
|
|
20
21
|
|
|
21
22
|
// ---------------------------------------------------------------------------
|
|
22
23
|
// Tool Definitions
|
|
@@ -33,16 +34,18 @@ export function createSearchToolDefinition(): Tool {
|
|
|
33
34
|
return {
|
|
34
35
|
name: 'mcp_search_tool_bm25',
|
|
35
36
|
description:
|
|
36
|
-
'Search the catalog of available tools
|
|
37
|
-
'
|
|
38
|
-
'
|
|
39
|
-
'
|
|
37
|
+
'Search the catalog of available tools. Returns tool names, descriptions, and server info. ' +
|
|
38
|
+
'Use this FIRST to find relevant tools before calling them.\n\n' +
|
|
39
|
+
'Query forms:\n' +
|
|
40
|
+
'- "select:Read,Edit,Grep" — fetch these exact tools by name\n' +
|
|
41
|
+
'- "notebook jupyter" — keyword search, up to limit best matches\n' +
|
|
42
|
+
'- "+slack send" — require "slack" in the name, rank by remaining terms',
|
|
40
43
|
inputSchema: {
|
|
41
44
|
type: 'object' as const,
|
|
42
45
|
properties: {
|
|
43
46
|
query: {
|
|
44
47
|
type: 'string',
|
|
45
|
-
description: '
|
|
48
|
+
description: 'Query to find tools. Use "select:<tool_name>" for direct selection, or keywords to search. Prefix keywords with + to require them.',
|
|
46
49
|
},
|
|
47
50
|
limit: {
|
|
48
51
|
type: 'number',
|
|
@@ -97,7 +100,8 @@ export function createGetSchemaToolDefinition(): Tool {
|
|
|
97
100
|
description:
|
|
98
101
|
'Get the full input schema (parameters) for a specific tool. ' +
|
|
99
102
|
'Call this after mcp_search_tool_bm25 to get the parameter details ' +
|
|
100
|
-
'needed to call a tool correctly.'
|
|
103
|
+
'needed to call a tool correctly. ' +
|
|
104
|
+
'Do NOT call the discovered tool directly; after reading the schema, call mcp_execute_tool.',
|
|
101
105
|
inputSchema: {
|
|
102
106
|
type: 'object' as const,
|
|
103
107
|
properties: {
|
|
@@ -105,10 +109,10 @@ export function createGetSchemaToolDefinition(): Tool {
|
|
|
105
109
|
type: 'string',
|
|
106
110
|
description: 'The exact tool name returned by mcp_search_tool_bm25.',
|
|
107
111
|
},
|
|
108
|
-
|
|
112
|
+
serverId: {
|
|
109
113
|
type: 'string',
|
|
110
114
|
description:
|
|
111
|
-
'Optional: The server
|
|
115
|
+
'Optional: The server ID provided in mcp_search_tool_bm25. Required if multiple tools have the same name.',
|
|
112
116
|
},
|
|
113
117
|
},
|
|
114
118
|
required: ['toolName'],
|
|
@@ -141,10 +145,10 @@ export function createExecuteToolDefinition(): Tool {
|
|
|
141
145
|
type: 'string',
|
|
142
146
|
description: 'The exact tool name from mcp_search_tool_bm25 results.',
|
|
143
147
|
},
|
|
144
|
-
|
|
148
|
+
serverId: {
|
|
145
149
|
type: 'string',
|
|
146
150
|
description:
|
|
147
|
-
'Optional: The server
|
|
151
|
+
'Optional: The server ID provided in mcp_search_tool_bm25. Required if multiple tools have the same name.',
|
|
148
152
|
},
|
|
149
153
|
args: {
|
|
150
154
|
type: 'object',
|
|
@@ -187,7 +191,7 @@ export async function executeMetaTool(
|
|
|
187
191
|
router: ToolRouter,
|
|
188
192
|
callToolFn?: CallToolFn
|
|
189
193
|
): Promise<CallToolResult | null> {
|
|
190
|
-
const resolveToolSchema = (name: string, namespace?: string): { tool?:
|
|
194
|
+
const resolveToolSchema = (name: string, namespace?: string): { tool?: IndexedTool; error?: CallToolResult } => {
|
|
191
195
|
try {
|
|
192
196
|
return { tool: router.getToolSchema(name, namespace) };
|
|
193
197
|
} catch (err) {
|
|
@@ -206,6 +210,53 @@ export async function executeMetaTool(
|
|
|
206
210
|
const query = String(args.query ?? '');
|
|
207
211
|
const limit = Math.min(Number(args.limit) || 5, 20);
|
|
208
212
|
|
|
213
|
+
// Fast path: Check for select: prefix
|
|
214
|
+
const selectMatch = query.match(/^select:(.+)$/i);
|
|
215
|
+
if (selectMatch) {
|
|
216
|
+
const requested = selectMatch[1]!
|
|
217
|
+
.split(',')
|
|
218
|
+
.map((s) => s.trim())
|
|
219
|
+
.filter(Boolean);
|
|
220
|
+
|
|
221
|
+
const found: any[] = [];
|
|
222
|
+
const errors: string[] = [];
|
|
223
|
+
|
|
224
|
+
for (const requestedToolName of requested) {
|
|
225
|
+
const { tool, error } = resolveToolSchema(requestedToolName);
|
|
226
|
+
if (error) {
|
|
227
|
+
const errorMsg = error.content[0]?.type === 'text' ? error.content[0].text : 'Unknown error';
|
|
228
|
+
errors.push(`- **${requestedToolName}**: ${errorMsg}`);
|
|
229
|
+
} else if (tool) {
|
|
230
|
+
found.push(tool);
|
|
231
|
+
} else {
|
|
232
|
+
errors.push(`- **${requestedToolName}**: Tool not found. Try searching with mcp_search_tool_bm25.`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const lines: string[] = [];
|
|
237
|
+
|
|
238
|
+
if (found.length > 0) {
|
|
239
|
+
lines.push(...found.map((t, i) =>
|
|
240
|
+
`${i + 1}. **${t.name}** (server: ${t.serverName}, serverId: ${t.serverId})\n ${t.description}`
|
|
241
|
+
));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (errors.length > 0) {
|
|
245
|
+
if (lines.length > 0) lines.push(""); // Add empty line spacing
|
|
246
|
+
lines.push("Errors resolving some tools:");
|
|
247
|
+
lines.push(...errors);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const text = lines.length > 0
|
|
251
|
+
? lines.join('\n')
|
|
252
|
+
: `No tools found matching select query: ${requested.join(', ')}`;
|
|
253
|
+
|
|
254
|
+
return {
|
|
255
|
+
content: [{ type: 'text', text }],
|
|
256
|
+
isError: found.length === 0,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
209
260
|
const results = await router.searchTools(query, limit);
|
|
210
261
|
|
|
211
262
|
const text = results.length === 0
|
|
@@ -213,7 +264,7 @@ export async function executeMetaTool(
|
|
|
213
264
|
: results
|
|
214
265
|
.map(
|
|
215
266
|
(t, i) =>
|
|
216
|
-
`${i + 1}. **${t.name}** (server: ${t.serverName})\n` +
|
|
267
|
+
`${i + 1}. **${t.name}** (server: ${t.serverName}, serverId: ${t.serverId})\n` +
|
|
217
268
|
` ${t.description}\n` +
|
|
218
269
|
` Estimated tokens: ${t.estimatedTokens}`
|
|
219
270
|
)
|
|
@@ -236,7 +287,7 @@ export async function executeMetaTool(
|
|
|
236
287
|
: results
|
|
237
288
|
.map(
|
|
238
289
|
(t, i) =>
|
|
239
|
-
`${i + 1}. **${t.name}** (server: ${t.serverName})\n` +
|
|
290
|
+
`${i + 1}. **${t.name}** (server: ${t.serverName}, serverId: ${t.serverId})\n` +
|
|
240
291
|
` ${t.description}\n` +
|
|
241
292
|
` Estimated tokens: ${t.estimatedTokens}`
|
|
242
293
|
)
|
|
@@ -250,7 +301,7 @@ export async function executeMetaTool(
|
|
|
250
301
|
|
|
251
302
|
case 'mcp_get_tool_schema': {
|
|
252
303
|
const name = String(args.toolName ?? '');
|
|
253
|
-
const namespace = String(args.
|
|
304
|
+
const namespace = String(args.serverId ?? '') || undefined;
|
|
254
305
|
const { tool, error } = resolveToolSchema(name, namespace);
|
|
255
306
|
|
|
256
307
|
if (error) {
|
|
@@ -273,6 +324,13 @@ export async function executeMetaTool(
|
|
|
273
324
|
name: tool.name,
|
|
274
325
|
description: tool.description,
|
|
275
326
|
inputSchema: tool.inputSchema,
|
|
327
|
+
executionInstructions: {
|
|
328
|
+
nextTool: 'mcp_execute_tool',
|
|
329
|
+
toolName: tool.name,
|
|
330
|
+
serverId: tool.serverId,
|
|
331
|
+
note:
|
|
332
|
+
'Do not call this discovered tool directly unless it was explicitly registered as a runtime tool. Execute it via mcp_execute_tool and pass these parameters inside args.',
|
|
333
|
+
},
|
|
276
334
|
};
|
|
277
335
|
|
|
278
336
|
return {
|
|
@@ -283,7 +341,7 @@ export async function executeMetaTool(
|
|
|
283
341
|
|
|
284
342
|
case 'mcp_execute_tool': {
|
|
285
343
|
const targetToolName = String(args.toolName ?? '');
|
|
286
|
-
const namespace = String(args.
|
|
344
|
+
const namespace = String(args.serverId ?? '') || undefined;
|
|
287
345
|
const toolArgs = (args.args as Record<string, unknown>) ?? {};
|
|
288
346
|
|
|
289
347
|
if (!targetToolName) {
|
package/src/shared/tool-index.ts
CHANGED
|
@@ -24,6 +24,8 @@ export interface ToolSummary {
|
|
|
24
24
|
description: string;
|
|
25
25
|
/** Server that owns this tool */
|
|
26
26
|
serverName: string;
|
|
27
|
+
/** Unique ID of the server */
|
|
28
|
+
serverId: string;
|
|
27
29
|
/** Session the tool belongs to */
|
|
28
30
|
sessionId: string;
|
|
29
31
|
/** Estimated token cost of the full inputSchema */
|
|
@@ -33,6 +35,7 @@ export interface ToolSummary {
|
|
|
33
35
|
/** A tool with routing metadata attached during indexing. */
|
|
34
36
|
export interface IndexedTool extends Tool {
|
|
35
37
|
sessionId: string;
|
|
38
|
+
serverId: string;
|
|
36
39
|
serverName: string;
|
|
37
40
|
}
|
|
38
41
|
|
|
@@ -177,6 +180,7 @@ export class ToolIndex {
|
|
|
177
180
|
name: tool.name,
|
|
178
181
|
description: tool.description ?? '',
|
|
179
182
|
serverName: tool.serverName,
|
|
183
|
+
serverId: tool.serverId,
|
|
180
184
|
sessionId: tool.sessionId,
|
|
181
185
|
estimatedTokens,
|
|
182
186
|
});
|
|
@@ -258,8 +262,55 @@ export class ToolIndex {
|
|
|
258
262
|
async search(query: string, topK = 5): Promise<ToolSummary[]> {
|
|
259
263
|
if (this.tools.size === 0) return [];
|
|
260
264
|
|
|
261
|
-
const queryLower = query.toLowerCase();
|
|
262
|
-
|
|
265
|
+
const queryLower = query.toLowerCase().trim();
|
|
266
|
+
|
|
267
|
+
// Fast path: Exact tool name match (supports duplicate names across servers)
|
|
268
|
+
const exactMatches = [...this.toolSummaries.values()].filter(
|
|
269
|
+
(summary) => summary.name.toLowerCase() === queryLower
|
|
270
|
+
);
|
|
271
|
+
if (exactMatches.length > 0) {
|
|
272
|
+
return exactMatches.slice(0, topK);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Fast path: MCP prefix match (e.g. "mcp__github")
|
|
276
|
+
if (queryLower.startsWith('mcp__') && queryLower.length > 5) {
|
|
277
|
+
const prefixMatches = [...this.toolSummaries.values()]
|
|
278
|
+
.filter((t) => t.name.toLowerCase().startsWith(queryLower))
|
|
279
|
+
.slice(0, topK);
|
|
280
|
+
if (prefixMatches.length > 0) return prefixMatches;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const queryTermsRaw = queryLower.split(/\s+/).filter((t) => t.length > 0);
|
|
284
|
+
const requiredTerms: string[] = [];
|
|
285
|
+
const optionalTerms: string[] = [];
|
|
286
|
+
|
|
287
|
+
for (const term of queryTermsRaw) {
|
|
288
|
+
if (term.startsWith('+') && term.length > 1) {
|
|
289
|
+
requiredTerms.push(term.slice(1));
|
|
290
|
+
} else {
|
|
291
|
+
optionalTerms.push(term);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const allScoringTerms =
|
|
296
|
+
requiredTerms.length > 0 ? [...requiredTerms, ...optionalTerms] : queryTermsRaw;
|
|
297
|
+
const normalizedQueryText = allScoringTerms.join(' ').trim();
|
|
298
|
+
const queryTokens = this.tokenize(allScoringTerms.join(' '));
|
|
299
|
+
|
|
300
|
+
// Pre-filter: only keep documents that contain ALL required terms
|
|
301
|
+
const candidateKeys = new Set<string>();
|
|
302
|
+
for (const docKey of this.toolSummaries.keys()) {
|
|
303
|
+
if (requiredTerms.length > 0) {
|
|
304
|
+
const text = this.searchTexts.get(docKey) || '';
|
|
305
|
+
const summary = this.toolSummaries.get(docKey)!;
|
|
306
|
+
const nameLower = summary.name.toLowerCase();
|
|
307
|
+
const matchesAll = requiredTerms.every(
|
|
308
|
+
(term) => text.includes(term) || nameLower.includes(term)
|
|
309
|
+
);
|
|
310
|
+
if (!matchesAll) continue;
|
|
311
|
+
}
|
|
312
|
+
candidateKeys.add(docKey);
|
|
313
|
+
}
|
|
263
314
|
|
|
264
315
|
// 1. Keyword scores (BM25)
|
|
265
316
|
const keywordScores = new Map<string, number>();
|
|
@@ -267,7 +318,12 @@ export class ToolIndex {
|
|
|
267
318
|
const k1 = 1.2;
|
|
268
319
|
const b = 0.75;
|
|
269
320
|
|
|
270
|
-
for (const
|
|
321
|
+
for (const docKey of candidateKeys) {
|
|
322
|
+
const docTf = this.tfVectors.get(docKey);
|
|
323
|
+
if (!docTf) continue;
|
|
324
|
+
|
|
325
|
+
const summary = this.toolSummaries.get(docKey)!;
|
|
326
|
+
|
|
271
327
|
let score = 0;
|
|
272
328
|
const docLen = this.docLengths.get(docKey) ?? 0;
|
|
273
329
|
|
|
@@ -276,16 +332,30 @@ export class ToolIndex {
|
|
|
276
332
|
if (tfVal === 0) continue;
|
|
277
333
|
|
|
278
334
|
const idf = this.idf.get(tok) ?? 0;
|
|
279
|
-
|
|
280
335
|
// BM25 formula:
|
|
281
336
|
// score = idf * (tf * (k1 + 1)) / (tf + k1 * (1 - b + b * (docLen / avgDocLength)))
|
|
282
337
|
const numerator = tfVal * (k1 + 1);
|
|
283
338
|
const denominator = tfVal + k1 * (1 - b + b * (docLen / this.avgDocLength));
|
|
284
|
-
|
|
339
|
+
|
|
285
340
|
score += idf * (numerator / denominator);
|
|
286
341
|
}
|
|
287
342
|
|
|
288
|
-
|
|
343
|
+
// Name heuristics: give massive boosts for exact server/tool name matches
|
|
344
|
+
const serverLower = (summary.serverName || summary.serverId || '').toLowerCase();
|
|
345
|
+
const toolLower = summary.name.toLowerCase();
|
|
346
|
+
|
|
347
|
+
for (const term of allScoringTerms) {
|
|
348
|
+
if (serverLower.includes(term)) {
|
|
349
|
+
score += 10;
|
|
350
|
+
}
|
|
351
|
+
if (toolLower.includes(term)) {
|
|
352
|
+
score += 5;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (score > 0) {
|
|
357
|
+
keywordScores.set(docKey, score);
|
|
358
|
+
}
|
|
289
359
|
}
|
|
290
360
|
|
|
291
361
|
// 2. Embedding scores (optional)
|
|
@@ -293,11 +363,14 @@ export class ToolIndex {
|
|
|
293
363
|
|
|
294
364
|
if (this.options.embedFn && this.embeddings.size > 0) {
|
|
295
365
|
try {
|
|
296
|
-
const [queryEmbedding] = await this.options.embedFn([
|
|
366
|
+
const [queryEmbedding] = await this.options.embedFn([normalizedQueryText]);
|
|
297
367
|
if (queryEmbedding) {
|
|
298
368
|
embeddingScores = new Map();
|
|
299
|
-
for (const
|
|
300
|
-
|
|
369
|
+
for (const docKey of candidateKeys) {
|
|
370
|
+
const vec = this.embeddings.get(docKey);
|
|
371
|
+
if (vec) {
|
|
372
|
+
embeddingScores.set(docKey, this.cosineSimilarity(queryEmbedding, vec));
|
|
373
|
+
}
|
|
301
374
|
}
|
|
302
375
|
}
|
|
303
376
|
} catch {
|
|
@@ -309,7 +382,7 @@ export class ToolIndex {
|
|
|
309
382
|
const kw = this.options.keywordWeight;
|
|
310
383
|
const finalScores: Array<{ docKey: string; score: number }> = [];
|
|
311
384
|
|
|
312
|
-
for (const docKey of
|
|
385
|
+
for (const docKey of candidateKeys) {
|
|
313
386
|
const kwScore = keywordScores.get(docKey) ?? 0;
|
|
314
387
|
const embScore = embeddingScores?.get(docKey) ?? 0;
|
|
315
388
|
|
|
@@ -389,7 +462,7 @@ export class ToolIndex {
|
|
|
389
462
|
const list = this.tools.get(name) ?? [];
|
|
390
463
|
if (!namespace) return list;
|
|
391
464
|
|
|
392
|
-
return list.filter((t) => t.sessionId === namespace || t.
|
|
465
|
+
return list.filter((t) => t.sessionId === namespace || t.serverId === namespace);
|
|
393
466
|
}
|
|
394
467
|
|
|
395
468
|
/** All indexed tool names. */
|
|
@@ -463,7 +536,7 @@ export class ToolIndex {
|
|
|
463
536
|
}
|
|
464
537
|
|
|
465
538
|
private getDocumentKey(tool: IndexedTool): string {
|
|
466
|
-
return `${tool.sessionId}::${tool.
|
|
539
|
+
return `${tool.sessionId}::${tool.serverId}::${tool.name}`;
|
|
467
540
|
}
|
|
468
541
|
|
|
469
542
|
/** Simple whitespace + camelCase + snake_case tokenizer. */
|
|
@@ -219,10 +219,10 @@ export class ToolRouter {
|
|
|
219
219
|
if (matches.length === 0) return undefined;
|
|
220
220
|
|
|
221
221
|
if (matches.length > 1) {
|
|
222
|
-
const servers = matches.map((m) => m.
|
|
222
|
+
const servers = matches.map((m) => m.serverId).join(', ');
|
|
223
223
|
throw new Error(
|
|
224
224
|
`Tool "${toolName}" is provided by multiple servers: [${servers}]. ` +
|
|
225
|
-
`Please specify the desired "
|
|
225
|
+
`Please specify the desired "serverId" as a namespace.`
|
|
226
226
|
);
|
|
227
227
|
}
|
|
228
228
|
|
|
@@ -372,6 +372,7 @@ export class ToolRouter {
|
|
|
372
372
|
for (const tool of tools) {
|
|
373
373
|
result.push({
|
|
374
374
|
...tool,
|
|
375
|
+
serverId,
|
|
375
376
|
serverName: serverName,
|
|
376
377
|
sessionId,
|
|
377
378
|
});
|
|
@@ -409,20 +410,20 @@ export class ToolRouter {
|
|
|
409
410
|
});
|
|
410
411
|
}
|
|
411
412
|
} else {
|
|
412
|
-
// Auto-group by server
|
|
413
|
+
// Auto-group by server ID
|
|
413
414
|
const serverTools = new Map<string, string[]>();
|
|
414
415
|
for (const tool of this.allTools) {
|
|
415
|
-
const group = tool.
|
|
416
|
+
const group = tool.serverId;
|
|
416
417
|
if (!serverTools.has(group)) {
|
|
417
418
|
serverTools.set(group, []);
|
|
418
419
|
}
|
|
419
420
|
serverTools.get(group)!.push(tool.name);
|
|
420
421
|
}
|
|
421
422
|
|
|
422
|
-
for (const [
|
|
423
|
-
this.groupsMap.set(
|
|
423
|
+
for (const [serverId, tools] of serverTools) {
|
|
424
|
+
this.groupsMap.set(serverId, {
|
|
424
425
|
tools,
|
|
425
|
-
active: this.activeGroups.size === 0 || this.activeGroups.has(
|
|
426
|
+
active: this.activeGroups.size === 0 || this.activeGroups.has(serverId),
|
|
426
427
|
});
|
|
427
428
|
}
|
|
428
429
|
}
|