@juspay/neurolink 9.27.0 → 9.28.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.
- package/CHANGELOG.md +6 -0
- package/README.md +59 -9
- package/dist/cli/commands/config.d.ts +4 -4
- package/dist/cli/commands/mcp.d.ts +87 -0
- package/dist/cli/commands/mcp.js +1524 -0
- package/dist/cli/loop/optionsSchema.js +4 -0
- package/dist/core/modules/ToolsManager.js +29 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +27 -1
- package/dist/lib/core/modules/ToolsManager.js +29 -2
- package/dist/lib/index.d.ts +2 -1
- package/dist/lib/index.js +27 -1
- package/dist/lib/mcp/agentExposure.d.ts +228 -0
- package/dist/lib/mcp/agentExposure.js +357 -0
- package/dist/lib/mcp/batching/index.d.ts +11 -0
- package/dist/lib/mcp/batching/index.js +11 -0
- package/dist/lib/mcp/batching/requestBatcher.d.ts +202 -0
- package/dist/lib/mcp/batching/requestBatcher.js +442 -0
- package/dist/lib/mcp/caching/index.d.ts +11 -0
- package/dist/lib/mcp/caching/index.js +11 -0
- package/dist/lib/mcp/caching/toolCache.d.ts +221 -0
- package/dist/lib/mcp/caching/toolCache.js +434 -0
- package/dist/lib/mcp/elicitation/elicitationManager.d.ts +169 -0
- package/dist/lib/mcp/elicitation/elicitationManager.js +377 -0
- package/dist/lib/mcp/elicitation/index.d.ts +11 -0
- package/dist/lib/mcp/elicitation/index.js +12 -0
- package/dist/lib/mcp/elicitation/types.d.ts +278 -0
- package/dist/lib/mcp/elicitation/types.js +11 -0
- package/dist/lib/mcp/elicitationProtocol.d.ts +228 -0
- package/dist/lib/mcp/elicitationProtocol.js +376 -0
- package/dist/lib/mcp/enhancedToolDiscovery.d.ts +205 -0
- package/dist/lib/mcp/enhancedToolDiscovery.js +482 -0
- package/dist/lib/mcp/index.d.ts +38 -1
- package/dist/lib/mcp/index.js +36 -3
- package/dist/lib/mcp/mcpRegistryClient.d.ts +332 -0
- package/dist/lib/mcp/mcpRegistryClient.js +489 -0
- package/dist/lib/mcp/mcpServerBase.d.ts +227 -0
- package/dist/lib/mcp/mcpServerBase.js +374 -0
- package/dist/lib/mcp/multiServerManager.d.ts +310 -0
- package/dist/lib/mcp/multiServerManager.js +580 -0
- package/dist/lib/mcp/routing/index.d.ts +11 -0
- package/dist/lib/mcp/routing/index.js +11 -0
- package/dist/lib/mcp/routing/toolRouter.d.ts +219 -0
- package/dist/lib/mcp/routing/toolRouter.js +417 -0
- package/dist/lib/mcp/serverCapabilities.d.ts +341 -0
- package/dist/lib/mcp/serverCapabilities.js +503 -0
- package/dist/lib/mcp/toolAnnotations.d.ts +154 -0
- package/dist/lib/mcp/toolAnnotations.js +240 -0
- package/dist/lib/mcp/toolConverter.d.ts +178 -0
- package/dist/lib/mcp/toolConverter.js +259 -0
- package/dist/lib/mcp/toolIntegration.d.ts +136 -0
- package/dist/lib/mcp/toolIntegration.js +335 -0
- package/dist/lib/neurolink.d.ts +275 -2
- package/dist/lib/neurolink.js +596 -56
- package/dist/lib/providers/litellm.d.ts +10 -0
- package/dist/lib/providers/litellm.js +104 -2
- package/dist/lib/types/configTypes.d.ts +56 -0
- package/dist/lib/types/generateTypes.d.ts +4 -0
- package/dist/lib/types/index.d.ts +1 -1
- package/dist/lib/types/modelTypes.d.ts +6 -6
- package/dist/lib/types/streamTypes.d.ts +2 -0
- package/dist/lib/types/tools.d.ts +2 -0
- package/dist/lib/utils/pricing.js +177 -17
- package/dist/lib/utils/schemaConversion.d.ts +6 -1
- package/dist/lib/utils/schemaConversion.js +50 -28
- package/dist/lib/workflow/config.d.ts +16 -16
- package/dist/mcp/agentExposure.d.ts +228 -0
- package/dist/mcp/agentExposure.js +356 -0
- package/dist/mcp/batching/index.d.ts +11 -0
- package/dist/mcp/batching/index.js +10 -0
- package/dist/mcp/batching/requestBatcher.d.ts +202 -0
- package/dist/mcp/batching/requestBatcher.js +441 -0
- package/dist/mcp/caching/index.d.ts +11 -0
- package/dist/mcp/caching/index.js +10 -0
- package/dist/mcp/caching/toolCache.d.ts +221 -0
- package/dist/mcp/caching/toolCache.js +433 -0
- package/dist/mcp/elicitation/elicitationManager.d.ts +169 -0
- package/dist/mcp/elicitation/elicitationManager.js +376 -0
- package/dist/mcp/elicitation/index.d.ts +11 -0
- package/dist/mcp/elicitation/index.js +11 -0
- package/dist/mcp/elicitation/types.d.ts +278 -0
- package/dist/mcp/elicitation/types.js +10 -0
- package/dist/mcp/elicitationProtocol.d.ts +228 -0
- package/dist/mcp/elicitationProtocol.js +375 -0
- package/dist/mcp/enhancedToolDiscovery.d.ts +205 -0
- package/dist/mcp/enhancedToolDiscovery.js +481 -0
- package/dist/mcp/index.d.ts +38 -1
- package/dist/mcp/index.js +36 -3
- package/dist/mcp/mcpRegistryClient.d.ts +332 -0
- package/dist/mcp/mcpRegistryClient.js +488 -0
- package/dist/mcp/mcpServerBase.d.ts +227 -0
- package/dist/mcp/mcpServerBase.js +373 -0
- package/dist/mcp/multiServerManager.d.ts +310 -0
- package/dist/mcp/multiServerManager.js +579 -0
- package/dist/mcp/routing/index.d.ts +11 -0
- package/dist/mcp/routing/index.js +10 -0
- package/dist/mcp/routing/toolRouter.d.ts +219 -0
- package/dist/mcp/routing/toolRouter.js +416 -0
- package/dist/mcp/serverCapabilities.d.ts +341 -0
- package/dist/mcp/serverCapabilities.js +502 -0
- package/dist/mcp/toolAnnotations.d.ts +154 -0
- package/dist/mcp/toolAnnotations.js +239 -0
- package/dist/mcp/toolConverter.d.ts +178 -0
- package/dist/mcp/toolConverter.js +258 -0
- package/dist/mcp/toolIntegration.d.ts +136 -0
- package/dist/mcp/toolIntegration.js +334 -0
- package/dist/neurolink.d.ts +275 -2
- package/dist/neurolink.js +596 -56
- package/dist/providers/litellm.d.ts +10 -0
- package/dist/providers/litellm.js +104 -2
- package/dist/types/configTypes.d.ts +56 -0
- package/dist/types/generateTypes.d.ts +4 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/streamTypes.d.ts +2 -0
- package/dist/types/tools.d.ts +2 -0
- package/dist/utils/pricing.js +177 -17
- package/dist/utils/schemaConversion.d.ts +6 -1
- package/dist/utils/schemaConversion.js +50 -28
- package/package.json +1 -1
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Server Base Class
|
|
3
|
+
*
|
|
4
|
+
* Abstract base class for creating custom MCP servers with consistent patterns
|
|
5
|
+
* for tool registration, execution, and lifecycle management.
|
|
6
|
+
*
|
|
7
|
+
* Implements Mastra-style MCPServerBase features including:
|
|
8
|
+
* - Tool annotation support (readOnlyHint, destructiveHint, idempotentHint)
|
|
9
|
+
* - Lifecycle hooks (onInit, onStart, onStop)
|
|
10
|
+
* - Event emission for tool operations
|
|
11
|
+
* - Conversion to MCPServerInfo format
|
|
12
|
+
*
|
|
13
|
+
* @module mcp/mcpServerBase
|
|
14
|
+
* @since 8.39.0
|
|
15
|
+
*/
|
|
16
|
+
import { EventEmitter } from "events";
|
|
17
|
+
import { withTimeout } from "../utils/async/withTimeout.js";
|
|
18
|
+
import { ErrorFactory } from "../utils/errorHandling.js";
|
|
19
|
+
/**
|
|
20
|
+
* Abstract base class for MCP servers
|
|
21
|
+
*
|
|
22
|
+
* Provides a foundation for creating custom MCP servers with consistent
|
|
23
|
+
* patterns for tool registration, execution, and lifecycle management.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* class MyCustomServer extends MCPServerBase {
|
|
28
|
+
* constructor() {
|
|
29
|
+
* super({
|
|
30
|
+
* id: "my-custom-server",
|
|
31
|
+
* name: "My Custom Server",
|
|
32
|
+
* description: "Provides custom functionality",
|
|
33
|
+
* category: "custom",
|
|
34
|
+
* });
|
|
35
|
+
*
|
|
36
|
+
* // Register tools in constructor or init
|
|
37
|
+
* this.registerTool({
|
|
38
|
+
* name: "myTool",
|
|
39
|
+
* description: "Does something useful",
|
|
40
|
+
* annotations: {
|
|
41
|
+
* readOnlyHint: true,
|
|
42
|
+
* idempotentHint: true,
|
|
43
|
+
* },
|
|
44
|
+
* execute: async (params, context) => {
|
|
45
|
+
* return { success: true, data: "result" };
|
|
46
|
+
* },
|
|
47
|
+
* });
|
|
48
|
+
* }
|
|
49
|
+
* }
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export class MCPServerBase extends EventEmitter {
|
|
53
|
+
config;
|
|
54
|
+
tools = new Map();
|
|
55
|
+
isInitialized = false;
|
|
56
|
+
isRunning = false;
|
|
57
|
+
constructor(config) {
|
|
58
|
+
super();
|
|
59
|
+
// Apply defaults
|
|
60
|
+
this.config = {
|
|
61
|
+
id: config.id,
|
|
62
|
+
name: config.name,
|
|
63
|
+
description: config.description ?? "",
|
|
64
|
+
version: config.version ?? "1.0.0",
|
|
65
|
+
category: config.category ?? "custom",
|
|
66
|
+
transport: config.transport ?? "stdio",
|
|
67
|
+
metadata: config.metadata ?? {},
|
|
68
|
+
defaultTimeoutMs: config.defaultTimeoutMs ?? 30_000,
|
|
69
|
+
defaultAnnotations: config.defaultAnnotations ?? {},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Initialize the server
|
|
74
|
+
* Override in subclasses for async initialization
|
|
75
|
+
*/
|
|
76
|
+
async init() {
|
|
77
|
+
if (this.isInitialized) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
await this.onInit();
|
|
81
|
+
this.isInitialized = true;
|
|
82
|
+
this.emit("serverReady", {
|
|
83
|
+
tools: Array.from(this.tools.keys()),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Hook for subclass initialization
|
|
88
|
+
* Override to perform async setup
|
|
89
|
+
*/
|
|
90
|
+
async onInit() {
|
|
91
|
+
// Override in subclasses
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Start the server
|
|
95
|
+
*/
|
|
96
|
+
async start() {
|
|
97
|
+
if (!this.isInitialized) {
|
|
98
|
+
await this.init();
|
|
99
|
+
}
|
|
100
|
+
if (this.isRunning) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
await this.onStart();
|
|
104
|
+
this.isRunning = true;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Hook for subclass start logic
|
|
108
|
+
*/
|
|
109
|
+
async onStart() {
|
|
110
|
+
// Override in subclasses
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Stop the server
|
|
114
|
+
*/
|
|
115
|
+
async stop(reason) {
|
|
116
|
+
if (!this.isRunning) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
await this.onStop();
|
|
120
|
+
this.isRunning = false;
|
|
121
|
+
this.emit("serverStopped", { reason });
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Hook for subclass stop logic
|
|
125
|
+
*/
|
|
126
|
+
async onStop() {
|
|
127
|
+
// Override in subclasses
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Register a tool with the server
|
|
131
|
+
*/
|
|
132
|
+
registerTool(tool) {
|
|
133
|
+
// Validate tool
|
|
134
|
+
this.validateTool(tool);
|
|
135
|
+
// Merge with default annotations
|
|
136
|
+
const mergedTool = {
|
|
137
|
+
...tool,
|
|
138
|
+
annotations: {
|
|
139
|
+
...this.config.defaultAnnotations,
|
|
140
|
+
...tool.annotations,
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
this.tools.set(tool.name, mergedTool);
|
|
144
|
+
this.emit("toolRegistered", {
|
|
145
|
+
toolName: tool.name,
|
|
146
|
+
tool: mergedTool,
|
|
147
|
+
});
|
|
148
|
+
return this;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Register multiple tools at once
|
|
152
|
+
*/
|
|
153
|
+
registerTools(tools) {
|
|
154
|
+
for (const tool of tools) {
|
|
155
|
+
this.registerTool(tool);
|
|
156
|
+
}
|
|
157
|
+
return this;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Validate tool configuration
|
|
161
|
+
*/
|
|
162
|
+
validateTool(tool) {
|
|
163
|
+
if (!tool.name || typeof tool.name !== "string") {
|
|
164
|
+
throw ErrorFactory.invalidConfiguration("tool.name", "Tool name is required and must be a string");
|
|
165
|
+
}
|
|
166
|
+
if (tool.name.length > 64) {
|
|
167
|
+
throw ErrorFactory.invalidConfiguration("tool.name", "Tool name must be 64 characters or less", { toolName: tool.name });
|
|
168
|
+
}
|
|
169
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(tool.name)) {
|
|
170
|
+
throw ErrorFactory.invalidConfiguration("tool.name", "Tool name must start with a letter or underscore and contain only alphanumeric characters, underscores, and hyphens", { toolName: tool.name });
|
|
171
|
+
}
|
|
172
|
+
if (!tool.description || typeof tool.description !== "string") {
|
|
173
|
+
throw ErrorFactory.invalidConfiguration("tool.description", "Tool description is required and must be a string", { toolName: tool.name });
|
|
174
|
+
}
|
|
175
|
+
if (typeof tool.execute !== "function") {
|
|
176
|
+
throw ErrorFactory.invalidConfiguration("tool.execute", "Tool execute function is required", { toolName: tool.name });
|
|
177
|
+
}
|
|
178
|
+
if (this.tools.has(tool.name)) {
|
|
179
|
+
throw ErrorFactory.invalidConfiguration("tool.name", `Tool '${tool.name}' is already registered`, { toolName: tool.name });
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Execute a tool by name
|
|
184
|
+
*/
|
|
185
|
+
async executeTool(toolName, params, context) {
|
|
186
|
+
const tool = this.tools.get(toolName);
|
|
187
|
+
if (!tool) {
|
|
188
|
+
return {
|
|
189
|
+
success: false,
|
|
190
|
+
error: `Tool '${toolName}' not found on server '${this.config.id}'`,
|
|
191
|
+
metadata: {
|
|
192
|
+
toolName,
|
|
193
|
+
serverId: this.config.id,
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
const startTime = Date.now();
|
|
198
|
+
const toolTimeoutMs = this.config.defaultTimeoutMs ?? 30_000;
|
|
199
|
+
try {
|
|
200
|
+
const result = await withTimeout(tool.execute(params, context ?? {}), toolTimeoutMs, ErrorFactory.toolTimeout(toolName, toolTimeoutMs).message);
|
|
201
|
+
const duration = Date.now() - startTime;
|
|
202
|
+
this.emit("toolExecuted", {
|
|
203
|
+
toolName,
|
|
204
|
+
duration,
|
|
205
|
+
success: true,
|
|
206
|
+
});
|
|
207
|
+
// Ensure result conforms to ToolResult
|
|
208
|
+
if (this.isToolResult(result)) {
|
|
209
|
+
return {
|
|
210
|
+
...result,
|
|
211
|
+
metadata: {
|
|
212
|
+
...result.metadata,
|
|
213
|
+
toolName,
|
|
214
|
+
serverId: this.config.id,
|
|
215
|
+
executionTime: duration,
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
success: true,
|
|
221
|
+
data: result,
|
|
222
|
+
metadata: {
|
|
223
|
+
toolName,
|
|
224
|
+
serverId: this.config.id,
|
|
225
|
+
executionTime: duration,
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
const duration = Date.now() - startTime;
|
|
231
|
+
this.emit("toolError", {
|
|
232
|
+
toolName,
|
|
233
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
234
|
+
});
|
|
235
|
+
this.emit("toolExecuted", {
|
|
236
|
+
toolName,
|
|
237
|
+
duration,
|
|
238
|
+
success: false,
|
|
239
|
+
});
|
|
240
|
+
return {
|
|
241
|
+
success: false,
|
|
242
|
+
error: error instanceof Error ? error.message : String(error),
|
|
243
|
+
metadata: {
|
|
244
|
+
toolName,
|
|
245
|
+
serverId: this.config.id,
|
|
246
|
+
executionTime: duration,
|
|
247
|
+
},
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Type guard to check if result is a ToolResult
|
|
253
|
+
*/
|
|
254
|
+
isToolResult(result) {
|
|
255
|
+
return (result !== null &&
|
|
256
|
+
typeof result === "object" &&
|
|
257
|
+
"success" in result &&
|
|
258
|
+
typeof result.success === "boolean");
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Get all registered tools
|
|
262
|
+
*/
|
|
263
|
+
getTools() {
|
|
264
|
+
return Array.from(this.tools.values());
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Get a specific tool by name
|
|
268
|
+
*/
|
|
269
|
+
getTool(name) {
|
|
270
|
+
return this.tools.get(name);
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Check if a tool exists
|
|
274
|
+
*/
|
|
275
|
+
hasTool(name) {
|
|
276
|
+
return this.tools.has(name);
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Remove a tool
|
|
280
|
+
*/
|
|
281
|
+
removeTool(name) {
|
|
282
|
+
return this.tools.delete(name);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Get server info in MCPServerInfo format
|
|
286
|
+
*/
|
|
287
|
+
toServerInfo() {
|
|
288
|
+
return {
|
|
289
|
+
id: this.config.id,
|
|
290
|
+
name: this.config.name,
|
|
291
|
+
description: this.config.description,
|
|
292
|
+
transport: this.config.transport,
|
|
293
|
+
status: this.isRunning ? "connected" : "stopped",
|
|
294
|
+
tools: this.getTools().map((tool) => ({
|
|
295
|
+
name: tool.name,
|
|
296
|
+
description: tool.description,
|
|
297
|
+
inputSchema: tool.inputSchema,
|
|
298
|
+
execute: (params, context) => this.executeTool(tool.name, params, context),
|
|
299
|
+
})),
|
|
300
|
+
metadata: {
|
|
301
|
+
...this.config.metadata,
|
|
302
|
+
category: this.config.category,
|
|
303
|
+
version: this.config.version,
|
|
304
|
+
},
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Get tools filtered by annotations
|
|
309
|
+
*/
|
|
310
|
+
getToolsByAnnotation(annotation, value) {
|
|
311
|
+
return this.getTools().filter((tool) => {
|
|
312
|
+
const annotationValue = tool.annotations?.[annotation];
|
|
313
|
+
if (Array.isArray(value) && Array.isArray(annotationValue)) {
|
|
314
|
+
return value.some((v) => annotationValue.includes(v));
|
|
315
|
+
}
|
|
316
|
+
return annotationValue === value;
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Get read-only tools
|
|
321
|
+
*/
|
|
322
|
+
getReadOnlyTools() {
|
|
323
|
+
return this.getToolsByAnnotation("readOnlyHint", true);
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Get destructive tools
|
|
327
|
+
*/
|
|
328
|
+
getDestructiveTools() {
|
|
329
|
+
return this.getToolsByAnnotation("destructiveHint", true);
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Get idempotent tools
|
|
333
|
+
*/
|
|
334
|
+
getIdempotentTools() {
|
|
335
|
+
return this.getToolsByAnnotation("idempotentHint", true);
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Get tools that require confirmation
|
|
339
|
+
*/
|
|
340
|
+
getToolsRequiringConfirmation() {
|
|
341
|
+
return this.getToolsByAnnotation("requiresConfirmation", true);
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Server identification
|
|
345
|
+
*/
|
|
346
|
+
get id() {
|
|
347
|
+
return this.config.id;
|
|
348
|
+
}
|
|
349
|
+
get name() {
|
|
350
|
+
return this.config.name;
|
|
351
|
+
}
|
|
352
|
+
get description() {
|
|
353
|
+
return this.config.description;
|
|
354
|
+
}
|
|
355
|
+
get version() {
|
|
356
|
+
return this.config.version;
|
|
357
|
+
}
|
|
358
|
+
get category() {
|
|
359
|
+
return this.config.category;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Check if server is initialized
|
|
363
|
+
*/
|
|
364
|
+
get initialized() {
|
|
365
|
+
return this.isInitialized;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Check if server is running
|
|
369
|
+
*/
|
|
370
|
+
get running() {
|
|
371
|
+
return this.isRunning;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
//# sourceMappingURL=mcpServerBase.js.map
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-Server Manager
|
|
3
|
+
*
|
|
4
|
+
* Coordinates multiple MCP servers with load balancing, failover,
|
|
5
|
+
* and unified tool discovery across all registered servers.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Load balancing strategies (round-robin, least-loaded, random)
|
|
9
|
+
* - Health-aware routing
|
|
10
|
+
* - Automatic failover
|
|
11
|
+
* - Unified tool namespace management
|
|
12
|
+
* - Cross-server tool discovery
|
|
13
|
+
*
|
|
14
|
+
* @module mcp/multiServerManager
|
|
15
|
+
* @since 8.39.0
|
|
16
|
+
*/
|
|
17
|
+
import { EventEmitter } from "events";
|
|
18
|
+
import type { MCPServerInfo } from "../types/mcpTypes.js";
|
|
19
|
+
import type { JsonObject } from "../types/common.js";
|
|
20
|
+
/**
|
|
21
|
+
* Load balancing strategies
|
|
22
|
+
*/
|
|
23
|
+
export type LoadBalancingStrategy = "round-robin" | "least-loaded" | "random" | "weighted" | "failover-only";
|
|
24
|
+
/**
|
|
25
|
+
* Server weight for weighted load balancing
|
|
26
|
+
*/
|
|
27
|
+
export type ServerWeight = {
|
|
28
|
+
serverId: string;
|
|
29
|
+
weight: number;
|
|
30
|
+
priority: number;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Server group definition
|
|
34
|
+
*/
|
|
35
|
+
export type ServerGroup = {
|
|
36
|
+
/**
|
|
37
|
+
* Group identifier
|
|
38
|
+
*/
|
|
39
|
+
id: string;
|
|
40
|
+
/**
|
|
41
|
+
* Human-readable name
|
|
42
|
+
*/
|
|
43
|
+
name: string;
|
|
44
|
+
/**
|
|
45
|
+
* Description of the group
|
|
46
|
+
*/
|
|
47
|
+
description?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Server IDs in this group
|
|
50
|
+
*/
|
|
51
|
+
servers: string[];
|
|
52
|
+
/**
|
|
53
|
+
* Load balancing strategy for this group
|
|
54
|
+
*/
|
|
55
|
+
strategy: LoadBalancingStrategy;
|
|
56
|
+
/**
|
|
57
|
+
* Weights for weighted strategy
|
|
58
|
+
*/
|
|
59
|
+
weights?: ServerWeight[];
|
|
60
|
+
/**
|
|
61
|
+
* Whether to enable health-aware routing
|
|
62
|
+
*/
|
|
63
|
+
healthAware?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Minimum healthy servers before alerting
|
|
66
|
+
*/
|
|
67
|
+
minHealthyServers?: number;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Unified tool entry from multiple servers
|
|
71
|
+
*/
|
|
72
|
+
export type UnifiedTool = {
|
|
73
|
+
/**
|
|
74
|
+
* Tool name
|
|
75
|
+
*/
|
|
76
|
+
name: string;
|
|
77
|
+
/**
|
|
78
|
+
* Tool description
|
|
79
|
+
*/
|
|
80
|
+
description: string;
|
|
81
|
+
/**
|
|
82
|
+
* Servers that provide this tool
|
|
83
|
+
*/
|
|
84
|
+
servers: Array<{
|
|
85
|
+
serverId: string;
|
|
86
|
+
serverName: string;
|
|
87
|
+
inputSchema?: JsonObject;
|
|
88
|
+
priority: number;
|
|
89
|
+
}>;
|
|
90
|
+
/**
|
|
91
|
+
* Whether this tool has naming conflicts
|
|
92
|
+
*/
|
|
93
|
+
hasConflict: boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Preferred server for this tool
|
|
96
|
+
*/
|
|
97
|
+
preferredServerId?: string;
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Multi-server manager configuration
|
|
101
|
+
*/
|
|
102
|
+
export type MultiServerManagerConfig = {
|
|
103
|
+
/**
|
|
104
|
+
* Default load balancing strategy
|
|
105
|
+
*/
|
|
106
|
+
defaultStrategy?: LoadBalancingStrategy;
|
|
107
|
+
/**
|
|
108
|
+
* Enable health-aware routing by default
|
|
109
|
+
*/
|
|
110
|
+
healthAwareRouting?: boolean;
|
|
111
|
+
/**
|
|
112
|
+
* Health check interval in milliseconds
|
|
113
|
+
*/
|
|
114
|
+
healthCheckInterval?: number;
|
|
115
|
+
/**
|
|
116
|
+
* Maximum retries on failover
|
|
117
|
+
*/
|
|
118
|
+
maxFailoverRetries?: number;
|
|
119
|
+
/**
|
|
120
|
+
* Tool namespace separator
|
|
121
|
+
*/
|
|
122
|
+
namespaceSeparator?: string;
|
|
123
|
+
/**
|
|
124
|
+
* Enable automatic tool namespace prefixing
|
|
125
|
+
*/
|
|
126
|
+
autoNamespace?: boolean;
|
|
127
|
+
/**
|
|
128
|
+
* Conflict resolution strategy.
|
|
129
|
+
* Reserved for future conflict resolution strategy — currently stored but not
|
|
130
|
+
* consumed by any routing or tool-merge logic.
|
|
131
|
+
*/
|
|
132
|
+
conflictResolution?: "first-wins" | "last-wins" | "namespace" | "explicit";
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Server metrics for load balancing
|
|
136
|
+
*/
|
|
137
|
+
type ServerMetrics = {
|
|
138
|
+
activeRequests: number;
|
|
139
|
+
totalRequests: number;
|
|
140
|
+
completedRequests: number;
|
|
141
|
+
averageResponseTime: number;
|
|
142
|
+
errorRate: number;
|
|
143
|
+
lastHealthCheck?: Date;
|
|
144
|
+
isHealthy: boolean;
|
|
145
|
+
};
|
|
146
|
+
/**
|
|
147
|
+
* Multi-Server Manager
|
|
148
|
+
*
|
|
149
|
+
* Coordinates multiple MCP servers for unified tool access
|
|
150
|
+
* with load balancing and failover capabilities.
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```typescript
|
|
154
|
+
* const manager = new MultiServerManager({
|
|
155
|
+
* defaultStrategy: "round-robin",
|
|
156
|
+
* healthAwareRouting: true,
|
|
157
|
+
* autoNamespace: true,
|
|
158
|
+
* });
|
|
159
|
+
*
|
|
160
|
+
* // Add servers
|
|
161
|
+
* manager.addServer(server1Info);
|
|
162
|
+
* manager.addServer(server2Info);
|
|
163
|
+
*
|
|
164
|
+
* // Create a group for redundant servers
|
|
165
|
+
* manager.createGroup({
|
|
166
|
+
* id: "data-servers",
|
|
167
|
+
* name: "Data Processing Servers",
|
|
168
|
+
* servers: ["server1", "server2"],
|
|
169
|
+
* strategy: "least-loaded",
|
|
170
|
+
* });
|
|
171
|
+
*
|
|
172
|
+
* // Get unified tool list
|
|
173
|
+
* const tools = manager.getUnifiedTools();
|
|
174
|
+
*
|
|
175
|
+
* // Execute with automatic routing
|
|
176
|
+
* const result = await manager.executeTool("readFile", { path: "/data" });
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
export declare class MultiServerManager extends EventEmitter {
|
|
180
|
+
private config;
|
|
181
|
+
private servers;
|
|
182
|
+
private groups;
|
|
183
|
+
private metrics;
|
|
184
|
+
private roundRobinCounters;
|
|
185
|
+
private toolPreferences;
|
|
186
|
+
constructor(config?: MultiServerManagerConfig);
|
|
187
|
+
/**
|
|
188
|
+
* Add a server to the manager
|
|
189
|
+
*/
|
|
190
|
+
addServer(server: MCPServerInfo): void;
|
|
191
|
+
/**
|
|
192
|
+
* Remove a server from the manager
|
|
193
|
+
*/
|
|
194
|
+
removeServer(serverId: string): boolean;
|
|
195
|
+
/**
|
|
196
|
+
* Update server info
|
|
197
|
+
*/
|
|
198
|
+
updateServer(serverId: string, updates: Partial<MCPServerInfo>): void;
|
|
199
|
+
/**
|
|
200
|
+
* Create a server group
|
|
201
|
+
*/
|
|
202
|
+
createGroup(group: ServerGroup): void;
|
|
203
|
+
/**
|
|
204
|
+
* Remove a server group
|
|
205
|
+
*/
|
|
206
|
+
removeGroup(groupId: string): boolean;
|
|
207
|
+
/**
|
|
208
|
+
* Add a server to a group
|
|
209
|
+
*/
|
|
210
|
+
addServerToGroup(serverId: string, groupId: string): void;
|
|
211
|
+
/**
|
|
212
|
+
* Remove a server from a group
|
|
213
|
+
*/
|
|
214
|
+
removeServerFromGroup(serverId: string, groupId: string): boolean;
|
|
215
|
+
/**
|
|
216
|
+
* Get unified tool list from all servers
|
|
217
|
+
*/
|
|
218
|
+
getUnifiedTools(): UnifiedTool[];
|
|
219
|
+
/**
|
|
220
|
+
* Get namespaced tools (server.toolName format)
|
|
221
|
+
*/
|
|
222
|
+
getNamespacedTools(): Array<{
|
|
223
|
+
fullName: string;
|
|
224
|
+
toolName: string;
|
|
225
|
+
serverId: string;
|
|
226
|
+
serverName: string;
|
|
227
|
+
description: string;
|
|
228
|
+
inputSchema?: JsonObject;
|
|
229
|
+
}>;
|
|
230
|
+
/**
|
|
231
|
+
* Set tool preference for routing
|
|
232
|
+
*/
|
|
233
|
+
setToolPreference(toolName: string, serverId: string): void;
|
|
234
|
+
/**
|
|
235
|
+
* Clear tool preference
|
|
236
|
+
*/
|
|
237
|
+
clearToolPreference(toolName: string): void;
|
|
238
|
+
/**
|
|
239
|
+
* Select a server for a tool using load balancing
|
|
240
|
+
*/
|
|
241
|
+
selectServer(toolName: string, groupId?: string): {
|
|
242
|
+
serverId: string;
|
|
243
|
+
server: MCPServerInfo;
|
|
244
|
+
} | null;
|
|
245
|
+
/**
|
|
246
|
+
* Apply load balancing strategy
|
|
247
|
+
*/
|
|
248
|
+
private applyStrategy;
|
|
249
|
+
/**
|
|
250
|
+
* Get server priority (lower = higher priority)
|
|
251
|
+
*
|
|
252
|
+
* @param serverId - The server to look up
|
|
253
|
+
* @param groupId - Optional group to scope the lookup to, avoiding
|
|
254
|
+
* nondeterministic iteration across all groups.
|
|
255
|
+
*/
|
|
256
|
+
private getServerPriority;
|
|
257
|
+
/**
|
|
258
|
+
* Update server metrics
|
|
259
|
+
*/
|
|
260
|
+
updateMetrics(serverId: string, updates: Partial<ServerMetrics>): void;
|
|
261
|
+
/**
|
|
262
|
+
* Mark request started
|
|
263
|
+
*/
|
|
264
|
+
requestStarted(serverId: string): void;
|
|
265
|
+
/**
|
|
266
|
+
* Mark request completed
|
|
267
|
+
*/
|
|
268
|
+
requestCompleted(serverId: string, duration: number, success: boolean): void;
|
|
269
|
+
/**
|
|
270
|
+
* Get all servers
|
|
271
|
+
*/
|
|
272
|
+
getServers(): MCPServerInfo[];
|
|
273
|
+
/**
|
|
274
|
+
* Get server by ID
|
|
275
|
+
*/
|
|
276
|
+
getServer(serverId: string): MCPServerInfo | undefined;
|
|
277
|
+
/**
|
|
278
|
+
* Get all groups
|
|
279
|
+
*/
|
|
280
|
+
getGroups(): ServerGroup[];
|
|
281
|
+
/**
|
|
282
|
+
* Get group by ID
|
|
283
|
+
*/
|
|
284
|
+
getGroup(groupId: string): ServerGroup | undefined;
|
|
285
|
+
/**
|
|
286
|
+
* Get server metrics
|
|
287
|
+
*/
|
|
288
|
+
getServerMetrics(serverId: string): ServerMetrics | undefined;
|
|
289
|
+
/**
|
|
290
|
+
* Get all metrics
|
|
291
|
+
*/
|
|
292
|
+
getAllMetrics(): Map<string, ServerMetrics>;
|
|
293
|
+
/**
|
|
294
|
+
* Get statistics
|
|
295
|
+
*/
|
|
296
|
+
getStatistics(): {
|
|
297
|
+
totalServers: number;
|
|
298
|
+
healthyServers: number;
|
|
299
|
+
totalGroups: number;
|
|
300
|
+
totalTools: number;
|
|
301
|
+
conflictingTools: number;
|
|
302
|
+
totalRequests: number;
|
|
303
|
+
activeRequests: number;
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Global multi-server manager instance
|
|
308
|
+
*/
|
|
309
|
+
export declare const globalMultiServerManager: MultiServerManager;
|
|
310
|
+
export {};
|