@mcp-ts/sdk 1.5.3 → 1.6.0

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.
Files changed (46) hide show
  1. package/dist/adapters/agui-adapter.d.mts +1 -1
  2. package/dist/adapters/agui-adapter.d.ts +1 -1
  3. package/dist/adapters/agui-adapter.js +69 -18
  4. package/dist/adapters/agui-adapter.js.map +1 -1
  5. package/dist/adapters/agui-adapter.mjs +69 -18
  6. package/dist/adapters/agui-adapter.mjs.map +1 -1
  7. package/dist/adapters/agui-middleware.d.mts +1 -1
  8. package/dist/adapters/agui-middleware.d.ts +1 -1
  9. package/dist/adapters/ai-adapter.d.mts +1 -1
  10. package/dist/adapters/ai-adapter.d.ts +1 -1
  11. package/dist/adapters/ai-adapter.js +69 -18
  12. package/dist/adapters/ai-adapter.js.map +1 -1
  13. package/dist/adapters/ai-adapter.mjs +69 -18
  14. package/dist/adapters/ai-adapter.mjs.map +1 -1
  15. package/dist/adapters/langchain-adapter.d.mts +1 -1
  16. package/dist/adapters/langchain-adapter.d.ts +1 -1
  17. package/dist/adapters/langchain-adapter.js +69 -18
  18. package/dist/adapters/langchain-adapter.js.map +1 -1
  19. package/dist/adapters/langchain-adapter.mjs +69 -18
  20. package/dist/adapters/langchain-adapter.mjs.map +1 -1
  21. package/dist/client/react.js.map +1 -1
  22. package/dist/client/react.mjs.map +1 -1
  23. package/dist/index.d.mts +2 -2
  24. package/dist/index.d.ts +2 -2
  25. package/dist/index.js +200 -42
  26. package/dist/index.js.map +1 -1
  27. package/dist/index.mjs +200 -43
  28. package/dist/index.mjs.map +1 -1
  29. package/dist/server/index.js +1 -3
  30. package/dist/server/index.js.map +1 -1
  31. package/dist/server/index.mjs +1 -3
  32. package/dist/server/index.mjs.map +1 -1
  33. package/dist/shared/index.d.mts +15 -8
  34. package/dist/shared/index.d.ts +15 -8
  35. package/dist/shared/index.js +199 -39
  36. package/dist/shared/index.js.map +1 -1
  37. package/dist/shared/index.mjs +199 -40
  38. package/dist/shared/index.mjs.map +1 -1
  39. package/dist/{tool-router-DsKhRmJm.d.ts → tool-router-Bn9R0KWr.d.ts} +56 -7
  40. package/dist/{tool-router-DK0RJblO.d.mts → tool-router-_O2tIwf7.d.mts} +56 -7
  41. package/package.json +3 -1
  42. package/src/server/mcp/oauth-client.ts +4 -4
  43. package/src/shared/index.ts +4 -0
  44. package/src/shared/meta-tools.ts +163 -37
  45. package/src/shared/tool-index.ts +123 -7
  46. package/src/shared/tool-router.ts +40 -7
@@ -80,6 +80,39 @@ interface ToolSummary {
80
80
  /** Estimated token cost of the full inputSchema */
81
81
  estimatedTokens: number;
82
82
  }
83
+ /** Server-level summary derived from indexed tools. */
84
+ interface ToolServerSummary {
85
+ /** Human-readable server name */
86
+ serverName: string;
87
+ /** Stable server identifier */
88
+ serverId: string;
89
+ /** Session the server belongs to */
90
+ sessionId: string;
91
+ /** Number of indexed tools for this server */
92
+ toolCount: number;
93
+ }
94
+ /** Optional filters for search and listing. */
95
+ interface ToolSearchOptions {
96
+ /** Restrict results to this server ID. */
97
+ serverId?: string;
98
+ /** Restrict results to servers whose name or ID matches this value. */
99
+ serverName?: string;
100
+ }
101
+ /** Paginated tool listing result. */
102
+ interface ToolListResult {
103
+ tools: ToolSummary[];
104
+ totalCount: number;
105
+ returnedCount: number;
106
+ nextCursor?: string;
107
+ servers: ToolServerSummary[];
108
+ }
109
+ interface ToolLookupOptions {
110
+ /**
111
+ * Allow namespace to match a fragment of serverName after exact
112
+ * sessionId/serverId matching fails.
113
+ */
114
+ allowServerNameFragment?: boolean;
115
+ }
83
116
  /** A tool with routing metadata attached during indexing. */
84
117
  interface IndexedTool extends Tool {
85
118
  sessionId: string;
@@ -147,7 +180,7 @@ declare class ToolIndex {
147
180
  *
148
181
  * `score = keywordWeight × keyword_score + (1 - keywordWeight) × cosine_score`
149
182
  */
150
- search(query: string, topK?: number): Promise<ToolSummary[]>;
183
+ search(query: string, topK?: number, options?: ToolSearchOptions): Promise<ToolSummary[]>;
151
184
  /**
152
185
  * Search tools using a regex pattern.
153
186
  * Matches against name, description, and parameter metadata.
@@ -155,11 +188,19 @@ declare class ToolIndex {
155
188
  searchRegex(pattern: string, topK?: number): ToolSummary[];
156
189
  /**
157
190
  * Get tool definition(s) by name.
158
- * If namespace is provided, it tries to match sessionId or serverName.
191
+ * If namespace is provided, exact sessionId/serverId matches take precedence.
192
+ * Falls back to serverName fragment matching only when explicitly allowed.
159
193
  */
160
- getTool(name: string, namespace?: string): IndexedTool[];
194
+ getTool(name: string, namespace?: string, options?: ToolLookupOptions): IndexedTool[];
161
195
  /** All indexed tool names. */
162
196
  getToolNames(): string[];
197
+ /** List indexed servers with tool counts. */
198
+ listServers(options?: ToolSearchOptions): ToolServerSummary[];
199
+ /** List tools deterministically, optionally scoped to a server. */
200
+ listTools(options?: ToolSearchOptions & {
201
+ limit?: number;
202
+ cursor?: string;
203
+ }): ToolListResult;
163
204
  /** Number of indexed tools (including duplicates). */
164
205
  get size(): number;
165
206
  /** Total estimated token cost of all indexed tool schemas. */
@@ -174,6 +215,7 @@ declare class ToolIndex {
174
215
  /** Build a single searchable string from tool metadata. */
175
216
  private buildSearchableText;
176
217
  private getDocumentKey;
218
+ private matchesServer;
177
219
  /** Simple whitespace + camelCase + snake_case tokenizer. */
178
220
  private tokenize;
179
221
  /** Cosine similarity between two vectors. */
@@ -262,7 +304,7 @@ declare class ToolRouter {
262
304
  * This is the main method adapters should call.
263
305
  *
264
306
  * - `all` → returns all tools (unchanged behavior)
265
- * - `search` → returns only meta-tools (mcp_search_tool_bm25, mcp_get_tool_schema, mcp_execute_tool)
307
+ * - `search` → returns only meta-tools (mcp_search_tools, mcp_get_tool_schema, mcp_execute_tool)
266
308
  * - `groups` → returns tools from active groups only
267
309
  */
268
310
  getFilteredTools(): Promise<Tool[]>;
@@ -270,17 +312,24 @@ declare class ToolRouter {
270
312
  * Search tools by natural-language query.
271
313
  * Works regardless of strategy.
272
314
  */
273
- searchTools(query: string, topK?: number): Promise<ToolSummary[]>;
315
+ searchTools(query: string, topK?: number, options?: ToolSearchOptions): Promise<ToolSummary[]>;
274
316
  /**
275
317
  * Search tools by regex pattern.
276
318
  * Matches against name, description, and parameter metadata.
277
319
  */
278
320
  searchToolsRegex(pattern: string, topK?: number): Promise<ToolSummary[]>;
321
+ /** List connected MCP servers with indexed tool counts. */
322
+ listServers(options?: ToolSearchOptions): Promise<ToolServerSummary[]>;
323
+ /** List tools deterministically, optionally scoped to a server. */
324
+ listTools(options?: ToolSearchOptions & {
325
+ limit?: number;
326
+ cursor?: string;
327
+ }): Promise<ToolListResult>;
279
328
  /**
280
329
  * Get the full tool definition by name.
281
330
  * If tool name is ambiguous, use namespace to specify the server.
282
331
  */
283
- getToolSchema(toolName: string, namespace?: string): IndexedTool | undefined;
332
+ getToolSchema(toolName: string, namespace?: string, options?: ToolLookupOptions): IndexedTool | undefined;
284
333
  /**
285
334
  * Get compact (schema-less) summaries for all tools.
286
335
  */
@@ -325,4 +374,4 @@ declare class ToolRouter {
325
374
  private getMetaToolDefinitions;
326
375
  }
327
376
 
328
- export { type CompactTool as C, type EmbedFn as E, type IndexedTool as I, SchemaCompressor as S, type ToolGroupInfo as T, type CompressionStats as a, ToolIndex as b, type ToolIndexOptions as c, ToolRouter as d, type ToolRouterClientInput as e, type ToolRouterOptions as f, type ToolRouterStrategy as g, type ToolSummary as h };
377
+ export { type CompactTool as C, type EmbedFn as E, type IndexedTool as I, SchemaCompressor as S, type ToolGroupInfo as T, type CompressionStats as a, ToolIndex as b, type ToolIndexOptions as c, type ToolListResult as d, ToolRouter as e, type ToolRouterClientInput as f, type ToolRouterOptions as g, type ToolRouterStrategy as h, type ToolSearchOptions as i, type ToolServerSummary as j, type ToolSummary as k };
@@ -80,6 +80,39 @@ interface ToolSummary {
80
80
  /** Estimated token cost of the full inputSchema */
81
81
  estimatedTokens: number;
82
82
  }
83
+ /** Server-level summary derived from indexed tools. */
84
+ interface ToolServerSummary {
85
+ /** Human-readable server name */
86
+ serverName: string;
87
+ /** Stable server identifier */
88
+ serverId: string;
89
+ /** Session the server belongs to */
90
+ sessionId: string;
91
+ /** Number of indexed tools for this server */
92
+ toolCount: number;
93
+ }
94
+ /** Optional filters for search and listing. */
95
+ interface ToolSearchOptions {
96
+ /** Restrict results to this server ID. */
97
+ serverId?: string;
98
+ /** Restrict results to servers whose name or ID matches this value. */
99
+ serverName?: string;
100
+ }
101
+ /** Paginated tool listing result. */
102
+ interface ToolListResult {
103
+ tools: ToolSummary[];
104
+ totalCount: number;
105
+ returnedCount: number;
106
+ nextCursor?: string;
107
+ servers: ToolServerSummary[];
108
+ }
109
+ interface ToolLookupOptions {
110
+ /**
111
+ * Allow namespace to match a fragment of serverName after exact
112
+ * sessionId/serverId matching fails.
113
+ */
114
+ allowServerNameFragment?: boolean;
115
+ }
83
116
  /** A tool with routing metadata attached during indexing. */
84
117
  interface IndexedTool extends Tool {
85
118
  sessionId: string;
@@ -147,7 +180,7 @@ declare class ToolIndex {
147
180
  *
148
181
  * `score = keywordWeight × keyword_score + (1 - keywordWeight) × cosine_score`
149
182
  */
150
- search(query: string, topK?: number): Promise<ToolSummary[]>;
183
+ search(query: string, topK?: number, options?: ToolSearchOptions): Promise<ToolSummary[]>;
151
184
  /**
152
185
  * Search tools using a regex pattern.
153
186
  * Matches against name, description, and parameter metadata.
@@ -155,11 +188,19 @@ declare class ToolIndex {
155
188
  searchRegex(pattern: string, topK?: number): ToolSummary[];
156
189
  /**
157
190
  * Get tool definition(s) by name.
158
- * If namespace is provided, it tries to match sessionId or serverName.
191
+ * If namespace is provided, exact sessionId/serverId matches take precedence.
192
+ * Falls back to serverName fragment matching only when explicitly allowed.
159
193
  */
160
- getTool(name: string, namespace?: string): IndexedTool[];
194
+ getTool(name: string, namespace?: string, options?: ToolLookupOptions): IndexedTool[];
161
195
  /** All indexed tool names. */
162
196
  getToolNames(): string[];
197
+ /** List indexed servers with tool counts. */
198
+ listServers(options?: ToolSearchOptions): ToolServerSummary[];
199
+ /** List tools deterministically, optionally scoped to a server. */
200
+ listTools(options?: ToolSearchOptions & {
201
+ limit?: number;
202
+ cursor?: string;
203
+ }): ToolListResult;
163
204
  /** Number of indexed tools (including duplicates). */
164
205
  get size(): number;
165
206
  /** Total estimated token cost of all indexed tool schemas. */
@@ -174,6 +215,7 @@ declare class ToolIndex {
174
215
  /** Build a single searchable string from tool metadata. */
175
216
  private buildSearchableText;
176
217
  private getDocumentKey;
218
+ private matchesServer;
177
219
  /** Simple whitespace + camelCase + snake_case tokenizer. */
178
220
  private tokenize;
179
221
  /** Cosine similarity between two vectors. */
@@ -262,7 +304,7 @@ declare class ToolRouter {
262
304
  * This is the main method adapters should call.
263
305
  *
264
306
  * - `all` → returns all tools (unchanged behavior)
265
- * - `search` → returns only meta-tools (mcp_search_tool_bm25, mcp_get_tool_schema, mcp_execute_tool)
307
+ * - `search` → returns only meta-tools (mcp_search_tools, mcp_get_tool_schema, mcp_execute_tool)
266
308
  * - `groups` → returns tools from active groups only
267
309
  */
268
310
  getFilteredTools(): Promise<Tool[]>;
@@ -270,17 +312,24 @@ declare class ToolRouter {
270
312
  * Search tools by natural-language query.
271
313
  * Works regardless of strategy.
272
314
  */
273
- searchTools(query: string, topK?: number): Promise<ToolSummary[]>;
315
+ searchTools(query: string, topK?: number, options?: ToolSearchOptions): Promise<ToolSummary[]>;
274
316
  /**
275
317
  * Search tools by regex pattern.
276
318
  * Matches against name, description, and parameter metadata.
277
319
  */
278
320
  searchToolsRegex(pattern: string, topK?: number): Promise<ToolSummary[]>;
321
+ /** List connected MCP servers with indexed tool counts. */
322
+ listServers(options?: ToolSearchOptions): Promise<ToolServerSummary[]>;
323
+ /** List tools deterministically, optionally scoped to a server. */
324
+ listTools(options?: ToolSearchOptions & {
325
+ limit?: number;
326
+ cursor?: string;
327
+ }): Promise<ToolListResult>;
279
328
  /**
280
329
  * Get the full tool definition by name.
281
330
  * If tool name is ambiguous, use namespace to specify the server.
282
331
  */
283
- getToolSchema(toolName: string, namespace?: string): IndexedTool | undefined;
332
+ getToolSchema(toolName: string, namespace?: string, options?: ToolLookupOptions): IndexedTool | undefined;
284
333
  /**
285
334
  * Get compact (schema-less) summaries for all tools.
286
335
  */
@@ -325,4 +374,4 @@ declare class ToolRouter {
325
374
  private getMetaToolDefinitions;
326
375
  }
327
376
 
328
- export { type CompactTool as C, type EmbedFn as E, type IndexedTool as I, SchemaCompressor as S, type ToolGroupInfo as T, type CompressionStats as a, ToolIndex as b, type ToolIndexOptions as c, ToolRouter as d, type ToolRouterClientInput as e, type ToolRouterOptions as f, type ToolRouterStrategy as g, type ToolSummary as h };
377
+ export { type CompactTool as C, type EmbedFn as E, type IndexedTool as I, SchemaCompressor as S, type ToolGroupInfo as T, type CompressionStats as a, ToolIndex as b, type ToolIndexOptions as c, type ToolListResult as d, ToolRouter as e, type ToolRouterClientInput as f, type ToolRouterOptions as g, type ToolRouterStrategy as h, type ToolSearchOptions as i, type ToolServerSummary as j, type ToolSummary as k };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-ts/sdk",
3
- "version": "1.5.3",
3
+ "version": "1.6.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -84,6 +84,8 @@
84
84
  "test": "npx playwright test",
85
85
  "test:ui": "npx playwright test --ui",
86
86
  "test:debug": "npx playwright test --debug",
87
+ "benchmark:toolrouter": "npm run build && node benchmarks/toolrouter-efficiency.mjs",
88
+ "benchmark:toolrouter:live": "npm run build && node benchmarks/toolrouter-live.mjs",
87
89
  "supabase:push": "supabase db push",
88
90
  "supabase:reset": "supabase db reset",
89
91
  "supabase:status": "supabase status",
@@ -1126,9 +1126,10 @@ export class MCPClient {
1126
1126
  * @returns Server name or undefined
1127
1127
  */
1128
1128
  getServerName(): string | undefined {
1129
- const info = (this.client as any)?.getServerVersion();
1130
- console.log('server info ->', info);
1131
- return info?.title ?? info?.name ?? this.serverName;
1129
+ // Temporarily avoid deriving serverName from serverVersion metadata.
1130
+ // const info = (this.client as any)?.getServerVersion();
1131
+ // return info?.title ?? info?.name ?? this.serverName;
1132
+ return this.serverName;
1132
1133
  }
1133
1134
 
1134
1135
  /**
@@ -1228,4 +1229,3 @@ export class MCPClient {
1228
1229
  }
1229
1230
 
1230
1231
  }
1231
-
@@ -87,6 +87,9 @@ export {
87
87
  export {
88
88
  ToolIndex,
89
89
  type ToolSummary,
90
+ type ToolServerSummary,
91
+ type ToolSearchOptions,
92
+ type ToolListResult,
90
93
  type IndexedTool,
91
94
  type ToolIndexOptions,
92
95
  type EmbedFn,
@@ -100,6 +103,7 @@ export {
100
103
 
101
104
  export {
102
105
  createSearchToolDefinition,
106
+ createListServersToolDefinition,
103
107
  createRegexSearchToolDefinition,
104
108
  createGetSchemaToolDefinition,
105
109
  createExecuteToolDefinition,
@@ -7,7 +7,7 @@
7
7
  * only the tools it actually needs.
8
8
  *
9
9
  * Meta-tools:
10
- * • `mcp_search_tool_bm25` BM25 natural language search
10
+ * • `mcp_search_tools` Search/list available tools
11
11
  * • `mcp_search_tool_regex` — Regex pattern search
12
12
  * • `mcp_get_tool_schema` — Get full inputSchema for a discovered tool
13
13
  * • `mcp_execute_tool` — Execute a discovered tool
@@ -17,14 +17,14 @@
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
+ import type { IndexedTool, ToolLookupOptions } from './tool-index.js';
21
21
 
22
22
  // ---------------------------------------------------------------------------
23
23
  // Tool Definitions
24
24
  // ---------------------------------------------------------------------------
25
25
 
26
26
  /**
27
- * Creates the `mcp_search_tool_bm25` tool definition.
27
+ * Creates the `mcp_search_tools` tool definition.
28
28
  *
29
29
  * This tool lets the LLM search the full catalog of available MCP tools
30
30
  * using a BM25 natural-language query. Returns tool names and descriptions
@@ -32,7 +32,7 @@ import type { IndexedTool } from './tool-index.js';
32
32
  */
33
33
  export function createSearchToolDefinition(): Tool {
34
34
  return {
35
- name: 'mcp_search_tool_bm25',
35
+ name: 'mcp_search_tools',
36
36
  description:
37
37
  'Search the catalog of available tools. Returns tool names, descriptions, and server info. ' +
38
38
  'Use this FIRST to find relevant tools before calling them.\n\n' +
@@ -47,9 +47,28 @@ export function createSearchToolDefinition(): Tool {
47
47
  type: 'string',
48
48
  description: 'Query to find tools. Use "select:<tool_name>" for direct selection, or keywords to search. Prefix keywords with + to require them.',
49
49
  },
50
+ operation: {
51
+ type: 'string',
52
+ enum: ['search', 'list'],
53
+ description:
54
+ 'Operation to perform. Use "search" to find relevant tools by capability. Use "list" with serverId or serverName when the user asks for every tool from a connected MCP server.',
55
+ },
56
+ serverId: {
57
+ type: 'string',
58
+ description: 'Optional server ID to restrict search/list results to one MCP server.',
59
+ },
60
+ serverName: {
61
+ type: 'string',
62
+ description:
63
+ 'Optional server name fragment to restrict search/list results to matching MCP servers, e.g. "supabase".',
64
+ },
50
65
  limit: {
51
66
  type: 'number',
52
- description: 'Maximum number of results to return (default: 5, max: 20).',
67
+ description: 'Maximum number of results to return (default: 5 for search, 20 for list; max: 20 for search, 100 for list).',
68
+ },
69
+ cursor: {
70
+ type: 'string',
71
+ description: 'Optional pagination cursor returned by operation "list".',
53
72
  },
54
73
  },
55
74
  required: ['query'],
@@ -57,6 +76,31 @@ export function createSearchToolDefinition(): Tool {
57
76
  };
58
77
  }
59
78
 
79
+ /**
80
+ * Creates the `mcp_list_servers` tool definition.
81
+ *
82
+ * This tool lets the LLM inspect connected MCP servers before doing
83
+ * server-scoped tool discovery.
84
+ */
85
+ export function createListServersToolDefinition(): Tool {
86
+ return {
87
+ name: 'mcp_list_servers',
88
+ description:
89
+ 'List connected MCP servers and their tool counts. ' +
90
+ 'Use this when mcp_search_tools returns no matches, then retry mcp_search_tools with serverId or serverName.',
91
+ inputSchema: {
92
+ type: 'object' as const,
93
+ properties: {
94
+ query: {
95
+ type: 'string',
96
+ description:
97
+ 'Optional server filter text. Matches server name or serverId, e.g. "web" or "supabase".',
98
+ },
99
+ },
100
+ },
101
+ };
102
+ }
103
+
60
104
  /**
61
105
  * Creates the `mcp_search_tool_regex` tool definition.
62
106
  *
@@ -89,7 +133,7 @@ export function createRegexSearchToolDefinition(): Tool {
89
133
  /**
90
134
  * Creates the `mcp_get_tool_schema` tool definition.
91
135
  *
92
- * After discovering tools via `mcp_search_tool_bm25` or
136
+ * After discovering tools via `mcp_search_tools` or
93
137
  * `mcp_search_tool_regex`, the LLM calls this to load the full
94
138
  * inputSchema for a specific tool so it can construct the correct
95
139
  * arguments.
@@ -99,7 +143,7 @@ export function createGetSchemaToolDefinition(): Tool {
99
143
  name: 'mcp_get_tool_schema',
100
144
  description:
101
145
  'Get the full input schema (parameters) for a specific tool. ' +
102
- 'Call this after mcp_search_tool_bm25 to get the parameter details ' +
146
+ 'Call this after mcp_search_tools to get the parameter details ' +
103
147
  'needed to call a tool correctly. ' +
104
148
  'Do NOT call the discovered tool directly; after reading the schema, call mcp_execute_tool.',
105
149
  inputSchema: {
@@ -107,12 +151,12 @@ export function createGetSchemaToolDefinition(): Tool {
107
151
  properties: {
108
152
  toolName: {
109
153
  type: 'string',
110
- description: 'The exact tool name returned by mcp_search_tool_bm25.',
154
+ description: 'The exact tool name returned by mcp_search_tools.',
111
155
  },
112
156
  serverId: {
113
157
  type: 'string',
114
158
  description:
115
- 'Optional: The server ID provided in mcp_search_tool_bm25. Required if multiple tools have the same name.',
159
+ 'Optional: The server ID provided in mcp_search_tools. Required if multiple tools have the same name.',
116
160
  },
117
161
  },
118
162
  required: ['toolName'],
@@ -124,7 +168,7 @@ export function createGetSchemaToolDefinition(): Tool {
124
168
  * Creates the `mcp_execute_tool` tool definition.
125
169
  *
126
170
  * This is the execution meta-tool — the LLM calls this to execute any
127
- * tool discovered via `mcp_search_tool_bm25` or `mcp_search_tool_regex`.
171
+ * tool discovered via `mcp_search_tools` or `mcp_search_tool_regex`.
128
172
  * The LLM should first call `mcp_get_tool_schema` to know the correct
129
173
  * arguments.
130
174
  *
@@ -135,7 +179,7 @@ export function createExecuteToolDefinition(): Tool {
135
179
  return {
136
180
  name: 'mcp_execute_tool',
137
181
  description:
138
- 'Execute a tool that was discovered via mcp_search_tool_bm25. ' +
182
+ 'Execute a tool that was discovered via mcp_search_tools. ' +
139
183
  'You MUST call mcp_get_tool_schema first to know the correct parameters. ' +
140
184
  'Pass the exact tool name and its arguments.',
141
185
  inputSchema: {
@@ -143,12 +187,12 @@ export function createExecuteToolDefinition(): Tool {
143
187
  properties: {
144
188
  toolName: {
145
189
  type: 'string',
146
- description: 'The exact tool name from mcp_search_tool_bm25 results.',
190
+ description: 'The exact tool name from mcp_search_tools results.',
147
191
  },
148
192
  serverId: {
149
193
  type: 'string',
150
194
  description:
151
- 'Optional: The server ID provided in mcp_search_tool_bm25. Required if multiple tools have the same name.',
195
+ 'Optional: The server ID provided in mcp_search_tools. Required if multiple tools have the same name.',
152
196
  },
153
197
  args: {
154
198
  type: 'object',
@@ -179,7 +223,7 @@ export type CallToolFn = (
179
223
  /**
180
224
  * Execute a meta-tool call and return the result in MCP CallToolResult format.
181
225
  *
182
- * @param toolName - One of the meta-tool names (mcp_search_tool_bm25, mcp_search_tool_regex, etc.)
226
+ * @param toolName - One of the meta-tool names (mcp_search_tools, mcp_list_servers, mcp_search_tool_regex, etc.)
183
227
  * @param args - The arguments from the LLM's tool call
184
228
  * @param router - The ToolRouter to query
185
229
  * @param callToolFn - Optional callback for executing real tools (required for mcp_execute_tool)
@@ -191,9 +235,13 @@ export async function executeMetaTool(
191
235
  router: ToolRouter,
192
236
  callToolFn?: CallToolFn
193
237
  ): Promise<CallToolResult | null> {
194
- const resolveToolSchema = (name: string, namespace?: string): { tool?: IndexedTool; error?: CallToolResult } => {
238
+ const resolveToolSchema = (
239
+ name: string,
240
+ namespace?: string,
241
+ options?: ToolLookupOptions
242
+ ): { tool?: IndexedTool; error?: CallToolResult } => {
195
243
  try {
196
- return { tool: router.getToolSchema(name, namespace) };
244
+ return { tool: router.getToolSchema(name, namespace, options) };
197
245
  } catch (err) {
198
246
  const errorMessage = err instanceof Error ? err.message : String(err);
199
247
  return {
@@ -206,13 +254,61 @@ export async function executeMetaTool(
206
254
  };
207
255
 
208
256
  switch (toolName) {
209
- case 'mcp_search_tool_bm25': {
257
+ case 'mcp_search_tools': {
210
258
  const query = String(args.query ?? '');
259
+ const operation = String(args.operation ?? 'search');
260
+ const serverId = String(args.serverId ?? '') || undefined;
261
+ const serverName = String(args.serverName ?? '') || undefined;
262
+
263
+ if (operation === 'list') {
264
+ const limit = Math.min(Number(args.limit) || 20, 100);
265
+ const cursor = String(args.cursor ?? '') || undefined;
266
+ const result = await router.listTools({
267
+ serverId,
268
+ serverName: serverName ?? (!serverId && query ? query : undefined),
269
+ limit,
270
+ cursor,
271
+ });
272
+
273
+ const serverText = result.servers.length > 0
274
+ ? result.servers
275
+ .map((server) => `${server.serverName} (serverId: ${server.serverId}, tools: ${server.toolCount})`)
276
+ .join(', ')
277
+ : 'none';
278
+
279
+ const lines: string[] = [
280
+ 'operation: list',
281
+ `servers: ${serverText}`,
282
+ `totalCount: ${result.totalCount}`,
283
+ `returnedCount: ${result.returnedCount}`,
284
+ `nextCursor: ${result.nextCursor ?? 'null'}`,
285
+ '',
286
+ ];
287
+
288
+ if (result.tools.length > 0) {
289
+ lines.push(...formatToolSummaries(result.tools));
290
+ } else {
291
+ lines.push(
292
+ serverId || serverName
293
+ ? 'No tools found for the requested server scope.'
294
+ : 'No tools found. Try operation "search" or provide serverId/serverName.'
295
+ );
296
+ }
297
+
298
+ return {
299
+ content: [{ type: 'text', text: lines.join('\n') }],
300
+ isError: false,
301
+ };
302
+ }
303
+
211
304
  const limit = Math.min(Number(args.limit) || 5, 20);
305
+ const searchOptions = { serverId, serverName };
212
306
 
213
307
  // Fast path: Check for select: prefix
214
308
  const selectMatch = query.match(/^select:(.+)$/i);
215
309
  if (selectMatch) {
310
+ await router.listTools({ serverId, serverName, limit: 1 });
311
+
216
312
  const requested = selectMatch[1]!
217
313
  .split(',')
218
314
  .map((s) => s.trim())
@@ -221,15 +317,19 @@ export async function executeMetaTool(
221
317
  const found: any[] = [];
222
318
  const errors: string[] = [];
223
319
 
320
+ const namespace = serverId ?? serverName;
321
+
224
322
  for (const requestedToolName of requested) {
225
- const { tool, error } = resolveToolSchema(requestedToolName);
323
+ const { tool, error } = resolveToolSchema(requestedToolName, namespace, {
324
+ allowServerNameFragment: Boolean(serverName && !serverId),
325
+ });
226
326
  if (error) {
227
327
  const errorMsg = error.content[0]?.type === 'text' ? error.content[0].text : 'Unknown error';
228
328
  errors.push(`- **${requestedToolName}**: ${errorMsg}`);
229
329
  } else if (tool) {
230
330
  found.push(tool);
231
331
  } else {
232
- errors.push(`- **${requestedToolName}**: Tool not found. Try searching with mcp_search_tool_bm25.`);
332
+ errors.push(`- **${requestedToolName}**: Tool not found. Try searching with mcp_search_tools.`);
233
333
  }
234
334
  }
235
335
 
@@ -257,16 +357,31 @@ export async function executeMetaTool(
257
357
  };
258
358
  }
259
359
 
260
- const results = await router.searchTools(query, limit);
360
+ const results = await router.searchTools(query, limit, searchOptions);
261
361
 
262
362
  const text = results.length === 0
263
- ? 'No tools found matching your query. Try different keywords.'
264
- : results
363
+ ? 'No tools found matching your query. Call mcp_list_servers to inspect connected servers, then retry mcp_search_tools with serverId or serverName.'
364
+ : formatToolSummaries(results).join('\n');
365
+
366
+ return {
367
+ content: [{ type: 'text', text }],
368
+ isError: false,
369
+ };
370
+ }
371
+
372
+ case 'mcp_list_servers': {
373
+ const query = String(args.query ?? '').trim();
374
+ const servers = await router.listServers({
375
+ serverName: query || undefined,
376
+ });
377
+
378
+ const text = servers.length === 0
379
+ ? 'No connected servers found.'
380
+ : servers
265
381
  .map(
266
- (t, i) =>
267
- `${i + 1}. **${t.name}** (server: ${t.serverName}, serverId: ${t.serverId})\n` +
268
- ` ${t.description}\n` +
269
- ` Estimated tokens: ${t.estimatedTokens}`
382
+ (server, i) =>
383
+ `${i + 1}. **${server.serverName}** (serverId: ${server.serverId}, sessionId: ${server.sessionId})\n` +
384
+ ` Tool count: ${server.toolCount}`
270
385
  )
271
386
  .join('\n');
272
387
 
@@ -284,14 +399,7 @@ export async function executeMetaTool(
284
399
 
285
400
  const text = results.length === 0
286
401
  ? 'No tools matched your regex pattern. Try a broader pattern.'
287
- : results
288
- .map(
289
- (t, i) =>
290
- `${i + 1}. **${t.name}** (server: ${t.serverName}, serverId: ${t.serverId})\n` +
291
- ` ${t.description}\n` +
292
- ` Estimated tokens: ${t.estimatedTokens}`
293
- )
294
- .join('\n');
402
+ : formatToolSummaries(results).join('\n');
295
403
 
296
404
  return {
297
405
  content: [{ type: 'text', text }],
@@ -313,7 +421,7 @@ export async function executeMetaTool(
313
421
  content: [
314
422
  {
315
423
  type: 'text',
316
- text: `Tool "${name}" not found. Use mcp_search_tool_bm25 to find available tools first.`,
424
+ text: `Tool "${name}" not found. Use mcp_search_tools to find available tools first.`,
317
425
  },
318
426
  ],
319
427
  isError: true,
@@ -362,7 +470,7 @@ export async function executeMetaTool(
362
470
  content: [
363
471
  {
364
472
  type: 'text',
365
- text: `Tool "${targetToolName}" not found. Use mcp_search_tool_bm25 to discover available tools first.`,
473
+ text: `Tool "${targetToolName}" not found. Use mcp_search_tools to discover available tools first.`,
366
474
  },
367
475
  ],
368
476
  isError: true,
@@ -404,10 +512,28 @@ export async function executeMetaTool(
404
512
  }
405
513
  }
406
514
 
515
+ function formatToolSummaries(
516
+ tools: Array<{
517
+ name: string;
518
+ description: string;
519
+ serverName: string;
520
+ serverId: string;
521
+ estimatedTokens: number;
522
+ }>
523
+ ): string[] {
524
+ return tools.map(
525
+ (t, i) =>
526
+ `${i + 1}. **${t.name}** (server: ${t.serverName}, serverId: ${t.serverId})\n` +
527
+ ` ${t.description}\n` +
528
+ ` Estimated tokens: ${t.estimatedTokens}`
529
+ );
530
+ }
531
+
407
532
  /** Check if a tool name is one of the meta-tools. */
408
533
  export function isMetaTool(toolName: string): boolean {
409
534
  return (
410
- toolName === 'mcp_search_tool_bm25' ||
535
+ toolName === 'mcp_search_tools' ||
536
+ toolName === 'mcp_list_servers' ||
411
537
  toolName === 'mcp_search_tool_regex' ||
412
538
  toolName === 'mcp_get_tool_schema' ||
413
539
  toolName === 'mcp_execute_tool'