@juspay/neurolink 1.5.3 → 1.9.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 (176) hide show
  1. package/CHANGELOG.md +241 -1
  2. package/README.md +113 -20
  3. package/dist/agent/direct-tools.d.ts +1203 -0
  4. package/dist/agent/direct-tools.js +387 -0
  5. package/dist/cli/commands/agent-generate.d.ts +2 -0
  6. package/dist/cli/commands/agent-generate.js +70 -0
  7. package/dist/cli/commands/config.d.ts +76 -9
  8. package/dist/cli/commands/config.js +358 -233
  9. package/dist/cli/commands/mcp.d.ts +2 -1
  10. package/dist/cli/commands/mcp.js +874 -146
  11. package/dist/cli/commands/ollama.d.ts +8 -0
  12. package/dist/cli/commands/ollama.js +333 -0
  13. package/dist/cli/index.js +591 -327
  14. package/dist/cli/utils/complete-setup.d.ts +19 -0
  15. package/dist/cli/utils/complete-setup.js +81 -0
  16. package/dist/cli/utils/env-manager.d.ts +44 -0
  17. package/dist/cli/utils/env-manager.js +226 -0
  18. package/dist/cli/utils/interactive-setup.d.ts +48 -0
  19. package/dist/cli/utils/interactive-setup.js +302 -0
  20. package/dist/core/dynamic-models.d.ts +208 -0
  21. package/dist/core/dynamic-models.js +250 -0
  22. package/dist/core/factory.d.ts +13 -6
  23. package/dist/core/factory.js +180 -50
  24. package/dist/core/types.d.ts +8 -3
  25. package/dist/core/types.js +7 -4
  26. package/dist/index.d.ts +16 -16
  27. package/dist/index.js +16 -16
  28. package/dist/lib/agent/direct-tools.d.ts +1203 -0
  29. package/dist/lib/agent/direct-tools.js +387 -0
  30. package/dist/lib/core/dynamic-models.d.ts +208 -0
  31. package/dist/lib/core/dynamic-models.js +250 -0
  32. package/dist/lib/core/factory.d.ts +13 -6
  33. package/dist/lib/core/factory.js +180 -50
  34. package/dist/lib/core/types.d.ts +8 -3
  35. package/dist/lib/core/types.js +7 -4
  36. package/dist/lib/index.d.ts +16 -16
  37. package/dist/lib/index.js +16 -16
  38. package/dist/lib/mcp/auto-discovery.d.ts +120 -0
  39. package/dist/lib/mcp/auto-discovery.js +793 -0
  40. package/dist/lib/mcp/client.d.ts +66 -0
  41. package/dist/lib/mcp/client.js +245 -0
  42. package/dist/lib/mcp/config.d.ts +31 -0
  43. package/dist/lib/mcp/config.js +74 -0
  44. package/dist/lib/mcp/context-manager.d.ts +4 -4
  45. package/dist/lib/mcp/context-manager.js +24 -18
  46. package/dist/lib/mcp/factory.d.ts +28 -11
  47. package/dist/lib/mcp/factory.js +36 -29
  48. package/dist/lib/mcp/function-calling.d.ts +51 -0
  49. package/dist/lib/mcp/function-calling.js +510 -0
  50. package/dist/lib/mcp/index.d.ts +190 -0
  51. package/dist/lib/mcp/index.js +156 -0
  52. package/dist/lib/mcp/initialize-tools.d.ts +28 -0
  53. package/dist/lib/mcp/initialize-tools.js +209 -0
  54. package/dist/lib/mcp/initialize.d.ts +17 -0
  55. package/dist/lib/mcp/initialize.js +51 -0
  56. package/dist/lib/mcp/logging.d.ts +71 -0
  57. package/dist/lib/mcp/logging.js +183 -0
  58. package/dist/lib/mcp/manager.d.ts +67 -0
  59. package/dist/lib/mcp/manager.js +176 -0
  60. package/dist/lib/mcp/neurolink-mcp-client.d.ts +96 -0
  61. package/dist/lib/mcp/neurolink-mcp-client.js +417 -0
  62. package/dist/lib/mcp/orchestrator.d.ts +3 -3
  63. package/dist/lib/mcp/orchestrator.js +46 -43
  64. package/dist/lib/mcp/registry.d.ts +2 -2
  65. package/dist/lib/mcp/registry.js +42 -33
  66. package/dist/lib/mcp/servers/ai-providers/ai-analysis-tools.d.ts +1 -1
  67. package/dist/lib/mcp/servers/ai-providers/ai-analysis-tools.js +205 -66
  68. package/dist/lib/mcp/servers/ai-providers/ai-core-server.js +143 -99
  69. package/dist/lib/mcp/servers/ai-providers/ai-workflow-tools.d.ts +6 -6
  70. package/dist/lib/mcp/servers/ai-providers/ai-workflow-tools.js +404 -251
  71. package/dist/lib/mcp/servers/utilities/utility-server.d.ts +8 -0
  72. package/dist/lib/mcp/servers/utilities/utility-server.js +326 -0
  73. package/dist/lib/mcp/tool-integration.d.ts +67 -0
  74. package/dist/lib/mcp/tool-integration.js +179 -0
  75. package/dist/lib/mcp/unified-registry.d.ts +269 -0
  76. package/dist/lib/mcp/unified-registry.js +1411 -0
  77. package/dist/lib/neurolink.d.ts +68 -6
  78. package/dist/lib/neurolink.js +314 -42
  79. package/dist/lib/providers/agent-enhanced-provider.d.ts +59 -0
  80. package/dist/lib/providers/agent-enhanced-provider.js +242 -0
  81. package/dist/lib/providers/amazonBedrock.d.ts +3 -3
  82. package/dist/lib/providers/amazonBedrock.js +54 -50
  83. package/dist/lib/providers/anthropic.d.ts +2 -2
  84. package/dist/lib/providers/anthropic.js +92 -84
  85. package/dist/lib/providers/azureOpenAI.d.ts +2 -2
  86. package/dist/lib/providers/azureOpenAI.js +97 -86
  87. package/dist/lib/providers/function-calling-provider.d.ts +70 -0
  88. package/dist/lib/providers/function-calling-provider.js +359 -0
  89. package/dist/lib/providers/googleAIStudio.d.ts +10 -5
  90. package/dist/lib/providers/googleAIStudio.js +60 -38
  91. package/dist/lib/providers/googleVertexAI.d.ts +3 -3
  92. package/dist/lib/providers/googleVertexAI.js +96 -86
  93. package/dist/lib/providers/huggingFace.d.ts +31 -0
  94. package/dist/lib/providers/huggingFace.js +362 -0
  95. package/dist/lib/providers/index.d.ts +14 -8
  96. package/dist/lib/providers/index.js +18 -12
  97. package/dist/lib/providers/mcp-provider.d.ts +62 -0
  98. package/dist/lib/providers/mcp-provider.js +183 -0
  99. package/dist/lib/providers/mistralAI.d.ts +32 -0
  100. package/dist/lib/providers/mistralAI.js +223 -0
  101. package/dist/lib/providers/ollama.d.ts +51 -0
  102. package/dist/lib/providers/ollama.js +508 -0
  103. package/dist/lib/providers/openAI.d.ts +7 -3
  104. package/dist/lib/providers/openAI.js +45 -33
  105. package/dist/lib/utils/logger.js +2 -2
  106. package/dist/lib/utils/providerUtils.js +59 -22
  107. package/dist/mcp/auto-discovery.d.ts +120 -0
  108. package/dist/mcp/auto-discovery.js +794 -0
  109. package/dist/mcp/client.d.ts +66 -0
  110. package/dist/mcp/client.js +245 -0
  111. package/dist/mcp/config.d.ts +31 -0
  112. package/dist/mcp/config.js +74 -0
  113. package/dist/mcp/context-manager.d.ts +4 -4
  114. package/dist/mcp/context-manager.js +24 -18
  115. package/dist/mcp/factory.d.ts +28 -11
  116. package/dist/mcp/factory.js +36 -29
  117. package/dist/mcp/function-calling.d.ts +51 -0
  118. package/dist/mcp/function-calling.js +510 -0
  119. package/dist/mcp/index.d.ts +190 -0
  120. package/dist/mcp/index.js +156 -0
  121. package/dist/mcp/initialize-tools.d.ts +28 -0
  122. package/dist/mcp/initialize-tools.js +210 -0
  123. package/dist/mcp/initialize.d.ts +17 -0
  124. package/dist/mcp/initialize.js +51 -0
  125. package/dist/mcp/logging.d.ts +71 -0
  126. package/dist/mcp/logging.js +183 -0
  127. package/dist/mcp/manager.d.ts +67 -0
  128. package/dist/mcp/manager.js +176 -0
  129. package/dist/mcp/neurolink-mcp-client.d.ts +96 -0
  130. package/dist/mcp/neurolink-mcp-client.js +417 -0
  131. package/dist/mcp/orchestrator.d.ts +3 -3
  132. package/dist/mcp/orchestrator.js +46 -43
  133. package/dist/mcp/registry.d.ts +2 -2
  134. package/dist/mcp/registry.js +42 -33
  135. package/dist/mcp/servers/ai-providers/ai-analysis-tools.d.ts +1 -1
  136. package/dist/mcp/servers/ai-providers/ai-analysis-tools.js +205 -66
  137. package/dist/mcp/servers/ai-providers/ai-core-server.js +143 -99
  138. package/dist/mcp/servers/ai-providers/ai-workflow-tools.d.ts +6 -6
  139. package/dist/mcp/servers/ai-providers/ai-workflow-tools.js +404 -253
  140. package/dist/mcp/servers/utilities/utility-server.d.ts +8 -0
  141. package/dist/mcp/servers/utilities/utility-server.js +326 -0
  142. package/dist/mcp/tool-integration.d.ts +67 -0
  143. package/dist/mcp/tool-integration.js +179 -0
  144. package/dist/mcp/unified-registry.d.ts +269 -0
  145. package/dist/mcp/unified-registry.js +1411 -0
  146. package/dist/neurolink.d.ts +68 -6
  147. package/dist/neurolink.js +314 -42
  148. package/dist/providers/agent-enhanced-provider.d.ts +59 -0
  149. package/dist/providers/agent-enhanced-provider.js +242 -0
  150. package/dist/providers/amazonBedrock.d.ts +3 -3
  151. package/dist/providers/amazonBedrock.js +54 -50
  152. package/dist/providers/anthropic.d.ts +2 -2
  153. package/dist/providers/anthropic.js +92 -84
  154. package/dist/providers/azureOpenAI.d.ts +2 -2
  155. package/dist/providers/azureOpenAI.js +97 -86
  156. package/dist/providers/function-calling-provider.d.ts +70 -0
  157. package/dist/providers/function-calling-provider.js +359 -0
  158. package/dist/providers/googleAIStudio.d.ts +10 -5
  159. package/dist/providers/googleAIStudio.js +60 -38
  160. package/dist/providers/googleVertexAI.d.ts +3 -3
  161. package/dist/providers/googleVertexAI.js +96 -86
  162. package/dist/providers/huggingFace.d.ts +31 -0
  163. package/dist/providers/huggingFace.js +362 -0
  164. package/dist/providers/index.d.ts +14 -8
  165. package/dist/providers/index.js +18 -12
  166. package/dist/providers/mcp-provider.d.ts +62 -0
  167. package/dist/providers/mcp-provider.js +183 -0
  168. package/dist/providers/mistralAI.d.ts +32 -0
  169. package/dist/providers/mistralAI.js +223 -0
  170. package/dist/providers/ollama.d.ts +51 -0
  171. package/dist/providers/ollama.js +508 -0
  172. package/dist/providers/openAI.d.ts +7 -3
  173. package/dist/providers/openAI.js +45 -33
  174. package/dist/utils/logger.js +2 -2
  175. package/dist/utils/providerUtils.js +59 -22
  176. package/package.json +28 -4
@@ -0,0 +1,794 @@
1
+ /**
2
+ * NeuroLink MCP Auto-Discovery System
3
+ * Automatically discovers MCP servers from common configuration locations across different tools
4
+ * Supports VS Code, Cursor, Claude Desktop, Windsurf, Roo Code, and generic configurations
5
+ */
6
+ import * as fs from "fs/promises";
7
+ import * as path from "path";
8
+ import * as os from "os";
9
+ import { MCPToolRegistry } from "./registry.js";
10
+ import { autoDiscoveryLogger } from "./logging.js";
11
+ /**
12
+ * Claude Desktop configuration parser
13
+ */
14
+ class ClaudeDesktopParser {
15
+ canParse(filePath, content) {
16
+ return (filePath.includes("claude_desktop_config.json") &&
17
+ content &&
18
+ typeof content === "object" &&
19
+ (content.mcpServers || content.mcp_servers));
20
+ }
21
+ parse(filePath, content, source) {
22
+ const servers = [];
23
+ const mcpServers = content.mcpServers || content.mcp_servers || {};
24
+ for (const [serverId, serverConfig] of Object.entries(mcpServers)) {
25
+ if (typeof serverConfig === "object" && serverConfig !== null) {
26
+ const config = serverConfig;
27
+ servers.push({
28
+ id: serverId,
29
+ title: config.title || serverId,
30
+ command: config.command || "node",
31
+ args: config.args || [],
32
+ env: config.env || {},
33
+ cwd: config.cwd,
34
+ source,
35
+ configPath: filePath,
36
+ rawConfig: config,
37
+ });
38
+ }
39
+ }
40
+ return servers;
41
+ }
42
+ }
43
+ /**
44
+ * VS Code configuration parser
45
+ */
46
+ class VSCodeParser {
47
+ canParse(filePath, content) {
48
+ return ((filePath.includes(".vscode/mcp.json") ||
49
+ filePath.includes("settings.json")) &&
50
+ content &&
51
+ typeof content === "object");
52
+ }
53
+ parse(filePath, content, source) {
54
+ const servers = [];
55
+ // Handle .vscode/mcp.json format
56
+ if (filePath.includes("mcp.json")) {
57
+ const mcpServers = content.mcpServers || content.servers || {};
58
+ for (const [serverId, serverConfig] of Object.entries(mcpServers)) {
59
+ if (typeof serverConfig === "object" && serverConfig !== null) {
60
+ const config = serverConfig;
61
+ servers.push({
62
+ id: serverId,
63
+ title: config.title || serverId,
64
+ command: config.command || "node",
65
+ args: config.args || [],
66
+ env: config.env || {},
67
+ cwd: config.cwd,
68
+ source,
69
+ configPath: filePath,
70
+ rawConfig: config,
71
+ });
72
+ }
73
+ }
74
+ }
75
+ // Handle settings.json with MCP configuration
76
+ if (filePath.includes("settings.json") && content.mcp) {
77
+ const mcpConfig = content.mcp;
78
+ if (mcpConfig.servers) {
79
+ for (const [serverId, serverConfig] of Object.entries(mcpConfig.servers)) {
80
+ if (typeof serverConfig === "object" && serverConfig !== null) {
81
+ const config = serverConfig;
82
+ servers.push({
83
+ id: serverId,
84
+ title: config.title || serverId,
85
+ command: config.command || "node",
86
+ args: config.args || [],
87
+ env: config.env || {},
88
+ cwd: config.cwd,
89
+ source,
90
+ configPath: filePath,
91
+ rawConfig: config,
92
+ });
93
+ }
94
+ }
95
+ }
96
+ }
97
+ return servers;
98
+ }
99
+ }
100
+ /**
101
+ * Cursor configuration parser
102
+ */
103
+ class CursorParser {
104
+ canParse(filePath, content) {
105
+ return (filePath.includes(".cursor/mcp.json") ||
106
+ (filePath.includes("mcp.json") && content && typeof content === "object"));
107
+ }
108
+ parse(filePath, content, source) {
109
+ const servers = [];
110
+ const mcpServers = content.mcpServers || content.servers || {};
111
+ for (const [serverId, serverConfig] of Object.entries(mcpServers)) {
112
+ if (typeof serverConfig === "object" && serverConfig !== null) {
113
+ const config = serverConfig;
114
+ servers.push({
115
+ id: serverId,
116
+ title: config.title || serverId,
117
+ command: config.command || "node",
118
+ args: config.args || [],
119
+ env: config.env || {},
120
+ cwd: config.cwd,
121
+ source,
122
+ configPath: filePath,
123
+ rawConfig: config,
124
+ });
125
+ }
126
+ }
127
+ return servers;
128
+ }
129
+ }
130
+ /**
131
+ * Windsurf configuration parser
132
+ */
133
+ class WindsurfParser {
134
+ canParse(filePath, content) {
135
+ return (filePath.includes("windsurf/mcp_config.json") &&
136
+ content &&
137
+ typeof content === "object");
138
+ }
139
+ parse(filePath, content, source) {
140
+ const servers = [];
141
+ const mcpServers = content.mcpServers || content.servers || {};
142
+ for (const [serverId, serverConfig] of Object.entries(mcpServers)) {
143
+ if (typeof serverConfig === "object" && serverConfig !== null) {
144
+ const config = serverConfig;
145
+ servers.push({
146
+ id: serverId,
147
+ title: config.title || serverId,
148
+ command: config.command || "node",
149
+ args: config.args || [],
150
+ env: config.env || {},
151
+ cwd: config.cwd,
152
+ source,
153
+ configPath: filePath,
154
+ rawConfig: config,
155
+ });
156
+ }
157
+ }
158
+ return servers;
159
+ }
160
+ }
161
+ /**
162
+ * Cline AI Coder configuration parser
163
+ */
164
+ class ClineParser {
165
+ canParse(filePath, content) {
166
+ return (filePath.includes("cline_mcp_settings.json") &&
167
+ content &&
168
+ typeof content === "object" &&
169
+ (content.mcpServers || content.servers));
170
+ }
171
+ parse(filePath, content, source) {
172
+ const servers = [];
173
+ const mcpServers = content.mcpServers || content.servers || {};
174
+ for (const [serverId, serverConfig] of Object.entries(mcpServers)) {
175
+ if (typeof serverConfig === "object" && serverConfig !== null) {
176
+ const config = serverConfig;
177
+ servers.push({
178
+ id: serverId,
179
+ title: config.title || serverId,
180
+ command: config.command || "node",
181
+ args: config.args || [],
182
+ env: config.env || {},
183
+ cwd: config.cwd,
184
+ source,
185
+ configPath: filePath,
186
+ rawConfig: config,
187
+ });
188
+ }
189
+ }
190
+ return servers;
191
+ }
192
+ }
193
+ /**
194
+ * Continue Dev configuration parser
195
+ */
196
+ class ContinueParser {
197
+ canParse(filePath, content) {
198
+ return ((filePath.includes("continue/config.json") ||
199
+ filePath.includes(".continue/config.json")) &&
200
+ content &&
201
+ typeof content === "object" &&
202
+ (content.mcpServers || content.contextProviders?.mcp));
203
+ }
204
+ parse(filePath, content, source) {
205
+ const servers = [];
206
+ // Continue may have MCP servers in contextProviders.mcp or directly in mcpServers
207
+ const mcpServers = content.mcpServers || content.contextProviders?.mcp || {};
208
+ for (const [serverId, serverConfig] of Object.entries(mcpServers)) {
209
+ if (typeof serverConfig === "object" && serverConfig !== null) {
210
+ const config = serverConfig;
211
+ servers.push({
212
+ id: serverId,
213
+ title: config.title || serverId,
214
+ command: config.command || "node",
215
+ args: config.args || [],
216
+ env: config.env || {},
217
+ cwd: config.cwd,
218
+ source,
219
+ configPath: filePath,
220
+ rawConfig: config,
221
+ });
222
+ }
223
+ }
224
+ return servers;
225
+ }
226
+ }
227
+ /**
228
+ * Aider configuration parser
229
+ */
230
+ class AiderParser {
231
+ canParse(filePath, content) {
232
+ return ((filePath.includes(".aider") || filePath.includes("aider.conf")) &&
233
+ content &&
234
+ typeof content === "object" &&
235
+ content.mcp_servers);
236
+ }
237
+ parse(filePath, content, source) {
238
+ const servers = [];
239
+ const mcpServers = content.mcp_servers || {};
240
+ for (const [serverId, serverConfig] of Object.entries(mcpServers)) {
241
+ if (typeof serverConfig === "object" && serverConfig !== null) {
242
+ const config = serverConfig;
243
+ servers.push({
244
+ id: serverId,
245
+ title: config.title || serverId,
246
+ command: config.command || "node",
247
+ args: config.args || [],
248
+ env: config.env || {},
249
+ cwd: config.cwd,
250
+ source,
251
+ configPath: filePath,
252
+ rawConfig: config,
253
+ });
254
+ }
255
+ }
256
+ return servers;
257
+ }
258
+ }
259
+ /**
260
+ * Generic MCP configuration parser
261
+ */
262
+ class GenericParser {
263
+ canParse(filePath, content) {
264
+ return ((filePath.includes("mcp.json") ||
265
+ filePath.includes("mcp-config.json") ||
266
+ filePath.includes("mcp_config.json")) &&
267
+ content &&
268
+ typeof content === "object");
269
+ }
270
+ parse(filePath, content, source) {
271
+ const servers = [];
272
+ const mcpServers = content.mcpServers || content.servers || content;
273
+ for (const [serverId, serverConfig] of Object.entries(mcpServers)) {
274
+ if (typeof serverConfig === "object" && serverConfig !== null) {
275
+ const config = serverConfig;
276
+ servers.push({
277
+ id: serverId,
278
+ title: config.title || serverId,
279
+ command: config.command || "node",
280
+ args: config.args || [],
281
+ env: config.env || {},
282
+ cwd: config.cwd,
283
+ source,
284
+ configPath: filePath,
285
+ rawConfig: config,
286
+ });
287
+ }
288
+ }
289
+ return servers;
290
+ }
291
+ }
292
+ /**
293
+ * MCP Auto-Discovery Engine
294
+ */
295
+ export class MCPAutoDiscovery {
296
+ parsers = [
297
+ new ClaudeDesktopParser(),
298
+ new ClineParser(), // Move Cline parser before VSCode parser
299
+ new VSCodeParser(),
300
+ new CursorParser(),
301
+ new WindsurfParser(),
302
+ new ContinueParser(),
303
+ new AiderParser(),
304
+ new GenericParser(),
305
+ ];
306
+ /**
307
+ * Discover MCP servers from all common locations
308
+ *
309
+ * @param options Discovery configuration options
310
+ * @returns Discovery result with found servers
311
+ */
312
+ async discoverServers(options = {}) {
313
+ const startTime = Date.now();
314
+ const discovered = [];
315
+ const sources = [];
316
+ const errors = [];
317
+ let configFilesFound = 0;
318
+ const { searchWorkspace = true, searchGlobal = true, searchCommonPaths = true, followSymlinks = false, maxDepth = 3, includeInactive = true, preferredTools = [], } = options;
319
+ // Define search paths based on options
320
+ const searchPaths = this.getSearchPaths({
321
+ searchWorkspace,
322
+ searchGlobal,
323
+ searchCommonPaths,
324
+ });
325
+ autoDiscoveryLogger.debug(`Starting discovery with ${searchPaths.length} search paths`);
326
+ // Search each path
327
+ for (const searchPath of searchPaths) {
328
+ try {
329
+ const pathResults = await this.searchPath(searchPath, maxDepth, followSymlinks, includeInactive);
330
+ discovered.push(...pathResults.servers);
331
+ sources.push(...pathResults.sources);
332
+ configFilesFound += pathResults.configFilesFound;
333
+ if (pathResults.errors.length > 0) {
334
+ errors.push(...pathResults.errors);
335
+ }
336
+ }
337
+ catch (error) {
338
+ const errorMessage = error instanceof Error ? error.message : String(error);
339
+ errors.push(`Error searching path ${searchPath.path}: ${errorMessage}`);
340
+ }
341
+ }
342
+ // Remove duplicates and apply prioritization
343
+ const uniqueServers = this.deduplicateServers(discovered, preferredTools);
344
+ const duplicatesRemoved = discovered.length - uniqueServers.length;
345
+ const executionTime = Date.now() - startTime;
346
+ autoDiscoveryLogger.debug(`Discovery completed in ${executionTime}ms: ` +
347
+ `${uniqueServers.length} servers found, ${duplicatesRemoved} duplicates removed`);
348
+ return {
349
+ discovered: uniqueServers,
350
+ sources: [...new Set(sources)], // Remove duplicate sources
351
+ errors,
352
+ stats: {
353
+ configFilesFound,
354
+ serversDiscovered: uniqueServers.length,
355
+ duplicatesRemoved,
356
+ executionTime,
357
+ },
358
+ };
359
+ }
360
+ /**
361
+ * Auto-register discovered servers with a registry
362
+ *
363
+ * @param registry Target MCP registry
364
+ * @param options Discovery options
365
+ * @returns Registration results
366
+ */
367
+ async autoRegisterServers(registry, options = {}) {
368
+ const discoveryResult = await this.discoverServers(options);
369
+ const registered = [];
370
+ const failed = [];
371
+ const errors = [...discoveryResult.errors];
372
+ for (const server of discoveryResult.discovered) {
373
+ try {
374
+ // Convert discovered server to NeuroLink MCP server format
375
+ const mcpServer = await this.convertToMCPServer(server);
376
+ // Attempt to register
377
+ await registry.registerServer(mcpServer);
378
+ registered.push(server.id);
379
+ autoDiscoveryLogger.info(`Successfully registered server '${server.id}' from ${server.source.tool}`);
380
+ }
381
+ catch (error) {
382
+ const errorMessage = error instanceof Error ? error.message : String(error);
383
+ failed.push(server.id);
384
+ errors.push(`Failed to register server '${server.id}': ${errorMessage}`);
385
+ autoDiscoveryLogger.error(`Failed to register server '${server.id}': ${errorMessage}`);
386
+ }
387
+ }
388
+ return { registered, failed, errors };
389
+ }
390
+ /**
391
+ * Get standard search paths for MCP configurations
392
+ */
393
+ getSearchPaths(options) {
394
+ const paths = [];
395
+ const homeDir = os.homedir();
396
+ const currentDir = process.cwd();
397
+ // Global configurations
398
+ if (options.searchGlobal) {
399
+ // Claude Desktop
400
+ paths.push({
401
+ path: path.join(homeDir, "Library", "Application Support", "Claude"),
402
+ source: {
403
+ tool: "Claude Desktop",
404
+ type: "global",
405
+ priority: 9,
406
+ description: "Claude Desktop global configuration",
407
+ },
408
+ patterns: ["claude_desktop_config.json"],
409
+ });
410
+ // Cursor global
411
+ paths.push({
412
+ path: path.join(homeDir, ".cursor"),
413
+ source: {
414
+ tool: "Cursor",
415
+ type: "global",
416
+ priority: 8,
417
+ description: "Cursor global configuration",
418
+ },
419
+ patterns: ["mcp.json"],
420
+ });
421
+ // Windsurf global
422
+ paths.push({
423
+ path: path.join(homeDir, ".codeium", "windsurf"),
424
+ source: {
425
+ tool: "Windsurf",
426
+ type: "global",
427
+ priority: 8,
428
+ description: "Windsurf global configuration",
429
+ },
430
+ patterns: ["mcp_config.json"],
431
+ });
432
+ // VS Code global
433
+ const vscodeGlobalPaths = [
434
+ path.join(homeDir, "Library", "Application Support", "Code", "User"), // macOS
435
+ path.join(homeDir, ".config", "Code", "User"), // Linux
436
+ path.join(homeDir, "AppData", "Roaming", "Code", "User"), // Windows
437
+ ];
438
+ for (const vscodeGlobalPath of vscodeGlobalPaths) {
439
+ paths.push({
440
+ path: vscodeGlobalPath,
441
+ source: {
442
+ tool: "VS Code",
443
+ type: "global",
444
+ priority: 7,
445
+ description: "VS Code global configuration",
446
+ },
447
+ patterns: ["settings.json"],
448
+ });
449
+ }
450
+ // Cline AI Coder - stored in VS Code extension globalStorage
451
+ const clineGlobalPaths = [
452
+ path.join(homeDir, "Library", "Application Support", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings"), // macOS
453
+ path.join(homeDir, ".config", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings"), // Linux
454
+ path.join(homeDir, "AppData", "Roaming", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings"), // Windows
455
+ // VS Code Insiders
456
+ path.join(homeDir, "Library", "Application Support", "Code - Insiders", "User", "globalStorage", "saoudrizwan.claude-dev", "settings"), // macOS
457
+ path.join(homeDir, ".config", "Code - Insiders", "User", "globalStorage", "saoudrizwan.claude-dev", "settings"), // Linux
458
+ path.join(homeDir, "AppData", "Roaming", "Code - Insiders", "User", "globalStorage", "saoudrizwan.claude-dev", "settings"), // Windows
459
+ ];
460
+ for (const clineGlobalPath of clineGlobalPaths) {
461
+ paths.push({
462
+ path: clineGlobalPath,
463
+ source: {
464
+ tool: "Cline AI Coder",
465
+ type: "global",
466
+ priority: 8,
467
+ description: "Cline AI Coder extension configuration",
468
+ },
469
+ patterns: ["cline_mcp_settings.json"],
470
+ });
471
+ }
472
+ // Continue Dev global configuration
473
+ paths.push({
474
+ path: path.join(homeDir, ".continue"),
475
+ source: {
476
+ tool: "Continue Dev",
477
+ type: "global",
478
+ priority: 7,
479
+ description: "Continue Dev global configuration",
480
+ },
481
+ patterns: ["config.json"],
482
+ });
483
+ // Aider global configuration
484
+ paths.push({
485
+ path: path.join(homeDir, ".aider"),
486
+ source: {
487
+ tool: "Aider",
488
+ type: "global",
489
+ priority: 7,
490
+ description: "Aider global configuration",
491
+ },
492
+ patterns: ["config.json", "aider.conf"],
493
+ });
494
+ }
495
+ // Workspace/project configurations
496
+ if (options.searchWorkspace) {
497
+ // VS Code workspace
498
+ paths.push({
499
+ path: path.join(currentDir, ".vscode"),
500
+ source: {
501
+ tool: "VS Code",
502
+ type: "workspace",
503
+ priority: 9,
504
+ description: "VS Code workspace configuration",
505
+ },
506
+ patterns: ["mcp.json", "settings.json"],
507
+ });
508
+ // Cursor project
509
+ paths.push({
510
+ path: path.join(currentDir, ".cursor"),
511
+ source: {
512
+ tool: "Cursor",
513
+ type: "project",
514
+ priority: 9,
515
+ description: "Cursor project configuration",
516
+ },
517
+ patterns: ["mcp.json"],
518
+ });
519
+ }
520
+ // Common file locations
521
+ if (options.searchCommonPaths) {
522
+ paths.push({
523
+ path: currentDir,
524
+ source: {
525
+ tool: "Generic",
526
+ type: "project",
527
+ priority: 6,
528
+ description: "Generic project configuration",
529
+ },
530
+ patterns: [
531
+ "mcp.json",
532
+ ".mcp-config.json",
533
+ "mcp_config.json",
534
+ ".mcp-servers.json",
535
+ ],
536
+ });
537
+ }
538
+ return paths;
539
+ }
540
+ /**
541
+ * Search a specific path for MCP configurations
542
+ */
543
+ async searchPath(searchConfig, maxDepth, followSymlinks, includeInactive) {
544
+ const servers = [];
545
+ const sources = [];
546
+ const errors = [];
547
+ let configFilesFound = 0;
548
+ try {
549
+ // Check if path exists
550
+ await fs.access(searchConfig.path);
551
+ autoDiscoveryLogger.debug(`Searching path: ${searchConfig.path}`);
552
+ }
553
+ catch {
554
+ // Path doesn't exist, skip silently
555
+ autoDiscoveryLogger.debug(`Path does not exist: ${searchConfig.path}`);
556
+ return { servers, sources, configFilesFound, errors };
557
+ }
558
+ // Search for each pattern
559
+ for (const pattern of searchConfig.patterns) {
560
+ const filePath = path.join(searchConfig.path, pattern);
561
+ try {
562
+ await fs.access(filePath);
563
+ autoDiscoveryLogger.debug(`Found config file: ${filePath}`);
564
+ // Read and parse the file with resilient JSON parsing
565
+ const content = await fs.readFile(filePath, "utf-8");
566
+ const parsedContent = this.parseJsonResilient(content, filePath);
567
+ configFilesFound++;
568
+ // Try to parse with available parsers
569
+ let parsed = false;
570
+ for (const parser of this.parsers) {
571
+ if (parser.canParse(filePath, parsedContent)) {
572
+ autoDiscoveryLogger.debug(`Using parser for ${searchConfig.source.tool}: ${parser.constructor.name}`);
573
+ const parsedServers = parser.parse(filePath, parsedContent, searchConfig.source);
574
+ autoDiscoveryLogger.debug(`Parsed ${parsedServers.length} servers from ${filePath}`);
575
+ servers.push(...parsedServers);
576
+ sources.push(searchConfig.source);
577
+ parsed = true;
578
+ break;
579
+ }
580
+ }
581
+ if (!parsed) {
582
+ autoDiscoveryLogger.debug(`No parser could handle ${filePath}`);
583
+ }
584
+ }
585
+ catch (error) {
586
+ if (error.code !== "ENOENT") {
587
+ // File exists but couldn't be parsed
588
+ const errorMessage = error instanceof Error ? error.message : String(error);
589
+ errors.push(`Error parsing ${filePath}: ${errorMessage}`);
590
+ autoDiscoveryLogger.debug(`Error parsing ${filePath}: ${errorMessage}`);
591
+ }
592
+ }
593
+ }
594
+ return { servers, sources, configFilesFound, errors };
595
+ }
596
+ /**
597
+ * Parse JSON with resilience to common syntax issues
598
+ */
599
+ parseJsonResilient(content, filePath) {
600
+ try {
601
+ // First, try standard JSON parsing
602
+ return JSON.parse(content);
603
+ }
604
+ catch (error) {
605
+ const originalError = error instanceof Error ? error.message : String(error);
606
+ try {
607
+ // Attempt to fix common JSON issues
608
+ let fixedContent = content;
609
+ // Remove trailing commas before closing braces and brackets
610
+ fixedContent = fixedContent.replace(/,(\s*[}\]])/g, "$1");
611
+ // Remove single-line comments (// comments)
612
+ fixedContent = fixedContent.replace(/\/\/.*$/gm, "");
613
+ // Remove multi-line comments (/* comments */)
614
+ fixedContent = fixedContent.replace(/\/\*[\s\S]*?\*\//g, "");
615
+ // Remove trailing commas after the last property in objects
616
+ fixedContent = fixedContent.replace(/,(\s*})/g, "$1");
617
+ // Remove trailing commas after the last element in arrays
618
+ fixedContent = fixedContent.replace(/,(\s*])/g, "$1");
619
+ // Fix unescaped control characters in strings
620
+ fixedContent = fixedContent.replace(/("(?:[^"\\]|\\.)+")/g, (match) => {
621
+ try {
622
+ // Try to parse the string to see if it's valid
623
+ JSON.parse(`{${match}}`);
624
+ return match;
625
+ }
626
+ catch {
627
+ // If invalid, escape common control characters
628
+ return match
629
+ .replace(/\n/g, "\\n")
630
+ .replace(/\r/g, "\\r")
631
+ .replace(/\t/g, "\\t")
632
+ .replace(/\f/g, "\\f")
633
+ .replace(/\b/g, "\\b");
634
+ }
635
+ });
636
+ // Fix unquoted object keys
637
+ fixedContent = fixedContent.replace(/([{,]\s*)([a-zA-Z_$][a-zA-Z0-9_$]*)\s*:/g, '$1"$2":');
638
+ // Try parsing the fixed content
639
+ const result = JSON.parse(fixedContent);
640
+ autoDiscoveryLogger.debug(`Successfully repaired JSON syntax issues in ${filePath}`);
641
+ return result;
642
+ }
643
+ catch (secondError) {
644
+ // If we still can't parse it, try one more time with aggressive fixes
645
+ try {
646
+ let aggressiveFixedContent = content;
647
+ // Remove any non-printable characters except standard whitespace
648
+ aggressiveFixedContent = aggressiveFixedContent.replace(
649
+ // eslint-disable-next-line no-control-regex
650
+ /[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, "");
651
+ // Apply all previous fixes
652
+ aggressiveFixedContent = aggressiveFixedContent.replace(/,(\s*[}\]])/g, "$1");
653
+ aggressiveFixedContent = aggressiveFixedContent.replace(/\/\/.*$/gm, "");
654
+ aggressiveFixedContent = aggressiveFixedContent.replace(/\/\*[\s\S]*?\*\//g, "");
655
+ aggressiveFixedContent = aggressiveFixedContent.replace(/,(\s*})/g, "$1");
656
+ aggressiveFixedContent = aggressiveFixedContent.replace(/,(\s*])/g, "$1");
657
+ const result = JSON.parse(aggressiveFixedContent);
658
+ autoDiscoveryLogger.debug(`Successfully repaired JSON with aggressive fixes in ${filePath}`);
659
+ return result;
660
+ }
661
+ catch (thirdError) {
662
+ // If all attempts fail, throw a comprehensive error but don't crash the discovery
663
+ const secondErrorMessage = secondError instanceof Error
664
+ ? secondError.message
665
+ : String(secondError);
666
+ const thirdErrorMessage = thirdError instanceof Error
667
+ ? thirdError.message
668
+ : String(thirdError);
669
+ autoDiscoveryLogger.warn(`Unable to repair JSON in ${filePath}. ` +
670
+ `Original: ${originalError}. After basic repair: ${secondErrorMessage}. ` +
671
+ `After aggressive repair: ${thirdErrorMessage}`);
672
+ // Return empty object so discovery can continue
673
+ return {};
674
+ }
675
+ }
676
+ }
677
+ }
678
+ /**
679
+ * Remove duplicate servers and apply prioritization
680
+ */
681
+ deduplicateServers(servers, preferredTools) {
682
+ const serverMap = new Map();
683
+ // Sort by priority (higher priority first)
684
+ const sortedServers = servers.sort((a, b) => {
685
+ // Preferred tools get highest priority
686
+ const aPreferred = preferredTools.includes(a.source.tool);
687
+ const bPreferred = preferredTools.includes(b.source.tool);
688
+ if (aPreferred && !bPreferred) {
689
+ return -1;
690
+ }
691
+ if (!aPreferred && bPreferred) {
692
+ return 1;
693
+ }
694
+ // Then by source priority
695
+ return b.source.priority - a.source.priority;
696
+ });
697
+ // Keep the highest priority version of each server
698
+ for (const server of sortedServers) {
699
+ const key = server.id;
700
+ if (!serverMap.has(key)) {
701
+ serverMap.set(key, server);
702
+ }
703
+ }
704
+ return Array.from(serverMap.values());
705
+ }
706
+ /**
707
+ * Map tool names to valid MCP server categories
708
+ */
709
+ mapToolToCategory(toolName) {
710
+ const normalizedTool = toolName.toLowerCase().replace(/\s+/g, "-");
711
+ // Map known tools to appropriate categories
712
+ const categoryMap = {
713
+ "claude-desktop": "ai-providers",
714
+ "vs-code": "development",
715
+ cursor: "development",
716
+ windsurf: "development",
717
+ "roo-code": "development",
718
+ generic: "integrations",
719
+ };
720
+ return categoryMap[normalizedTool] || "custom";
721
+ }
722
+ /**
723
+ * Convert discovered server to NeuroLink MCP server format
724
+ */
725
+ async convertToMCPServer(server) {
726
+ // This is a simplified conversion
727
+ // In a real implementation, you might want to actually spawn the server
728
+ // and discover its tools via MCP protocol
729
+ const mcpServer = {
730
+ id: server.id,
731
+ title: server.title,
732
+ category: this.mapToolToCategory(server.source.tool),
733
+ tools: {
734
+ // Placeholder - would be populated by actual MCP discovery
735
+ [`${server.id}-placeholder`]: {
736
+ name: `${server.id}-placeholder`,
737
+ description: `Placeholder tool for discovered server ${server.id}`,
738
+ category: "discovered",
739
+ isImplemented: false,
740
+ execute: async (params, context) => {
741
+ return {
742
+ success: false,
743
+ error: "This is a placeholder tool. Actual server discovery not yet implemented.",
744
+ };
745
+ },
746
+ },
747
+ },
748
+ registerTool(tool) {
749
+ // Check for duplicate tool names
750
+ if (this.tools[tool.name]) {
751
+ throw new Error(`Tool '${tool.name}' already exists in discovered server '${this.id}'`);
752
+ }
753
+ // Register the tool
754
+ this.tools[tool.name] = {
755
+ ...tool,
756
+ // Add server metadata to tool
757
+ metadata: {
758
+ ...tool.metadata,
759
+ serverId: this.id,
760
+ serverCategory: this.category,
761
+ registeredAt: Date.now(),
762
+ },
763
+ };
764
+ return this;
765
+ },
766
+ metadata: {
767
+ source: server.source.tool,
768
+ configPath: server.configPath,
769
+ command: server.command,
770
+ args: server.args,
771
+ env: server.env,
772
+ cwd: server.cwd,
773
+ discoveredAt: Date.now(),
774
+ },
775
+ };
776
+ return mcpServer;
777
+ }
778
+ }
779
+ /**
780
+ * Default auto-discovery instance
781
+ */
782
+ export const defaultAutoDiscovery = new MCPAutoDiscovery();
783
+ /**
784
+ * Utility function to discover servers with default instance
785
+ */
786
+ export async function discoverMCPServers(options) {
787
+ return defaultAutoDiscovery.discoverServers(options);
788
+ }
789
+ /**
790
+ * Utility function to auto-register discovered servers
791
+ */
792
+ export async function autoRegisterMCPServers(registry, options) {
793
+ return defaultAutoDiscovery.autoRegisterServers(registry, options);
794
+ }