mcp-filter 1.0.1 → 1.0.2
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 +23 -13
- package/dist/index.js +0 -0
- package/package.json +1 -1
- package/dist/core/config-loader.d.ts +0 -22
- package/dist/core/config-loader.d.ts.map +0 -1
- package/dist/core/config-loader.js +0 -308
- package/dist/core/config-loader.js.map +0 -1
- package/dist/core/config-schema.d.ts +0 -1363
- package/dist/core/config-schema.d.ts.map +0 -1
- package/dist/core/config-schema.js +0 -139
- package/dist/core/config-schema.js.map +0 -1
- package/dist/core/server-manager.d.ts +0 -51
- package/dist/core/server-manager.d.ts.map +0 -1
- package/dist/core/server-manager.js +0 -149
- package/dist/core/server-manager.js.map +0 -1
- package/dist/features/discovery/config-init.d.ts +0 -37
- package/dist/features/discovery/config-init.d.ts.map +0 -1
- package/dist/features/discovery/config-init.js +0 -95
- package/dist/features/discovery/config-init.js.map +0 -1
- package/dist/features/discovery/discovery-handler.d.ts +0 -128
- package/dist/features/discovery/discovery-handler.d.ts.map +0 -1
- package/dist/features/discovery/discovery-handler.js +0 -629
- package/dist/features/discovery/discovery-handler.js.map +0 -1
- package/dist/features/discovery/initialization.d.ts +0 -126
- package/dist/features/discovery/initialization.d.ts.map +0 -1
- package/dist/features/discovery/initialization.js +0 -314
- package/dist/features/discovery/initialization.js.map +0 -1
- package/dist/features/discovery/search-phase.d.ts +0 -19
- package/dist/features/discovery/search-phase.d.ts.map +0 -1
- package/dist/features/discovery/search-phase.js +0 -76
- package/dist/features/discovery/search-phase.js.map +0 -1
- package/dist/features/discovery/simple-rag.d.ts +0 -30
- package/dist/features/discovery/simple-rag.d.ts.map +0 -1
- package/dist/features/discovery/simple-rag.js +0 -85
- package/dist/features/discovery/simple-rag.js.map +0 -1
- package/dist/features/filtering/filter.d.ts +0 -18
- package/dist/features/filtering/filter.d.ts.map +0 -1
- package/dist/features/filtering/filter.js +0 -43
- package/dist/features/filtering/filter.js.map +0 -1
- package/dist/features/overrides/override-manager.d.ts +0 -25
- package/dist/features/overrides/override-manager.d.ts.map +0 -1
- package/dist/features/overrides/override-manager.js +0 -67
- package/dist/features/overrides/override-manager.js.map +0 -1
- package/dist/utils/debug-logger.d.ts +0 -2
- package/dist/utils/debug-logger.d.ts.map +0 -1
- package/dist/utils/debug-logger.js +0 -33
- package/dist/utils/debug-logger.js.map +0 -1
|
@@ -1,629 +0,0 @@
|
|
|
1
|
-
import { SearchPhase } from "./search-phase.js";
|
|
2
|
-
import { SimpleRAG } from "./simple-rag.js";
|
|
3
|
-
import { ConfigInitializer } from "./config-init.js";
|
|
4
|
-
import { logger } from "../../logger.js";
|
|
5
|
-
import { z } from "zod";
|
|
6
|
-
import { RequestSchema, ResultSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
7
|
-
// Zod schemas for custom request methods
|
|
8
|
-
const DiscoverToolsRequestSchema = RequestSchema.extend({
|
|
9
|
-
method: z.literal("discover_tools"),
|
|
10
|
-
params: z.object({
|
|
11
|
-
query: z.string().optional(),
|
|
12
|
-
serverId: z.string().optional(),
|
|
13
|
-
initialized: z.boolean().optional(),
|
|
14
|
-
limit: z.number().int().positive().optional(),
|
|
15
|
-
}).optional(),
|
|
16
|
-
});
|
|
17
|
-
const CallToolRequestSchema = RequestSchema.extend({
|
|
18
|
-
method: z.literal("call_tool"),
|
|
19
|
-
params: z.object({
|
|
20
|
-
toolName: z.string(),
|
|
21
|
-
toolArguments: z.record(z.any()).optional(),
|
|
22
|
-
serverId: z.string().optional(),
|
|
23
|
-
}),
|
|
24
|
-
});
|
|
25
|
-
const InitRequestSchema = RequestSchema.extend({
|
|
26
|
-
method: z.literal("initialize_mcp_servers"),
|
|
27
|
-
params: z.object({
|
|
28
|
-
configPath: z.string().optional(),
|
|
29
|
-
servers: z.array(z.object({
|
|
30
|
-
id: z.string(),
|
|
31
|
-
transport: z.any(),
|
|
32
|
-
filters: z.object({
|
|
33
|
-
include: z.array(z.string()).optional(),
|
|
34
|
-
exclude: z.array(z.string()).optional(),
|
|
35
|
-
}).optional(),
|
|
36
|
-
enabled: z.boolean().optional(),
|
|
37
|
-
})).optional(),
|
|
38
|
-
}).optional(),
|
|
39
|
-
});
|
|
40
|
-
// Result schemas for custom methods
|
|
41
|
-
const DiscoverToolsResultSchema = ResultSchema.extend({
|
|
42
|
-
tools: z.array(z.object({
|
|
43
|
-
name: z.string(),
|
|
44
|
-
serverId: z.string(),
|
|
45
|
-
description: z.string(),
|
|
46
|
-
initialized: z.boolean(),
|
|
47
|
-
arguments: z.array(z.object({
|
|
48
|
-
name: z.string(),
|
|
49
|
-
description: z.string(),
|
|
50
|
-
})).optional(),
|
|
51
|
-
})),
|
|
52
|
-
servers: z.array(z.object({
|
|
53
|
-
id: z.string(),
|
|
54
|
-
initialized: z.boolean(),
|
|
55
|
-
toolCount: z.number(),
|
|
56
|
-
short_description: z.string().optional(),
|
|
57
|
-
methodNames: z.array(z.string()).optional(),
|
|
58
|
-
})),
|
|
59
|
-
summary: z.object({
|
|
60
|
-
totalTools: z.number(),
|
|
61
|
-
searchEnabled: z.boolean(),
|
|
62
|
-
message: z.string(),
|
|
63
|
-
}).optional(),
|
|
64
|
-
});
|
|
65
|
-
// Init result is a union: either instructions (not initialized) or summary (initialized)
|
|
66
|
-
const InitResultSchema = ResultSchema.extend({
|
|
67
|
-
initialized: z.boolean(),
|
|
68
|
-
}).and(z.union([
|
|
69
|
-
// Not initialized - has instructions with RGC prompt
|
|
70
|
-
z.object({
|
|
71
|
-
initialized: z.literal(false),
|
|
72
|
-
instructions: z.object({
|
|
73
|
-
summary: z.string(),
|
|
74
|
-
role: z.string().optional(),
|
|
75
|
-
goal: z.string().optional(),
|
|
76
|
-
context: z.string().optional(),
|
|
77
|
-
servers: z.array(z.string()).optional(),
|
|
78
|
-
steps: z.array(z.object({
|
|
79
|
-
step: z.number(),
|
|
80
|
-
action: z.string(),
|
|
81
|
-
reason: z.string().optional(),
|
|
82
|
-
note: z.string().optional(),
|
|
83
|
-
})),
|
|
84
|
-
important: z.string(),
|
|
85
|
-
validationErrors: z.object({
|
|
86
|
-
message: z.string(),
|
|
87
|
-
errors: z.array(z.string()),
|
|
88
|
-
help: z.string(),
|
|
89
|
-
}).optional(),
|
|
90
|
-
toolOverrides: z.object({
|
|
91
|
-
note: z.string(),
|
|
92
|
-
example: z.string(),
|
|
93
|
-
}).optional(),
|
|
94
|
-
}),
|
|
95
|
-
}),
|
|
96
|
-
// Initialized - has summary with concise server-level format
|
|
97
|
-
z.object({
|
|
98
|
-
initialized: z.literal(true),
|
|
99
|
-
summary: z.object({
|
|
100
|
-
totalTools: z.number(),
|
|
101
|
-
connectedServers: z.array(z.object({
|
|
102
|
-
id: z.string(),
|
|
103
|
-
short_description: z.string().optional(),
|
|
104
|
-
toolCount: z.number(),
|
|
105
|
-
methodNames: z.array(z.string()).optional(),
|
|
106
|
-
})),
|
|
107
|
-
erroredServers: z.array(z.object({
|
|
108
|
-
id: z.string(),
|
|
109
|
-
short_description: z.string().optional(),
|
|
110
|
-
error: z.string().optional(),
|
|
111
|
-
})),
|
|
112
|
-
}),
|
|
113
|
-
descriptionPrompt: z.object({
|
|
114
|
-
role: z.string(),
|
|
115
|
-
goal: z.string(),
|
|
116
|
-
context: z.string(),
|
|
117
|
-
examples: z.array(z.string()),
|
|
118
|
-
}),
|
|
119
|
-
searchEnabled: z.boolean(),
|
|
120
|
-
message: z.string(),
|
|
121
|
-
}),
|
|
122
|
-
]));
|
|
123
|
-
/**
|
|
124
|
-
* Handles discovery-related MCP methods
|
|
125
|
-
*/
|
|
126
|
-
export class DiscoveryHandler {
|
|
127
|
-
serverManager;
|
|
128
|
-
initializationTracker;
|
|
129
|
-
overrideManager;
|
|
130
|
-
searchPhase;
|
|
131
|
-
simpleRAG;
|
|
132
|
-
configInitializer;
|
|
133
|
-
validationErrors;
|
|
134
|
-
constructor(serverManager, initializationTracker, overrideManager, validationErrors) {
|
|
135
|
-
this.serverManager = serverManager;
|
|
136
|
-
this.initializationTracker = initializationTracker;
|
|
137
|
-
this.overrideManager = overrideManager;
|
|
138
|
-
this.searchPhase = new SearchPhase();
|
|
139
|
-
this.simpleRAG = new SimpleRAG();
|
|
140
|
-
this.configInitializer = new ConfigInitializer();
|
|
141
|
-
this.validationErrors = validationErrors;
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Register discovery methods with the MCP server
|
|
145
|
-
*/
|
|
146
|
-
registerHandlers(server) {
|
|
147
|
-
// #region agent log
|
|
148
|
-
fetch('http://127.0.0.1:7243/ingest/198b6a8b-7e4f-4c81-a922-32b1e2e171c8', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ location: 'discovery-handler.ts:64', message: 'registerHandlers entry', data: { serverExists: !!server }, timestamp: Date.now(), sessionId: 'debug-session', runId: 'run2', hypothesisId: 'D' }) }).catch(() => { });
|
|
149
|
-
// #endregion
|
|
150
|
-
// Register discover-tools method
|
|
151
|
-
// #region agent log
|
|
152
|
-
fetch('http://127.0.0.1:7243/ingest/198b6a8b-7e4f-4c81-a922-32b1e2e171c8', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ location: 'discovery-handler.ts:67', message: 'registering discover-tools handler', data: { schemaType: typeof DiscoverToolsRequestSchema }, timestamp: Date.now(), sessionId: 'debug-session', runId: 'run2', hypothesisId: 'D' }) }).catch(() => { });
|
|
153
|
-
// #endregion
|
|
154
|
-
// Register discover-tools method with result schema validation
|
|
155
|
-
server.setRequestHandler(DiscoverToolsRequestSchema, async (request) => {
|
|
156
|
-
// #region agent log
|
|
157
|
-
fetch('http://127.0.0.1:7243/ingest/198b6a8b-7e4f-4c81-a922-32b1e2e171c8', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ location: 'discovery-handler.ts:71', message: 'discover-tools handler called', data: { hasParams: !!request.params }, timestamp: Date.now(), sessionId: 'debug-session', runId: 'run2', hypothesisId: 'D' }) }).catch(() => { });
|
|
158
|
-
// #endregion
|
|
159
|
-
const params = request.params;
|
|
160
|
-
const result = await this.discoverTools(params || {});
|
|
161
|
-
// Validate result against schema
|
|
162
|
-
const validatedResult = DiscoverToolsResultSchema.parse(result);
|
|
163
|
-
return validatedResult;
|
|
164
|
-
});
|
|
165
|
-
// Register initialize_mcp_servers method with result schema validation
|
|
166
|
-
// #region agent log
|
|
167
|
-
fetch('http://127.0.0.1:7243/ingest/198b6a8b-7e4f-4c81-a922-32b1e2e171c8', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ location: 'discovery-handler.ts:79', message: 'registering initialize_mcp_servers handler', data: { schemaType: typeof InitRequestSchema }, timestamp: Date.now(), sessionId: 'debug-session', runId: 'run2', hypothesisId: 'D' }) }).catch(() => { });
|
|
168
|
-
// #endregion
|
|
169
|
-
server.setRequestHandler(InitRequestSchema, async (request) => {
|
|
170
|
-
// #region agent log
|
|
171
|
-
fetch('http://127.0.0.1:7243/ingest/198b6a8b-7e4f-4c81-a922-32b1e2e171c8', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ location: 'discovery-handler.ts:83', message: 'initialize_mcp_servers handler called', data: { hasParams: !!request.params }, timestamp: Date.now(), sessionId: 'debug-session', runId: 'run2', hypothesisId: 'D' }) }).catch(() => { });
|
|
172
|
-
// #endregion
|
|
173
|
-
const params = request.params;
|
|
174
|
-
const result = await this.init(params?.configPath, params?.servers);
|
|
175
|
-
// #region agent log
|
|
176
|
-
fetch('http://127.0.0.1:7243/ingest/198b6a8b-7e4f-4c81-a922-32b1e2e171c8', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ location: 'discovery-handler.ts:226', message: 'init result before validation', data: { initialized: result.initialized, hasSummary: !!result.summary, hasInstructions: !!result.instructions, hasDescriptionPrompt: !!result.descriptionPrompt, hasSearchEnabled: typeof result.searchEnabled, hasMessage: !!result.message, summaryKeys: result.initialized ? Object.keys(result.summary || {}) : undefined, topLevelKeys: Object.keys(result) }, timestamp: Date.now(), sessionId: 'debug-session', runId: 'run2', hypothesisId: 'D' }) }).catch(() => { });
|
|
177
|
-
// #endregion
|
|
178
|
-
// Validate result against schema
|
|
179
|
-
const validatedResult = InitResultSchema.parse(result);
|
|
180
|
-
// #region agent log
|
|
181
|
-
fetch('http://127.0.0.1:7243/ingest/198b6a8b-7e4f-4c81-a922-32b1e2e171c8', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ location: 'discovery-handler.ts:230', message: 'init result validated', data: { initialized: validatedResult.initialized }, timestamp: Date.now(), sessionId: 'debug-session', runId: 'run2', hypothesisId: 'D' }) }).catch(() => { });
|
|
182
|
-
// #endregion
|
|
183
|
-
return validatedResult;
|
|
184
|
-
});
|
|
185
|
-
// Register call_tool method
|
|
186
|
-
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
187
|
-
const params = request.params;
|
|
188
|
-
return await this.callTool(params);
|
|
189
|
-
});
|
|
190
|
-
// #region agent log
|
|
191
|
-
fetch('http://127.0.0.1:7243/ingest/198b6a8b-7e4f-4c81-a922-32b1e2e171c8', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ location: 'discovery-handler.ts:96', message: 'registerHandlers completed', data: {}, timestamp: Date.now(), sessionId: 'debug-session', runId: 'run2', hypothesisId: 'D' }) }).catch(() => { });
|
|
192
|
-
// #endregion
|
|
193
|
-
}
|
|
194
|
-
/**
|
|
195
|
-
* Call a specific tool from an upstream MCP server
|
|
196
|
-
*/
|
|
197
|
-
async callTool(params) {
|
|
198
|
-
const servers = params.serverId
|
|
199
|
-
? [this.serverManager.getServer(params.serverId)].filter((s) => s !== null && s !== undefined)
|
|
200
|
-
: this.serverManager.getEnabledServers();
|
|
201
|
-
const searchedServerNames = [];
|
|
202
|
-
const uninitializedServerNames = [];
|
|
203
|
-
// Find server with tool
|
|
204
|
-
for (const server of servers) {
|
|
205
|
-
if (!server)
|
|
206
|
-
continue;
|
|
207
|
-
if (!this.initializationTracker.isInitialized(server.id)) {
|
|
208
|
-
uninitializedServerNames.push(server.id);
|
|
209
|
-
continue;
|
|
210
|
-
}
|
|
211
|
-
searchedServerNames.push(server.id);
|
|
212
|
-
try {
|
|
213
|
-
const tools = await server.client.listTools();
|
|
214
|
-
const tool = tools.tools.find((t) => t.name === params.toolName);
|
|
215
|
-
if (tool && !server.filter.shouldExclude(tool.name)) {
|
|
216
|
-
// Execute tool
|
|
217
|
-
return await server.client.callTool({
|
|
218
|
-
name: params.toolName,
|
|
219
|
-
arguments: params.toolArguments || {},
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
catch (error) {
|
|
224
|
-
// Continue searching
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
// Check if it's an uninitialized server issue
|
|
228
|
-
if (uninitializedServerNames.length > 0) {
|
|
229
|
-
const error = this.initializationTracker.createInitializationError(uninitializedServerNames[0]);
|
|
230
|
-
throw new Error(`${error.error.message}\n\n${JSON.stringify(error.initializationInstructions, null, 2)}`);
|
|
231
|
-
}
|
|
232
|
-
// Build error message with server names
|
|
233
|
-
const serverList = searchedServerNames.length > 0
|
|
234
|
-
? ` Searched servers: ${searchedServerNames.join(", ")}.`
|
|
235
|
-
: "";
|
|
236
|
-
throw new Error(`Tool '${params.toolName}' not found or excluded.${serverList}`);
|
|
237
|
-
}
|
|
238
|
-
/**
|
|
239
|
-
* Discover tools across all configured servers
|
|
240
|
-
*/
|
|
241
|
-
async discoverTools(params) {
|
|
242
|
-
// Check if we have any servers configured
|
|
243
|
-
const allServers = this.serverManager.getAllServers();
|
|
244
|
-
if (allServers.length === 0) {
|
|
245
|
-
// Return empty response if no servers configured
|
|
246
|
-
return {
|
|
247
|
-
tools: [],
|
|
248
|
-
servers: [],
|
|
249
|
-
summary: {
|
|
250
|
-
totalTools: 0,
|
|
251
|
-
searchEnabled: false,
|
|
252
|
-
message: "No servers configured. Call discover_tools to initialize.",
|
|
253
|
-
},
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
// Check if servers need initialization - if so, initialize them first
|
|
257
|
-
const uninitializedServers = allServers.filter((server) => !this.initializationTracker.isInitialized(server.id));
|
|
258
|
-
if (uninitializedServers.length > 0) {
|
|
259
|
-
// Initialize servers by calling init() internally
|
|
260
|
-
await this.init();
|
|
261
|
-
// Continue with tool discovery even if some servers failed to initialize
|
|
262
|
-
}
|
|
263
|
-
const tools = [];
|
|
264
|
-
const servers = params.serverId
|
|
265
|
-
? [this.serverManager.getServer(params.serverId)].filter(Boolean)
|
|
266
|
-
: this.serverManager.getEnabledServers();
|
|
267
|
-
for (const server of servers) {
|
|
268
|
-
if (!server)
|
|
269
|
-
continue;
|
|
270
|
-
const isInitialized = this.initializationTracker.isInitialized(server.id);
|
|
271
|
-
// Filter by initialization state if specified
|
|
272
|
-
if (params.initialized !== undefined && params.initialized !== isInitialized) {
|
|
273
|
-
continue;
|
|
274
|
-
}
|
|
275
|
-
try {
|
|
276
|
-
if (!isInitialized) {
|
|
277
|
-
// Return placeholder for uninitialized servers
|
|
278
|
-
continue;
|
|
279
|
-
}
|
|
280
|
-
const upstreamTools = await server.client.listTools();
|
|
281
|
-
const filteredTools = server.filter.filterList(upstreamTools.tools);
|
|
282
|
-
for (const tool of filteredTools) {
|
|
283
|
-
// Apply overrides
|
|
284
|
-
const toolWithOverrides = this.overrideManager.applyOverrides(server.id, tool, true // Use brief descriptions
|
|
285
|
-
);
|
|
286
|
-
// Extract arguments
|
|
287
|
-
const arguments_ = tool.inputSchema?.properties
|
|
288
|
-
? Object.entries(tool.inputSchema.properties).map(([name, prop]) => ({
|
|
289
|
-
name,
|
|
290
|
-
description: typeof prop === "object" && "description" in prop
|
|
291
|
-
? prop.description || ""
|
|
292
|
-
: "",
|
|
293
|
-
}))
|
|
294
|
-
: undefined;
|
|
295
|
-
tools.push({
|
|
296
|
-
name: tool.name,
|
|
297
|
-
serverId: server.id,
|
|
298
|
-
description: toolWithOverrides.description || "",
|
|
299
|
-
initialized: true,
|
|
300
|
-
arguments: arguments_,
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
catch (error) {
|
|
305
|
-
logger.error(`Error discovering tools from server ${server.id}: ${error}`);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
// Apply search if query provided - always use both algorithms
|
|
309
|
-
let searchResults = tools;
|
|
310
|
-
if (params.query) {
|
|
311
|
-
const searchableTools = tools.map((t) => ({
|
|
312
|
-
name: t.name,
|
|
313
|
-
description: t.description,
|
|
314
|
-
serverId: t.serverId,
|
|
315
|
-
}));
|
|
316
|
-
// Always use both phase (keyword) and RAG (semantic) search
|
|
317
|
-
const ragResults = this.simpleRAG.search(searchableTools.map((t) => ({
|
|
318
|
-
name: t.name,
|
|
319
|
-
description: t.description,
|
|
320
|
-
})), params.query);
|
|
321
|
-
// Calculate phase (keyword) search scores using same logic as fuzzySearch
|
|
322
|
-
const queryLower = params.query.toLowerCase();
|
|
323
|
-
const phaseScoredResults = [];
|
|
324
|
-
for (const tool of searchableTools) {
|
|
325
|
-
// Filter by serverId if specified
|
|
326
|
-
if (params.serverId && tool.serverId !== params.serverId) {
|
|
327
|
-
continue;
|
|
328
|
-
}
|
|
329
|
-
let score = 0;
|
|
330
|
-
// Exact match in name
|
|
331
|
-
if (tool.name.toLowerCase() === queryLower) {
|
|
332
|
-
score = 1.0;
|
|
333
|
-
}
|
|
334
|
-
// Name contains query
|
|
335
|
-
else if (tool.name.toLowerCase().includes(queryLower)) {
|
|
336
|
-
score = 0.8;
|
|
337
|
-
}
|
|
338
|
-
// Description contains query
|
|
339
|
-
else if (tool.description?.toLowerCase().includes(queryLower)) {
|
|
340
|
-
score = 0.6;
|
|
341
|
-
}
|
|
342
|
-
// Simple substring match
|
|
343
|
-
else {
|
|
344
|
-
const nameWords = tool.name.toLowerCase().split(/[_\-\s]+/);
|
|
345
|
-
const queryWords = queryLower.split(/[_\-\s]+/);
|
|
346
|
-
let matches = 0;
|
|
347
|
-
for (const qw of queryWords) {
|
|
348
|
-
if (nameWords.some((nw) => nw.includes(qw) || qw.includes(nw))) {
|
|
349
|
-
matches++;
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
if (matches > 0) {
|
|
353
|
-
score = matches / Math.max(nameWords.length, queryWords.length);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
// Include if score > 0 (no threshold filtering, we'll combine with RAG)
|
|
357
|
-
if (score > 0) {
|
|
358
|
-
phaseScoredResults.push({ tool, score });
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
// Build score map: tool key -> max confidence score from either algorithm
|
|
362
|
-
const scoreMap = new Map();
|
|
363
|
-
const toolMap = new Map(tools.map((t) => [`${t.name}:${t.serverId}`, t]));
|
|
364
|
-
// Add phase search scores
|
|
365
|
-
for (const result of phaseScoredResults) {
|
|
366
|
-
const key = `${result.tool.name}:${result.tool.serverId}`;
|
|
367
|
-
scoreMap.set(key, Math.max(scoreMap.get(key) ?? 0, result.score));
|
|
368
|
-
}
|
|
369
|
-
// Add RAG search scores (already has scores)
|
|
370
|
-
for (const result of ragResults) {
|
|
371
|
-
const matching = tools.find((t) => t.name === result.tool.name);
|
|
372
|
-
if (matching) {
|
|
373
|
-
const key = `${matching.name}:${matching.serverId}`;
|
|
374
|
-
scoreMap.set(key, Math.max(scoreMap.get(key) ?? 0, result.score));
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
// Combine results: include tools from either algorithm, sorted by highest confidence
|
|
378
|
-
const scoredResults = Array.from(scoreMap.entries())
|
|
379
|
-
.map(([key, score]) => ({
|
|
380
|
-
tool: toolMap.get(key),
|
|
381
|
-
score,
|
|
382
|
-
}))
|
|
383
|
-
.filter((r) => r.tool !== undefined)
|
|
384
|
-
.sort((a, b) => b.score - a.score) // Sort by confidence descending
|
|
385
|
-
.map((r) => r.tool);
|
|
386
|
-
searchResults = scoredResults;
|
|
387
|
-
}
|
|
388
|
-
// Apply limit (default: 3) - results are already sorted by highest confidence
|
|
389
|
-
const limit = params.limit ?? 3;
|
|
390
|
-
const limitedResults = searchResults.slice(0, limit);
|
|
391
|
-
// Build server status
|
|
392
|
-
const serverStatus = this.serverManager
|
|
393
|
-
.getEnabledServers()
|
|
394
|
-
.map((server) => {
|
|
395
|
-
const shortDescription = server.config.short_description ||
|
|
396
|
-
server.config.shortDescription ||
|
|
397
|
-
undefined;
|
|
398
|
-
const serverTools = searchResults.filter((t) => t.serverId === server.id);
|
|
399
|
-
return {
|
|
400
|
-
id: server.id,
|
|
401
|
-
initialized: this.initializationTracker.isInitialized(server.id),
|
|
402
|
-
toolCount: serverTools.length,
|
|
403
|
-
short_description: shortDescription && shortDescription.length < 100 ? shortDescription : undefined,
|
|
404
|
-
methodNames: serverTools.map((t) => t.name),
|
|
405
|
-
};
|
|
406
|
-
});
|
|
407
|
-
// If initialized and no query, return summary format
|
|
408
|
-
const allInitialized = serverStatus.every((s) => s.initialized);
|
|
409
|
-
if (allInitialized && !params.query) {
|
|
410
|
-
return {
|
|
411
|
-
tools: limitedResults,
|
|
412
|
-
servers: serverStatus,
|
|
413
|
-
summary: {
|
|
414
|
-
totalTools: searchResults.length,
|
|
415
|
-
searchEnabled: true,
|
|
416
|
-
message: `Found ${searchResults.length} tool(s) across ${serverStatus.length} server(s). Showing ${limitedResults.length} of ${searchResults.length} (limit: ${limit}). Use query parameter for semantic search.`,
|
|
417
|
-
},
|
|
418
|
-
};
|
|
419
|
-
}
|
|
420
|
-
return {
|
|
421
|
-
tools: limitedResults,
|
|
422
|
-
servers: serverStatus,
|
|
423
|
-
};
|
|
424
|
-
}
|
|
425
|
-
/**
|
|
426
|
-
* Single unified method: returns initialization instructions if not initialized,
|
|
427
|
-
* or returns tool summaries from config if initialized
|
|
428
|
-
*/
|
|
429
|
-
async init(configPath, servers) {
|
|
430
|
-
const allServers = this.serverManager.getAllServers();
|
|
431
|
-
// Stage 1: No config file - no servers configured
|
|
432
|
-
if (allServers.length === 0) {
|
|
433
|
-
const initError = this.initializationTracker.createConfigInitError();
|
|
434
|
-
return {
|
|
435
|
-
initialized: false,
|
|
436
|
-
instructions: {
|
|
437
|
-
summary: initError.initializationInstructions.summary,
|
|
438
|
-
role: initError.initializationInstructions.role,
|
|
439
|
-
goal: initError.initializationInstructions.goal,
|
|
440
|
-
context: initError.initializationInstructions.context,
|
|
441
|
-
steps: initError.initializationInstructions.steps,
|
|
442
|
-
important: initError.initializationInstructions.important,
|
|
443
|
-
},
|
|
444
|
-
};
|
|
445
|
-
}
|
|
446
|
-
// Stage 2: Invalid config file - validation errors exist
|
|
447
|
-
if (this.validationErrors && !this.validationErrors.valid) {
|
|
448
|
-
const invalidConfigError = this.initializationTracker.createInvalidConfigError(this.validationErrors.formattedErrors || []);
|
|
449
|
-
return {
|
|
450
|
-
initialized: false,
|
|
451
|
-
instructions: {
|
|
452
|
-
summary: invalidConfigError.summary,
|
|
453
|
-
role: invalidConfigError.role,
|
|
454
|
-
goal: invalidConfigError.goal,
|
|
455
|
-
context: invalidConfigError.context,
|
|
456
|
-
steps: invalidConfigError.steps,
|
|
457
|
-
important: invalidConfigError.important,
|
|
458
|
-
validationErrors: invalidConfigError.validationErrors,
|
|
459
|
-
},
|
|
460
|
-
};
|
|
461
|
-
}
|
|
462
|
-
// Stage 3: Valid config but servers not initialized
|
|
463
|
-
const uninitializedServers = allServers.filter((server) => !this.initializationTracker.isInitialized(server.id));
|
|
464
|
-
// #region agent log
|
|
465
|
-
fetch('http://127.0.0.1:7243/ingest/198b6a8b-7e4f-4c81-a922-32b1e2e171c8', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ location: 'discovery-handler.ts:506', message: 'Stage 3 check', data: { allServersCount: allServers.length, uninitializedCount: uninitializedServers.length, uninitializedIds: uninitializedServers.map(s => s.id), initializationStatus: allServers.map(s => ({ id: s.id, initialized: this.initializationTracker.isInitialized(s.id) })) }, timestamp: Date.now(), sessionId: 'debug-session', runId: 'run1', hypothesisId: 'D' }) }).catch(() => { });
|
|
466
|
-
// #endregion
|
|
467
|
-
if (uninitializedServers.length > 0) {
|
|
468
|
-
// Actually connect to servers when discover_tools is called (code performs initialization)
|
|
469
|
-
// Retry connection up to 3 times with delays for servers that might not be ready
|
|
470
|
-
// #region agent log
|
|
471
|
-
fetch('http://127.0.0.1:7243/ingest/198b6a8b-7e4f-4c81-a922-32b1e2e171c8', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ location: 'discovery-handler.ts:510', message: 'Stage 3 connecting servers', data: { uninitializedIds: uninitializedServers.map(s => s.id) }, timestamp: Date.now(), sessionId: 'debug-session', runId: 'run1', hypothesisId: 'D' }) }).catch(() => { });
|
|
472
|
-
// #endregion
|
|
473
|
-
for (const server of uninitializedServers) {
|
|
474
|
-
let connected = false;
|
|
475
|
-
for (let attempt = 0; attempt < 3; attempt++) {
|
|
476
|
-
try {
|
|
477
|
-
// #region agent log
|
|
478
|
-
fetch('http://127.0.0.1:7243/ingest/198b6a8b-7e4f-4c81-a922-32b1e2e171c8', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ location: 'discovery-handler.ts:515', message: 'connection attempt', data: { serverId: server.id, attempt: attempt + 1 }, timestamp: Date.now(), sessionId: 'debug-session', runId: 'run1', hypothesisId: 'B' }) }).catch(() => { });
|
|
479
|
-
// #endregion
|
|
480
|
-
await this.serverManager.connectServer(server.id);
|
|
481
|
-
// #region agent log
|
|
482
|
-
fetch('http://127.0.0.1:7243/ingest/198b6a8b-7e4f-4c81-a922-32b1e2e171c8', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ location: 'discovery-handler.ts:518', message: 'connection attempt succeeded', data: { serverId: server.id, attempt: attempt + 1, isInitialized: this.initializationTracker.isInitialized(server.id) }, timestamp: Date.now(), sessionId: 'debug-session', runId: 'run1', hypothesisId: 'B' }) }).catch(() => { });
|
|
483
|
-
// #endregion
|
|
484
|
-
connected = true;
|
|
485
|
-
break;
|
|
486
|
-
}
|
|
487
|
-
catch (error) {
|
|
488
|
-
// #region agent log
|
|
489
|
-
fetch('http://127.0.0.1:7243/ingest/198b6a8b-7e4f-4c81-a922-32b1e2e171c8', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ location: 'discovery-handler.ts:522', message: 'connection attempt failed', data: { serverId: server.id, attempt: attempt + 1, errorMessage: error instanceof Error ? error.message : String(error) }, timestamp: Date.now(), sessionId: 'debug-session', runId: 'run1', hypothesisId: 'B' }) }).catch(() => { });
|
|
490
|
-
// #endregion
|
|
491
|
-
if (attempt < 2) {
|
|
492
|
-
// Wait a bit before retrying
|
|
493
|
-
await new Promise(resolve => setTimeout(resolve, 500));
|
|
494
|
-
}
|
|
495
|
-
else {
|
|
496
|
-
logger.error(`Failed to connect to server ${server.id} after ${attempt + 1} attempts: ${error}`);
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
if (!connected) {
|
|
501
|
-
// #region agent log
|
|
502
|
-
fetch('http://127.0.0.1:7243/ingest/198b6a8b-7e4f-4c81-a922-32b1e2e171c8', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ location: 'discovery-handler.ts:530', message: 'server connection failed after retries', data: { serverId: server.id, isInitialized: this.initializationTracker.isInitialized(server.id) }, timestamp: Date.now(), sessionId: 'debug-session', runId: 'run1', hypothesisId: 'B' }) }).catch(() => { });
|
|
503
|
-
// #endregion
|
|
504
|
-
logger.warn(`Server ${server.id} could not be connected after retries`);
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
// After connecting, check if all servers are now initialized
|
|
508
|
-
const stillUninitialized = allServers.filter((server) => !this.initializationTracker.isInitialized(server.id));
|
|
509
|
-
// #region agent log
|
|
510
|
-
fetch('http://127.0.0.1:7243/ingest/198b6a8b-7e4f-4c81-a922-32b1e2e171c8', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ location: 'discovery-handler.ts:535', message: 'Stage 3 after connection attempts', data: { stillUninitializedCount: stillUninitialized.length, stillUninitializedIds: stillUninitialized.map(s => s.id), initializationStatus: allServers.map(s => ({ id: s.id, initialized: this.initializationTracker.isInitialized(s.id) })) }, timestamp: Date.now(), sessionId: 'debug-session', runId: 'run1', hypothesisId: 'D' }) }).catch(() => { });
|
|
511
|
-
// #endregion
|
|
512
|
-
if (stillUninitialized.length > 0) {
|
|
513
|
-
// Some servers failed to initialize - return instructions
|
|
514
|
-
const instructions = this.initializationTracker.createInitInstructions(stillUninitialized.map((s) => s.id));
|
|
515
|
-
return {
|
|
516
|
-
initialized: false,
|
|
517
|
-
instructions: {
|
|
518
|
-
summary: instructions.summary,
|
|
519
|
-
role: instructions.role,
|
|
520
|
-
goal: instructions.goal,
|
|
521
|
-
context: instructions.context,
|
|
522
|
-
servers: instructions.servers,
|
|
523
|
-
steps: instructions.steps,
|
|
524
|
-
important: instructions.important,
|
|
525
|
-
toolOverrides: instructions.toolOverrides,
|
|
526
|
-
},
|
|
527
|
-
};
|
|
528
|
-
}
|
|
529
|
-
// All servers initialized - fall through to Stage 4
|
|
530
|
-
}
|
|
531
|
-
// Stage 4: All servers are initialized - return concise server-level summaries
|
|
532
|
-
// Check if config file needs to be created (for backward compatibility)
|
|
533
|
-
const needsConfig = servers && servers.length > 0;
|
|
534
|
-
if (needsConfig) {
|
|
535
|
-
const path = configPath || ".mcp-filter.json";
|
|
536
|
-
try {
|
|
537
|
-
await this.configInitializer.initConfig(path, servers);
|
|
538
|
-
}
|
|
539
|
-
catch (error) {
|
|
540
|
-
logger.error(`Failed to create config file: ${error}`);
|
|
541
|
-
// Continue even if config creation fails
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
// Collect server information for concise summaries and RGC prompt
|
|
545
|
-
const connectedServers = [];
|
|
546
|
-
const erroredServers = [];
|
|
547
|
-
const serverInfoForPrompt = [];
|
|
548
|
-
let totalTools = 0;
|
|
549
|
-
for (const server of allServers) {
|
|
550
|
-
// Only process initialized servers
|
|
551
|
-
if (!this.initializationTracker.isInitialized(server.id)) {
|
|
552
|
-
// Server not initialized - add to errored servers
|
|
553
|
-
const shortDescription = server.config.short_description ||
|
|
554
|
-
server.config.shortDescription ||
|
|
555
|
-
undefined;
|
|
556
|
-
erroredServers.push({
|
|
557
|
-
id: server.id,
|
|
558
|
-
short_description: shortDescription && shortDescription.length < 100 ? shortDescription : undefined,
|
|
559
|
-
error: "Server not initialized",
|
|
560
|
-
});
|
|
561
|
-
continue;
|
|
562
|
-
}
|
|
563
|
-
try {
|
|
564
|
-
const toolsResponse = await server.client.listTools();
|
|
565
|
-
const filteredTools = server.filter.filterList(toolsResponse.tools);
|
|
566
|
-
const toolNames = filteredTools.map((t) => t.name);
|
|
567
|
-
totalTools += toolNames.length;
|
|
568
|
-
// Get short_description from config if available
|
|
569
|
-
// Check both new format (mcpServers) and old format (servers) config
|
|
570
|
-
const shortDescription = server.config.short_description ||
|
|
571
|
-
server.config.shortDescription ||
|
|
572
|
-
undefined;
|
|
573
|
-
connectedServers.push({
|
|
574
|
-
id: server.id,
|
|
575
|
-
short_description: shortDescription && shortDescription.length < 100 ? shortDescription : undefined,
|
|
576
|
-
toolCount: toolNames.length,
|
|
577
|
-
toolNames,
|
|
578
|
-
});
|
|
579
|
-
serverInfoForPrompt.push({
|
|
580
|
-
id: server.id,
|
|
581
|
-
toolNames,
|
|
582
|
-
});
|
|
583
|
-
}
|
|
584
|
-
catch (error) {
|
|
585
|
-
logger.error(`Error listing tools from server ${server.id}: ${error}`);
|
|
586
|
-
const shortDescription = server.config.short_description ||
|
|
587
|
-
server.config.shortDescription ||
|
|
588
|
-
undefined;
|
|
589
|
-
erroredServers.push({
|
|
590
|
-
id: server.id,
|
|
591
|
-
short_description: shortDescription && shortDescription.length < 100 ? shortDescription : undefined,
|
|
592
|
-
error: error instanceof Error ? error.message : String(error),
|
|
593
|
-
});
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
// Create RGC prompt for generating short_description
|
|
597
|
-
const descriptionPrompt = this.initializationTracker.createShortDescriptionPrompt(serverInfoForPrompt);
|
|
598
|
-
return {
|
|
599
|
-
initialized: true,
|
|
600
|
-
summary: {
|
|
601
|
-
totalTools,
|
|
602
|
-
connectedServers: connectedServers.map((s) => ({
|
|
603
|
-
id: s.id,
|
|
604
|
-
short_description: s.short_description,
|
|
605
|
-
toolCount: s.toolCount,
|
|
606
|
-
methodNames: s.toolNames,
|
|
607
|
-
})),
|
|
608
|
-
erroredServers: erroredServers.map((s) => ({
|
|
609
|
-
id: s.id,
|
|
610
|
-
short_description: s.short_description,
|
|
611
|
-
error: s.error,
|
|
612
|
-
})),
|
|
613
|
-
},
|
|
614
|
-
descriptionPrompt: {
|
|
615
|
-
role: descriptionPrompt.role,
|
|
616
|
-
goal: descriptionPrompt.goal,
|
|
617
|
-
context: descriptionPrompt.context,
|
|
618
|
-
examples: descriptionPrompt.examples,
|
|
619
|
-
},
|
|
620
|
-
searchEnabled: true,
|
|
621
|
-
message: `Found ${totalTools} tool(s) across ${connectedServers.length} server(s). Use discover_tools with query parameter for semantic search.\n\n` +
|
|
622
|
-
`OPTIONAL: Optimize tool names and descriptions using 'toolOverrides' in .mcp-filter.json. ` +
|
|
623
|
-
`Only optimize when necessary: non-English names, excessively long names (>50 chars), or unclear descriptions. ` +
|
|
624
|
-
`Don't optimize if names are already short, clear, and in English. ` +
|
|
625
|
-
`Only include information the LLM doesn't already know - remove obvious information that's clear from the name.`,
|
|
626
|
-
};
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
//# sourceMappingURL=discovery-handler.js.map
|