@juspay/neurolink 1.10.0 → 1.11.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 (144) hide show
  1. package/CHANGELOG.md +43 -33
  2. package/README.md +16 -0
  3. package/dist/agent/direct-tools.d.ts +9 -9
  4. package/dist/cli/commands/agent-generate.d.ts +1 -2
  5. package/dist/cli/commands/agent-generate.js +5 -8
  6. package/dist/cli/commands/config.d.ts +2 -2
  7. package/dist/cli/commands/config.js +1 -1
  8. package/dist/cli/commands/mcp.js +91 -100
  9. package/dist/cli/commands/ollama.d.ts +2 -7
  10. package/dist/cli/commands/ollama.js +5 -8
  11. package/dist/cli/index.js +185 -276
  12. package/dist/core/factory.js +9 -10
  13. package/dist/index.d.ts +23 -0
  14. package/dist/index.js +35 -0
  15. package/dist/lib/agent/direct-tools.d.ts +9 -9
  16. package/dist/lib/core/factory.js +9 -10
  17. package/dist/lib/index.d.ts +23 -0
  18. package/dist/lib/index.js +35 -0
  19. package/dist/lib/mcp/adapters/plugin-bridge.d.ts +39 -0
  20. package/dist/lib/mcp/adapters/plugin-bridge.js +82 -0
  21. package/dist/lib/mcp/auto-discovery.d.ts +38 -96
  22. package/dist/lib/mcp/auto-discovery.js +100 -744
  23. package/dist/lib/mcp/client.js +4 -4
  24. package/dist/lib/mcp/context-manager.js +72 -1
  25. package/dist/lib/mcp/contracts/mcp-contract.d.ts +162 -0
  26. package/dist/lib/mcp/contracts/mcp-contract.js +58 -0
  27. package/dist/lib/mcp/core/plugin-manager.d.ts +45 -0
  28. package/dist/lib/mcp/core/plugin-manager.js +110 -0
  29. package/dist/lib/mcp/demo/plugin-demo.d.ts +20 -0
  30. package/dist/lib/mcp/demo/plugin-demo.js +116 -0
  31. package/dist/lib/mcp/ecosystem.d.ts +75 -0
  32. package/dist/lib/mcp/ecosystem.js +161 -0
  33. package/dist/lib/mcp/external-client.d.ts +88 -0
  34. package/dist/lib/mcp/external-client.js +323 -0
  35. package/dist/lib/mcp/external-manager.d.ts +112 -0
  36. package/dist/lib/mcp/external-manager.js +302 -0
  37. package/dist/lib/mcp/factory.d.ts +4 -4
  38. package/dist/lib/mcp/function-calling.js +59 -34
  39. package/dist/lib/mcp/index.d.ts +39 -184
  40. package/dist/lib/mcp/index.js +72 -150
  41. package/dist/lib/mcp/initialize.js +5 -5
  42. package/dist/lib/mcp/logging.d.ts +27 -60
  43. package/dist/lib/mcp/logging.js +77 -165
  44. package/dist/lib/mcp/neurolink-mcp-client.js +31 -3
  45. package/dist/lib/mcp/orchestrator.d.ts +1 -1
  46. package/dist/lib/mcp/orchestrator.js +13 -12
  47. package/dist/lib/mcp/plugin-manager.d.ts +98 -0
  48. package/dist/lib/mcp/plugin-manager.js +294 -0
  49. package/dist/lib/mcp/plugins/core/filesystem-mcp.d.ts +35 -0
  50. package/dist/lib/mcp/plugins/core/filesystem-mcp.js +139 -0
  51. package/dist/lib/mcp/plugins/filesystem-mcp.d.ts +36 -0
  52. package/dist/lib/mcp/plugins/filesystem-mcp.js +54 -0
  53. package/dist/lib/mcp/registry.d.ts +27 -176
  54. package/dist/lib/mcp/registry.js +31 -372
  55. package/dist/lib/mcp/security-manager.d.ts +85 -0
  56. package/dist/lib/mcp/security-manager.js +344 -0
  57. package/dist/lib/mcp/servers/ai-providers/ai-workflow-tools.d.ts +2 -2
  58. package/dist/lib/mcp/tool-integration.d.ts +4 -14
  59. package/dist/lib/mcp/tool-integration.js +43 -21
  60. package/dist/lib/mcp/tool-registry.d.ts +66 -0
  61. package/dist/lib/mcp/tool-registry.js +160 -0
  62. package/dist/lib/mcp/unified-mcp.d.ts +123 -0
  63. package/dist/lib/mcp/unified-mcp.js +246 -0
  64. package/dist/lib/mcp/unified-registry.d.ts +42 -229
  65. package/dist/lib/mcp/unified-registry.js +96 -1346
  66. package/dist/lib/neurolink.d.ts +3 -4
  67. package/dist/lib/neurolink.js +17 -18
  68. package/dist/lib/providers/agent-enhanced-provider.js +2 -2
  69. package/dist/lib/providers/amazonBedrock.js +2 -2
  70. package/dist/lib/providers/anthropic.js +3 -3
  71. package/dist/lib/providers/azureOpenAI.js +3 -3
  72. package/dist/lib/providers/function-calling-provider.js +34 -25
  73. package/dist/lib/providers/googleAIStudio.js +9 -3
  74. package/dist/lib/providers/googleVertexAI.js +2 -2
  75. package/dist/lib/providers/huggingFace.js +2 -2
  76. package/dist/lib/providers/mcp-provider.js +33 -5
  77. package/dist/lib/providers/mistralAI.js +2 -2
  78. package/dist/lib/providers/ollama.js +2 -2
  79. package/dist/lib/providers/openAI.js +2 -2
  80. package/dist/lib/utils/providerUtils-fixed.js +9 -9
  81. package/dist/mcp/adapters/plugin-bridge.d.ts +39 -0
  82. package/dist/mcp/adapters/plugin-bridge.js +82 -0
  83. package/dist/mcp/auto-discovery.d.ts +38 -96
  84. package/dist/mcp/auto-discovery.js +100 -745
  85. package/dist/mcp/client.js +4 -4
  86. package/dist/mcp/context-manager.js +72 -1
  87. package/dist/mcp/contracts/mcp-contract.d.ts +162 -0
  88. package/dist/mcp/contracts/mcp-contract.js +58 -0
  89. package/dist/mcp/core/plugin-manager.d.ts +45 -0
  90. package/dist/mcp/core/plugin-manager.js +110 -0
  91. package/dist/mcp/demo/plugin-demo.d.ts +20 -0
  92. package/dist/mcp/demo/plugin-demo.js +116 -0
  93. package/dist/mcp/ecosystem.d.ts +75 -0
  94. package/dist/mcp/ecosystem.js +162 -0
  95. package/dist/mcp/external-client.d.ts +88 -0
  96. package/dist/mcp/external-client.js +323 -0
  97. package/dist/mcp/external-manager.d.ts +112 -0
  98. package/dist/mcp/external-manager.js +302 -0
  99. package/dist/mcp/factory.d.ts +4 -4
  100. package/dist/mcp/function-calling.js +59 -34
  101. package/dist/mcp/index.d.ts +39 -184
  102. package/dist/mcp/index.js +72 -150
  103. package/dist/mcp/initialize.js +5 -5
  104. package/dist/mcp/logging.d.ts +27 -60
  105. package/dist/mcp/logging.js +77 -165
  106. package/dist/mcp/neurolink-mcp-client.js +31 -3
  107. package/dist/mcp/orchestrator.d.ts +1 -1
  108. package/dist/mcp/orchestrator.js +13 -12
  109. package/dist/mcp/plugin-manager.d.ts +98 -0
  110. package/dist/mcp/plugin-manager.js +295 -0
  111. package/dist/mcp/plugins/core/filesystem-mcp.d.ts +35 -0
  112. package/dist/mcp/plugins/core/filesystem-mcp.js +139 -0
  113. package/dist/mcp/plugins/core/neurolink-mcp.json +17 -0
  114. package/dist/mcp/plugins/filesystem-mcp.d.ts +36 -0
  115. package/dist/mcp/plugins/filesystem-mcp.js +54 -0
  116. package/dist/mcp/registry.d.ts +27 -176
  117. package/dist/mcp/registry.js +31 -372
  118. package/dist/mcp/security-manager.d.ts +85 -0
  119. package/dist/mcp/security-manager.js +344 -0
  120. package/dist/mcp/servers/ai-providers/ai-workflow-tools.d.ts +2 -2
  121. package/dist/mcp/tool-integration.d.ts +4 -14
  122. package/dist/mcp/tool-integration.js +43 -21
  123. package/dist/mcp/tool-registry.d.ts +66 -0
  124. package/dist/mcp/tool-registry.js +160 -0
  125. package/dist/mcp/unified-mcp.d.ts +123 -0
  126. package/dist/mcp/unified-mcp.js +246 -0
  127. package/dist/mcp/unified-registry.d.ts +42 -229
  128. package/dist/mcp/unified-registry.js +96 -1345
  129. package/dist/neurolink.d.ts +3 -4
  130. package/dist/neurolink.js +17 -18
  131. package/dist/providers/agent-enhanced-provider.js +2 -2
  132. package/dist/providers/amazonBedrock.js +2 -2
  133. package/dist/providers/anthropic.js +3 -3
  134. package/dist/providers/azureOpenAI.js +3 -3
  135. package/dist/providers/function-calling-provider.js +34 -25
  136. package/dist/providers/googleAIStudio.js +9 -3
  137. package/dist/providers/googleVertexAI.js +2 -2
  138. package/dist/providers/huggingFace.js +2 -2
  139. package/dist/providers/mcp-provider.js +33 -5
  140. package/dist/providers/mistralAI.js +2 -2
  141. package/dist/providers/ollama.js +2 -2
  142. package/dist/providers/openAI.js +2 -2
  143. package/dist/utils/providerUtils-fixed.js +9 -9
  144. package/package.json +1 -1
@@ -1,794 +1,149 @@
1
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
2
+ * Auto-Discovery Module for MCP Plugins
3
+ * Handles automatic plugin discovery and registration
5
4
  */
6
5
  import * as fs from "fs/promises";
7
6
  import * as path from "path";
8
- import * as os from "os";
9
- import { MCPToolRegistry } from "./registry.js";
10
7
  import { autoDiscoveryLogger } from "./logging.js";
11
8
  /**
12
- * Claude Desktop configuration parser
9
+ * Default auto-discovery configuration
13
10
  */
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
- }
11
+ const DEFAULT_CONFIG = {
12
+ searchPaths: ["./src/lib/mcp/plugins", "./neurolink-mcp", "./node_modules"],
13
+ manifestName: "neurolink-mcp.json",
14
+ maxDepth: 3,
15
+ };
259
16
  /**
260
- * Generic MCP configuration parser
17
+ * Auto-discovery service for MCP plugins
261
18
  */
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");
19
+ export class AutoDiscovery {
20
+ config;
21
+ constructor(config = {}) {
22
+ this.config = { ...DEFAULT_CONFIG, ...config };
269
23
  }
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
24
  /**
307
- * Discover MCP servers from all common locations
308
- *
309
- * @param options Discovery configuration options
310
- * @returns Discovery result with found servers
25
+ * Discover all available MCP plugins
311
26
  */
312
- async discoverServers(options = {}) {
313
- const startTime = Date.now();
27
+ async discover() {
314
28
  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) {
29
+ for (const searchPath of this.config.searchPaths) {
373
30
  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}`);
31
+ const plugins = await this.discoverInPath(searchPath);
32
+ discovered.push(...plugins);
380
33
  }
381
34
  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}`);
35
+ autoDiscoveryLogger.debug(`Failed to discover in ${searchPath}:`, error);
386
36
  }
387
37
  }
388
- return { registered, failed, errors };
38
+ autoDiscoveryLogger.info(`Auto-discovery completed: ${discovered.length} plugins found`);
39
+ return discovered;
389
40
  }
390
41
  /**
391
- * Get standard search paths for MCP configurations
42
+ * Discover plugins in a specific path
392
43
  */
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
- });
44
+ async discoverInPath(basePath, depth = 0) {
45
+ if (depth > this.config.maxDepth) {
46
+ return [];
519
47
  }
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;
48
+ const discovered = [];
548
49
  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;
50
+ const items = await fs.readdir(basePath);
51
+ for (const item of items) {
52
+ const itemPath = path.join(basePath, item);
53
+ const stats = await fs.stat(itemPath);
54
+ if (stats.isDirectory()) {
55
+ // Check for manifest in this directory
56
+ const manifestPath = path.join(itemPath, this.config.manifestName);
57
+ try {
58
+ await fs.access(manifestPath);
59
+ const plugin = await this.loadPlugin(manifestPath, itemPath);
60
+ if (plugin) {
61
+ discovered.push(plugin);
62
+ }
63
+ }
64
+ catch {
65
+ // No manifest, continue searching subdirectories
66
+ const subPlugins = await this.discoverInPath(itemPath, depth + 1);
67
+ discovered.push(...subPlugins);
579
68
  }
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
69
  }
592
70
  }
593
71
  }
594
- return { servers, sources, configFilesFound, errors };
72
+ catch (error) {
73
+ autoDiscoveryLogger.debug(`Failed to scan ${basePath}:`, error);
74
+ }
75
+ return discovered;
595
76
  }
596
77
  /**
597
- * Parse JSON with resilience to common syntax issues
78
+ * Load plugin from manifest
598
79
  */
599
- parseJsonResilient(content, filePath) {
80
+ async loadPlugin(manifestPath, pluginPath) {
600
81
  try {
601
- // First, try standard JSON parsing
602
- return JSON.parse(content);
82
+ const manifestContent = await fs.readFile(manifestPath, "utf-8");
83
+ const metadata = JSON.parse(manifestContent);
84
+ // Validate basic metadata
85
+ if (!metadata.name || !metadata.version || !metadata.main) {
86
+ autoDiscoveryLogger.warn(`Invalid manifest at ${manifestPath}`);
87
+ return null;
88
+ }
89
+ const entryPath = path.resolve(pluginPath, metadata.main);
90
+ return {
91
+ metadata,
92
+ entryPath,
93
+ source: this.determineSource(pluginPath),
94
+ constructor: undefined,
95
+ };
603
96
  }
604
97
  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
- }
98
+ autoDiscoveryLogger.error(`Failed to load plugin manifest at ${manifestPath}:`, error);
99
+ return null;
676
100
  }
677
101
  }
678
102
  /**
679
- * Remove duplicate servers and apply prioritization
103
+ * Determine plugin source based on path
680
104
  */
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
- }
105
+ determineSource(pluginPath) {
106
+ if (pluginPath.includes("node_modules")) {
107
+ return "installed";
703
108
  }
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;
109
+ if (pluginPath.includes("src/lib/mcp/plugins")) {
110
+ return "core";
111
+ }
112
+ return "project";
777
113
  }
778
114
  }
779
115
  /**
780
116
  * Default auto-discovery instance
781
117
  */
782
- export const defaultAutoDiscovery = new MCPAutoDiscovery();
118
+ export const autoDiscovery = new AutoDiscovery();
783
119
  /**
784
- * Utility function to discover servers with default instance
120
+ * Discover MCP servers using auto-discovery
785
121
  */
786
- export async function discoverMCPServers(options) {
787
- return defaultAutoDiscovery.discoverServers(options);
122
+ export async function discoverMCPServers(options = {}) {
123
+ const discovery = new AutoDiscovery(options);
124
+ return discovery.discover();
788
125
  }
789
126
  /**
790
- * Utility function to auto-register discovered servers
127
+ * Auto-register discovered MCP servers
791
128
  */
792
- export async function autoRegisterMCPServers(registry, options) {
793
- return defaultAutoDiscovery.autoRegisterServers(registry, options);
129
+ export async function autoRegisterMCPServers(options = {}) {
130
+ const discovered = await discoverMCPServers(options);
131
+ let registered = 0;
132
+ let failed = 0;
133
+ for (const plugin of discovered) {
134
+ try {
135
+ // Registration logic would go here
136
+ autoDiscoveryLogger.info(`Registered plugin: ${plugin.metadata.name}`);
137
+ registered++;
138
+ }
139
+ catch (error) {
140
+ autoDiscoveryLogger.error(`Failed to register ${plugin.metadata.name}:`, error);
141
+ failed++;
142
+ }
143
+ }
144
+ return {
145
+ registered,
146
+ failed,
147
+ plugins: discovered,
148
+ };
794
149
  }