@juspay/neurolink 7.13.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 (55) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +89 -25
  3. package/dist/config/conversationMemoryConfig.js +2 -1
  4. package/dist/context/ContextManager.js +15 -4
  5. package/dist/context/config.js +5 -1
  6. package/dist/context/utils.js +1 -1
  7. package/dist/core/baseProvider.d.ts +16 -1
  8. package/dist/core/baseProvider.js +208 -9
  9. package/dist/core/conversationMemoryManager.js +3 -2
  10. package/dist/core/factory.js +13 -2
  11. package/dist/factories/providerFactory.js +5 -11
  12. package/dist/factories/providerRegistry.js +2 -2
  13. package/dist/lib/config/conversationMemoryConfig.js +2 -1
  14. package/dist/lib/context/ContextManager.js +15 -4
  15. package/dist/lib/context/config.js +5 -1
  16. package/dist/lib/context/utils.js +1 -1
  17. package/dist/lib/core/baseProvider.d.ts +16 -1
  18. package/dist/lib/core/baseProvider.js +208 -9
  19. package/dist/lib/core/conversationMemoryManager.js +3 -2
  20. package/dist/lib/core/factory.js +13 -2
  21. package/dist/lib/factories/providerFactory.js +5 -11
  22. package/dist/lib/factories/providerRegistry.js +2 -2
  23. package/dist/lib/mcp/externalServerManager.d.ts +115 -0
  24. package/dist/lib/mcp/externalServerManager.js +677 -0
  25. package/dist/lib/mcp/mcpCircuitBreaker.d.ts +184 -0
  26. package/dist/lib/mcp/mcpCircuitBreaker.js +338 -0
  27. package/dist/lib/mcp/mcpClientFactory.d.ts +104 -0
  28. package/dist/lib/mcp/mcpClientFactory.js +416 -0
  29. package/dist/lib/mcp/toolDiscoveryService.d.ts +192 -0
  30. package/dist/lib/mcp/toolDiscoveryService.js +578 -0
  31. package/dist/lib/neurolink.d.ts +111 -16
  32. package/dist/lib/neurolink.js +517 -50
  33. package/dist/lib/providers/googleVertex.d.ts +1 -1
  34. package/dist/lib/providers/googleVertex.js +23 -7
  35. package/dist/lib/types/externalMcp.d.ts +282 -0
  36. package/dist/lib/types/externalMcp.js +6 -0
  37. package/dist/lib/types/generateTypes.d.ts +0 -1
  38. package/dist/lib/types/index.d.ts +1 -0
  39. package/dist/mcp/externalServerManager.d.ts +115 -0
  40. package/dist/mcp/externalServerManager.js +677 -0
  41. package/dist/mcp/mcpCircuitBreaker.d.ts +184 -0
  42. package/dist/mcp/mcpCircuitBreaker.js +338 -0
  43. package/dist/mcp/mcpClientFactory.d.ts +104 -0
  44. package/dist/mcp/mcpClientFactory.js +416 -0
  45. package/dist/mcp/toolDiscoveryService.d.ts +192 -0
  46. package/dist/mcp/toolDiscoveryService.js +578 -0
  47. package/dist/neurolink.d.ts +111 -16
  48. package/dist/neurolink.js +517 -50
  49. package/dist/providers/googleVertex.d.ts +1 -1
  50. package/dist/providers/googleVertex.js +23 -7
  51. package/dist/types/externalMcp.d.ts +282 -0
  52. package/dist/types/externalMcp.js +6 -0
  53. package/dist/types/generateTypes.d.ts +0 -1
  54. package/dist/types/index.d.ts +1 -0
  55. package/package.json +1 -1
package/dist/neurolink.js CHANGED
@@ -29,6 +29,7 @@ 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";
32
33
  import { ContextManager } from "./context/ContextManager.js";
33
34
  import { defaultContextConfig } from "./context/config.js";
34
35
  // Core types imported from core/types.js
@@ -39,6 +40,8 @@ export class NeuroLink {
39
40
  // Tool registration support
40
41
  customTools = new Map();
41
42
  inMemoryServers = new Map();
43
+ // External MCP server management
44
+ externalServerManager;
42
45
  // Enhanced error handling support
43
46
  toolCircuitBreakers = new Map();
44
47
  toolExecutionMetrics = new Map();
@@ -73,6 +76,32 @@ export class NeuroLink {
73
76
  maxTurnsPerSession: memoryConfig.maxTurnsPerSession,
74
77
  });
75
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
+ });
76
105
  }
77
106
  /**
78
107
  * Initialize MCP registry with enhanced error handling and resource cleanup
@@ -131,7 +160,9 @@ export class NeuroLink {
131
160
  * @returns The original prompt text as a string.
132
161
  */
133
162
  _extractOriginalPrompt(optionsOrPrompt) {
134
- return typeof optionsOrPrompt === 'string' ? optionsOrPrompt : optionsOrPrompt.input.text;
163
+ return typeof optionsOrPrompt === "string"
164
+ ? optionsOrPrompt
165
+ : optionsOrPrompt.input.text;
135
166
  }
136
167
  /**
137
168
  * Enables automatic context summarization for the NeuroLink instance.
@@ -321,6 +352,7 @@ export class NeuroLink {
321
352
  // Try MCP-enhanced generation first (if not explicitly disabled)
322
353
  if (!options.disableTools) {
323
354
  try {
355
+ logger.debug(`[${functionTag}] Attempting MCP generation...`);
324
356
  const mcpResult = await this.tryMCPGeneration(options);
325
357
  if (mcpResult && mcpResult.content) {
326
358
  logger.debug(`[${functionTag}] MCP generation successful`);
@@ -328,6 +360,13 @@ export class NeuroLink {
328
360
  await storeConversationTurn(this.conversationMemory, options, mcpResult);
329
361
  return mcpResult;
330
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
+ }
331
370
  }
332
371
  catch (error) {
333
372
  logger.debug(`[${functionTag}] MCP generation failed, falling back`, {
@@ -367,34 +406,7 @@ export class NeuroLink {
367
406
  ? await getBestProvider()
368
407
  : options.provider;
369
408
  // Get available tools
370
- let availableTools = [];
371
- try {
372
- // 1. Get MCP server tools (existing functionality)
373
- const mcpTools = await toolRegistry.listTools();
374
- const mappedMcpTools = mcpTools.map((tool) => ({
375
- name: tool.name || "Unknown",
376
- description: tool.description || "No description available",
377
- server: tool.serverId || "Unknown", // Fix: use serverId instead of server
378
- category: tool.category,
379
- }));
380
- // 2. ✅ NEW: Get custom tools from this NeuroLink instance
381
- const customTools = Array.from(this.customTools.entries()).map(([name, tool]) => ({
382
- name,
383
- description: tool.description || "Custom tool",
384
- server: "custom",
385
- category: "user-defined",
386
- }));
387
- // 3. ✅ NEW: Combine all tools for AI generation
388
- availableTools = [...mappedMcpTools, ...customTools];
389
- logger.debug(`[${functionTag}] Available tools for AI generation:`, {
390
- mcpTools: mappedMcpTools.length,
391
- customTools: customTools.length,
392
- total: availableTools.length,
393
- });
394
- }
395
- catch (error) {
396
- mcpLogger.warn(`[${functionTag}] Failed to get tools`, { error });
397
- }
409
+ const availableTools = await this.getAllAvailableTools();
398
410
  // Create tool-aware system prompt
399
411
  const enhancedSystemPrompt = this.createToolAwareSystemPrompt(options.systemPrompt, availableTools);
400
412
  // Get conversation messages for context
@@ -417,7 +429,7 @@ export class NeuroLink {
417
429
  if (!result || !result.content || result.content.trim().length === 0) {
418
430
  return null; // Let caller fall back to direct generation
419
431
  }
420
- // Return enhanced result
432
+ // Return enhanced result with external tool information
421
433
  return {
422
434
  content: result.content,
423
435
  provider: providerName,
@@ -432,9 +444,14 @@ export class NeuroLink {
432
444
  success: true, // Assume success if tool executed (AI providers handle failures differently)
433
445
  serverId: teRecord.serverId || undefined,
434
446
  };
435
- }) || [], // ✅ NEW: Add missing toolExecutions with proper format
447
+ }) || [],
436
448
  enhancedWithTools: true,
437
- 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
+ })),
438
455
  // Include analytics and evaluation from BaseProvider
439
456
  analytics: result.analytics,
440
457
  evaluation: result.evaluation,
@@ -537,8 +554,23 @@ export class NeuroLink {
537
554
  return originalSystemPrompt || "";
538
555
  }
539
556
  const toolDescriptions = availableTools
540
- .map((tool) => `- ${tool.name}: ${tool.description} (from ${tool.server})`)
541
- .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");
542
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.`;
543
575
  return (originalSystemPrompt || "") + toolPrompt;
544
576
  }
@@ -912,6 +944,14 @@ export class NeuroLink {
912
944
  async executeTool(toolName, params = {}, options) {
913
945
  const functionTag = "NeuroLink.executeTool";
914
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
+ });
915
955
  // Emit tool start event
916
956
  this.emitter.emit("tool:start", {
917
957
  toolName,
@@ -1104,7 +1144,37 @@ export class NeuroLink {
1104
1144
  }
1105
1145
  }
1106
1146
  }
1107
- // 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
1108
1178
  try {
1109
1179
  const context = {
1110
1180
  sessionId: `neurolink-tool-${Date.now()}`,
@@ -1168,13 +1238,41 @@ export class NeuroLink {
1168
1238
  }
1169
1239
  }
1170
1240
  }
1171
- // 4. Combine all tools
1172
- 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());
1173
1270
  mcpLogger.debug("Tool discovery results", {
1174
1271
  mcpTools: mcpTools.length,
1175
1272
  customTools: customTools.length,
1176
1273
  inMemoryTools: inMemoryTools.length,
1177
- total: allTools.length,
1274
+ externalMCPTools: externalMCPTools.length,
1275
+ total: uniqueTools.length,
1178
1276
  });
1179
1277
  // Check memory usage after tool enumeration
1180
1278
  const endMemory = MemoryManager.getMemoryUsageMB();
@@ -1182,11 +1280,11 @@ export class NeuroLink {
1182
1280
  if (memoryDelta > 10) {
1183
1281
  mcpLogger.debug(`🔍 Tool listing used ${memoryDelta}MB memory (large tool registry detected)`);
1184
1282
  // Suggest periodic cleanup for large tool registries
1185
- if (allTools.length > 100) {
1283
+ if (uniqueTools.length > 100) {
1186
1284
  mcpLogger.debug("💡 Suggestion: Consider using tool categories or lazy loading for large tool sets");
1187
1285
  }
1188
1286
  }
1189
- return allTools;
1287
+ return uniqueTools;
1190
1288
  }
1191
1289
  catch (error) {
1192
1290
  mcpLogger.error("Failed to list available tools", { error });
@@ -1394,17 +1492,44 @@ export class NeuroLink {
1394
1492
  */
1395
1493
  async getMCPStatus() {
1396
1494
  try {
1397
- // Simplified MCP status - unified registry removed
1495
+ // Get built-in tools
1398
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
+ }));
1399
1520
  return {
1400
1521
  mcpInitialized: this.mcpInitialized,
1401
- totalServers: 1, // Only tool registry now
1402
- availableServers: 1,
1403
- autoDiscoveredCount: 0, // No auto-discovery
1404
- totalTools: allTools.length,
1405
- autoDiscoveredServers: [], // No auto-discovery
1522
+ totalServers,
1523
+ availableServers,
1524
+ autoDiscoveredCount: 0, // No auto-discovery for external servers
1525
+ totalTools,
1526
+ autoDiscoveredServers: [],
1406
1527
  customToolsCount: this.customTools.size,
1407
1528
  inMemoryServersCount: this.inMemoryServers.size,
1529
+ externalMCPServersCount: externalStats.totalServers,
1530
+ externalMCPConnectedCount: externalStats.connectedServers,
1531
+ externalMCPFailedCount: externalStats.failedServers,
1532
+ externalMCPServers,
1408
1533
  };
1409
1534
  }
1410
1535
  catch (error) {
@@ -1417,6 +1542,10 @@ export class NeuroLink {
1417
1542
  autoDiscoveredServers: [],
1418
1543
  customToolsCount: this.customTools.size,
1419
1544
  inMemoryServersCount: this.inMemoryServers.size,
1545
+ externalMCPServersCount: 0,
1546
+ externalMCPConnectedCount: 0,
1547
+ externalMCPFailedCount: 0,
1548
+ externalMCPServers: [],
1420
1549
  error: error instanceof Error ? error.message : String(error),
1421
1550
  };
1422
1551
  }
@@ -1426,8 +1555,54 @@ export class NeuroLink {
1426
1555
  * @returns Promise resolving to array of MCP server information
1427
1556
  */
1428
1557
  async listMCPServers() {
1429
- // Simplified MCP servers listing - unified registry removed
1430
- 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;
1431
1606
  }
1432
1607
  /**
1433
1608
  * Test connectivity to a specific MCP server
@@ -1435,8 +1610,29 @@ export class NeuroLink {
1435
1610
  * @returns Promise resolving to true if server is reachable
1436
1611
  */
1437
1612
  async testMCPServer(serverId) {
1438
- // Simplified MCP server testing - unified registry removed
1439
- 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
+ }
1440
1636
  }
1441
1637
  // ==================== PROVIDER HEALTH CHECKING ====================
1442
1638
  /**
@@ -1678,6 +1874,277 @@ export class NeuroLink {
1678
1874
  }
1679
1875
  await this.conversationMemory.clearAllSessions();
1680
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
+ }
1681
2148
  }
1682
2149
  // Create default instance
1683
2150
  export const neurolink = new NeuroLink();