@juspay/neurolink 7.14.0 → 7.14.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/agent/directTools.d.ts +3 -3
  3. package/dist/agent/directTools.js +1 -1
  4. package/dist/cli/commands/mcp.js +67 -207
  5. package/dist/cli/factories/commandFactory.js +7 -1
  6. package/dist/cli/utils/interactiveSetup.js +1 -1
  7. package/dist/core/baseProvider.d.ts +6 -40
  8. package/dist/core/baseProvider.js +102 -75
  9. package/dist/core/dynamicModels.d.ts +14 -14
  10. package/dist/core/dynamicModels.js +1 -1
  11. package/dist/core/evaluation.js +1 -1
  12. package/dist/core/factory.js +1 -12
  13. package/dist/index.d.ts +23 -4
  14. package/dist/index.js +22 -1
  15. package/dist/lib/agent/directTools.js +1 -1
  16. package/dist/lib/core/baseProvider.d.ts +6 -40
  17. package/dist/lib/core/baseProvider.js +102 -75
  18. package/dist/lib/core/dynamicModels.js +1 -1
  19. package/dist/lib/core/evaluation.js +1 -1
  20. package/dist/lib/core/factory.js +1 -12
  21. package/dist/lib/index.d.ts +23 -4
  22. package/dist/lib/index.js +22 -1
  23. package/dist/lib/mcp/externalServerManager.d.ts +46 -13
  24. package/dist/lib/mcp/externalServerManager.js +393 -32
  25. package/dist/lib/mcp/mcpClientFactory.d.ts +5 -4
  26. package/dist/lib/mcp/mcpClientFactory.js +8 -3
  27. package/dist/lib/mcp/toolDiscoveryService.d.ts +1 -0
  28. package/dist/lib/mcp/toolDiscoveryService.js +76 -8
  29. package/dist/lib/mcp/toolRegistry.d.ts +15 -11
  30. package/dist/lib/mcp/toolRegistry.js +118 -55
  31. package/dist/lib/models/modelResolver.js +1 -1
  32. package/dist/lib/neurolink.d.ts +32 -31
  33. package/dist/lib/neurolink.js +173 -210
  34. package/dist/lib/providers/googleVertex.d.ts +6 -0
  35. package/dist/lib/providers/googleVertex.js +11 -0
  36. package/dist/lib/providers/huggingFace.js +1 -1
  37. package/dist/lib/providers/mistral.js +3 -3
  38. package/dist/lib/providers/ollama.js +1 -1
  39. package/dist/lib/providers/openAI.d.ts +3 -2
  40. package/dist/lib/providers/openAI.js +2 -2
  41. package/dist/lib/providers/openaiCompatible.d.ts +1 -1
  42. package/dist/lib/providers/openaiCompatible.js +2 -2
  43. package/dist/lib/providers/sagemaker/config.js +1 -1
  44. package/dist/lib/sdk/toolRegistration.d.ts +4 -13
  45. package/dist/lib/sdk/toolRegistration.js +19 -66
  46. package/dist/lib/types/cli.d.ts +0 -1
  47. package/dist/lib/types/cli.js +0 -1
  48. package/dist/lib/types/common.d.ts +1 -2
  49. package/dist/lib/types/common.js +0 -1
  50. package/dist/lib/types/contextTypes.d.ts +1 -1
  51. package/dist/lib/types/contextTypes.js +3 -3
  52. package/dist/lib/types/externalMcp.d.ts +6 -0
  53. package/dist/lib/types/externalMcp.js +1 -0
  54. package/dist/lib/types/index.d.ts +2 -3
  55. package/dist/lib/types/index.js +0 -1
  56. package/dist/lib/types/mcpTypes.d.ts +53 -99
  57. package/dist/lib/types/providers.d.ts +0 -1
  58. package/dist/lib/types/providers.js +0 -1
  59. package/dist/lib/types/tools.d.ts +2 -2
  60. package/dist/lib/types/tools.js +2 -2
  61. package/dist/lib/utils/factoryProcessing.js +1 -1
  62. package/dist/lib/utils/mcpDefaults.d.ts +54 -0
  63. package/dist/lib/utils/mcpDefaults.js +125 -0
  64. package/dist/lib/utils/providerConfig.d.ts +1 -1
  65. package/dist/lib/utils/providerConfig.js +2 -2
  66. package/dist/lib/utils/providerHealth.js +6 -6
  67. package/dist/mcp/externalServerManager.d.ts +46 -13
  68. package/dist/mcp/externalServerManager.js +393 -32
  69. package/dist/mcp/mcpClientFactory.d.ts +5 -4
  70. package/dist/mcp/mcpClientFactory.js +8 -3
  71. package/dist/mcp/toolDiscoveryService.d.ts +1 -0
  72. package/dist/mcp/toolDiscoveryService.js +76 -8
  73. package/dist/mcp/toolRegistry.d.ts +15 -11
  74. package/dist/mcp/toolRegistry.js +118 -55
  75. package/dist/models/modelResolver.js +1 -1
  76. package/dist/neurolink.d.ts +32 -31
  77. package/dist/neurolink.js +173 -210
  78. package/dist/providers/googleVertex.d.ts +6 -0
  79. package/dist/providers/googleVertex.js +11 -0
  80. package/dist/providers/huggingFace.js +1 -1
  81. package/dist/providers/mistral.js +3 -3
  82. package/dist/providers/ollama.js +1 -1
  83. package/dist/providers/openAI.d.ts +3 -2
  84. package/dist/providers/openAI.js +2 -2
  85. package/dist/providers/openaiCompatible.d.ts +1 -1
  86. package/dist/providers/openaiCompatible.js +2 -2
  87. package/dist/providers/sagemaker/config.js +1 -1
  88. package/dist/sdk/toolRegistration.d.ts +4 -13
  89. package/dist/sdk/toolRegistration.js +19 -66
  90. package/dist/types/cli.d.ts +0 -1
  91. package/dist/types/cli.js +0 -1
  92. package/dist/types/common.d.ts +1 -2
  93. package/dist/types/common.js +0 -1
  94. package/dist/types/contextTypes.d.ts +1 -1
  95. package/dist/types/contextTypes.js +3 -3
  96. package/dist/types/externalMcp.d.ts +6 -0
  97. package/dist/types/externalMcp.js +1 -0
  98. package/dist/types/index.d.ts +2 -3
  99. package/dist/types/index.js +0 -1
  100. package/dist/types/mcpTypes.d.ts +53 -99
  101. package/dist/types/providers.d.ts +0 -1
  102. package/dist/types/providers.js +0 -1
  103. package/dist/types/tools.d.ts +2 -2
  104. package/dist/types/tools.js +2 -2
  105. package/dist/utils/factoryProcessing.js +1 -1
  106. package/dist/utils/mcpDefaults.d.ts +54 -0
  107. package/dist/utils/mcpDefaults.js +125 -0
  108. package/dist/utils/providerConfig.d.ts +1 -1
  109. package/dist/utils/providerConfig.js +2 -2
  110. package/dist/utils/providerHealth.js +6 -6
  111. package/package.json +1 -1
@@ -10,16 +10,74 @@ import { EventEmitter } from "events";
10
10
  import { mcpLogger } from "../utils/logger.js";
11
11
  import { MCPClientFactory } from "./mcpClientFactory.js";
12
12
  import { ToolDiscoveryService } from "./toolDiscoveryService.js";
13
+ import { toolRegistry } from "./toolRegistry.js";
14
+ import { detectCategory } from "../utils/mcpDefaults.js";
13
15
  /**
14
- * ExternalServerManager
15
- * Core class for managing external MCP servers
16
+ * Type guard to validate if an object can be safely used as Record<string, JsonValue>
16
17
  */
18
+ function isValidJsonRecord(value) {
19
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
20
+ return false;
21
+ }
22
+ const record = value;
23
+ return Object.values(record).every((val) => {
24
+ // JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue }
25
+ if (val === null ||
26
+ typeof val === "string" ||
27
+ typeof val === "number" ||
28
+ typeof val === "boolean") {
29
+ return true;
30
+ }
31
+ if (Array.isArray(val)) {
32
+ return val.every((item) => isValidJsonRecord(item) ||
33
+ typeof item === "string" ||
34
+ typeof item === "number" ||
35
+ typeof item === "boolean" ||
36
+ item === null);
37
+ }
38
+ if (typeof val === "object" && val !== null) {
39
+ return isValidJsonRecord(val);
40
+ }
41
+ return false;
42
+ });
43
+ }
44
+ /**
45
+ * Safely converts unknown metadata to Record<string, JsonValue> or returns undefined
46
+ */
47
+ function safeMetadataConversion(metadata) {
48
+ return isValidJsonRecord(metadata) ? metadata : undefined;
49
+ }
50
+ /**
51
+ * Type guard to validate external MCP server configuration
52
+ */
53
+ function isValidExternalMCPServerConfig(config) {
54
+ if (typeof config !== "object" || config === null) {
55
+ return false;
56
+ }
57
+ const record = config;
58
+ return (typeof record.command === "string" &&
59
+ (record.args === undefined || Array.isArray(record.args)) &&
60
+ (record.env === undefined ||
61
+ (typeof record.env === "object" && record.env !== null)) &&
62
+ (record.transport === undefined || typeof record.transport === "string") &&
63
+ (record.timeout === undefined || typeof record.timeout === "number") &&
64
+ (record.retries === undefined || typeof record.retries === "number") &&
65
+ (record.healthCheckInterval === undefined ||
66
+ typeof record.healthCheckInterval === "number") &&
67
+ (record.autoRestart === undefined ||
68
+ typeof record.autoRestart === "boolean") &&
69
+ (record.cwd === undefined || typeof record.cwd === "string") &&
70
+ (record.url === undefined || typeof record.url === "string") &&
71
+ (record.metadata === undefined ||
72
+ (typeof record.metadata === "object" && record.metadata !== null)));
73
+ }
17
74
  export class ExternalServerManager extends EventEmitter {
18
75
  servers = new Map();
19
76
  config;
20
77
  isShuttingDown = false;
21
78
  toolDiscovery;
22
- constructor(config = {}) {
79
+ enableMainRegistryIntegration;
80
+ constructor(config = {}, options = {}) {
23
81
  super();
24
82
  // Set defaults for configuration
25
83
  this.config = {
@@ -32,6 +90,9 @@ export class ExternalServerManager extends EventEmitter {
32
90
  enablePerformanceMonitoring: config.enablePerformanceMonitoring ?? true,
33
91
  logLevel: config.logLevel ?? "info",
34
92
  };
93
+ // Enable main tool registry integration by default
94
+ this.enableMainRegistryIntegration =
95
+ options.enableMainRegistryIntegration ?? true;
35
96
  // Initialize tool discovery service
36
97
  this.toolDiscovery = new ToolDiscoveryService();
37
98
  // Forward tool discovery events
@@ -46,6 +107,99 @@ export class ExternalServerManager extends EventEmitter {
46
107
  process.on("SIGTERM", () => this.shutdown());
47
108
  process.on("beforeExit", () => this.shutdown());
48
109
  }
110
+ /**
111
+ * Load MCP server configurations from .mcp-config.json file
112
+ * Automatically registers servers found in the configuration
113
+ * @param configPath Optional path to config file (defaults to .mcp-config.json in cwd)
114
+ * @returns Promise resolving to number of servers loaded
115
+ */
116
+ async loadMCPConfiguration(configPath) {
117
+ const fs = await import("fs");
118
+ const path = await import("path");
119
+ const finalConfigPath = configPath || path.join(process.cwd(), ".mcp-config.json");
120
+ if (!fs.existsSync(finalConfigPath)) {
121
+ mcpLogger.debug(`[ExternalServerManager] No MCP config found at ${finalConfigPath}`);
122
+ return { serversLoaded: 0, errors: [] };
123
+ }
124
+ mcpLogger.debug(`[ExternalServerManager] Loading MCP configuration from ${finalConfigPath}`);
125
+ try {
126
+ const configContent = fs.readFileSync(finalConfigPath, "utf8");
127
+ const config = JSON.parse(configContent);
128
+ if (!config.mcpServers || typeof config.mcpServers !== "object") {
129
+ mcpLogger.debug("[ExternalServerManager] No mcpServers found in configuration");
130
+ return { serversLoaded: 0, errors: [] };
131
+ }
132
+ let serversLoaded = 0;
133
+ const errors = [];
134
+ for (const [serverId, serverConfig] of Object.entries(config.mcpServers)) {
135
+ try {
136
+ // Validate and convert config format to MCPServerInfo
137
+ if (!isValidExternalMCPServerConfig(serverConfig)) {
138
+ throw new Error(`Invalid server config for ${serverId}: missing required properties or wrong types`);
139
+ }
140
+ const externalConfig = {
141
+ id: serverId,
142
+ name: serverId,
143
+ description: `External MCP server: ${serverId}`,
144
+ transport: typeof serverConfig.transport === "string"
145
+ ? serverConfig.transport
146
+ : "stdio",
147
+ status: "initializing",
148
+ tools: [],
149
+ command: serverConfig.command,
150
+ args: Array.isArray(serverConfig.args)
151
+ ? serverConfig.args
152
+ : [],
153
+ env: typeof serverConfig.env === "object" && serverConfig.env !== null
154
+ ? serverConfig.env
155
+ : {},
156
+ timeout: typeof serverConfig.timeout === "number"
157
+ ? serverConfig.timeout
158
+ : undefined,
159
+ retries: typeof serverConfig.retries === "number"
160
+ ? serverConfig.retries
161
+ : undefined,
162
+ healthCheckInterval: typeof serverConfig.healthCheckInterval === "number"
163
+ ? serverConfig.healthCheckInterval
164
+ : undefined,
165
+ autoRestart: typeof serverConfig.autoRestart === "boolean"
166
+ ? serverConfig.autoRestart
167
+ : undefined,
168
+ cwd: typeof serverConfig.cwd === "string"
169
+ ? serverConfig.cwd
170
+ : undefined,
171
+ url: typeof serverConfig.url === "string"
172
+ ? serverConfig.url
173
+ : undefined,
174
+ metadata: safeMetadataConversion(serverConfig.metadata),
175
+ };
176
+ const result = await this.addServer(serverId, externalConfig);
177
+ if (result.success) {
178
+ serversLoaded++;
179
+ mcpLogger.debug(`[ExternalServerManager] Successfully loaded MCP server: ${serverId}`);
180
+ }
181
+ else {
182
+ const error = `Failed to load server ${serverId}: ${result.error}`;
183
+ errors.push(error);
184
+ mcpLogger.warn(`[ExternalServerManager] ${error}`);
185
+ }
186
+ }
187
+ catch (error) {
188
+ const errorMsg = `Failed to load MCP server ${serverId}: ${error instanceof Error ? error.message : String(error)}`;
189
+ errors.push(errorMsg);
190
+ mcpLogger.warn(`[ExternalServerManager] ${errorMsg}`);
191
+ // Continue with other servers - don't let one failure break everything
192
+ }
193
+ }
194
+ mcpLogger.info(`[ExternalServerManager] MCP configuration loading complete: ${serversLoaded} servers loaded, ${errors.length} errors`);
195
+ return { serversLoaded, errors };
196
+ }
197
+ catch (error) {
198
+ const errorMsg = `Failed to load MCP configuration: ${error instanceof Error ? error.message : String(error)}`;
199
+ mcpLogger.error(`[ExternalServerManager] ${errorMsg}`);
200
+ return { serversLoaded: 0, errors: [errorMsg] };
201
+ }
202
+ }
49
203
  /**
50
204
  * Validate external MCP server configuration
51
205
  */
@@ -93,11 +247,42 @@ export class ExternalServerManager extends EventEmitter {
93
247
  };
94
248
  }
95
249
  /**
96
- * Add a new external MCP server
250
+ * Convert MCPServerInfo format (keeping for backward compatibility)
251
+ * Helper function for transitioning to zero-conversion architecture
97
252
  */
98
- async addServer(serverId, config) {
253
+ convertConfigToMCPServerInfo(serverId, config) {
254
+ return {
255
+ id: serverId,
256
+ name: String(config.metadata?.title || serverId),
257
+ description: `External MCP server (${config.transport})`,
258
+ status: "initializing",
259
+ transport: config.transport,
260
+ command: config.command,
261
+ args: config.args,
262
+ env: config.env,
263
+ tools: [], // Will be populated after server connection
264
+ metadata: {
265
+ category: "external",
266
+ // Store additional ExternalMCPServerConfig fields in metadata
267
+ timeout: config.timeout,
268
+ retries: config.retries,
269
+ healthCheckInterval: config.healthCheckInterval,
270
+ autoRestart: config.autoRestart,
271
+ cwd: config.cwd,
272
+ url: config.url,
273
+ ...(safeMetadataConversion(config.metadata) || {}),
274
+ },
275
+ };
276
+ }
277
+ async addServer(serverId, configOrServerInfo) {
99
278
  const startTime = Date.now();
100
279
  try {
280
+ // Use MCPServerInfo directly (zero-conversion architecture)
281
+ const serverInfo = "transport" in configOrServerInfo &&
282
+ "command" in configOrServerInfo &&
283
+ !("tools" in configOrServerInfo)
284
+ ? this.convertConfigToMCPServerInfo(serverId, configOrServerInfo)
285
+ : configOrServerInfo;
101
286
  // Check server limit
102
287
  if (this.servers.size >= this.config.maxServers) {
103
288
  return {
@@ -107,8 +292,26 @@ export class ExternalServerManager extends EventEmitter {
107
292
  duration: Date.now() - startTime,
108
293
  };
109
294
  }
110
- // Validate configuration
111
- const validation = this.validateConfig(config);
295
+ // Validate configuration (for backward compatibility, create temporary config)
296
+ const tempConfig = {
297
+ id: serverId,
298
+ name: serverInfo.name,
299
+ description: serverInfo.description,
300
+ transport: serverInfo.transport,
301
+ status: serverInfo.status,
302
+ tools: serverInfo.tools,
303
+ command: serverInfo.command || "",
304
+ args: serverInfo.args || [],
305
+ env: serverInfo.env || {},
306
+ timeout: serverInfo.metadata?.timeout,
307
+ retries: serverInfo.metadata?.retries,
308
+ healthCheckInterval: serverInfo.metadata?.healthCheckInterval,
309
+ autoRestart: serverInfo.metadata?.autoRestart,
310
+ cwd: serverInfo.metadata?.cwd,
311
+ url: serverInfo.metadata?.url,
312
+ metadata: safeMetadataConversion(serverInfo.metadata),
313
+ };
314
+ const validation = this.validateConfig(tempConfig);
112
315
  if (!validation.isValid) {
113
316
  return {
114
317
  success: false,
@@ -127,19 +330,19 @@ export class ExternalServerManager extends EventEmitter {
127
330
  };
128
331
  }
129
332
  mcpLogger.info(`[ExternalServerManager] Adding server: ${serverId}`, {
130
- command: config.command,
131
- transport: config.transport,
333
+ command: serverInfo.command,
334
+ transport: serverInfo.transport,
132
335
  });
133
- // Create server instance
336
+ // Create server instance as RuntimeMCPServerInfo (transition to zero-conversion)
134
337
  const instance = {
135
- config: { ...config, id: serverId },
338
+ ...serverInfo,
136
339
  process: null,
137
340
  client: null,
138
- transport: null,
341
+ transportInstance: null,
139
342
  status: "initializing",
140
343
  reconnectAttempts: 0,
141
344
  maxReconnectAttempts: this.config.maxRestartAttempts,
142
- tools: new Map(),
345
+ toolsMap: new Map(),
143
346
  metrics: {
144
347
  totalConnections: 0,
145
348
  totalDisconnections: 0,
@@ -148,21 +351,41 @@ export class ExternalServerManager extends EventEmitter {
148
351
  averageResponseTime: 0,
149
352
  lastResponseTime: 0,
150
353
  },
354
+ config: tempConfig,
151
355
  };
152
356
  // Store the instance
153
357
  this.servers.set(serverId, instance);
154
358
  // Start the server
155
359
  await this.startServer(serverId);
156
360
  const finalInstance = this.servers.get(serverId);
361
+ // Convert RuntimeMCPServerInfo to ExternalMCPServerInstance for return
362
+ const convertedInstance = {
363
+ config: finalInstance.config,
364
+ process: finalInstance.process,
365
+ client: finalInstance.client,
366
+ transport: finalInstance.transportInstance,
367
+ status: finalInstance.status,
368
+ lastError: finalInstance.lastError,
369
+ startTime: finalInstance.startTime,
370
+ lastHealthCheck: finalInstance.lastHealthCheck,
371
+ reconnectAttempts: finalInstance.reconnectAttempts,
372
+ maxReconnectAttempts: finalInstance.maxReconnectAttempts,
373
+ tools: finalInstance.toolsMap,
374
+ toolsArray: finalInstance.toolsArray,
375
+ capabilities: finalInstance.capabilities,
376
+ healthTimer: finalInstance.healthTimer,
377
+ restartTimer: finalInstance.restartTimer,
378
+ metrics: finalInstance.metrics,
379
+ };
157
380
  return {
158
381
  success: true,
159
- data: finalInstance,
382
+ data: convertedInstance,
160
383
  serverId,
161
384
  duration: Date.now() - startTime,
162
385
  metadata: {
163
386
  timestamp: Date.now(),
164
387
  operation: "addServer",
165
- toolsDiscovered: finalInstance.tools.size,
388
+ toolsDiscovered: finalInstance.tools.length,
166
389
  },
167
390
  };
168
391
  }
@@ -249,9 +472,9 @@ export class ExternalServerManager extends EventEmitter {
249
472
  }
250
473
  // Store client components
251
474
  instance.client = clientResult.client;
252
- instance.transport = clientResult.transport;
475
+ instance.transportInstance = clientResult.transport;
253
476
  instance.process = clientResult.process || null;
254
- instance.capabilities = clientResult.capabilities;
477
+ instance.capabilities = safeMetadataConversion(clientResult.capabilities);
255
478
  instance.startTime = new Date();
256
479
  instance.lastHealthCheck = new Date();
257
480
  instance.metrics.totalConnections++;
@@ -279,12 +502,16 @@ export class ExternalServerManager extends EventEmitter {
279
502
  this.updateServerStatus(serverId, "connected");
280
503
  // Discover tools from the server
281
504
  await this.discoverServerTools(serverId);
505
+ // Register tools with main registry if integration is enabled
506
+ if (this.enableMainRegistryIntegration) {
507
+ await this.registerServerToolsWithMainRegistry(serverId);
508
+ }
282
509
  // Start health monitoring
283
510
  this.startHealthMonitoring(serverId);
284
511
  // Emit connected event
285
512
  this.emit("connected", {
286
513
  serverId,
287
- toolCount: instance.tools.size,
514
+ toolCount: instance.toolsMap.size,
288
515
  timestamp: new Date(),
289
516
  });
290
517
  mcpLogger.info(`[ExternalServerManager] Server started successfully: ${serverId}`);
@@ -316,18 +543,22 @@ export class ExternalServerManager extends EventEmitter {
316
543
  clearTimeout(instance.restartTimer);
317
544
  instance.restartTimer = undefined;
318
545
  }
546
+ // Unregister tools from main registry if integration is enabled
547
+ if (this.enableMainRegistryIntegration) {
548
+ this.unregisterServerToolsFromMainRegistry(serverId);
549
+ }
319
550
  // Clear server tools from discovery service
320
551
  this.toolDiscovery.clearServerTools(serverId);
321
552
  // Close MCP client using factory cleanup
322
- if (instance.client && instance.transport) {
553
+ if (instance.client && instance.transportInstance) {
323
554
  try {
324
- await MCPClientFactory.closeClient(instance.client, instance.transport, instance.process || undefined);
555
+ await MCPClientFactory.closeClient(instance.client, instance.transportInstance, instance.process || undefined);
325
556
  }
326
557
  catch (error) {
327
558
  mcpLogger.debug(`[ExternalServerManager] Error closing client for ${serverId}:`, error);
328
559
  }
329
560
  instance.client = null;
330
- instance.transport = null;
561
+ instance.transportInstance = null;
331
562
  instance.process = null;
332
563
  }
333
564
  this.updateServerStatus(serverId, "stopped");
@@ -347,7 +578,17 @@ export class ExternalServerManager extends EventEmitter {
347
578
  return;
348
579
  }
349
580
  const oldStatus = instance.status;
350
- instance.status = newStatus;
581
+ // Map ExternalMCPServerStatus to MCPServerInfo status
582
+ const mappedStatus = newStatus === "connecting" || newStatus === "restarting"
583
+ ? "initializing"
584
+ : newStatus === "stopping" || newStatus === "stopped"
585
+ ? "stopping"
586
+ : newStatus === "connected"
587
+ ? "connected"
588
+ : newStatus === "disconnected"
589
+ ? "disconnected"
590
+ : "failed";
591
+ instance.status = mappedStatus;
351
592
  // Emit status change event
352
593
  this.emit("statusChanged", {
353
594
  serverId,
@@ -476,7 +717,7 @@ export class ExternalServerManager extends EventEmitter {
476
717
  status: instance.status,
477
718
  checkedAt: new Date(),
478
719
  responseTime,
479
- toolCount: instance.tools.size,
720
+ toolCount: instance.toolsMap.size,
480
721
  issues,
481
722
  performance: {
482
723
  uptime: instance.startTime
@@ -502,16 +743,64 @@ export class ExternalServerManager extends EventEmitter {
502
743
  }
503
744
  }
504
745
  /**
505
- * Get server instance
746
+ * Get server instance - converted to ExternalMCPServerInstance for compatibility
506
747
  */
507
748
  getServer(serverId) {
508
- return this.servers.get(serverId);
749
+ const runtime = this.servers.get(serverId);
750
+ if (!runtime) {
751
+ return undefined;
752
+ }
753
+ return {
754
+ config: runtime.config,
755
+ process: runtime.process,
756
+ client: runtime.client,
757
+ transport: runtime.transportInstance,
758
+ status: runtime.status,
759
+ lastError: runtime.lastError,
760
+ startTime: runtime.startTime,
761
+ lastHealthCheck: runtime.lastHealthCheck,
762
+ reconnectAttempts: runtime.reconnectAttempts,
763
+ maxReconnectAttempts: runtime.maxReconnectAttempts,
764
+ tools: runtime.toolsMap,
765
+ toolsArray: runtime.toolsArray,
766
+ capabilities: runtime.capabilities,
767
+ healthTimer: runtime.healthTimer,
768
+ restartTimer: runtime.restartTimer,
769
+ metrics: runtime.metrics,
770
+ };
509
771
  }
510
772
  /**
511
- * Get all servers
773
+ * Get all servers - converted to ExternalMCPServerInstance for compatibility
512
774
  */
513
775
  getAllServers() {
514
- return new Map(this.servers);
776
+ const converted = new Map();
777
+ for (const [serverId, runtime] of this.servers.entries()) {
778
+ converted.set(serverId, {
779
+ config: runtime.config,
780
+ process: runtime.process,
781
+ client: runtime.client,
782
+ transport: runtime.transportInstance,
783
+ status: runtime.status,
784
+ lastError: runtime.lastError,
785
+ startTime: runtime.startTime,
786
+ lastHealthCheck: runtime.lastHealthCheck,
787
+ reconnectAttempts: runtime.reconnectAttempts,
788
+ maxReconnectAttempts: runtime.maxReconnectAttempts,
789
+ tools: runtime.toolsMap,
790
+ toolsArray: runtime.toolsArray,
791
+ capabilities: runtime.capabilities,
792
+ healthTimer: runtime.healthTimer,
793
+ restartTimer: runtime.restartTimer,
794
+ metrics: runtime.metrics,
795
+ });
796
+ }
797
+ return converted;
798
+ }
799
+ /**
800
+ * List servers as MCPServerInfo - ZERO conversion needed
801
+ */
802
+ listServers() {
803
+ return Array.from(this.servers.values());
515
804
  }
516
805
  /**
517
806
  * Get server statuses
@@ -527,7 +816,7 @@ export class ExternalServerManager extends EventEmitter {
527
816
  isHealthy: instance.status === "connected",
528
817
  status: instance.status,
529
818
  checkedAt: instance.lastHealthCheck || new Date(),
530
- toolCount: instance.tools.size,
819
+ toolCount: instance.toolsMap.size,
531
820
  issues: instance.lastError ? [instance.lastError] : [],
532
821
  performance: {
533
822
  uptime,
@@ -569,7 +858,7 @@ export class ExternalServerManager extends EventEmitter {
569
858
  else if (instance.status === "failed") {
570
859
  failedServers++;
571
860
  }
572
- totalTools += instance.tools.size;
861
+ totalTools += instance.toolsMap.size;
573
862
  totalConnections += instance.metrics.totalConnections;
574
863
  totalErrors += instance.metrics.totalErrors;
575
864
  }
@@ -594,10 +883,16 @@ export class ExternalServerManager extends EventEmitter {
594
883
  mcpLogger.debug(`[ExternalServerManager] Discovering tools for server: ${serverId}`);
595
884
  const discoveryResult = await this.toolDiscovery.discoverTools(serverId, instance.client, this.config.defaultTimeout);
596
885
  if (discoveryResult.success) {
597
- // Update instance tools
598
- instance.tools.clear();
886
+ instance.toolsMap.clear();
887
+ instance.toolsArray = undefined;
888
+ instance.tools = [];
599
889
  for (const tool of discoveryResult.tools) {
600
- instance.tools.set(tool.name, tool);
890
+ instance.toolsMap.set(tool.name, tool);
891
+ instance.tools.push({
892
+ name: tool.name,
893
+ description: tool.description,
894
+ inputSchema: tool.inputSchema,
895
+ });
601
896
  }
602
897
  mcpLogger.info(`[ExternalServerManager] Discovered ${discoveryResult.toolCount} tools for ${serverId}`);
603
898
  }
@@ -609,6 +904,72 @@ export class ExternalServerManager extends EventEmitter {
609
904
  mcpLogger.error(`[ExternalServerManager] Tool discovery error for ${serverId}:`, error);
610
905
  }
611
906
  }
907
+ /**
908
+ * Register server tools with main tool registry for unified access
909
+ * This enables external MCP tools to be accessed via the main toolRegistry.executeTool()
910
+ */
911
+ async registerServerToolsWithMainRegistry(serverId) {
912
+ const instance = this.servers.get(serverId);
913
+ if (!instance) {
914
+ throw new Error(`Server '${serverId}' not found`);
915
+ }
916
+ try {
917
+ mcpLogger.debug(`[ExternalServerManager] Registering ${instance.toolsMap.size} tools with main registry for server: ${serverId}`);
918
+ for (const [toolName, tool] of instance.toolsMap.entries()) {
919
+ const toolId = `${serverId}.${toolName}`;
920
+ const toolInfo = {
921
+ name: toolName,
922
+ description: tool.description || toolName,
923
+ inputSchema: tool.inputSchema || {},
924
+ serverId: serverId,
925
+ category: detectCategory({ isExternal: true, serverId }),
926
+ };
927
+ // Register with main tool registry
928
+ try {
929
+ toolRegistry.registerTool(toolId, toolInfo, {
930
+ execute: async (params, context) => {
931
+ // Execute tool via ExternalServerManager for proper lifecycle management
932
+ return await this.executeTool(serverId, toolName, params, { timeout: this.config.defaultTimeout });
933
+ },
934
+ });
935
+ mcpLogger.debug(`[ExternalServerManager] Registered tool with main registry: ${toolId}`);
936
+ }
937
+ catch (registrationError) {
938
+ mcpLogger.warn(`[ExternalServerManager] Failed to register tool ${toolId} with main registry:`, registrationError);
939
+ }
940
+ }
941
+ mcpLogger.info(`[ExternalServerManager] Successfully registered ${instance.toolsMap.size} tools with main registry for ${serverId}`);
942
+ }
943
+ catch (error) {
944
+ mcpLogger.error(`[ExternalServerManager] Failed to register tools with main registry for ${serverId}:`, error);
945
+ }
946
+ }
947
+ /**
948
+ * Unregister server tools from main tool registry
949
+ */
950
+ unregisterServerToolsFromMainRegistry(serverId) {
951
+ const instance = this.servers.get(serverId);
952
+ if (!instance || !this.enableMainRegistryIntegration) {
953
+ return;
954
+ }
955
+ try {
956
+ mcpLogger.debug(`[ExternalServerManager] Unregistering tools from main registry for server: ${serverId}`);
957
+ for (const [toolName] of instance.toolsMap.entries()) {
958
+ const toolId = `${serverId}.${toolName}`;
959
+ try {
960
+ toolRegistry.removeTool(toolId);
961
+ mcpLogger.debug(`[ExternalServerManager] Unregistered tool from main registry: ${toolId}`);
962
+ }
963
+ catch (error) {
964
+ mcpLogger.debug(`[ExternalServerManager] Failed to unregister tool ${toolId}:`, error);
965
+ }
966
+ }
967
+ mcpLogger.debug(`[ExternalServerManager] Completed unregistering tools from main registry for ${serverId}`);
968
+ }
969
+ catch (error) {
970
+ mcpLogger.error(`[ExternalServerManager] Error unregistering tools from main registry for ${serverId}:`, error);
971
+ }
972
+ }
612
973
  /**
613
974
  * Execute a tool on a specific server
614
975
  */
@@ -7,7 +7,8 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
7
7
  import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
8
8
  import type { ClientCapabilities } from "@modelcontextprotocol/sdk/types.js";
9
9
  import { ChildProcess } from "child_process";
10
- import type { ExternalMCPServerConfig, MCPTransportType } from "../types/externalMcp.js";
10
+ import type { MCPTransportType } from "../types/externalMcp.js";
11
+ import type { MCPServerInfo } from "../types/mcpTypes.js";
11
12
  /**
12
13
  * MCP client creation result
13
14
  */
@@ -37,7 +38,7 @@ export declare class MCPClientFactory {
37
38
  /**
38
39
  * Create an MCP client for the given server configuration
39
40
  */
40
- static createClient(config: ExternalMCPServerConfig, timeout?: number): Promise<MCPClientResult>;
41
+ static createClient(config: MCPServerInfo, timeout?: number): Promise<MCPClientResult>;
41
42
  /**
42
43
  * Internal client creation logic
43
44
  */
@@ -81,7 +82,7 @@ export declare class MCPClientFactory {
81
82
  /**
82
83
  * Test connection to an MCP server
83
84
  */
84
- static testConnection(config: ExternalMCPServerConfig, timeout?: number): Promise<{
85
+ static testConnection(config: MCPServerInfo, timeout?: number): Promise<{
85
86
  success: boolean;
86
87
  error?: string;
87
88
  capabilities?: ClientCapabilities;
@@ -89,7 +90,7 @@ export declare class MCPClientFactory {
89
90
  /**
90
91
  * Validate MCP server configuration for client creation
91
92
  */
92
- static validateClientConfig(config: ExternalMCPServerConfig): {
93
+ static validateClientConfig(config: MCPServerInfo): {
93
94
  isValid: boolean;
94
95
  errors: string[];
95
96
  };
@@ -133,8 +133,12 @@ export class MCPClientFactory {
133
133
  command: config.command,
134
134
  args: config.args,
135
135
  });
136
+ // Validate command is present
137
+ if (!config.command) {
138
+ throw new Error(`Command is required for stdio transport`);
139
+ }
136
140
  // Spawn the process
137
- const childProcess = spawn(config.command, config.args, {
141
+ const childProcess = spawn(config.command, config.args || [], {
138
142
  stdio: ["pipe", "pipe", "pipe"],
139
143
  env: {
140
144
  ...process.env,
@@ -159,13 +163,14 @@ export class MCPClientFactory {
159
163
  processErrorPromise,
160
164
  ]);
161
165
  // Check if process is still running
162
- if (childProcess.killed || childProcess.exitCode !== null) {
166
+ if (childProcess.killed ||
167
+ childProcess.exitCode !== null) {
163
168
  throw new Error("Process failed to start or exited immediately");
164
169
  }
165
170
  // Create transport
166
171
  const transport = new StdioClientTransport({
167
172
  command: config.command,
168
- args: config.args,
173
+ args: config.args || [],
169
174
  env: Object.fromEntries(Object.entries({
170
175
  ...process.env,
171
176
  ...config.env,
@@ -94,6 +94,7 @@ export interface ToolRegistryEvents {
94
94
  * Handles automatic tool discovery and registration from external MCP servers
95
95
  */
96
96
  export declare class ToolDiscoveryService extends EventEmitter {
97
+ private serverToolStorage;
97
98
  private toolRegistry;
98
99
  private serverTools;
99
100
  private discoveryInProgress;