@juspay/neurolink 7.12.0 → 7.14.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 (65) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +90 -25
  3. package/dist/config/conversationMemoryConfig.js +2 -1
  4. package/dist/context/ContextManager.d.ts +28 -0
  5. package/dist/context/ContextManager.js +113 -0
  6. package/dist/context/config.d.ts +5 -0
  7. package/dist/context/config.js +42 -0
  8. package/dist/context/types.d.ts +20 -0
  9. package/dist/context/types.js +1 -0
  10. package/dist/context/utils.d.ts +7 -0
  11. package/dist/context/utils.js +8 -0
  12. package/dist/core/baseProvider.d.ts +16 -1
  13. package/dist/core/baseProvider.js +208 -9
  14. package/dist/core/conversationMemoryManager.js +3 -2
  15. package/dist/core/factory.js +13 -2
  16. package/dist/factories/providerFactory.js +5 -11
  17. package/dist/factories/providerRegistry.js +2 -2
  18. package/dist/lib/config/conversationMemoryConfig.js +2 -1
  19. package/dist/lib/context/ContextManager.d.ts +28 -0
  20. package/dist/lib/context/ContextManager.js +113 -0
  21. package/dist/lib/context/config.d.ts +5 -0
  22. package/dist/lib/context/config.js +42 -0
  23. package/dist/lib/context/types.d.ts +20 -0
  24. package/dist/lib/context/types.js +1 -0
  25. package/dist/lib/context/utils.d.ts +7 -0
  26. package/dist/lib/context/utils.js +8 -0
  27. package/dist/lib/core/baseProvider.d.ts +16 -1
  28. package/dist/lib/core/baseProvider.js +208 -9
  29. package/dist/lib/core/conversationMemoryManager.js +3 -2
  30. package/dist/lib/core/factory.js +13 -2
  31. package/dist/lib/factories/providerFactory.js +5 -11
  32. package/dist/lib/factories/providerRegistry.js +2 -2
  33. package/dist/lib/mcp/externalServerManager.d.ts +115 -0
  34. package/dist/lib/mcp/externalServerManager.js +677 -0
  35. package/dist/lib/mcp/mcpCircuitBreaker.d.ts +184 -0
  36. package/dist/lib/mcp/mcpCircuitBreaker.js +338 -0
  37. package/dist/lib/mcp/mcpClientFactory.d.ts +104 -0
  38. package/dist/lib/mcp/mcpClientFactory.js +416 -0
  39. package/dist/lib/mcp/toolDiscoveryService.d.ts +192 -0
  40. package/dist/lib/mcp/toolDiscoveryService.js +578 -0
  41. package/dist/lib/neurolink.d.ts +128 -16
  42. package/dist/lib/neurolink.js +555 -49
  43. package/dist/lib/providers/googleVertex.d.ts +1 -1
  44. package/dist/lib/providers/googleVertex.js +23 -7
  45. package/dist/lib/types/externalMcp.d.ts +282 -0
  46. package/dist/lib/types/externalMcp.js +6 -0
  47. package/dist/lib/types/generateTypes.d.ts +0 -1
  48. package/dist/lib/types/index.d.ts +1 -0
  49. package/dist/mcp/externalServerManager.d.ts +115 -0
  50. package/dist/mcp/externalServerManager.js +677 -0
  51. package/dist/mcp/mcpCircuitBreaker.d.ts +184 -0
  52. package/dist/mcp/mcpCircuitBreaker.js +338 -0
  53. package/dist/mcp/mcpClientFactory.d.ts +104 -0
  54. package/dist/mcp/mcpClientFactory.js +416 -0
  55. package/dist/mcp/toolDiscoveryService.d.ts +192 -0
  56. package/dist/mcp/toolDiscoveryService.js +578 -0
  57. package/dist/neurolink.d.ts +128 -16
  58. package/dist/neurolink.js +555 -49
  59. package/dist/providers/googleVertex.d.ts +1 -1
  60. package/dist/providers/googleVertex.js +23 -7
  61. package/dist/types/externalMcp.d.ts +282 -0
  62. package/dist/types/externalMcp.js +6 -0
  63. package/dist/types/generateTypes.d.ts +0 -1
  64. package/dist/types/index.d.ts +1 -0
  65. package/package.json +1 -1
package/dist/neurolink.js CHANGED
@@ -29,13 +29,19 @@ import { ErrorFactory, NeuroLinkError, withTimeout, withRetry, isRetriableError,
29
29
  import { EventEmitter } from "events";
30
30
  import { ConversationMemoryManager } from "./core/conversationMemoryManager.js";
31
31
  import { applyConversationMemoryDefaults, getConversationMessages, storeConversationTurn, } from "./utils/conversationMemoryUtils.js";
32
+ import { ExternalServerManager } from "./mcp/externalServerManager.js";
33
+ import { ContextManager } from "./context/ContextManager.js";
34
+ import { defaultContextConfig } from "./context/config.js";
32
35
  // Core types imported from core/types.js
33
36
  export class NeuroLink {
34
37
  mcpInitialized = false;
35
38
  emitter = new EventEmitter();
39
+ contextManager = null;
36
40
  // Tool registration support
37
41
  customTools = new Map();
38
42
  inMemoryServers = new Map();
43
+ // External MCP server management
44
+ externalServerManager;
39
45
  // Enhanced error handling support
40
46
  toolCircuitBreakers = new Map();
41
47
  toolExecutionMetrics = new Map();
@@ -70,6 +76,32 @@ export class NeuroLink {
70
76
  maxTurnsPerSession: memoryConfig.maxTurnsPerSession,
71
77
  });
72
78
  }
79
+ // Initialize external server manager
80
+ this.externalServerManager = new ExternalServerManager({
81
+ maxServers: 20,
82
+ defaultTimeout: 15000,
83
+ enableAutoRestart: true,
84
+ enablePerformanceMonitoring: true,
85
+ });
86
+ // Forward external server events
87
+ this.externalServerManager.on("connected", (event) => {
88
+ this.emitter.emit("externalMCP:serverConnected", event);
89
+ });
90
+ this.externalServerManager.on("disconnected", (event) => {
91
+ this.emitter.emit("externalMCP:serverDisconnected", event);
92
+ });
93
+ this.externalServerManager.on("failed", (event) => {
94
+ this.emitter.emit("externalMCP:serverFailed", event);
95
+ });
96
+ this.externalServerManager.on("toolDiscovered", (event) => {
97
+ this.emitter.emit("externalMCP:toolDiscovered", event);
98
+ // Tools are already registered on server connection, no need to duplicate here
99
+ });
100
+ this.externalServerManager.on("toolRemoved", (event) => {
101
+ this.emitter.emit("externalMCP:toolRemoved", event);
102
+ // Unregister removed tools from main tool registry
103
+ this.unregisterExternalMCPToolFromRegistry(event.toolName);
104
+ });
73
105
  }
74
106
  /**
75
107
  * Initialize MCP registry with enhanced error handling and resource cleanup
@@ -120,7 +152,35 @@ export class NeuroLink {
120
152
  * MAIN ENTRY POINT: Enhanced generate method with new function signature
121
153
  * Replaces both generateText and legacy methods
122
154
  */
155
+ /**
156
+ * Extracts the original prompt text from the provided input.
157
+ * If a string is provided, it returns the string directly.
158
+ * If a GenerateOptions object is provided, it returns the input text from the object.
159
+ * @param optionsOrPrompt The prompt input, either as a string or a GenerateOptions object.
160
+ * @returns The original prompt text as a string.
161
+ */
162
+ _extractOriginalPrompt(optionsOrPrompt) {
163
+ return typeof optionsOrPrompt === "string"
164
+ ? optionsOrPrompt
165
+ : optionsOrPrompt.input.text;
166
+ }
167
+ /**
168
+ * Enables automatic context summarization for the NeuroLink instance.
169
+ * Once enabled, the instance will maintain conversation history and
170
+ * automatically summarize it when it exceeds token limits.
171
+ * @param config Optional configuration to override default summarization settings.
172
+ */
173
+ enableContextSummarization(config) {
174
+ const contextConfig = {
175
+ ...defaultContextConfig,
176
+ ...config,
177
+ };
178
+ // Pass the internal generator function directly, bound to the correct `this` context.
179
+ this.contextManager = new ContextManager(this.generateTextInternal.bind(this), contextConfig);
180
+ logger.info("[NeuroLink] Automatic context summarization enabled.");
181
+ }
123
182
  async generate(optionsOrPrompt) {
183
+ const originalPrompt = this._extractOriginalPrompt(optionsOrPrompt);
124
184
  // Convert string prompt to full options
125
185
  const options = typeof optionsOrPrompt === "string"
126
186
  ? { input: { text: optionsOrPrompt } }
@@ -129,6 +189,11 @@ export class NeuroLink {
129
189
  if (!options.input?.text || typeof options.input.text !== "string") {
130
190
  throw new Error("Input text is required and must be a non-empty string");
131
191
  }
192
+ // Handle Context Management if enabled
193
+ if (this.contextManager) {
194
+ // Get the full context for the prompt without permanently adding the user's turn yet
195
+ options.input.text = this.contextManager.getContextForPrompt("user", options.input.text);
196
+ }
132
197
  const startTime = Date.now();
133
198
  // Emit generation start event
134
199
  this.emitter.emit("generation:start", {
@@ -240,6 +305,11 @@ export class NeuroLink {
240
305
  }
241
306
  : undefined,
242
307
  };
308
+ // Add both the user's turn and the AI's response to the permanent history
309
+ if (this.contextManager) {
310
+ await this.contextManager.addTurn("user", originalPrompt);
311
+ await this.contextManager.addTurn("assistant", generateResult.content);
312
+ }
243
313
  return generateResult;
244
314
  }
245
315
  /**
@@ -282,6 +352,7 @@ export class NeuroLink {
282
352
  // Try MCP-enhanced generation first (if not explicitly disabled)
283
353
  if (!options.disableTools) {
284
354
  try {
355
+ logger.debug(`[${functionTag}] Attempting MCP generation...`);
285
356
  const mcpResult = await this.tryMCPGeneration(options);
286
357
  if (mcpResult && mcpResult.content) {
287
358
  logger.debug(`[${functionTag}] MCP generation successful`);
@@ -289,6 +360,13 @@ export class NeuroLink {
289
360
  await storeConversationTurn(this.conversationMemory, options, mcpResult);
290
361
  return mcpResult;
291
362
  }
363
+ else {
364
+ logger.debug(`[${functionTag}] MCP generation returned empty result:`, {
365
+ hasResult: !!mcpResult,
366
+ hasContent: !!(mcpResult && mcpResult.content),
367
+ contentLength: mcpResult?.content?.length || 0,
368
+ });
369
+ }
292
370
  }
293
371
  catch (error) {
294
372
  logger.debug(`[${functionTag}] MCP generation failed, falling back`, {
@@ -328,34 +406,7 @@ export class NeuroLink {
328
406
  ? await getBestProvider()
329
407
  : options.provider;
330
408
  // Get available tools
331
- let availableTools = [];
332
- try {
333
- // 1. Get MCP server tools (existing functionality)
334
- const mcpTools = await toolRegistry.listTools();
335
- const mappedMcpTools = mcpTools.map((tool) => ({
336
- name: tool.name || "Unknown",
337
- description: tool.description || "No description available",
338
- server: tool.serverId || "Unknown", // Fix: use serverId instead of server
339
- category: tool.category,
340
- }));
341
- // 2. ✅ NEW: Get custom tools from this NeuroLink instance
342
- const customTools = Array.from(this.customTools.entries()).map(([name, tool]) => ({
343
- name,
344
- description: tool.description || "Custom tool",
345
- server: "custom",
346
- category: "user-defined",
347
- }));
348
- // 3. ✅ NEW: Combine all tools for AI generation
349
- availableTools = [...mappedMcpTools, ...customTools];
350
- logger.debug(`[${functionTag}] Available tools for AI generation:`, {
351
- mcpTools: mappedMcpTools.length,
352
- customTools: customTools.length,
353
- total: availableTools.length,
354
- });
355
- }
356
- catch (error) {
357
- mcpLogger.warn(`[${functionTag}] Failed to get tools`, { error });
358
- }
409
+ const availableTools = await this.getAllAvailableTools();
359
410
  // Create tool-aware system prompt
360
411
  const enhancedSystemPrompt = this.createToolAwareSystemPrompt(options.systemPrompt, availableTools);
361
412
  // Get conversation messages for context
@@ -378,7 +429,7 @@ export class NeuroLink {
378
429
  if (!result || !result.content || result.content.trim().length === 0) {
379
430
  return null; // Let caller fall back to direct generation
380
431
  }
381
- // Return enhanced result
432
+ // Return enhanced result with external tool information
382
433
  return {
383
434
  content: result.content,
384
435
  provider: providerName,
@@ -393,9 +444,14 @@ export class NeuroLink {
393
444
  success: true, // Assume success if tool executed (AI providers handle failures differently)
394
445
  serverId: teRecord.serverId || undefined,
395
446
  };
396
- }) || [], // ✅ NEW: Add missing toolExecutions with proper format
447
+ }) || [],
397
448
  enhancedWithTools: true,
398
- availableTools: availableTools.length > 0 ? availableTools : undefined,
449
+ availableTools: availableTools.map((tool) => ({
450
+ name: tool.name,
451
+ description: tool.description,
452
+ server: tool.server,
453
+ category: tool.category,
454
+ })),
399
455
  // Include analytics and evaluation from BaseProvider
400
456
  analytics: result.analytics,
401
457
  evaluation: result.evaluation,
@@ -498,8 +554,23 @@ export class NeuroLink {
498
554
  return originalSystemPrompt || "";
499
555
  }
500
556
  const toolDescriptions = availableTools
501
- .map((tool) => `- ${tool.name}: ${tool.description} (from ${tool.server})`)
502
- .join("\n");
557
+ .map((tool) => {
558
+ const toolWithSchema = tool;
559
+ const schema = (toolWithSchema.inputSchema ||
560
+ toolWithSchema.parameters);
561
+ let params = "";
562
+ if (schema && schema.properties) {
563
+ const requiredParams = new Set(schema.required || []);
564
+ params = Object.entries(schema.properties)
565
+ .map(([key, value]) => {
566
+ const required = requiredParams.has(key) ? " (required)" : "";
567
+ return ` - ${key}: ${value.type}${required}`;
568
+ })
569
+ .join("\n");
570
+ }
571
+ return `- ${tool.name}: ${tool.description} (from ${tool.server})\n${params}`;
572
+ })
573
+ .join("\n\n");
503
574
  const toolPrompt = `\n\nYou have access to these additional tools if needed:\n${toolDescriptions}\n\nIMPORTANT: You are a general-purpose AI assistant. Answer all requests directly and creatively. These tools are optional helpers - use them only when they would genuinely improve your response. For creative tasks like storytelling, writing, or general conversation, respond naturally without requiring tools.`;
504
575
  return (originalSystemPrompt || "") + toolPrompt;
505
576
  }
@@ -873,6 +944,14 @@ export class NeuroLink {
873
944
  async executeTool(toolName, params = {}, options) {
874
945
  const functionTag = "NeuroLink.executeTool";
875
946
  const executionStartTime = Date.now();
947
+ // Debug: Log tool execution attempt
948
+ logger.debug(`[${functionTag}] Tool execution requested:`, {
949
+ toolName,
950
+ params: typeof params === "object"
951
+ ? Object.keys(params).length + " params"
952
+ : params,
953
+ hasExternalManager: !!this.externalServerManager,
954
+ });
876
955
  // Emit tool start event
877
956
  this.emitter.emit("tool:start", {
878
957
  toolName,
@@ -1065,7 +1144,37 @@ export class NeuroLink {
1065
1144
  }
1066
1145
  }
1067
1146
  }
1068
- // If not found in custom tools or in-memory servers, try unified registry
1147
+ // Check external MCP servers
1148
+ const externalTools = this.externalServerManager.getAllTools();
1149
+ const externalTool = externalTools.find((tool) => tool.name === toolName);
1150
+ logger.debug(`[${functionTag}] External MCP tool search:`, {
1151
+ toolName,
1152
+ externalToolsCount: externalTools.length,
1153
+ foundTool: !!externalTool,
1154
+ isAvailable: externalTool?.isAvailable,
1155
+ serverId: externalTool?.serverId,
1156
+ });
1157
+ if (externalTool && externalTool.isAvailable) {
1158
+ try {
1159
+ mcpLogger.debug(`[${functionTag}] Executing external MCP tool: ${toolName} from ${externalTool.serverId}`);
1160
+ const result = await this.externalServerManager.executeTool(externalTool.serverId, toolName, params, { timeout: options.timeout });
1161
+ logger.debug(`[${functionTag}] External MCP tool execution successful:`, {
1162
+ toolName,
1163
+ serverId: externalTool.serverId,
1164
+ resultType: typeof result,
1165
+ });
1166
+ return result;
1167
+ }
1168
+ catch (error) {
1169
+ logger.error(`[${functionTag}] External MCP tool execution failed:`, {
1170
+ toolName,
1171
+ serverId: externalTool.serverId,
1172
+ error: error instanceof Error ? error.message : String(error),
1173
+ });
1174
+ throw ErrorFactory.toolExecutionFailed(toolName, error instanceof Error ? error : new Error(String(error)), externalTool.serverId);
1175
+ }
1176
+ }
1177
+ // If not found in custom tools, in-memory servers, or external servers, try unified registry
1069
1178
  try {
1070
1179
  const context = {
1071
1180
  sessionId: `neurolink-tool-${Date.now()}`,
@@ -1129,13 +1238,41 @@ export class NeuroLink {
1129
1238
  }
1130
1239
  }
1131
1240
  }
1132
- // 4. Combine all tools
1133
- const allTools = [...mcpTools, ...customTools, ...inMemoryTools];
1241
+ // 4. Get external MCP tools
1242
+ const externalMCPTools = this.externalServerManager
1243
+ .getAllTools()
1244
+ .map((tool) => ({
1245
+ name: tool.name,
1246
+ toolName: tool.name,
1247
+ description: tool.description,
1248
+ serverId: tool.serverId,
1249
+ category: tool.metadata?.category || "external-mcp",
1250
+ inputSchema: tool.inputSchema || {},
1251
+ isAvailable: tool.isAvailable,
1252
+ stats: tool.stats,
1253
+ }));
1254
+ // 5. Combine all tools with deduplication by name
1255
+ const combinedTools = [
1256
+ ...mcpTools,
1257
+ ...customTools,
1258
+ ...inMemoryTools,
1259
+ ...externalMCPTools,
1260
+ ];
1261
+ const uniqueTools = Array.from(combinedTools
1262
+ .reduce((map, tool) => {
1263
+ const key = tool.name;
1264
+ if (!map.has(key)) {
1265
+ map.set(key, tool);
1266
+ }
1267
+ return map;
1268
+ }, new Map())
1269
+ .values());
1134
1270
  mcpLogger.debug("Tool discovery results", {
1135
1271
  mcpTools: mcpTools.length,
1136
1272
  customTools: customTools.length,
1137
1273
  inMemoryTools: inMemoryTools.length,
1138
- total: allTools.length,
1274
+ externalMCPTools: externalMCPTools.length,
1275
+ total: uniqueTools.length,
1139
1276
  });
1140
1277
  // Check memory usage after tool enumeration
1141
1278
  const endMemory = MemoryManager.getMemoryUsageMB();
@@ -1143,11 +1280,11 @@ export class NeuroLink {
1143
1280
  if (memoryDelta > 10) {
1144
1281
  mcpLogger.debug(`🔍 Tool listing used ${memoryDelta}MB memory (large tool registry detected)`);
1145
1282
  // Suggest periodic cleanup for large tool registries
1146
- if (allTools.length > 100) {
1283
+ if (uniqueTools.length > 100) {
1147
1284
  mcpLogger.debug("💡 Suggestion: Consider using tool categories or lazy loading for large tool sets");
1148
1285
  }
1149
1286
  }
1150
- return allTools;
1287
+ return uniqueTools;
1151
1288
  }
1152
1289
  catch (error) {
1153
1290
  mcpLogger.error("Failed to list available tools", { error });
@@ -1355,17 +1492,44 @@ export class NeuroLink {
1355
1492
  */
1356
1493
  async getMCPStatus() {
1357
1494
  try {
1358
- // Simplified MCP status - unified registry removed
1495
+ // Get built-in tools
1359
1496
  const allTools = await toolRegistry.listTools();
1497
+ // Get external MCP server statistics
1498
+ const externalStats = this.externalServerManager.getStatistics();
1499
+ const externalServers = this.listExternalMCPServers();
1500
+ // Calculate totals
1501
+ const totalServers = 1 + externalStats.totalServers + this.inMemoryServers.size; // toolRegistry + external + in-memory
1502
+ const availableServers = 1 + externalStats.connectedServers; // toolRegistry always available + connected external
1503
+ const totalTools = allTools.length + externalStats.totalTools + this.customTools.size;
1504
+ // Convert external servers to MCPServerInfo format
1505
+ const externalMCPServers = externalServers.map((server) => ({
1506
+ id: server.serverId,
1507
+ name: typeof server.config.metadata?.title === "string"
1508
+ ? server.config.metadata.title
1509
+ : server.serverId,
1510
+ source: `external-${server.config.transport}`,
1511
+ status: server.status,
1512
+ hasServer: server.isHealthy,
1513
+ metadata: {
1514
+ transport: server.config.transport,
1515
+ command: server.config.command,
1516
+ toolCount: server.toolCount,
1517
+ uptime: server.uptime,
1518
+ },
1519
+ }));
1360
1520
  return {
1361
1521
  mcpInitialized: this.mcpInitialized,
1362
- totalServers: 1, // Only tool registry now
1363
- availableServers: 1,
1364
- autoDiscoveredCount: 0, // No auto-discovery
1365
- totalTools: allTools.length,
1366
- autoDiscoveredServers: [], // No auto-discovery
1522
+ totalServers,
1523
+ availableServers,
1524
+ autoDiscoveredCount: 0, // No auto-discovery for external servers
1525
+ totalTools,
1526
+ autoDiscoveredServers: [],
1367
1527
  customToolsCount: this.customTools.size,
1368
1528
  inMemoryServersCount: this.inMemoryServers.size,
1529
+ externalMCPServersCount: externalStats.totalServers,
1530
+ externalMCPConnectedCount: externalStats.connectedServers,
1531
+ externalMCPFailedCount: externalStats.failedServers,
1532
+ externalMCPServers,
1369
1533
  };
1370
1534
  }
1371
1535
  catch (error) {
@@ -1378,6 +1542,10 @@ export class NeuroLink {
1378
1542
  autoDiscoveredServers: [],
1379
1543
  customToolsCount: this.customTools.size,
1380
1544
  inMemoryServersCount: this.inMemoryServers.size,
1545
+ externalMCPServersCount: 0,
1546
+ externalMCPConnectedCount: 0,
1547
+ externalMCPFailedCount: 0,
1548
+ externalMCPServers: [],
1381
1549
  error: error instanceof Error ? error.message : String(error),
1382
1550
  };
1383
1551
  }
@@ -1387,8 +1555,54 @@ export class NeuroLink {
1387
1555
  * @returns Promise resolving to array of MCP server information
1388
1556
  */
1389
1557
  async listMCPServers() {
1390
- // Simplified MCP servers listing - unified registry removed
1391
- return [];
1558
+ const servers = [];
1559
+ // Add built-in toolRegistry as a server
1560
+ servers.push({
1561
+ id: "neurolink-direct",
1562
+ name: "NeuroLink Built-in Tools",
1563
+ source: "built-in",
1564
+ status: "connected",
1565
+ hasServer: true,
1566
+ metadata: {
1567
+ type: "direct-tools",
1568
+ description: "Built-in NeuroLink tools (getCurrentTime, readFile, etc.)",
1569
+ },
1570
+ });
1571
+ // Add in-memory servers
1572
+ for (const [serverId, serverConfig] of this.inMemoryServers.entries()) {
1573
+ servers.push({
1574
+ id: serverId,
1575
+ name: serverConfig.server.title || serverId,
1576
+ source: "in-memory",
1577
+ status: "connected",
1578
+ hasServer: true,
1579
+ metadata: {
1580
+ category: serverConfig.category,
1581
+ provider: serverConfig.metadata?.provider,
1582
+ version: serverConfig.metadata?.version,
1583
+ },
1584
+ });
1585
+ }
1586
+ // Add external MCP servers
1587
+ const externalServers = this.listExternalMCPServers();
1588
+ for (const server of externalServers) {
1589
+ servers.push({
1590
+ id: server.serverId,
1591
+ name: typeof server.config.metadata?.title === "string"
1592
+ ? server.config.metadata.title
1593
+ : server.serverId,
1594
+ source: `external-${server.config.transport}`,
1595
+ status: server.status,
1596
+ hasServer: server.isHealthy,
1597
+ metadata: {
1598
+ transport: server.config.transport,
1599
+ command: server.config.command,
1600
+ toolCount: server.toolCount,
1601
+ uptime: server.uptime,
1602
+ },
1603
+ });
1604
+ }
1605
+ return servers;
1392
1606
  }
1393
1607
  /**
1394
1608
  * Test connectivity to a specific MCP server
@@ -1396,8 +1610,29 @@ export class NeuroLink {
1396
1610
  * @returns Promise resolving to true if server is reachable
1397
1611
  */
1398
1612
  async testMCPServer(serverId) {
1399
- // Simplified MCP server testing - unified registry removed
1400
- return false; // No auto-discovery servers available
1613
+ try {
1614
+ // Test built-in tools
1615
+ if (serverId === "neurolink-direct") {
1616
+ const tools = await toolRegistry.listTools();
1617
+ return tools.length > 0;
1618
+ }
1619
+ // Test in-memory servers
1620
+ if (this.inMemoryServers.has(serverId)) {
1621
+ const serverConfig = this.inMemoryServers.get(serverId);
1622
+ return !!(serverConfig.server && serverConfig.server.tools);
1623
+ }
1624
+ // Test external MCP servers
1625
+ const externalServer = this.externalServerManager.getServer(serverId);
1626
+ if (externalServer) {
1627
+ return (externalServer.status === "connected" &&
1628
+ externalServer.client !== null);
1629
+ }
1630
+ return false;
1631
+ }
1632
+ catch (error) {
1633
+ mcpLogger.error(`[NeuroLink] Error testing MCP server ${serverId}:`, error);
1634
+ return false;
1635
+ }
1401
1636
  }
1402
1637
  // ==================== PROVIDER HEALTH CHECKING ====================
1403
1638
  /**
@@ -1639,6 +1874,277 @@ export class NeuroLink {
1639
1874
  }
1640
1875
  await this.conversationMemory.clearAllSessions();
1641
1876
  }
1877
+ // ===== EXTERNAL MCP SERVER METHODS =====
1878
+ /**
1879
+ * Add an external MCP server
1880
+ * Automatically discovers and registers tools from the server
1881
+ * @param serverId - Unique identifier for the server
1882
+ * @param config - External MCP server configuration
1883
+ * @returns Operation result with server instance
1884
+ */
1885
+ async addExternalMCPServer(serverId, config) {
1886
+ try {
1887
+ mcpLogger.info(`[NeuroLink] Adding external MCP server: ${serverId}`, {
1888
+ command: config.command,
1889
+ transport: config.transport,
1890
+ });
1891
+ const result = await this.externalServerManager.addServer(serverId, config);
1892
+ if (result.success) {
1893
+ mcpLogger.info(`[NeuroLink] External MCP server added successfully: ${serverId}`, {
1894
+ toolsDiscovered: result.metadata?.toolsDiscovered || 0,
1895
+ duration: result.duration,
1896
+ });
1897
+ // Emit server added event
1898
+ this.emitter.emit("externalMCP:serverAdded", {
1899
+ serverId,
1900
+ config,
1901
+ toolCount: result.metadata?.toolsDiscovered || 0,
1902
+ timestamp: Date.now(),
1903
+ });
1904
+ }
1905
+ else {
1906
+ mcpLogger.error(`[NeuroLink] Failed to add external MCP server: ${serverId}`, {
1907
+ error: result.error,
1908
+ });
1909
+ }
1910
+ return result;
1911
+ }
1912
+ catch (error) {
1913
+ mcpLogger.error(`[NeuroLink] Error adding external MCP server: ${serverId}`, error);
1914
+ throw error;
1915
+ }
1916
+ }
1917
+ /**
1918
+ * Remove an external MCP server
1919
+ * Stops the server and removes all its tools
1920
+ * @param serverId - ID of the server to remove
1921
+ * @returns Operation result
1922
+ */
1923
+ async removeExternalMCPServer(serverId) {
1924
+ try {
1925
+ mcpLogger.info(`[NeuroLink] Removing external MCP server: ${serverId}`);
1926
+ const result = await this.externalServerManager.removeServer(serverId);
1927
+ if (result.success) {
1928
+ mcpLogger.info(`[NeuroLink] External MCP server removed successfully: ${serverId}`);
1929
+ // Emit server removed event
1930
+ this.emitter.emit("externalMCP:serverRemoved", {
1931
+ serverId,
1932
+ timestamp: Date.now(),
1933
+ });
1934
+ }
1935
+ else {
1936
+ mcpLogger.error(`[NeuroLink] Failed to remove external MCP server: ${serverId}`, {
1937
+ error: result.error,
1938
+ });
1939
+ }
1940
+ return result;
1941
+ }
1942
+ catch (error) {
1943
+ mcpLogger.error(`[NeuroLink] Error removing external MCP server: ${serverId}`, error);
1944
+ throw error;
1945
+ }
1946
+ }
1947
+ /**
1948
+ * List all external MCP servers
1949
+ * @returns Array of server health information
1950
+ */
1951
+ listExternalMCPServers() {
1952
+ const serverStatuses = this.externalServerManager.getServerStatuses();
1953
+ const allServers = this.externalServerManager.getAllServers();
1954
+ return serverStatuses.map((health) => {
1955
+ const server = allServers.get(health.serverId);
1956
+ return {
1957
+ serverId: health.serverId,
1958
+ status: health.status,
1959
+ toolCount: health.toolCount,
1960
+ uptime: health.performance.uptime,
1961
+ isHealthy: health.isHealthy,
1962
+ config: server?.config || {},
1963
+ };
1964
+ });
1965
+ }
1966
+ /**
1967
+ * Get external MCP server status
1968
+ * @param serverId - ID of the server
1969
+ * @returns Server instance or undefined if not found
1970
+ */
1971
+ getExternalMCPServer(serverId) {
1972
+ return this.externalServerManager.getServer(serverId);
1973
+ }
1974
+ /**
1975
+ * Execute a tool from an external MCP server
1976
+ * @param serverId - ID of the server
1977
+ * @param toolName - Name of the tool
1978
+ * @param parameters - Tool parameters
1979
+ * @param options - Execution options
1980
+ * @returns Tool execution result
1981
+ */
1982
+ async executeExternalMCPTool(serverId, toolName, parameters, options) {
1983
+ try {
1984
+ mcpLogger.debug(`[NeuroLink] Executing external MCP tool: ${toolName} on ${serverId}`);
1985
+ const result = await this.externalServerManager.executeTool(serverId, toolName, parameters, options);
1986
+ mcpLogger.debug(`[NeuroLink] External MCP tool executed successfully: ${toolName}`);
1987
+ return result;
1988
+ }
1989
+ catch (error) {
1990
+ mcpLogger.error(`[NeuroLink] External MCP tool execution failed: ${toolName}`, error);
1991
+ throw error;
1992
+ }
1993
+ }
1994
+ /**
1995
+ * Get all tools from external MCP servers
1996
+ * @returns Array of external tool information
1997
+ */
1998
+ getExternalMCPTools() {
1999
+ return this.externalServerManager.getAllTools();
2000
+ }
2001
+ /**
2002
+ * Get tools from a specific external MCP server
2003
+ * @param serverId - ID of the server
2004
+ * @returns Array of tool information for the server
2005
+ */
2006
+ getExternalMCPServerTools(serverId) {
2007
+ return this.externalServerManager.getServerTools(serverId);
2008
+ }
2009
+ /**
2010
+ * Test connection to an external MCP server
2011
+ * @param config - Server configuration to test
2012
+ * @returns Test result with connection status
2013
+ */
2014
+ async testExternalMCPConnection(config) {
2015
+ try {
2016
+ const { MCPClientFactory } = await import("./mcp/mcpClientFactory.js");
2017
+ const testResult = await MCPClientFactory.testConnection(config, 10000);
2018
+ return {
2019
+ success: testResult.success,
2020
+ error: testResult.error,
2021
+ toolCount: testResult.capabilities ? 1 : 0, // Basic indication
2022
+ };
2023
+ }
2024
+ catch (error) {
2025
+ return {
2026
+ success: false,
2027
+ error: error instanceof Error ? error.message : String(error),
2028
+ };
2029
+ }
2030
+ }
2031
+ /**
2032
+ * Get external MCP server manager statistics
2033
+ * @returns Statistics about external servers and tools
2034
+ */
2035
+ getExternalMCPStatistics() {
2036
+ return this.externalServerManager.getStatistics();
2037
+ }
2038
+ /**
2039
+ * Shutdown all external MCP servers
2040
+ * Called automatically on process exit
2041
+ */
2042
+ async shutdownExternalMCPServers() {
2043
+ try {
2044
+ mcpLogger.info("[NeuroLink] Shutting down all external MCP servers...");
2045
+ // First, unregister all external MCP tools from the main tool registry
2046
+ this.unregisterAllExternalMCPToolsFromRegistry();
2047
+ // Then shutdown the external server manager
2048
+ await this.externalServerManager.shutdown();
2049
+ mcpLogger.info("[NeuroLink] All external MCP servers shut down successfully");
2050
+ }
2051
+ catch (error) {
2052
+ mcpLogger.error("[NeuroLink] Error shutting down external MCP servers:", error);
2053
+ throw error;
2054
+ }
2055
+ }
2056
+ /**
2057
+ * Convert external MCP tools to Vercel AI SDK tool format
2058
+ * This allows AI providers to use external tools directly
2059
+ */
2060
+ convertExternalMCPToolsToAISDKFormat() {
2061
+ const externalTools = this.externalServerManager.getAllTools();
2062
+ const aiSDKTools = {};
2063
+ for (const tool of externalTools) {
2064
+ if (tool.isAvailable) {
2065
+ // Create tool definition without parameters schema to avoid Zod issues
2066
+ // The AI provider will handle parameters dynamically based on the tool description
2067
+ const toolDefinition = {
2068
+ description: tool.description,
2069
+ execute: async (args) => {
2070
+ try {
2071
+ mcpLogger.debug(`[NeuroLink] Executing external MCP tool via AI SDK: ${tool.name}`, { args });
2072
+ const result = await this.externalServerManager.executeTool(tool.serverId, tool.name, args, { timeout: 30000 });
2073
+ mcpLogger.debug(`[NeuroLink] External MCP tool execution result: ${tool.name}`, {
2074
+ success: !!result,
2075
+ hasData: !!(result &&
2076
+ typeof result === "object" &&
2077
+ "content" in result),
2078
+ });
2079
+ return result;
2080
+ }
2081
+ catch (error) {
2082
+ mcpLogger.error(`[NeuroLink] External MCP tool execution failed: ${tool.name}`, error);
2083
+ throw error;
2084
+ }
2085
+ },
2086
+ };
2087
+ // Only add parameters if we have a valid schema - otherwise omit it entirely
2088
+ // This prevents Zod schema parsing errors
2089
+ aiSDKTools[tool.name] = toolDefinition;
2090
+ mcpLogger.debug(`[NeuroLink] Converted external MCP tool to AI SDK format: ${tool.name} from server ${tool.serverId}`);
2091
+ }
2092
+ }
2093
+ mcpLogger.info(`[NeuroLink] Converted ${Object.keys(aiSDKTools).length} external MCP tools to AI SDK format`);
2094
+ return aiSDKTools;
2095
+ }
2096
+ /**
2097
+ * Convert JSON Schema to AI SDK compatible format
2098
+ * For now, we'll skip schema validation and let the AI SDK handle parameters dynamically
2099
+ */
2100
+ convertJSONSchemaToAISDKFormat(inputSchema) {
2101
+ // The simplest approach: don't provide parameters schema
2102
+ // This lets the AI SDK handle the tool without schema validation
2103
+ // Tools will still work, they just won't have strict parameter validation
2104
+ return undefined;
2105
+ }
2106
+ /**
2107
+ * Unregister external MCP tools from a specific server
2108
+ */
2109
+ unregisterExternalMCPToolsFromRegistry(serverId) {
2110
+ try {
2111
+ const externalTools = this.externalServerManager.getServerTools(serverId);
2112
+ for (const tool of externalTools) {
2113
+ toolRegistry.removeTool(tool.name);
2114
+ mcpLogger.debug(`[NeuroLink] Unregistered external MCP tool from main registry: ${tool.name}`);
2115
+ }
2116
+ }
2117
+ catch (error) {
2118
+ mcpLogger.error(`[NeuroLink] Failed to unregister external MCP tools from registry for server ${serverId}:`, error);
2119
+ }
2120
+ }
2121
+ /**
2122
+ * Unregister a specific external MCP tool from the main registry
2123
+ */
2124
+ unregisterExternalMCPToolFromRegistry(toolName) {
2125
+ try {
2126
+ toolRegistry.removeTool(toolName);
2127
+ mcpLogger.debug(`[NeuroLink] Unregistered external MCP tool from main registry: ${toolName}`);
2128
+ }
2129
+ catch (error) {
2130
+ mcpLogger.error(`[NeuroLink] Failed to unregister external MCP tool ${toolName} from registry:`, error);
2131
+ }
2132
+ }
2133
+ /**
2134
+ * Unregister all external MCP tools from the main registry
2135
+ */
2136
+ unregisterAllExternalMCPToolsFromRegistry() {
2137
+ try {
2138
+ const externalTools = this.externalServerManager.getAllTools();
2139
+ for (const tool of externalTools) {
2140
+ toolRegistry.removeTool(tool.name);
2141
+ }
2142
+ mcpLogger.debug(`[NeuroLink] Unregistered ${externalTools.length} external MCP tools from main registry`);
2143
+ }
2144
+ catch (error) {
2145
+ mcpLogger.error("[NeuroLink] Failed to unregister all external MCP tools from registry:", error);
2146
+ }
2147
+ }
1642
2148
  }
1643
2149
  // Create default instance
1644
2150
  export const neurolink = new NeuroLink();