@juspay/neurolink 7.1.0 → 7.2.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 (92) hide show
  1. package/CHANGELOG.md +8 -2
  2. package/README.md +16 -11
  3. package/dist/cli/commands/config.d.ts +2 -2
  4. package/dist/cli/commands/config.js +22 -21
  5. package/dist/cli/commands/mcp.d.ts +79 -0
  6. package/dist/cli/commands/mcp.js +916 -0
  7. package/dist/cli/commands/models.d.ts +63 -0
  8. package/dist/cli/commands/models.js +653 -0
  9. package/dist/cli/commands/ollama.js +56 -55
  10. package/dist/cli/factories/commandFactory.d.ts +14 -0
  11. package/dist/cli/factories/commandFactory.js +346 -47
  12. package/dist/cli/index.js +25 -10
  13. package/dist/cli/utils/completeSetup.js +9 -8
  14. package/dist/cli/utils/envManager.js +7 -6
  15. package/dist/cli/utils/interactiveSetup.js +20 -19
  16. package/dist/core/analytics.js +25 -38
  17. package/dist/core/baseProvider.d.ts +8 -0
  18. package/dist/core/baseProvider.js +177 -68
  19. package/dist/core/constants.d.ts +11 -0
  20. package/dist/core/constants.js +17 -0
  21. package/dist/core/evaluation.js +25 -14
  22. package/dist/core/factory.js +19 -18
  23. package/dist/core/streamAnalytics.d.ts +65 -0
  24. package/dist/core/streamAnalytics.js +125 -0
  25. package/dist/lib/core/analytics.js +25 -38
  26. package/dist/lib/core/baseProvider.d.ts +8 -0
  27. package/dist/lib/core/baseProvider.js +177 -68
  28. package/dist/lib/core/constants.d.ts +11 -0
  29. package/dist/lib/core/constants.js +17 -0
  30. package/dist/lib/core/evaluation.js +25 -14
  31. package/dist/lib/core/factory.js +19 -18
  32. package/dist/lib/core/streamAnalytics.d.ts +65 -0
  33. package/dist/lib/core/streamAnalytics.js +125 -0
  34. package/dist/lib/models/modelRegistry.d.ts +132 -0
  35. package/dist/lib/models/modelRegistry.js +483 -0
  36. package/dist/lib/models/modelResolver.d.ts +115 -0
  37. package/dist/lib/models/modelResolver.js +467 -0
  38. package/dist/lib/neurolink.d.ts +4 -1
  39. package/dist/lib/neurolink.js +101 -67
  40. package/dist/lib/providers/anthropic.js +3 -0
  41. package/dist/lib/providers/googleAiStudio.js +13 -0
  42. package/dist/lib/providers/huggingFace.js +15 -3
  43. package/dist/lib/providers/mistral.js +19 -7
  44. package/dist/lib/providers/ollama.js +31 -7
  45. package/dist/lib/providers/openAI.js +12 -0
  46. package/dist/lib/sdk/toolRegistration.js +2 -2
  47. package/dist/lib/types/cli.d.ts +56 -1
  48. package/dist/lib/types/contextTypes.d.ts +110 -0
  49. package/dist/lib/types/contextTypes.js +176 -0
  50. package/dist/lib/types/index.d.ts +4 -1
  51. package/dist/lib/types/mcpTypes.d.ts +118 -7
  52. package/dist/lib/types/providers.d.ts +81 -0
  53. package/dist/lib/types/streamTypes.d.ts +44 -7
  54. package/dist/lib/types/tools.d.ts +9 -0
  55. package/dist/lib/types/universalProviderOptions.d.ts +3 -1
  56. package/dist/lib/types/universalProviderOptions.js +2 -1
  57. package/dist/lib/utils/logger.d.ts +7 -0
  58. package/dist/lib/utils/logger.js +11 -0
  59. package/dist/lib/utils/performance.d.ts +105 -0
  60. package/dist/lib/utils/performance.js +210 -0
  61. package/dist/lib/utils/retryHandler.d.ts +89 -0
  62. package/dist/lib/utils/retryHandler.js +269 -0
  63. package/dist/models/modelRegistry.d.ts +132 -0
  64. package/dist/models/modelRegistry.js +483 -0
  65. package/dist/models/modelResolver.d.ts +115 -0
  66. package/dist/models/modelResolver.js +468 -0
  67. package/dist/neurolink.d.ts +4 -1
  68. package/dist/neurolink.js +101 -67
  69. package/dist/providers/anthropic.js +3 -0
  70. package/dist/providers/googleAiStudio.js +13 -0
  71. package/dist/providers/huggingFace.js +15 -3
  72. package/dist/providers/mistral.js +19 -7
  73. package/dist/providers/ollama.js +31 -7
  74. package/dist/providers/openAI.js +12 -0
  75. package/dist/sdk/toolRegistration.js +2 -2
  76. package/dist/types/cli.d.ts +56 -1
  77. package/dist/types/contextTypes.d.ts +110 -0
  78. package/dist/types/contextTypes.js +177 -0
  79. package/dist/types/index.d.ts +4 -1
  80. package/dist/types/mcpTypes.d.ts +118 -7
  81. package/dist/types/providers.d.ts +81 -0
  82. package/dist/types/streamTypes.d.ts +44 -7
  83. package/dist/types/tools.d.ts +9 -0
  84. package/dist/types/universalProviderOptions.d.ts +3 -1
  85. package/dist/types/universalProviderOptions.js +3 -1
  86. package/dist/utils/logger.d.ts +7 -0
  87. package/dist/utils/logger.js +11 -0
  88. package/dist/utils/performance.d.ts +105 -0
  89. package/dist/utils/performance.js +210 -0
  90. package/dist/utils/retryHandler.d.ts +89 -0
  91. package/dist/utils/retryHandler.js +269 -0
  92. package/package.json +2 -1
@@ -0,0 +1,916 @@
1
+ /**
2
+ * MCP CLI Commands for NeuroLink
3
+ * Implements comprehensive MCP server management commands
4
+ * Part of Phase 4.2 - MCP CLI Commands
5
+ */
6
+ import { NeuroLink } from "../../lib/neurolink.js";
7
+ import { logger } from "../../lib/utils/logger.js";
8
+ import chalk from "chalk";
9
+ import ora from "ora";
10
+ import fs from "fs";
11
+ import path from "path";
12
+ /**
13
+ * Popular MCP servers registry
14
+ */
15
+ const POPULAR_MCP_SERVERS = {
16
+ filesystem: {
17
+ command: "npx",
18
+ args: [
19
+ "-y",
20
+ "@modelcontextprotocol/server-filesystem",
21
+ "/path/to/allowed/files",
22
+ ],
23
+ transport: "stdio",
24
+ description: "File system operations (read, write, create, list directories)",
25
+ },
26
+ github: {
27
+ command: "npx",
28
+ args: ["-y", "@modelcontextprotocol/server-github"],
29
+ env: { GITHUB_PERSONAL_ACCESS_TOKEN: "${GITHUB_PERSONAL_ACCESS_TOKEN}" },
30
+ transport: "stdio",
31
+ description: "GitHub repository management and file operations",
32
+ },
33
+ postgres: {
34
+ command: "npx",
35
+ args: ["-y", "@modelcontextprotocol/server-postgres"],
36
+ env: { DATABASE_URL: "${DATABASE_URL}" },
37
+ transport: "stdio",
38
+ description: "PostgreSQL database query and management",
39
+ },
40
+ sqlite: {
41
+ command: "npx",
42
+ args: ["-y", "@modelcontextprotocol/server-sqlite", "/path/to/database.db"],
43
+ transport: "stdio",
44
+ description: "SQLite database operations and queries",
45
+ },
46
+ brave: {
47
+ command: "npx",
48
+ args: ["-y", "@modelcontextprotocol/server-brave-search"],
49
+ env: { BRAVE_API_KEY: "${BRAVE_API_KEY}" },
50
+ transport: "stdio",
51
+ description: "Brave Search API for web search capabilities",
52
+ },
53
+ puppeteer: {
54
+ command: "npx",
55
+ args: ["-y", "@modelcontextprotocol/server-puppeteer"],
56
+ transport: "stdio",
57
+ description: "Web scraping and browser automation",
58
+ },
59
+ git: {
60
+ command: "npx",
61
+ args: ["-y", "@modelcontextprotocol/server-git"],
62
+ transport: "stdio",
63
+ description: "Git repository operations and version control",
64
+ },
65
+ memory: {
66
+ command: "npx",
67
+ args: ["-y", "@modelcontextprotocol/server-memory"],
68
+ transport: "stdio",
69
+ description: "Persistent memory and knowledge storage",
70
+ },
71
+ };
72
+ /**
73
+ * Convert SDK MCPStatus to CLI format with server list
74
+ */
75
+ function convertMCPStatusForCLI(status, // Use flexible object type
76
+ sdk) {
77
+ // Create server list from in-memory servers and discovered servers
78
+ const servers = [];
79
+ // Add in-memory servers
80
+ const inMemoryServers = sdk.getInMemoryServers();
81
+ inMemoryServers.forEach((config, name) => {
82
+ servers.push({
83
+ name,
84
+ connected: true, // In-memory servers are always "connected"
85
+ description: config.server?.title || "In-memory MCP server",
86
+ tools: [], // Could extract from config.server.tools if needed
87
+ });
88
+ });
89
+ // Add auto-discovered servers
90
+ if (status.autoDiscoveredServers &&
91
+ Array.isArray(status.autoDiscoveredServers)) {
92
+ status.autoDiscoveredServers.forEach((server) => {
93
+ servers.push({
94
+ name: server.name || server.id,
95
+ connected: server.status === "connected",
96
+ description: server.source
97
+ ? `Auto-discovered from ${server.source}`
98
+ : `Auto-discovered server`,
99
+ tools: [],
100
+ error: server.status === "failed"
101
+ ? "Connection failed"
102
+ : undefined,
103
+ });
104
+ });
105
+ }
106
+ return {
107
+ mcpInitialized: status.mcpInitialized,
108
+ totalServers: status.totalServers,
109
+ availableServers: status.availableServers,
110
+ autoDiscoveredCount: status.autoDiscoveredCount,
111
+ totalTools: status.totalTools,
112
+ customToolsCount: status.customToolsCount,
113
+ inMemoryServersCount: status.inMemoryServersCount,
114
+ error: status.error,
115
+ servers,
116
+ };
117
+ }
118
+ /**
119
+ * MCP CLI command factory
120
+ */
121
+ export class MCPCommandFactory {
122
+ /**
123
+ * Create the main MCP command with subcommands
124
+ */
125
+ static createMCPCommands() {
126
+ return {
127
+ command: "mcp <subcommand>",
128
+ describe: "Manage Model Context Protocol (MCP) servers",
129
+ builder: (yargs) => {
130
+ return yargs
131
+ .command("list", "List configured MCP servers with status", (yargs) => this.buildListOptions(yargs), (argv) => this.executeList(argv))
132
+ .command("install <server>", "Install popular MCP servers", (yargs) => this.buildInstallOptions(yargs), (argv) => this.executeInstall(argv))
133
+ .command("add <name> <command>", "Add custom MCP server configuration", (yargs) => this.buildAddOptions(yargs), (argv) => this.executeAdd(argv))
134
+ .command("test [server]", "Test connectivity to MCP servers", (yargs) => this.buildTestOptions(yargs), (argv) => this.executeTest(argv))
135
+ .command("exec <server> <tool>", "Execute tools from MCP servers", (yargs) => this.buildExecOptions(yargs), (argv) => this.executeExec(argv))
136
+ .command("remove <server>", "Remove MCP server configuration", (yargs) => this.buildRemoveOptions(yargs), (argv) => this.executeRemove(argv))
137
+ .option("format", {
138
+ choices: ["table", "json", "compact"],
139
+ default: "table",
140
+ description: "Output format",
141
+ })
142
+ .option("output", {
143
+ type: "string",
144
+ description: "Save output to file",
145
+ })
146
+ .option("quiet", {
147
+ type: "boolean",
148
+ alias: "q",
149
+ default: false,
150
+ description: "Suppress non-essential output",
151
+ })
152
+ .option("debug", {
153
+ type: "boolean",
154
+ default: false,
155
+ description: "Enable debug output",
156
+ })
157
+ .demandCommand(1, "Please specify an MCP subcommand")
158
+ .help();
159
+ },
160
+ handler: () => {
161
+ // No-op handler as subcommands handle everything
162
+ },
163
+ };
164
+ }
165
+ /**
166
+ * Create discover command (top-level command)
167
+ */
168
+ static createDiscoverCommand() {
169
+ return {
170
+ command: "discover",
171
+ describe: "Auto-discover MCP servers from various sources",
172
+ builder: (yargs) => {
173
+ return yargs
174
+ .option("auto-install", {
175
+ type: "boolean",
176
+ default: false,
177
+ description: "Automatically install discovered servers",
178
+ })
179
+ .option("source", {
180
+ choices: ["claude-desktop", "vscode", "all"],
181
+ default: "all",
182
+ description: "Source to discover servers from",
183
+ })
184
+ .option("format", {
185
+ choices: ["table", "json", "compact"],
186
+ default: "table",
187
+ description: "Output format",
188
+ })
189
+ .option("quiet", {
190
+ type: "boolean",
191
+ alias: "q",
192
+ default: false,
193
+ description: "Suppress non-essential output",
194
+ })
195
+ .example("neurolink discover", "Discover MCP servers from all sources")
196
+ .example("neurolink discover --source claude-desktop", "Discover from Claude Desktop only")
197
+ .example("neurolink discover --auto-install", "Discover and auto-install servers");
198
+ },
199
+ handler: async (argv) => await MCPCommandFactory.executeDiscover(argv),
200
+ };
201
+ }
202
+ /**
203
+ * Build options for list command
204
+ */
205
+ static buildListOptions(yargs) {
206
+ return yargs
207
+ .option("status", {
208
+ type: "boolean",
209
+ default: false,
210
+ description: "Check server connection status",
211
+ })
212
+ .option("detailed", {
213
+ type: "boolean",
214
+ default: false,
215
+ description: "Show detailed server information",
216
+ })
217
+ .example("neurolink mcp list", "List all configured MCP servers")
218
+ .example("neurolink mcp list --status", "List servers with connection status")
219
+ .example("neurolink mcp list --detailed", "Show detailed server information");
220
+ }
221
+ /**
222
+ * Build options for install command
223
+ */
224
+ static buildInstallOptions(yargs) {
225
+ return yargs
226
+ .positional("server", {
227
+ type: "string",
228
+ description: "Server name to install from popular registry",
229
+ choices: Object.keys(POPULAR_MCP_SERVERS),
230
+ demandOption: true,
231
+ })
232
+ .option("transport", {
233
+ choices: ["stdio", "sse", "ws"],
234
+ default: "stdio",
235
+ description: "Transport type for MCP communication",
236
+ })
237
+ .option("args", {
238
+ type: "array",
239
+ description: "Additional arguments for the server command",
240
+ })
241
+ .option("env", {
242
+ type: "string",
243
+ description: "Environment variables as JSON string",
244
+ })
245
+ .example("neurolink mcp install filesystem", "Install filesystem MCP server")
246
+ .example("neurolink mcp install github", "Install GitHub MCP server")
247
+ .example("neurolink mcp install postgres", "Install PostgreSQL MCP server");
248
+ }
249
+ /**
250
+ * Build options for add command
251
+ */
252
+ static buildAddOptions(yargs) {
253
+ return yargs
254
+ .positional("name", {
255
+ type: "string",
256
+ description: "Name for the custom MCP server",
257
+ demandOption: true,
258
+ })
259
+ .positional("command", {
260
+ type: "string",
261
+ description: "Command to execute the MCP server",
262
+ demandOption: true,
263
+ })
264
+ .option("transport", {
265
+ choices: ["stdio", "sse", "ws"],
266
+ default: "stdio",
267
+ description: "Transport type for MCP communication",
268
+ })
269
+ .option("args", {
270
+ type: "array",
271
+ description: "Arguments for the server command",
272
+ })
273
+ .option("env", {
274
+ type: "string",
275
+ description: "Environment variables as JSON string",
276
+ })
277
+ .example("neurolink mcp add my-server node", "Add custom Node.js MCP server")
278
+ .example("neurolink mcp add api-server python", "Add custom Python MCP server");
279
+ }
280
+ /**
281
+ * Build options for test command
282
+ */
283
+ static buildTestOptions(yargs) {
284
+ return yargs
285
+ .positional("server", {
286
+ type: "string",
287
+ description: "Server name to test (optional - tests all if not specified)",
288
+ })
289
+ .option("timeout", {
290
+ type: "number",
291
+ default: 10000,
292
+ description: "Test timeout in milliseconds",
293
+ })
294
+ .example("neurolink mcp test", "Test all configured servers")
295
+ .example("neurolink mcp test filesystem", "Test specific server")
296
+ .example("neurolink mcp test --timeout 5000", "Test with custom timeout");
297
+ }
298
+ /**
299
+ * Build options for exec command
300
+ */
301
+ static buildExecOptions(yargs) {
302
+ return yargs
303
+ .positional("server", {
304
+ type: "string",
305
+ description: "MCP server name",
306
+ demandOption: true,
307
+ })
308
+ .positional("tool", {
309
+ type: "string",
310
+ description: "Tool name to execute",
311
+ demandOption: true,
312
+ })
313
+ .option("params", {
314
+ type: "string",
315
+ description: "Tool parameters as JSON string",
316
+ })
317
+ .example("neurolink mcp exec filesystem read_file", "Execute read_file tool")
318
+ .example("neurolink mcp exec github list_repos", "Execute GitHub list_repos tool");
319
+ }
320
+ /**
321
+ * Build options for remove command
322
+ */
323
+ static buildRemoveOptions(yargs) {
324
+ return yargs
325
+ .positional("server", {
326
+ type: "string",
327
+ description: "Server name to remove",
328
+ demandOption: true,
329
+ })
330
+ .option("force", {
331
+ type: "boolean",
332
+ default: false,
333
+ description: "Force removal without confirmation",
334
+ })
335
+ .example("neurolink mcp remove filesystem", "Remove filesystem server")
336
+ .example("neurolink mcp remove old-server --force", "Force remove without confirmation");
337
+ }
338
+ /**
339
+ * Execute list command
340
+ */
341
+ static async executeList(argv) {
342
+ try {
343
+ const spinner = argv.quiet ? null : ora("Loading MCP servers...").start();
344
+ // Get configured servers from NeuroLink
345
+ const sdk = new NeuroLink();
346
+ const rawMcpStatus = await sdk.getMCPStatus();
347
+ const mcpStatus = convertMCPStatusForCLI(rawMcpStatus, sdk);
348
+ if (spinner) {
349
+ spinner.succeed(`Found ${mcpStatus.servers.length} MCP servers`);
350
+ }
351
+ if (mcpStatus.servers.length === 0) {
352
+ logger.always(chalk.yellow("No MCP servers configured."));
353
+ logger.always(chalk.blue("šŸ’” Use 'neurolink mcp install <server>' to install popular servers"));
354
+ logger.always(chalk.blue("šŸ’” Use 'neurolink discover' to find existing servers"));
355
+ return;
356
+ }
357
+ // Format and display results
358
+ if (argv.format === "json") {
359
+ logger.always(JSON.stringify(mcpStatus, null, 2));
360
+ }
361
+ else if (argv.format && argv.format === "compact") {
362
+ mcpStatus.servers.forEach((server) => {
363
+ const status = server.connected ? chalk.green("āœ“") : chalk.red("āœ—");
364
+ logger.always(`${status} ${server.name} - ${server.description || "No description"}`);
365
+ });
366
+ }
367
+ else {
368
+ // Table format
369
+ logger.always(chalk.bold("\nšŸ”§ MCP Servers:\n"));
370
+ for (const server of mcpStatus.servers) {
371
+ const status = server.connected
372
+ ? chalk.green("CONNECTED")
373
+ : chalk.red("DISCONNECTED");
374
+ logger.always(`${chalk.cyan(server.name)} ${status}`);
375
+ logger.always(` Command: ${server.command || "Unknown"}`);
376
+ logger.always(` Tools: ${server.tools?.length || 0} available`);
377
+ if (argv.detailed && server.tools) {
378
+ server.tools.forEach((tool) => {
379
+ logger.always(` • ${tool.name}: ${tool.description}`);
380
+ });
381
+ }
382
+ if (server.error) {
383
+ logger.always(` ${chalk.red("Error:")} ${server.error}`);
384
+ }
385
+ logger.always();
386
+ }
387
+ }
388
+ }
389
+ catch (error) {
390
+ logger.error(chalk.red(`āŒ List command failed: ${error.message}`));
391
+ process.exit(1);
392
+ }
393
+ }
394
+ /**
395
+ * Execute install command
396
+ */
397
+ static async executeInstall(argv) {
398
+ try {
399
+ const serverName = argv.server;
400
+ const serverConfig = POPULAR_MCP_SERVERS[serverName];
401
+ if (!serverConfig) {
402
+ logger.error(chalk.red(`āŒ Unknown server: ${serverName}`));
403
+ logger.always(chalk.blue("Available servers:"));
404
+ Object.keys(POPULAR_MCP_SERVERS).forEach((name) => {
405
+ logger.always(` • ${name}: ${POPULAR_MCP_SERVERS[name].description}`);
406
+ });
407
+ process.exit(1);
408
+ }
409
+ const spinner = argv.quiet
410
+ ? null
411
+ : ora(`Installing ${serverName} MCP server...`).start();
412
+ // Parse environment variables if provided
413
+ let env = serverConfig.env;
414
+ if (argv.env) {
415
+ try {
416
+ const parsedEnv = JSON.parse(argv.env);
417
+ env = { ...env, ...parsedEnv };
418
+ }
419
+ catch (error) {
420
+ if (spinner) {
421
+ spinner.fail();
422
+ }
423
+ logger.error(chalk.red("āŒ Invalid JSON in env parameter"));
424
+ process.exit(1);
425
+ }
426
+ }
427
+ // Create server configuration
428
+ const config = {
429
+ name: serverName,
430
+ command: serverConfig.command,
431
+ args: argv.args || serverConfig.args,
432
+ env,
433
+ transport: (argv.transport &&
434
+ (argv.transport === "websocket"
435
+ ? "ws"
436
+ : argv.transport)) ||
437
+ serverConfig.transport,
438
+ description: serverConfig.description,
439
+ installed: true,
440
+ status: "unknown",
441
+ };
442
+ // Add server to NeuroLink (using in-memory MCP server for now)
443
+ const sdk = new NeuroLink();
444
+ await sdk.addInMemoryMCPServer(serverName, {
445
+ server: {
446
+ title: serverConfig.description,
447
+ tools: {}, // Empty tools for external servers
448
+ description: serverConfig.description,
449
+ },
450
+ metadata: {
451
+ command: config.command,
452
+ args: config.args,
453
+ env: config.env,
454
+ transport: config.transport,
455
+ },
456
+ });
457
+ if (spinner) {
458
+ spinner.succeed(chalk.green(`āœ… Successfully installed ${serverName} MCP server`));
459
+ }
460
+ // Display configuration info
461
+ logger.always(chalk.bold("\nšŸ“‹ Server Configuration:"));
462
+ logger.always(`Name: ${config.name}`);
463
+ logger.always(`Command: ${config.command}`);
464
+ if (config.args?.length) {
465
+ logger.always(`Args: ${config.args.join(" ")}`);
466
+ }
467
+ if (config.env) {
468
+ logger.always(`Environment: ${Object.keys(config.env).length} variables`);
469
+ }
470
+ logger.always(`Transport: ${config.transport}`);
471
+ logger.always(`Description: ${config.description}`);
472
+ // Test connection
473
+ logger.always(chalk.blue("\nšŸ” Testing connection..."));
474
+ try {
475
+ const rawStatus = await sdk.getMCPStatus();
476
+ const status = convertMCPStatusForCLI(rawStatus, sdk);
477
+ const installedServer = status.servers.find((s) => s.name === serverName);
478
+ if (installedServer?.connected) {
479
+ logger.always(chalk.green("āœ… Server connected successfully"));
480
+ if (installedServer.tools?.length) {
481
+ logger.always(`šŸ› ļø Available tools: ${installedServer.tools.length}`);
482
+ }
483
+ }
484
+ else {
485
+ logger.always(chalk.yellow("āš ļø Server installed but not connected"));
486
+ if (installedServer?.error) {
487
+ logger.always(chalk.red(`Error: ${installedServer.error}`));
488
+ }
489
+ }
490
+ }
491
+ catch (testError) {
492
+ logger.always(chalk.yellow("āš ļø Could not test connection"));
493
+ }
494
+ }
495
+ catch (error) {
496
+ logger.error(chalk.red(`āŒ Install command failed: ${error.message}`));
497
+ process.exit(1);
498
+ }
499
+ }
500
+ /**
501
+ * Execute add command
502
+ */
503
+ static async executeAdd(argv) {
504
+ try {
505
+ const name = argv.name;
506
+ const command = argv.command;
507
+ const spinner = argv.quiet
508
+ ? null
509
+ : ora(`Adding custom MCP server: ${name}...`).start();
510
+ // Parse environment variables if provided
511
+ let env;
512
+ if (argv.env) {
513
+ try {
514
+ env = JSON.parse(argv.env);
515
+ }
516
+ catch (error) {
517
+ if (spinner) {
518
+ spinner.fail();
519
+ }
520
+ logger.error(chalk.red("āŒ Invalid JSON in env parameter"));
521
+ process.exit(1);
522
+ }
523
+ }
524
+ // Create server configuration
525
+ const config = {
526
+ name,
527
+ command,
528
+ args: argv.args,
529
+ env,
530
+ transport: (argv.transport === "websocket"
531
+ ? "ws"
532
+ : argv.transport) || "stdio",
533
+ installed: true,
534
+ status: "unknown",
535
+ };
536
+ // Add server to NeuroLink
537
+ const sdk = new NeuroLink();
538
+ await sdk.addInMemoryMCPServer(name, {
539
+ server: {
540
+ title: name,
541
+ tools: {}, // Empty tools for external servers
542
+ description: `Custom MCP server: ${command}`,
543
+ },
544
+ metadata: {
545
+ command: config.command,
546
+ args: config.args,
547
+ env: config.env,
548
+ transport: config.transport,
549
+ },
550
+ });
551
+ if (spinner) {
552
+ spinner.succeed(chalk.green(`āœ… Successfully added ${name} MCP server`));
553
+ }
554
+ // Display configuration
555
+ logger.always(chalk.bold("\nšŸ“‹ Server Configuration:"));
556
+ logger.always(`Name: ${config.name}`);
557
+ logger.always(`Command: ${config.command}`);
558
+ if (config.args?.length) {
559
+ logger.always(`Args: ${config.args.join(" ")}`);
560
+ }
561
+ if (config.env) {
562
+ logger.always(`Environment: ${Object.keys(config.env).length} variables`);
563
+ }
564
+ logger.always(`Transport: ${config.transport}`);
565
+ }
566
+ catch (error) {
567
+ logger.error(chalk.red(`āŒ Add command failed: ${error.message}`));
568
+ process.exit(1);
569
+ }
570
+ }
571
+ /**
572
+ * Execute test command
573
+ */
574
+ static async executeTest(argv) {
575
+ try {
576
+ const targetServer = argv.server;
577
+ const spinner = argv.quiet
578
+ ? null
579
+ : ora("Testing MCP server connections...").start();
580
+ const sdk = new NeuroLink();
581
+ const rawMcpStatus = await sdk.getMCPStatus();
582
+ const mcpStatus = convertMCPStatusForCLI(rawMcpStatus, sdk);
583
+ let serversToTest = mcpStatus.servers;
584
+ if (targetServer) {
585
+ serversToTest = mcpStatus.servers.filter((s) => s.name === targetServer);
586
+ if (serversToTest.length === 0) {
587
+ if (spinner) {
588
+ spinner.fail();
589
+ }
590
+ logger.error(chalk.red(`āŒ Server not found: ${targetServer}`));
591
+ process.exit(1);
592
+ }
593
+ }
594
+ if (spinner) {
595
+ spinner.succeed(`Testing ${serversToTest.length} servers`);
596
+ }
597
+ // Display test results
598
+ logger.always(chalk.bold("\n🧪 MCP Server Test Results:\n"));
599
+ for (const server of serversToTest) {
600
+ const status = server.connected
601
+ ? chalk.green("āœ… CONNECTED")
602
+ : chalk.red("āŒ DISCONNECTED");
603
+ logger.always(`${server.name}: ${status}`);
604
+ if (server.connected) {
605
+ logger.always(` Tools: ${server.tools?.length || 0} available`);
606
+ if (server.tools?.length) {
607
+ server.tools.slice(0, 3).forEach((tool) => {
608
+ logger.always(` • ${tool.name}`);
609
+ });
610
+ if (server.tools.length > 3) {
611
+ logger.always(` ... and ${server.tools.length - 3} more`);
612
+ }
613
+ }
614
+ }
615
+ else {
616
+ if (server.error) {
617
+ logger.always(` ${chalk.red("Error:")} ${server.error}`);
618
+ }
619
+ logger.always(chalk.yellow(" šŸ’” Try: neurolink mcp remove && neurolink mcp install"));
620
+ }
621
+ logger.always();
622
+ }
623
+ // Summary
624
+ const connected = serversToTest.filter((s) => s.connected).length;
625
+ const total = serversToTest.length;
626
+ if (connected === total) {
627
+ logger.always(chalk.green(`šŸŽ‰ All ${total} servers connected successfully`));
628
+ }
629
+ else {
630
+ logger.always(chalk.yellow(`āš ļø ${connected}/${total} servers connected`));
631
+ }
632
+ }
633
+ catch (error) {
634
+ logger.error(chalk.red(`āŒ Test command failed: ${error.message}`));
635
+ process.exit(1);
636
+ }
637
+ }
638
+ /**
639
+ * Execute exec command
640
+ */
641
+ static async executeExec(argv) {
642
+ try {
643
+ const serverName = argv.server;
644
+ const toolName = argv.tool;
645
+ const spinner = argv.quiet
646
+ ? null
647
+ : ora(`Executing ${toolName} on ${serverName}...`).start();
648
+ // Parse parameters if provided
649
+ let params = {};
650
+ if (argv.params) {
651
+ try {
652
+ params = JSON.parse(argv.params);
653
+ }
654
+ catch (error) {
655
+ if (spinner) {
656
+ spinner.fail();
657
+ }
658
+ logger.error(chalk.red("āŒ Invalid JSON in params parameter"));
659
+ process.exit(1);
660
+ }
661
+ }
662
+ const sdk = new NeuroLink();
663
+ // Check if server exists and is connected
664
+ const rawMcpStatus = await sdk.getMCPStatus();
665
+ const mcpStatus = convertMCPStatusForCLI(rawMcpStatus, sdk);
666
+ const server = mcpStatus.servers.find((s) => s.name === serverName);
667
+ if (!server) {
668
+ if (spinner) {
669
+ spinner.fail();
670
+ }
671
+ logger.error(chalk.red(`āŒ Server not found: ${serverName}`));
672
+ process.exit(1);
673
+ }
674
+ if (!server.connected) {
675
+ if (spinner) {
676
+ spinner.fail();
677
+ }
678
+ logger.error(chalk.red(`āŒ Server not connected: ${serverName}`));
679
+ logger.always(chalk.yellow("šŸ’” Try: neurolink mcp test " + serverName));
680
+ process.exit(1);
681
+ }
682
+ // Check if tool exists
683
+ const tool = server.tools?.find((t) => t.name === toolName);
684
+ if (!tool) {
685
+ if (spinner) {
686
+ spinner.fail();
687
+ }
688
+ logger.error(chalk.red(`āŒ Tool not found: ${toolName}`));
689
+ if (server.tools?.length) {
690
+ logger.always(chalk.blue("Available tools:"));
691
+ server.tools.forEach((t) => {
692
+ logger.always(` • ${t.name}: ${t.description}`);
693
+ });
694
+ }
695
+ process.exit(1);
696
+ }
697
+ // Execute the tool (This would need actual MCP execution logic)
698
+ // For now, showing a placeholder implementation
699
+ const result = {
700
+ tool: toolName,
701
+ server: serverName,
702
+ params,
703
+ result: "Tool execution not yet implemented in NeuroLink SDK",
704
+ timestamp: new Date().toISOString(),
705
+ };
706
+ if (spinner) {
707
+ spinner.succeed(chalk.green("āœ… Tool executed successfully"));
708
+ }
709
+ // Display results
710
+ if (argv.format === "json") {
711
+ logger.always(JSON.stringify(result, null, 2));
712
+ }
713
+ else {
714
+ logger.always(chalk.bold("\nšŸ› ļø Tool Execution Result:\n"));
715
+ logger.always(`Tool: ${toolName}`);
716
+ logger.always(`Server: ${serverName}`);
717
+ if (Object.keys(params).length > 0) {
718
+ logger.always(`Params: ${JSON.stringify(params)}`);
719
+ }
720
+ logger.always(`Result: ${result.result}`);
721
+ logger.always(`Time: ${result.timestamp}`);
722
+ }
723
+ }
724
+ catch (error) {
725
+ logger.error(chalk.red(`āŒ Exec command failed: ${error.message}`));
726
+ process.exit(1);
727
+ }
728
+ }
729
+ /**
730
+ * Execute remove command
731
+ */
732
+ static async executeRemove(argv) {
733
+ try {
734
+ const serverName = argv.server;
735
+ const sdk = new NeuroLink();
736
+ const rawMcpStatus = await sdk.getMCPStatus();
737
+ const mcpStatus = convertMCPStatusForCLI(rawMcpStatus, sdk);
738
+ const server = mcpStatus.servers.find((s) => s.name === serverName);
739
+ if (!server) {
740
+ logger.error(chalk.red(`āŒ Server not found: ${serverName}`));
741
+ process.exit(1);
742
+ }
743
+ // Confirmation unless forced
744
+ if (!argv.force) {
745
+ logger.always(chalk.yellow(`āš ļø This will remove the MCP server: ${serverName}`));
746
+ logger.always("Use --force flag to confirm removal");
747
+ process.exit(1);
748
+ }
749
+ const spinner = argv.quiet
750
+ ? null
751
+ : ora(`Removing MCP server: ${serverName}...`).start();
752
+ // Remove server (This would need actual removal logic in NeuroLink SDK)
753
+ // For now, showing a placeholder
754
+ logger.always(chalk.yellow("āš ļø Server removal not yet implemented in NeuroLink SDK"));
755
+ if (spinner) {
756
+ spinner.succeed(chalk.green(`āœ… Server ${serverName} removed successfully`));
757
+ }
758
+ }
759
+ catch (error) {
760
+ logger.error(chalk.red(`āŒ Remove command failed: ${error.message}`));
761
+ process.exit(1);
762
+ }
763
+ }
764
+ /**
765
+ * Execute discover command
766
+ */
767
+ static async executeDiscover(argv) {
768
+ try {
769
+ const spinner = argv.quiet
770
+ ? null
771
+ : ora("Discovering MCP servers...").start();
772
+ const discovered = [];
773
+ // Discover from Claude Desktop
774
+ if (argv.source === "claude-desktop" || argv.source === "all") {
775
+ const claudeServers = await this.discoverFromClaudeDesktop();
776
+ discovered.push(...claudeServers);
777
+ }
778
+ // Discover from VS Code
779
+ if (argv.source === "vscode" || argv.source === "all") {
780
+ const vscodeServers = await this.discoverFromVSCode();
781
+ discovered.push(...vscodeServers);
782
+ }
783
+ if (spinner) {
784
+ spinner.succeed(`Discovered ${discovered.length} MCP servers`);
785
+ }
786
+ if (discovered.length === 0) {
787
+ logger.always(chalk.yellow("No MCP servers discovered."));
788
+ logger.always(chalk.blue("šŸ’” Try installing popular servers: neurolink mcp install filesystem"));
789
+ return;
790
+ }
791
+ // Display discovered servers
792
+ if (argv.format === "json") {
793
+ logger.always(JSON.stringify(discovered, null, 2));
794
+ }
795
+ else {
796
+ logger.always(chalk.bold("\nšŸ” Discovered MCP Servers:\n"));
797
+ discovered.forEach((server) => {
798
+ logger.always(`${chalk.cyan(server.name)}`);
799
+ logger.always(` Command: ${server.command}`);
800
+ logger.always(` Source: ${server.description || "Unknown"}`);
801
+ logger.always(` Status: ${server.status}`);
802
+ logger.always();
803
+ });
804
+ }
805
+ // Auto-install if requested
806
+ if (argv.autoInstall && discovered.length > 0) {
807
+ logger.always(chalk.blue("šŸš€ Auto-installing discovered servers..."));
808
+ const sdk = new NeuroLink();
809
+ for (const server of discovered) {
810
+ try {
811
+ await sdk.addInMemoryMCPServer(server.name, {
812
+ server: {
813
+ title: server.name,
814
+ tools: {},
815
+ description: server.description,
816
+ },
817
+ metadata: {
818
+ command: server.command,
819
+ args: server.args,
820
+ env: server.env,
821
+ transport: server.transport,
822
+ },
823
+ });
824
+ logger.always(chalk.green(`āœ… Installed ${server.name}`));
825
+ }
826
+ catch (error) {
827
+ logger.always(chalk.red(`āŒ Failed to install ${server.name}: ${error.message}`));
828
+ }
829
+ }
830
+ }
831
+ }
832
+ catch (error) {
833
+ logger.error(chalk.red(`āŒ Discover command failed: ${error.message}`));
834
+ process.exit(1);
835
+ }
836
+ }
837
+ /**
838
+ * Discover servers from Claude Desktop configuration
839
+ */
840
+ static async discoverFromClaudeDesktop() {
841
+ const servers = [];
842
+ try {
843
+ // Common Claude Desktop config paths
844
+ const possiblePaths = [
845
+ path.join(process.env.HOME || "", "Library", "Application Support", "Claude", "claude_desktop_config.json"),
846
+ path.join(process.env.APPDATA || "", "Claude", "claude_desktop_config.json"),
847
+ path.join(process.env.HOME || "", ".config", "claude", "claude_desktop_config.json"),
848
+ ];
849
+ for (const configPath of possiblePaths) {
850
+ if (fs.existsSync(configPath)) {
851
+ const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
852
+ if (config.mcpServers) {
853
+ Object.entries(config.mcpServers).forEach(([name, serverConfig]) => {
854
+ const config = serverConfig;
855
+ servers.push({
856
+ name,
857
+ command: config.command,
858
+ args: config.args,
859
+ env: config.env,
860
+ transport: "stdio",
861
+ description: "Discovered from Claude Desktop",
862
+ installed: false,
863
+ status: "unknown",
864
+ });
865
+ });
866
+ }
867
+ break; // Found config file, stop searching
868
+ }
869
+ }
870
+ }
871
+ catch (error) {
872
+ // Ignore errors in discovery
873
+ }
874
+ return servers;
875
+ }
876
+ /**
877
+ * Discover servers from VS Code configuration
878
+ */
879
+ static async discoverFromVSCode() {
880
+ const servers = [];
881
+ try {
882
+ // VS Code settings paths
883
+ const possiblePaths = [
884
+ path.join(process.env.HOME || "", "Library", "Application Support", "Code", "User", "settings.json"),
885
+ path.join(process.env.APPDATA || "", "Code", "User", "settings.json"),
886
+ path.join(process.env.HOME || "", ".config", "Code", "User", "settings.json"),
887
+ ];
888
+ for (const settingsPath of possiblePaths) {
889
+ if (fs.existsSync(settingsPath)) {
890
+ const settings = JSON.parse(fs.readFileSync(settingsPath, "utf8"));
891
+ // Look for MCP-related extensions or configurations
892
+ if (settings["mcp.servers"]) {
893
+ Object.entries(settings["mcp.servers"]).forEach(([name, serverConfig]) => {
894
+ const config = serverConfig;
895
+ servers.push({
896
+ name,
897
+ command: config.command || "unknown",
898
+ args: config.args,
899
+ env: config.env,
900
+ transport: "stdio",
901
+ description: "Discovered from VS Code",
902
+ installed: false,
903
+ status: "unknown",
904
+ });
905
+ });
906
+ }
907
+ break;
908
+ }
909
+ }
910
+ }
911
+ catch (error) {
912
+ // Ignore errors in discovery
913
+ }
914
+ return servers;
915
+ }
916
+ }