@juspay/neurolink 7.13.0 → 7.14.1

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 (132) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +89 -25
  3. package/dist/agent/directTools.d.ts +3 -3
  4. package/dist/agent/directTools.js +1 -1
  5. package/dist/cli/commands/mcp.js +67 -207
  6. package/dist/cli/factories/commandFactory.js +7 -1
  7. package/dist/cli/utils/interactiveSetup.js +1 -1
  8. package/dist/config/conversationMemoryConfig.js +2 -1
  9. package/dist/context/ContextManager.js +15 -4
  10. package/dist/context/config.js +5 -1
  11. package/dist/context/utils.js +1 -1
  12. package/dist/core/baseProvider.d.ts +11 -30
  13. package/dist/core/baseProvider.js +268 -42
  14. package/dist/core/conversationMemoryManager.js +3 -2
  15. package/dist/core/dynamicModels.d.ts +14 -14
  16. package/dist/core/dynamicModels.js +1 -1
  17. package/dist/core/evaluation.js +1 -1
  18. package/dist/core/factory.js +1 -1
  19. package/dist/factories/providerFactory.js +5 -11
  20. package/dist/factories/providerRegistry.js +2 -2
  21. package/dist/index.d.ts +5 -4
  22. package/dist/index.js +1 -1
  23. package/dist/lib/agent/directTools.js +1 -1
  24. package/dist/lib/config/conversationMemoryConfig.js +2 -1
  25. package/dist/lib/context/ContextManager.js +15 -4
  26. package/dist/lib/context/config.js +5 -1
  27. package/dist/lib/context/utils.js +1 -1
  28. package/dist/lib/core/baseProvider.d.ts +11 -30
  29. package/dist/lib/core/baseProvider.js +268 -42
  30. package/dist/lib/core/conversationMemoryManager.js +3 -2
  31. package/dist/lib/core/dynamicModels.js +1 -1
  32. package/dist/lib/core/evaluation.js +1 -1
  33. package/dist/lib/core/factory.js +1 -1
  34. package/dist/lib/factories/providerFactory.js +5 -11
  35. package/dist/lib/factories/providerRegistry.js +2 -2
  36. package/dist/lib/index.d.ts +5 -4
  37. package/dist/lib/index.js +1 -1
  38. package/dist/lib/mcp/externalServerManager.d.ts +148 -0
  39. package/dist/lib/mcp/externalServerManager.js +1038 -0
  40. package/dist/lib/mcp/mcpCircuitBreaker.d.ts +184 -0
  41. package/dist/lib/mcp/mcpCircuitBreaker.js +338 -0
  42. package/dist/lib/mcp/mcpClientFactory.d.ts +105 -0
  43. package/dist/lib/mcp/mcpClientFactory.js +421 -0
  44. package/dist/lib/mcp/toolDiscoveryService.d.ts +193 -0
  45. package/dist/lib/mcp/toolDiscoveryService.js +646 -0
  46. package/dist/lib/mcp/toolRegistry.d.ts +15 -11
  47. package/dist/lib/mcp/toolRegistry.js +118 -55
  48. package/dist/lib/models/modelResolver.js +1 -1
  49. package/dist/lib/neurolink.d.ts +139 -43
  50. package/dist/lib/neurolink.js +604 -174
  51. package/dist/lib/providers/googleVertex.d.ts +7 -1
  52. package/dist/lib/providers/googleVertex.js +34 -7
  53. package/dist/lib/providers/huggingFace.js +1 -1
  54. package/dist/lib/providers/mistral.js +3 -3
  55. package/dist/lib/providers/ollama.js +1 -1
  56. package/dist/lib/providers/openAI.d.ts +3 -2
  57. package/dist/lib/providers/openAI.js +2 -2
  58. package/dist/lib/providers/openaiCompatible.d.ts +1 -1
  59. package/dist/lib/providers/openaiCompatible.js +2 -2
  60. package/dist/lib/providers/sagemaker/config.js +1 -1
  61. package/dist/lib/sdk/toolRegistration.d.ts +4 -13
  62. package/dist/lib/sdk/toolRegistration.js +19 -66
  63. package/dist/lib/types/cli.d.ts +0 -1
  64. package/dist/lib/types/cli.js +0 -1
  65. package/dist/lib/types/common.d.ts +1 -2
  66. package/dist/lib/types/common.js +0 -1
  67. package/dist/lib/types/contextTypes.d.ts +1 -1
  68. package/dist/lib/types/contextTypes.js +3 -3
  69. package/dist/lib/types/externalMcp.d.ts +288 -0
  70. package/dist/lib/types/externalMcp.js +7 -0
  71. package/dist/lib/types/generateTypes.d.ts +0 -1
  72. package/dist/lib/types/index.d.ts +2 -2
  73. package/dist/lib/types/index.js +0 -1
  74. package/dist/lib/types/mcpTypes.d.ts +53 -99
  75. package/dist/lib/types/providers.d.ts +0 -1
  76. package/dist/lib/types/providers.js +0 -1
  77. package/dist/lib/types/tools.d.ts +2 -2
  78. package/dist/lib/types/tools.js +2 -2
  79. package/dist/lib/utils/factoryProcessing.js +1 -1
  80. package/dist/lib/utils/mcpDefaults.d.ts +54 -0
  81. package/dist/lib/utils/mcpDefaults.js +125 -0
  82. package/dist/lib/utils/providerConfig.d.ts +1 -1
  83. package/dist/lib/utils/providerConfig.js +2 -2
  84. package/dist/lib/utils/providerHealth.js +6 -6
  85. package/dist/mcp/externalServerManager.d.ts +148 -0
  86. package/dist/mcp/externalServerManager.js +1038 -0
  87. package/dist/mcp/mcpCircuitBreaker.d.ts +184 -0
  88. package/dist/mcp/mcpCircuitBreaker.js +338 -0
  89. package/dist/mcp/mcpClientFactory.d.ts +105 -0
  90. package/dist/mcp/mcpClientFactory.js +421 -0
  91. package/dist/mcp/toolDiscoveryService.d.ts +193 -0
  92. package/dist/mcp/toolDiscoveryService.js +646 -0
  93. package/dist/mcp/toolRegistry.d.ts +15 -11
  94. package/dist/mcp/toolRegistry.js +118 -55
  95. package/dist/models/modelResolver.js +1 -1
  96. package/dist/neurolink.d.ts +139 -43
  97. package/dist/neurolink.js +604 -174
  98. package/dist/providers/googleVertex.d.ts +7 -1
  99. package/dist/providers/googleVertex.js +34 -7
  100. package/dist/providers/huggingFace.js +1 -1
  101. package/dist/providers/mistral.js +3 -3
  102. package/dist/providers/ollama.js +1 -1
  103. package/dist/providers/openAI.d.ts +3 -2
  104. package/dist/providers/openAI.js +2 -2
  105. package/dist/providers/openaiCompatible.d.ts +1 -1
  106. package/dist/providers/openaiCompatible.js +2 -2
  107. package/dist/providers/sagemaker/config.js +1 -1
  108. package/dist/sdk/toolRegistration.d.ts +4 -13
  109. package/dist/sdk/toolRegistration.js +19 -66
  110. package/dist/types/cli.d.ts +0 -1
  111. package/dist/types/cli.js +0 -1
  112. package/dist/types/common.d.ts +1 -2
  113. package/dist/types/common.js +0 -1
  114. package/dist/types/contextTypes.d.ts +1 -1
  115. package/dist/types/contextTypes.js +3 -3
  116. package/dist/types/externalMcp.d.ts +288 -0
  117. package/dist/types/externalMcp.js +7 -0
  118. package/dist/types/generateTypes.d.ts +0 -1
  119. package/dist/types/index.d.ts +2 -2
  120. package/dist/types/index.js +0 -1
  121. package/dist/types/mcpTypes.d.ts +53 -99
  122. package/dist/types/providers.d.ts +0 -1
  123. package/dist/types/providers.js +0 -1
  124. package/dist/types/tools.d.ts +2 -2
  125. package/dist/types/tools.js +2 -2
  126. package/dist/utils/factoryProcessing.js +1 -1
  127. package/dist/utils/mcpDefaults.d.ts +54 -0
  128. package/dist/utils/mcpDefaults.js +125 -0
  129. package/dist/utils/providerConfig.d.ts +1 -1
  130. package/dist/utils/providerConfig.js +2 -2
  131. package/dist/utils/providerHealth.js +6 -6
  132. package/package.json +1 -1
package/dist/neurolink.js CHANGED
@@ -21,7 +21,7 @@ import { toolRegistry } from "./mcp/toolRegistry.js";
21
21
  import { logger } from "./utils/logger.js";
22
22
  import { getBestProvider } from "./utils/providerUtils.js";
23
23
  import { ProviderRegistry } from "./factories/providerRegistry.js";
24
- import { validateTool, createMCPServerFromTools, } from "./sdk/toolRegistration.js";
24
+ import { createCustomToolServerInfo, detectCategory, } from "./utils/mcpDefaults.js";
25
25
  // Factory processing imports
26
26
  import { processFactoryOptions, enhanceTextGenerationOptions, validateFactoryConfig, processStreamingFactoryOptions, createCleanStreamOptions, } from "./utils/factoryProcessing.js";
27
27
  // Enhanced error handling imports
@@ -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
@@ -36,9 +37,9 @@ export class NeuroLink {
36
37
  mcpInitialized = false;
37
38
  emitter = new EventEmitter();
38
39
  contextManager = null;
39
- // Tool registration support
40
- customTools = new Map();
41
- inMemoryServers = new Map();
40
+ autoDiscoveredServerInfos = [];
41
+ // External MCP server management
42
+ externalServerManager;
42
43
  // Enhanced error handling support
43
44
  toolCircuitBreakers = new Map();
44
45
  toolExecutionMetrics = new Map();
@@ -73,6 +74,34 @@ export class NeuroLink {
73
74
  maxTurnsPerSession: memoryConfig.maxTurnsPerSession,
74
75
  });
75
76
  }
77
+ // Initialize external server manager with main registry integration
78
+ this.externalServerManager = new ExternalServerManager({
79
+ maxServers: 20,
80
+ defaultTimeout: 15000,
81
+ enableAutoRestart: true,
82
+ enablePerformanceMonitoring: true,
83
+ }, {
84
+ enableMainRegistryIntegration: true, // Enable integration with main toolRegistry
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
@@ -98,6 +127,26 @@ export class NeuroLink {
98
127
  ]);
99
128
  // Register all providers with lazy loading support
100
129
  await ProviderRegistry.registerAllProviders();
130
+ // Load MCP configuration from .mcp-config.json using ExternalServerManager
131
+ try {
132
+ const configResult = await this.externalServerManager.loadMCPConfiguration();
133
+ mcpLogger.debug("[NeuroLink] MCP configuration loaded successfully", {
134
+ serversLoaded: configResult.serversLoaded,
135
+ errors: configResult.errors.length,
136
+ });
137
+ if (configResult.errors.length > 0) {
138
+ mcpLogger.warn("[NeuroLink] Some MCP servers failed to load", {
139
+ errors: configResult.errors,
140
+ });
141
+ }
142
+ }
143
+ catch (configError) {
144
+ mcpLogger.warn("[NeuroLink] MCP configuration loading failed", {
145
+ error: configError instanceof Error
146
+ ? configError.message
147
+ : String(configError),
148
+ });
149
+ }
101
150
  this.mcpInitialized = true;
102
151
  // Monitor memory usage and provide cleanup suggestions
103
152
  const endMemory = MemoryManager.getMemoryUsageMB();
@@ -131,7 +180,9 @@ export class NeuroLink {
131
180
  * @returns The original prompt text as a string.
132
181
  */
133
182
  _extractOriginalPrompt(optionsOrPrompt) {
134
- return typeof optionsOrPrompt === 'string' ? optionsOrPrompt : optionsOrPrompt.input.text;
183
+ return typeof optionsOrPrompt === "string"
184
+ ? optionsOrPrompt
185
+ : optionsOrPrompt.input.text;
135
186
  }
136
187
  /**
137
188
  * Enables automatic context summarization for the NeuroLink instance.
@@ -321,6 +372,7 @@ export class NeuroLink {
321
372
  // Try MCP-enhanced generation first (if not explicitly disabled)
322
373
  if (!options.disableTools) {
323
374
  try {
375
+ logger.debug(`[${functionTag}] Attempting MCP generation...`);
324
376
  const mcpResult = await this.tryMCPGeneration(options);
325
377
  if (mcpResult && mcpResult.content) {
326
378
  logger.debug(`[${functionTag}] MCP generation successful`);
@@ -328,6 +380,13 @@ export class NeuroLink {
328
380
  await storeConversationTurn(this.conversationMemory, options, mcpResult);
329
381
  return mcpResult;
330
382
  }
383
+ else {
384
+ logger.debug(`[${functionTag}] MCP generation returned empty result:`, {
385
+ hasResult: !!mcpResult,
386
+ hasContent: !!(mcpResult && mcpResult.content),
387
+ contentLength: mcpResult?.content?.length || 0,
388
+ });
389
+ }
331
390
  }
332
391
  catch (error) {
333
392
  logger.debug(`[${functionTag}] MCP generation failed, falling back`, {
@@ -367,34 +426,7 @@ export class NeuroLink {
367
426
  ? await getBestProvider()
368
427
  : options.provider;
369
428
  // 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
- }
429
+ const availableTools = await this.getAllAvailableTools();
398
430
  // Create tool-aware system prompt
399
431
  const enhancedSystemPrompt = this.createToolAwareSystemPrompt(options.systemPrompt, availableTools);
400
432
  // Get conversation messages for context
@@ -404,7 +436,7 @@ export class NeuroLink {
404
436
  this);
405
437
  // Enable tool execution for the provider using BaseProvider method
406
438
  provider.setupToolExecutor({
407
- customTools: this.customTools,
439
+ customTools: this.getCustomTools(),
408
440
  executeTool: this.executeTool.bind(this),
409
441
  }, functionTag);
410
442
  const result = await provider.generate({
@@ -417,7 +449,7 @@ export class NeuroLink {
417
449
  if (!result || !result.content || result.content.trim().length === 0) {
418
450
  return null; // Let caller fall back to direct generation
419
451
  }
420
- // Return enhanced result
452
+ // Return enhanced result with external tool information
421
453
  return {
422
454
  content: result.content,
423
455
  provider: providerName,
@@ -432,9 +464,14 @@ export class NeuroLink {
432
464
  success: true, // Assume success if tool executed (AI providers handle failures differently)
433
465
  serverId: teRecord.serverId || undefined,
434
466
  };
435
- }) || [], // ✅ NEW: Add missing toolExecutions with proper format
467
+ }) || [],
436
468
  enhancedWithTools: true,
437
- availableTools: availableTools.length > 0 ? availableTools : undefined,
469
+ availableTools: availableTools.map((tool) => ({
470
+ name: tool.name,
471
+ description: tool.description,
472
+ server: tool.server,
473
+ category: tool.category,
474
+ })),
438
475
  // Include analytics and evaluation from BaseProvider
439
476
  analytics: result.analytics,
440
477
  evaluation: result.evaluation,
@@ -485,7 +522,7 @@ export class NeuroLink {
485
522
  this);
486
523
  // Enable tool execution for direct provider generation using BaseProvider method
487
524
  provider.setupToolExecutor({
488
- customTools: this.customTools,
525
+ customTools: this.getCustomTools(),
489
526
  executeTool: this.executeTool.bind(this),
490
527
  }, functionTag);
491
528
  const result = await provider.generate({
@@ -537,8 +574,23 @@ export class NeuroLink {
537
574
  return originalSystemPrompt || "";
538
575
  }
539
576
  const toolDescriptions = availableTools
540
- .map((tool) => `- ${tool.name}: ${tool.description} (from ${tool.server})`)
541
- .join("\n");
577
+ .map((tool) => {
578
+ const toolWithSchema = tool;
579
+ const schema = (toolWithSchema.inputSchema ||
580
+ toolWithSchema.parameters);
581
+ let params = "";
582
+ if (schema && schema.properties) {
583
+ const requiredParams = new Set(schema.required || []);
584
+ params = Object.entries(schema.properties)
585
+ .map(([key, value]) => {
586
+ const required = requiredParams.has(key) ? " (required)" : "";
587
+ return ` - ${key}: ${value.type}${required}`;
588
+ })
589
+ .join("\n");
590
+ }
591
+ return `- ${tool.name}: ${tool.description} (from ${tool.server})\n${params}`;
592
+ })
593
+ .join("\n\n");
542
594
  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
595
  return (originalSystemPrompt || "") + toolPrompt;
544
596
  }
@@ -572,7 +624,7 @@ export class NeuroLink {
572
624
  for (const result of toolResults) {
573
625
  if (result && typeof result === "object") {
574
626
  enhancedPrompt += `\n\nTool Results:\n`;
575
- // Handle any structured result generically
627
+ // Handle structured result generically
576
628
  try {
577
629
  const resultStr = typeof result === "string"
578
630
  ? result
@@ -682,7 +734,7 @@ export class NeuroLink {
682
734
  const provider = await AIProviderFactory.createBestProvider(providerName, options.model, true, this);
683
735
  // Enable tool execution for streaming using BaseProvider method
684
736
  provider.setupToolExecutor({
685
- customTools: this.customTools,
737
+ customTools: this.getCustomTools(),
686
738
  executeTool: this.executeTool.bind(this),
687
739
  }, functionTag);
688
740
  // Create clean options for provider (remove factoryConfig)
@@ -738,7 +790,7 @@ export class NeuroLink {
738
790
  this);
739
791
  // Enable tool execution for fallback streaming using BaseProvider method
740
792
  provider.setupToolExecutor({
741
- customTools: this.customTools,
793
+ customTools: this.getCustomTools(),
742
794
  executeTool: this.executeTool.bind(this),
743
795
  }, functionTag);
744
796
  // Create clean options for fallback provider (remove factoryConfig)
@@ -792,7 +844,7 @@ export class NeuroLink {
792
844
  /**
793
845
  * Register a custom tool that will be available to all AI providers
794
846
  * @param name - Unique name for the tool
795
- * @param tool - Tool configuration
847
+ * @param tool - Tool in MCPExecutableTool format (unified MCP protocol type)
796
848
  */
797
849
  registerTool(name, tool) {
798
850
  // Emit tool registration start event
@@ -801,18 +853,10 @@ export class NeuroLink {
801
853
  timestamp: Date.now(),
802
854
  });
803
855
  try {
804
- // Validate tool configuration
805
- validateTool(name, tool);
806
- // Store the tool
807
- this.customTools.set(name, tool);
808
- // Convert to MCP server format for integration
809
- const serverId = `custom-tool-${name}`;
810
- const mcpServer = createMCPServerFromTools(serverId, { [name]: tool }, {
811
- title: `Custom Tool: ${name}`,
812
- category: "custom",
813
- });
814
- // Store as in-memory server
815
- this.inMemoryServers.set(serverId, mcpServer);
856
+ // SMART DEFAULTS: Use utility to eliminate boilerplate creation
857
+ const mcpServerInfo = createCustomToolServerInfo(name, tool);
858
+ // Register with toolRegistry using MCPServerInfo directly
859
+ toolRegistry.registerServer(mcpServerInfo);
816
860
  logger.info(`Registered custom tool: ${name}`);
817
861
  // Emit tool registration success event
818
862
  this.emitter.emit("tools-register:end", {
@@ -828,10 +872,10 @@ export class NeuroLink {
828
872
  }
829
873
  /**
830
874
  * Register multiple tools at once - Supports both object and array formats
831
- * @param tools - Object mapping tool names to configurations OR Array of tools with names
875
+ * @param tools - Object mapping tool names to MCPExecutableTool format OR Array of tools with names
832
876
  *
833
- * Object format (existing): { toolName: SimpleTool, ... }
834
- * Array format (Lighthouse compatible): [{ name: string, tool: SimpleTool }, ...]
877
+ * Object format (existing): { toolName: MCPExecutableTool, ... }
878
+ * Array format (Lighthouse compatible): [{ name: string, tool: MCPExecutableTool }, ...]
835
879
  */
836
880
  registerTools(tools) {
837
881
  if (Array.isArray(tools)) {
@@ -853,40 +897,57 @@ export class NeuroLink {
853
897
  * @returns true if the tool was removed, false if it didn't exist
854
898
  */
855
899
  unregisterTool(name) {
856
- const removed = this.customTools.delete(name);
900
+ const serverId = `custom-tool-${name}`;
901
+ const removed = toolRegistry.unregisterServer(serverId);
857
902
  if (removed) {
858
- const serverId = `custom-tool-${name}`;
859
- this.inMemoryServers.delete(serverId);
860
903
  logger.info(`Unregistered custom tool: ${name}`);
861
904
  }
862
905
  return removed;
863
906
  }
864
907
  /**
865
908
  * Get all registered custom tools
866
- * @returns Map of tool names to configurations
909
+ * @returns Map of tool names to MCPExecutableTool format
867
910
  */
868
911
  getCustomTools() {
869
- return new Map(this.customTools);
912
+ // Get tools from toolRegistry with smart category detection
913
+ const customTools = toolRegistry.getToolsByCategory(detectCategory({ isCustomTool: true }));
914
+ const toolMap = new Map();
915
+ for (const tool of customTools) {
916
+ // Return MCPServerInfo.tools format directly - no conversion needed
917
+ toolMap.set(tool.name, {
918
+ name: tool.name,
919
+ description: tool.description || "",
920
+ inputSchema: {},
921
+ execute: async (args, context) => {
922
+ // Type guard to ensure context is compatible with ExecutionContext
923
+ const executionContext = context && typeof context === "object" && context !== null
924
+ ? context
925
+ : undefined;
926
+ return await toolRegistry.executeTool(tool.name, args, executionContext);
927
+ },
928
+ });
929
+ }
930
+ return toolMap;
870
931
  }
871
932
  /**
872
933
  * Add an in-memory MCP server (from git diff)
873
934
  * Allows registration of pre-instantiated server objects
874
935
  * @param serverId - Unique identifier for the server
875
- * @param config - Server configuration
936
+ * @param serverInfo - Server configuration
876
937
  */
877
- async addInMemoryMCPServer(serverId, config) {
938
+ async addInMemoryMCPServer(serverId, serverInfo) {
878
939
  try {
879
940
  mcpLogger.debug(`[NeuroLink] Registering in-memory MCP server: ${serverId}`);
880
- // Validate server configuration
881
- if (!config.server) {
882
- throw new Error(`Server object is required for ${serverId}`);
941
+ // Initialize tools array if not provided
942
+ if (!serverInfo.tools) {
943
+ serverInfo.tools = [];
883
944
  }
884
- // Store in registry
885
- this.inMemoryServers.set(serverId, config);
945
+ // ZERO CONVERSIONS: Pass MCPServerInfo directly to toolRegistry
946
+ await toolRegistry.registerServer(serverInfo);
886
947
  mcpLogger.info(`[NeuroLink] Successfully registered in-memory server: ${serverId}`, {
887
- category: config.category,
888
- provider: config.metadata?.provider,
889
- version: config.metadata?.version,
948
+ category: serverInfo.metadata?.category,
949
+ provider: serverInfo.metadata?.provider,
950
+ version: serverInfo.metadata?.version,
890
951
  });
891
952
  }
892
953
  catch (error) {
@@ -896,10 +957,41 @@ export class NeuroLink {
896
957
  }
897
958
  /**
898
959
  * Get all registered in-memory servers
899
- * @returns Map of server IDs to configurations
960
+ * @returns Map of server IDs to MCPServerInfo
900
961
  */
901
962
  getInMemoryServers() {
902
- return new Map(this.inMemoryServers);
963
+ // Get in-memory servers from toolRegistry
964
+ const serverInfos = toolRegistry.getBuiltInServerInfos();
965
+ const serverMap = new Map();
966
+ for (const serverInfo of serverInfos) {
967
+ if (detectCategory({
968
+ existingCategory: serverInfo.metadata?.category,
969
+ serverId: serverInfo.id,
970
+ }) === "in-memory") {
971
+ serverMap.set(serverInfo.id, serverInfo);
972
+ }
973
+ }
974
+ return serverMap;
975
+ }
976
+ /**
977
+ * Get in-memory servers as MCPServerInfo - ZERO conversion needed
978
+ * Now fetches from centralized tool registry instead of local duplication
979
+ * @returns Array of MCPServerInfo
980
+ */
981
+ getInMemoryServerInfos() {
982
+ // Get in-memory servers from centralized tool registry
983
+ const allServers = toolRegistry.getBuiltInServerInfos();
984
+ return allServers.filter((server) => detectCategory({
985
+ existingCategory: server.metadata?.category,
986
+ serverId: server.id,
987
+ }) === "in-memory");
988
+ }
989
+ /**
990
+ * Get auto-discovered servers as MCPServerInfo - ZERO conversion needed
991
+ * @returns Array of MCPServerInfo
992
+ */
993
+ getAutoDiscoveredServerInfos() {
994
+ return this.autoDiscoveredServerInfos;
903
995
  }
904
996
  /**
905
997
  * Execute a specific tool by name with robust error handling
@@ -912,6 +1004,14 @@ export class NeuroLink {
912
1004
  async executeTool(toolName, params = {}, options) {
913
1005
  const functionTag = "NeuroLink.executeTool";
914
1006
  const executionStartTime = Date.now();
1007
+ // Debug: Log tool execution attempt
1008
+ logger.debug(`[${functionTag}] Tool execution requested:`, {
1009
+ toolName,
1010
+ params: typeof params === "object"
1011
+ ? Object.keys(params).length + " params"
1012
+ : params,
1013
+ hasExternalManager: !!this.externalServerManager,
1014
+ });
915
1015
  // Emit tool start event
916
1016
  this.emitter.emit("tool:start", {
917
1017
  toolName,
@@ -1010,8 +1110,8 @@ export class NeuroLink {
1010
1110
  structuredError = ErrorFactory.toolTimeout(toolName, finalOptions.timeout);
1011
1111
  }
1012
1112
  else if (error.message.includes("not found")) {
1013
- const availableTools = Array.from(this.customTools.keys());
1014
- structuredError = ErrorFactory.toolNotFound(toolName, availableTools);
1113
+ const availableTools = await this.getAllAvailableTools();
1114
+ structuredError = ErrorFactory.toolNotFound(toolName, availableTools.map((t) => t.name));
1015
1115
  }
1016
1116
  else if (error.message.includes("validation") ||
1017
1117
  error.message.includes("parameter")) {
@@ -1053,58 +1153,37 @@ export class NeuroLink {
1053
1153
  */
1054
1154
  async executeToolInternal(toolName, params, options) {
1055
1155
  const functionTag = "NeuroLink.executeToolInternal";
1056
- // First check custom tools
1057
- if (this.customTools.has(toolName)) {
1058
- const tool = this.customTools.get(toolName);
1059
- // Validate parameters if schema is available
1060
- if (tool.parameters) {
1061
- try {
1062
- tool.parameters.parse(params);
1063
- }
1064
- catch (validationError) {
1065
- throw ErrorFactory.invalidParameters(toolName, validationError, params);
1066
- }
1067
- }
1068
- const context = {
1069
- sessionId: `direct-execution-${Date.now()}`,
1070
- logger,
1071
- };
1156
+ // Check external MCP servers
1157
+ const externalTools = this.externalServerManager.getAllTools();
1158
+ const externalTool = externalTools.find((tool) => tool.name === toolName);
1159
+ logger.debug(`[${functionTag}] External MCP tool search:`, {
1160
+ toolName,
1161
+ externalToolsCount: externalTools.length,
1162
+ foundTool: !!externalTool,
1163
+ isAvailable: externalTool?.isAvailable,
1164
+ serverId: externalTool?.serverId,
1165
+ });
1166
+ if (externalTool && externalTool.isAvailable) {
1072
1167
  try {
1073
- const result = await tool.execute(params, context);
1168
+ mcpLogger.debug(`[${functionTag}] Executing external MCP tool: ${toolName} from ${externalTool.serverId}`);
1169
+ const result = await this.externalServerManager.executeTool(externalTool.serverId, toolName, params, { timeout: options.timeout });
1170
+ logger.debug(`[${functionTag}] External MCP tool execution successful:`, {
1171
+ toolName,
1172
+ serverId: externalTool.serverId,
1173
+ resultType: typeof result,
1174
+ });
1074
1175
  return result;
1075
1176
  }
1076
1177
  catch (error) {
1077
- throw ErrorFactory.toolExecutionFailed(toolName, error instanceof Error ? error : new Error(String(error)));
1078
- }
1079
- }
1080
- // Then check in-memory servers
1081
- for (const [serverId, serverConfig] of this.inMemoryServers.entries()) {
1082
- const server = serverConfig.server;
1083
- if (server && server.tools) {
1084
- const tool = server.tools instanceof Map
1085
- ? server.tools.get(toolName)
1086
- : server.tools[toolName];
1087
- if (tool && typeof tool.execute === "function") {
1088
- try {
1089
- const result = await tool.execute(params);
1090
- // Handle MCP-style results
1091
- if (result && typeof result === "object" && "success" in result) {
1092
- if (result.success) {
1093
- return result.data;
1094
- }
1095
- else {
1096
- throw ErrorFactory.toolExecutionFailed(toolName, new Error(result.error || "Tool execution failed"), serverId);
1097
- }
1098
- }
1099
- return result;
1100
- }
1101
- catch (error) {
1102
- throw ErrorFactory.toolExecutionFailed(toolName, error instanceof Error ? error : new Error(String(error)), serverId);
1103
- }
1104
- }
1178
+ logger.error(`[${functionTag}] External MCP tool execution failed:`, {
1179
+ toolName,
1180
+ serverId: externalTool.serverId,
1181
+ error: error instanceof Error ? error.message : String(error),
1182
+ });
1183
+ throw ErrorFactory.toolExecutionFailed(toolName, error instanceof Error ? error : new Error(String(error)), externalTool.serverId);
1105
1184
  }
1106
1185
  }
1107
- // If not found in custom tools or in-memory servers, try unified registry
1186
+ // If not found in custom tools, in-memory servers, or external servers, try unified registry
1108
1187
  try {
1109
1188
  const context = {
1110
1189
  sessionId: `neurolink-tool-${Date.now()}`,
@@ -1139,42 +1218,71 @@ export class NeuroLink {
1139
1218
  serverId: tool.serverId === "direct" ? "neurolink-direct" : tool.serverId, // Update serverId for test compatibility
1140
1219
  }));
1141
1220
  // 2. Get custom tools from this NeuroLink instance
1142
- const customTools = Array.from(this.customTools.entries()).map(([name, tool]) => ({
1143
- name,
1144
- toolName: name, // Add toolName property for compatibility with tests
1221
+ const customTools = toolRegistry
1222
+ .getToolsByCategory(detectCategory({ isCustomTool: true }))
1223
+ .map((tool) => ({
1224
+ name: tool.name,
1225
+ toolName: tool.name, // Add toolName property for compatibility with tests
1145
1226
  description: tool.description || "Custom tool",
1146
- serverId: `custom-tool-${name}`, // Match the serverId pattern used in registerTool
1147
- category: "user-defined",
1148
- inputSchema: {},
1227
+ serverId: tool.serverId || `custom-tool-${tool.name}`,
1228
+ category: detectCategory({
1229
+ isCustomTool: true,
1230
+ serverId: tool.serverId,
1231
+ }),
1232
+ inputSchema: tool.inputSchema || {},
1149
1233
  }));
1150
1234
  // 3. Get tools from in-memory MCP servers
1151
- const inMemoryTools = [];
1152
- for (const [serverId, serverConfig] of this.inMemoryServers.entries()) {
1153
- const server = serverConfig.server;
1154
- if (server && server.tools) {
1155
- const tools = server.tools instanceof Map
1156
- ? Object.fromEntries(server.tools)
1157
- : server.tools;
1158
- for (const [toolName, toolDef] of Object.entries(tools)) {
1159
- const toolRecord = toolDef;
1160
- inMemoryTools.push({
1161
- name: toolName,
1162
- toolName, // Add toolName property for compatibility with tests
1163
- description: toolRecord.description || "In-memory MCP tool",
1164
- serverId,
1165
- category: serverConfig.category || "mcp-server",
1166
- inputSchema: {},
1167
- });
1168
- }
1235
+ const inMemoryTools = toolRegistry
1236
+ .getToolsByCategory("in-memory")
1237
+ .map((tool) => ({
1238
+ name: tool.name,
1239
+ toolName: tool.name, // Add toolName property for compatibility with tests
1240
+ description: tool.description || "In-memory MCP tool",
1241
+ serverId: tool.serverId || "unknown",
1242
+ category: tool.category || "in-memory",
1243
+ inputSchema: {},
1244
+ }));
1245
+ // 4. Get external MCP tools
1246
+ const externalMCPTools = this.externalServerManager
1247
+ .getAllTools()
1248
+ .map((tool) => ({
1249
+ name: tool.name,
1250
+ toolName: tool.name,
1251
+ description: tool.description,
1252
+ serverId: tool.serverId,
1253
+ category: detectCategory({
1254
+ existingCategory: typeof tool.metadata?.category === "string"
1255
+ ? tool.metadata.category
1256
+ : undefined,
1257
+ isExternal: true,
1258
+ serverId: tool.serverId,
1259
+ }),
1260
+ inputSchema: tool.inputSchema || {},
1261
+ isAvailable: tool.isAvailable,
1262
+ stats: tool.stats,
1263
+ }));
1264
+ // 5. Combine all tools with deduplication by name
1265
+ const combinedTools = [
1266
+ ...mcpTools,
1267
+ ...customTools,
1268
+ ...inMemoryTools,
1269
+ ...externalMCPTools,
1270
+ ];
1271
+ const uniqueTools = Array.from(combinedTools
1272
+ .reduce((map, tool) => {
1273
+ const key = tool.name;
1274
+ if (!map.has(key)) {
1275
+ map.set(key, tool);
1169
1276
  }
1170
- }
1171
- // 4. Combine all tools
1172
- const allTools = [...mcpTools, ...customTools, ...inMemoryTools];
1277
+ return map;
1278
+ }, new Map())
1279
+ .values());
1173
1280
  mcpLogger.debug("Tool discovery results", {
1174
1281
  mcpTools: mcpTools.length,
1175
1282
  customTools: customTools.length,
1176
1283
  inMemoryTools: inMemoryTools.length,
1177
- total: allTools.length,
1284
+ externalMCPTools: externalMCPTools.length,
1285
+ total: uniqueTools.length,
1178
1286
  });
1179
1287
  // Check memory usage after tool enumeration
1180
1288
  const endMemory = MemoryManager.getMemoryUsageMB();
@@ -1182,11 +1290,11 @@ export class NeuroLink {
1182
1290
  if (memoryDelta > 10) {
1183
1291
  mcpLogger.debug(`🔍 Tool listing used ${memoryDelta}MB memory (large tool registry detected)`);
1184
1292
  // Suggest periodic cleanup for large tool registries
1185
- if (allTools.length > 100) {
1293
+ if (uniqueTools.length > 100) {
1186
1294
  mcpLogger.debug("💡 Suggestion: Consider using tool categories or lazy loading for large tool sets");
1187
1295
  }
1188
1296
  }
1189
- return allTools;
1297
+ return uniqueTools;
1190
1298
  }
1191
1299
  catch (error) {
1192
1300
  mcpLogger.error("Failed to list available tools", { error });
@@ -1394,17 +1502,39 @@ export class NeuroLink {
1394
1502
  */
1395
1503
  async getMCPStatus() {
1396
1504
  try {
1397
- // Simplified MCP status - unified registry removed
1505
+ // Initialize MCP if not already initialized (loads external servers from config)
1506
+ await this.initializeMCP();
1507
+ // Get built-in tools
1398
1508
  const allTools = await toolRegistry.listTools();
1509
+ // Get external MCP server statistics
1510
+ const externalStats = this.externalServerManager.getStatistics();
1511
+ // DIRECT RETURNS - ZERO conversion
1512
+ const externalMCPServers = this.externalServerManager.listServers();
1513
+ const inMemoryServerInfos = this.getInMemoryServerInfos();
1514
+ const builtInServerInfos = toolRegistry.getBuiltInServerInfos();
1515
+ const autoDiscoveredServerInfos = this.getAutoDiscoveredServerInfos();
1516
+ // Calculate totals
1517
+ const totalServers = externalMCPServers.length +
1518
+ inMemoryServerInfos.length +
1519
+ builtInServerInfos.length +
1520
+ autoDiscoveredServerInfos.length;
1521
+ const availableServers = externalStats.connectedServers +
1522
+ inMemoryServerInfos.length +
1523
+ builtInServerInfos.length; // in-memory and built-in always available
1524
+ const totalTools = allTools.length + externalStats.totalTools;
1399
1525
  return {
1400
1526
  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
1406
- customToolsCount: this.customTools.size,
1407
- inMemoryServersCount: this.inMemoryServers.size,
1527
+ totalServers,
1528
+ availableServers,
1529
+ autoDiscoveredCount: autoDiscoveredServerInfos.length,
1530
+ totalTools,
1531
+ autoDiscoveredServers: autoDiscoveredServerInfos,
1532
+ customToolsCount: toolRegistry.getToolsByCategory(detectCategory({ isCustomTool: true })).length,
1533
+ inMemoryServersCount: inMemoryServerInfos.length,
1534
+ externalMCPServersCount: externalMCPServers.length,
1535
+ externalMCPConnectedCount: externalStats.connectedServers,
1536
+ externalMCPFailedCount: externalStats.failedServers,
1537
+ externalMCPServers,
1408
1538
  };
1409
1539
  }
1410
1540
  catch (error) {
@@ -1415,8 +1545,12 @@ export class NeuroLink {
1415
1545
  autoDiscoveredCount: 0,
1416
1546
  totalTools: 0,
1417
1547
  autoDiscoveredServers: [],
1418
- customToolsCount: this.customTools.size,
1419
- inMemoryServersCount: this.inMemoryServers.size,
1548
+ customToolsCount: toolRegistry.getToolsByCategory(detectCategory({ isCustomTool: true })).length,
1549
+ inMemoryServersCount: 0,
1550
+ externalMCPServersCount: 0,
1551
+ externalMCPConnectedCount: 0,
1552
+ externalMCPFailedCount: 0,
1553
+ externalMCPServers: [],
1420
1554
  error: error instanceof Error ? error.message : String(error),
1421
1555
  };
1422
1556
  }
@@ -1426,8 +1560,13 @@ export class NeuroLink {
1426
1560
  * @returns Promise resolving to array of MCP server information
1427
1561
  */
1428
1562
  async listMCPServers() {
1429
- // Simplified MCP servers listing - unified registry removed
1430
- return [];
1563
+ // DIRECT RETURNS - ZERO conversion logic
1564
+ return [
1565
+ ...this.externalServerManager.listServers(), // Direct return
1566
+ ...this.getInMemoryServerInfos(), // Direct return
1567
+ ...toolRegistry.getBuiltInServerInfos(), // Direct return
1568
+ ...this.getAutoDiscoveredServerInfos(), // Direct return
1569
+ ];
1431
1570
  }
1432
1571
  /**
1433
1572
  * Test connectivity to a specific MCP server
@@ -1435,8 +1574,30 @@ export class NeuroLink {
1435
1574
  * @returns Promise resolving to true if server is reachable
1436
1575
  */
1437
1576
  async testMCPServer(serverId) {
1438
- // Simplified MCP server testing - unified registry removed
1439
- return false; // No auto-discovery servers available
1577
+ try {
1578
+ // Test built-in tools
1579
+ if (serverId === "neurolink-direct") {
1580
+ const tools = await toolRegistry.listTools();
1581
+ return tools.length > 0;
1582
+ }
1583
+ // Test in-memory servers
1584
+ const inMemoryServers = this.getInMemoryServers();
1585
+ if (inMemoryServers.has(serverId)) {
1586
+ const serverInfo = inMemoryServers.get(serverId);
1587
+ return !!(serverInfo.tools && serverInfo.tools.length > 0);
1588
+ }
1589
+ // Test external MCP servers
1590
+ const externalServer = this.externalServerManager.getServer(serverId);
1591
+ if (externalServer) {
1592
+ return (externalServer.status === "connected" &&
1593
+ externalServer.client !== null);
1594
+ }
1595
+ return false;
1596
+ }
1597
+ catch (error) {
1598
+ mcpLogger.error(`[NeuroLink] Error testing MCP server ${serverId}:`, error);
1599
+ return false;
1600
+ }
1440
1601
  }
1441
1602
  // ==================== PROVIDER HEALTH CHECKING ====================
1442
1603
  /**
@@ -1589,14 +1750,12 @@ export class NeuroLink {
1589
1750
  * Get comprehensive tool health report
1590
1751
  * @returns Detailed health report for all tools
1591
1752
  */
1592
- getToolHealthReport() {
1753
+ async getToolHealthReport() {
1593
1754
  const tools = {};
1594
1755
  let healthyCount = 0;
1595
- // Get all tool names from all sources
1596
- const allToolNames = new Set([
1597
- ...this.customTools.keys(),
1598
- ...Array.from(this.inMemoryServers.values()).flatMap((server) => server.server?.tools ? Object.keys(server.server.tools) : []),
1599
- ]);
1756
+ // Get all tool names from toolRegistry
1757
+ const allTools = await toolRegistry.listTools();
1758
+ const allToolNames = new Set(allTools.map((tool) => tool.name));
1600
1759
  for (const toolName of allToolNames) {
1601
1760
  const metrics = this.toolExecutionMetrics.get(toolName);
1602
1761
  const circuitBreaker = this.toolCircuitBreakers.get(toolName);
@@ -1678,6 +1837,277 @@ export class NeuroLink {
1678
1837
  }
1679
1838
  await this.conversationMemory.clearAllSessions();
1680
1839
  }
1840
+ // ===== EXTERNAL MCP SERVER METHODS =====
1841
+ /**
1842
+ * Add an external MCP server
1843
+ * Automatically discovers and registers tools from the server
1844
+ * @param serverId - Unique identifier for the server
1845
+ * @param config - External MCP server configuration
1846
+ * @returns Operation result with server instance
1847
+ */
1848
+ async addExternalMCPServer(serverId, config) {
1849
+ try {
1850
+ mcpLogger.info(`[NeuroLink] Adding external MCP server: ${serverId}`, {
1851
+ command: config.command,
1852
+ transport: config.transport,
1853
+ });
1854
+ const result = await this.externalServerManager.addServer(serverId, config);
1855
+ if (result.success) {
1856
+ mcpLogger.info(`[NeuroLink] External MCP server added successfully: ${serverId}`, {
1857
+ toolsDiscovered: result.metadata?.toolsDiscovered || 0,
1858
+ duration: result.duration,
1859
+ });
1860
+ // Emit server added event
1861
+ this.emitter.emit("externalMCP:serverAdded", {
1862
+ serverId,
1863
+ config,
1864
+ toolCount: result.metadata?.toolsDiscovered || 0,
1865
+ timestamp: Date.now(),
1866
+ });
1867
+ }
1868
+ else {
1869
+ mcpLogger.error(`[NeuroLink] Failed to add external MCP server: ${serverId}`, {
1870
+ error: result.error,
1871
+ });
1872
+ }
1873
+ return result;
1874
+ }
1875
+ catch (error) {
1876
+ mcpLogger.error(`[NeuroLink] Error adding external MCP server: ${serverId}`, error);
1877
+ throw error;
1878
+ }
1879
+ }
1880
+ /**
1881
+ * Remove an external MCP server
1882
+ * Stops the server and removes all its tools
1883
+ * @param serverId - ID of the server to remove
1884
+ * @returns Operation result
1885
+ */
1886
+ async removeExternalMCPServer(serverId) {
1887
+ try {
1888
+ mcpLogger.info(`[NeuroLink] Removing external MCP server: ${serverId}`);
1889
+ const result = await this.externalServerManager.removeServer(serverId);
1890
+ if (result.success) {
1891
+ mcpLogger.info(`[NeuroLink] External MCP server removed successfully: ${serverId}`);
1892
+ // Emit server removed event
1893
+ this.emitter.emit("externalMCP:serverRemoved", {
1894
+ serverId,
1895
+ timestamp: Date.now(),
1896
+ });
1897
+ }
1898
+ else {
1899
+ mcpLogger.error(`[NeuroLink] Failed to remove external MCP server: ${serverId}`, {
1900
+ error: result.error,
1901
+ });
1902
+ }
1903
+ return result;
1904
+ }
1905
+ catch (error) {
1906
+ mcpLogger.error(`[NeuroLink] Error removing external MCP server: ${serverId}`, error);
1907
+ throw error;
1908
+ }
1909
+ }
1910
+ /**
1911
+ * List all external MCP servers
1912
+ * @returns Array of server health information
1913
+ */
1914
+ listExternalMCPServers() {
1915
+ const serverStatuses = this.externalServerManager.getServerStatuses();
1916
+ const allServers = this.externalServerManager.listServers();
1917
+ return serverStatuses.map((health) => {
1918
+ const server = allServers.find((s) => s.id === health.serverId);
1919
+ return {
1920
+ serverId: health.serverId,
1921
+ status: health.status,
1922
+ toolCount: health.toolCount,
1923
+ uptime: health.performance.uptime,
1924
+ isHealthy: health.isHealthy,
1925
+ config: server || {},
1926
+ };
1927
+ });
1928
+ }
1929
+ /**
1930
+ * Get external MCP server status
1931
+ * @param serverId - ID of the server
1932
+ * @returns Server instance or undefined if not found
1933
+ */
1934
+ getExternalMCPServer(serverId) {
1935
+ return this.externalServerManager.getServer(serverId);
1936
+ }
1937
+ /**
1938
+ * Execute a tool from an external MCP server
1939
+ * @param serverId - ID of the server
1940
+ * @param toolName - Name of the tool
1941
+ * @param parameters - Tool parameters
1942
+ * @param options - Execution options
1943
+ * @returns Tool execution result
1944
+ */
1945
+ async executeExternalMCPTool(serverId, toolName, parameters, options) {
1946
+ try {
1947
+ mcpLogger.debug(`[NeuroLink] Executing external MCP tool: ${toolName} on ${serverId}`);
1948
+ const result = await this.externalServerManager.executeTool(serverId, toolName, parameters, options);
1949
+ mcpLogger.debug(`[NeuroLink] External MCP tool executed successfully: ${toolName}`);
1950
+ return result;
1951
+ }
1952
+ catch (error) {
1953
+ mcpLogger.error(`[NeuroLink] External MCP tool execution failed: ${toolName}`, error);
1954
+ throw error;
1955
+ }
1956
+ }
1957
+ /**
1958
+ * Get all tools from external MCP servers
1959
+ * @returns Array of external tool information
1960
+ */
1961
+ getExternalMCPTools() {
1962
+ return this.externalServerManager.getAllTools();
1963
+ }
1964
+ /**
1965
+ * Get tools from a specific external MCP server
1966
+ * @param serverId - ID of the server
1967
+ * @returns Array of tool information for the server
1968
+ */
1969
+ getExternalMCPServerTools(serverId) {
1970
+ return this.externalServerManager.getServerTools(serverId);
1971
+ }
1972
+ /**
1973
+ * Test connection to an external MCP server
1974
+ * @param config - Server configuration to test
1975
+ * @returns Test result with connection status
1976
+ */
1977
+ async testExternalMCPConnection(config) {
1978
+ try {
1979
+ const { MCPClientFactory } = await import("./mcp/mcpClientFactory.js");
1980
+ const testResult = await MCPClientFactory.testConnection(config, 10000);
1981
+ return {
1982
+ success: testResult.success,
1983
+ error: testResult.error,
1984
+ toolCount: testResult.capabilities ? 1 : 0, // Basic indication
1985
+ };
1986
+ }
1987
+ catch (error) {
1988
+ return {
1989
+ success: false,
1990
+ error: error instanceof Error ? error.message : String(error),
1991
+ };
1992
+ }
1993
+ }
1994
+ /**
1995
+ * Get external MCP server manager statistics
1996
+ * @returns Statistics about external servers and tools
1997
+ */
1998
+ getExternalMCPStatistics() {
1999
+ return this.externalServerManager.getStatistics();
2000
+ }
2001
+ /**
2002
+ * Shutdown all external MCP servers
2003
+ * Called automatically on process exit
2004
+ */
2005
+ async shutdownExternalMCPServers() {
2006
+ try {
2007
+ mcpLogger.info("[NeuroLink] Shutting down all external MCP servers...");
2008
+ // First, unregister all external MCP tools from the main tool registry
2009
+ this.unregisterAllExternalMCPToolsFromRegistry();
2010
+ // Then shutdown the external server manager
2011
+ await this.externalServerManager.shutdown();
2012
+ mcpLogger.info("[NeuroLink] All external MCP servers shut down successfully");
2013
+ }
2014
+ catch (error) {
2015
+ mcpLogger.error("[NeuroLink] Error shutting down external MCP servers:", error);
2016
+ throw error;
2017
+ }
2018
+ }
2019
+ /**
2020
+ * Convert external MCP tools to Vercel AI SDK tool format
2021
+ * This allows AI providers to use external tools directly
2022
+ */
2023
+ convertExternalMCPToolsToAISDKFormat() {
2024
+ const externalTools = this.externalServerManager.getAllTools();
2025
+ const aiSDKTools = {};
2026
+ for (const tool of externalTools) {
2027
+ if (tool.isAvailable) {
2028
+ // Create tool definition without parameters schema to avoid Zod issues
2029
+ // The AI provider will handle parameters dynamically based on the tool description
2030
+ const toolDefinition = {
2031
+ description: tool.description,
2032
+ execute: async (args) => {
2033
+ try {
2034
+ mcpLogger.debug(`[NeuroLink] Executing external MCP tool via AI SDK: ${tool.name}`, { args });
2035
+ const result = await this.externalServerManager.executeTool(tool.serverId, tool.name, args, { timeout: 30000 });
2036
+ mcpLogger.debug(`[NeuroLink] External MCP tool execution result: ${tool.name}`, {
2037
+ success: !!result,
2038
+ hasData: !!(result &&
2039
+ typeof result === "object" &&
2040
+ "content" in result),
2041
+ });
2042
+ return result;
2043
+ }
2044
+ catch (error) {
2045
+ mcpLogger.error(`[NeuroLink] External MCP tool execution failed: ${tool.name}`, error);
2046
+ throw error;
2047
+ }
2048
+ },
2049
+ };
2050
+ // Only add parameters if we have a valid schema - otherwise omit it entirely
2051
+ // This prevents Zod schema parsing errors
2052
+ aiSDKTools[tool.name] = toolDefinition;
2053
+ mcpLogger.debug(`[NeuroLink] Converted external MCP tool to AI SDK format: ${tool.name} from server ${tool.serverId}`);
2054
+ }
2055
+ }
2056
+ mcpLogger.info(`[NeuroLink] Converted ${Object.keys(aiSDKTools).length} external MCP tools to AI SDK format`);
2057
+ return aiSDKTools;
2058
+ }
2059
+ /**
2060
+ * Convert JSON Schema to AI SDK compatible format
2061
+ * For now, we'll skip schema validation and let the AI SDK handle parameters dynamically
2062
+ */
2063
+ convertJSONSchemaToAISDKFormat(inputSchema) {
2064
+ // The simplest approach: don't provide parameters schema
2065
+ // This lets the AI SDK handle the tool without schema validation
2066
+ // Tools will still work, they just won't have strict parameter validation
2067
+ return undefined;
2068
+ }
2069
+ /**
2070
+ * Unregister external MCP tools from a specific server
2071
+ */
2072
+ unregisterExternalMCPToolsFromRegistry(serverId) {
2073
+ try {
2074
+ const externalTools = this.externalServerManager.getServerTools(serverId);
2075
+ for (const tool of externalTools) {
2076
+ toolRegistry.removeTool(tool.name);
2077
+ mcpLogger.debug(`[NeuroLink] Unregistered external MCP tool from main registry: ${tool.name}`);
2078
+ }
2079
+ }
2080
+ catch (error) {
2081
+ mcpLogger.error(`[NeuroLink] Failed to unregister external MCP tools from registry for server ${serverId}:`, error);
2082
+ }
2083
+ }
2084
+ /**
2085
+ * Unregister a specific external MCP tool from the main registry
2086
+ */
2087
+ unregisterExternalMCPToolFromRegistry(toolName) {
2088
+ try {
2089
+ toolRegistry.removeTool(toolName);
2090
+ mcpLogger.debug(`[NeuroLink] Unregistered external MCP tool from main registry: ${toolName}`);
2091
+ }
2092
+ catch (error) {
2093
+ mcpLogger.error(`[NeuroLink] Failed to unregister external MCP tool ${toolName} from registry:`, error);
2094
+ }
2095
+ }
2096
+ /**
2097
+ * Unregister all external MCP tools from the main registry
2098
+ */
2099
+ unregisterAllExternalMCPToolsFromRegistry() {
2100
+ try {
2101
+ const externalTools = this.externalServerManager.getAllTools();
2102
+ for (const tool of externalTools) {
2103
+ toolRegistry.removeTool(tool.name);
2104
+ }
2105
+ mcpLogger.debug(`[NeuroLink] Unregistered ${externalTools.length} external MCP tools from main registry`);
2106
+ }
2107
+ catch (error) {
2108
+ mcpLogger.error("[NeuroLink] Failed to unregister all external MCP tools from registry:", error);
2109
+ }
2110
+ }
1681
2111
  }
1682
2112
  // Create default instance
1683
2113
  export const neurolink = new NeuroLink();