@lanonasis/cli 3.1.13 → 3.3.15

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.
@@ -355,4 +355,303 @@ export function mcpCommands(program) {
355
355
  console.log(' --auto : Auto-detect based on authentication');
356
356
  }
357
357
  });
358
+ // Diagnose MCP connection issues
359
+ mcp.command('diagnose')
360
+ .description('Diagnose MCP connection issues')
361
+ .option('-v, --verbose', 'show detailed diagnostic information')
362
+ .action(async (options) => {
363
+ const config = new CLIConfig();
364
+ await config.init();
365
+ console.log(chalk.blue.bold('🔍 MCP Connection Diagnostic'));
366
+ console.log(chalk.cyan('━'.repeat(50)));
367
+ console.log();
368
+ const diagnostics = {
369
+ authenticationValid: false,
370
+ endpointsReachable: false,
371
+ transportTests: {
372
+ websocket: false,
373
+ http: false,
374
+ sse: false
375
+ },
376
+ connectionLatency: {},
377
+ currentConnection: null,
378
+ toolsAvailable: false,
379
+ healthCheckPassing: false
380
+ };
381
+ // Step 1: Check authentication status
382
+ console.log(chalk.cyan('1. Authentication Status'));
383
+ const token = config.getToken();
384
+ const vendorKey = config.getVendorKey();
385
+ if (!token && !vendorKey) {
386
+ console.log(chalk.red(' ✖ No authentication credentials found'));
387
+ console.log(chalk.gray(' → Run: lanonasis auth login'));
388
+ console.log(chalk.gray(' → MCP requires authentication for remote access'));
389
+ }
390
+ else {
391
+ try {
392
+ const isValid = await config.validateStoredCredentials();
393
+ diagnostics.authenticationValid = isValid;
394
+ if (isValid) {
395
+ console.log(chalk.green(' ✓ Authentication credentials are valid'));
396
+ }
397
+ else {
398
+ console.log(chalk.red(' ✖ Authentication credentials are invalid'));
399
+ console.log(chalk.gray(' → Run: lanonasis auth login'));
400
+ }
401
+ }
402
+ catch (error) {
403
+ console.log(chalk.yellow(' ⚠ Could not validate authentication'));
404
+ console.log(chalk.gray(` ${error instanceof Error ? error.message : 'Unknown error'}`));
405
+ }
406
+ }
407
+ // Step 2: Test endpoint availability
408
+ console.log(chalk.cyan('\n2. Endpoint Availability'));
409
+ const spinner1 = ora('Testing MCP endpoints...').start();
410
+ try {
411
+ await config.discoverServices(options.verbose);
412
+ const services = config.get('discoveredServices');
413
+ if (services) {
414
+ spinner1.succeed('MCP endpoints discovered');
415
+ diagnostics.endpointsReachable = true;
416
+ console.log(chalk.green(' ✓ Service discovery successful'));
417
+ if (options.verbose) {
418
+ console.log(chalk.gray(` HTTP: ${services.mcp_base}`));
419
+ console.log(chalk.gray(` WebSocket: ${services.mcp_ws_base}`));
420
+ console.log(chalk.gray(` SSE: ${services.mcp_sse_base}`));
421
+ }
422
+ }
423
+ else {
424
+ spinner1.warn('Using fallback endpoints');
425
+ console.log(chalk.yellow(' ⚠ Service discovery failed, using fallbacks'));
426
+ diagnostics.endpointsReachable = true; // Fallbacks still work
427
+ }
428
+ }
429
+ catch (error) {
430
+ spinner1.fail('Endpoint discovery failed');
431
+ console.log(chalk.red(' ✖ Cannot discover MCP endpoints'));
432
+ console.log(chalk.gray(` ${error instanceof Error ? error.message : 'Unknown error'}`));
433
+ }
434
+ // Step 3: Test transport protocols
435
+ console.log(chalk.cyan('\n3. Transport Protocol Tests'));
436
+ // Test HTTP/REST endpoint
437
+ if (diagnostics.authenticationValid) {
438
+ const httpSpinner = ora('Testing HTTP transport...').start();
439
+ try {
440
+ const startTime = Date.now();
441
+ const axios = (await import('axios')).default;
442
+ const httpUrl = config.getMCPRestUrl();
443
+ await axios.get(`${httpUrl}/health`, {
444
+ headers: {
445
+ 'Authorization': `Bearer ${token}`,
446
+ 'x-api-key': String(token || vendorKey)
447
+ },
448
+ timeout: 10000
449
+ });
450
+ const latency = Date.now() - startTime;
451
+ diagnostics.connectionLatency.http = latency;
452
+ diagnostics.transportTests.http = true;
453
+ httpSpinner.succeed(`HTTP transport working (${latency}ms)`);
454
+ console.log(chalk.green(` ✓ HTTP/REST endpoint reachable`));
455
+ }
456
+ catch (error) {
457
+ httpSpinner.fail('HTTP transport failed');
458
+ console.log(chalk.red(' ✖ HTTP/REST endpoint failed'));
459
+ if (options.verbose) {
460
+ console.log(chalk.gray(` Error: ${error.message}`));
461
+ }
462
+ }
463
+ // Test WebSocket endpoint
464
+ const wsSpinner = ora('Testing WebSocket transport...').start();
465
+ try {
466
+ const startTime = Date.now();
467
+ const wsUrl = config.getMCPServerUrl();
468
+ // Create a test WebSocket connection
469
+ const WebSocket = (await import('ws'));
470
+ const ws = new WebSocket(wsUrl, [], {
471
+ headers: {
472
+ 'Authorization': `Bearer ${token}`,
473
+ 'X-API-Key': String(token || vendorKey)
474
+ }
475
+ });
476
+ await new Promise((resolve, reject) => {
477
+ const timeout = setTimeout(() => {
478
+ ws.close();
479
+ reject(new Error('WebSocket connection timeout'));
480
+ }, 10000);
481
+ ws.on('open', () => {
482
+ clearTimeout(timeout);
483
+ const latency = Date.now() - startTime;
484
+ diagnostics.connectionLatency.websocket = latency;
485
+ diagnostics.transportTests.websocket = true;
486
+ ws.close();
487
+ resolve(true);
488
+ });
489
+ ws.on('error', (error) => {
490
+ clearTimeout(timeout);
491
+ reject(error);
492
+ });
493
+ });
494
+ wsSpinner.succeed(`WebSocket transport working (${diagnostics.connectionLatency.websocket}ms)`);
495
+ console.log(chalk.green(' ✓ WebSocket endpoint reachable'));
496
+ }
497
+ catch (error) {
498
+ wsSpinner.fail('WebSocket transport failed');
499
+ console.log(chalk.red(' ✖ WebSocket endpoint failed'));
500
+ if (options.verbose) {
501
+ console.log(chalk.gray(` Error: ${error.message}`));
502
+ }
503
+ }
504
+ // Test SSE endpoint
505
+ const sseSpinner = ora('Testing SSE transport...').start();
506
+ try {
507
+ const startTime = Date.now();
508
+ const sseUrl = config.getMCPSSEUrl();
509
+ // Test SSE endpoint with a quick connection test
510
+ const axios = (await import('axios')).default;
511
+ await axios.get(sseUrl.replace('/events', '/health'), {
512
+ headers: {
513
+ 'Authorization': `Bearer ${token}`,
514
+ 'x-api-key': String(token || vendorKey)
515
+ },
516
+ timeout: 10000
517
+ });
518
+ const latency = Date.now() - startTime;
519
+ diagnostics.connectionLatency.sse = latency;
520
+ diagnostics.transportTests.sse = true;
521
+ sseSpinner.succeed(`SSE transport working (${latency}ms)`);
522
+ console.log(chalk.green(' ✓ SSE endpoint reachable'));
523
+ }
524
+ catch (error) {
525
+ sseSpinner.fail('SSE transport failed');
526
+ console.log(chalk.red(' ✖ SSE endpoint failed'));
527
+ if (options.verbose) {
528
+ console.log(chalk.gray(` Error: ${error.message}`));
529
+ }
530
+ }
531
+ }
532
+ else {
533
+ console.log(chalk.gray(' - Skipped transport tests (authentication required)'));
534
+ }
535
+ // Step 4: Test current MCP connection
536
+ console.log(chalk.cyan('\n4. Current MCP Connection'));
537
+ const client = getMCPClient();
538
+ diagnostics.currentConnection = client.getConnectionStatus();
539
+ if (diagnostics.currentConnection.connected) {
540
+ console.log(chalk.green(' ✓ MCP client is connected'));
541
+ console.log(chalk.gray(` Mode: ${diagnostics.currentConnection.mode}`));
542
+ console.log(chalk.gray(` Server: ${diagnostics.currentConnection.server}`));
543
+ if (diagnostics.currentConnection.connectionUptime) {
544
+ const uptimeSeconds = Math.floor(diagnostics.currentConnection.connectionUptime / 1000);
545
+ console.log(chalk.gray(` Uptime: ${uptimeSeconds}s`));
546
+ }
547
+ if (diagnostics.currentConnection.lastHealthCheck) {
548
+ const healthCheckAge = Date.now() - diagnostics.currentConnection.lastHealthCheck.getTime();
549
+ console.log(chalk.gray(` Last health check: ${Math.floor(healthCheckAge / 1000)}s ago`));
550
+ }
551
+ }
552
+ else {
553
+ console.log(chalk.red(' ✖ MCP client is not connected'));
554
+ console.log(chalk.gray(' → Try: lanonasis mcp connect'));
555
+ }
556
+ // Step 5: Test tool availability
557
+ console.log(chalk.cyan('\n5. Tool Availability'));
558
+ if (diagnostics.currentConnection.connected) {
559
+ const toolSpinner = ora('Testing MCP tools...').start();
560
+ try {
561
+ const tools = await client.listTools();
562
+ diagnostics.toolsAvailable = tools.length > 0;
563
+ toolSpinner.succeed(`Found ${tools.length} available tools`);
564
+ console.log(chalk.green(` ✓ ${tools.length} MCP tools available`));
565
+ if (options.verbose && tools.length > 0) {
566
+ console.log(chalk.gray(' Available tools:'));
567
+ tools.slice(0, 5).forEach(tool => {
568
+ console.log(chalk.gray(` • ${tool.name}`));
569
+ });
570
+ if (tools.length > 5) {
571
+ console.log(chalk.gray(` ... and ${tools.length - 5} more`));
572
+ }
573
+ }
574
+ }
575
+ catch (error) {
576
+ toolSpinner.fail('Tool listing failed');
577
+ console.log(chalk.red(' ✖ Cannot list MCP tools'));
578
+ if (options.verbose) {
579
+ console.log(chalk.gray(` Error: ${error instanceof Error ? error.message : 'Unknown error'}`));
580
+ }
581
+ }
582
+ }
583
+ else {
584
+ console.log(chalk.gray(' - Skipped (not connected to MCP server)'));
585
+ }
586
+ // Step 6: Connection quality measurement
587
+ console.log(chalk.cyan('\n6. Connection Quality'));
588
+ if (Object.keys(diagnostics.connectionLatency).length > 0) {
589
+ console.log(chalk.green(' ✓ Latency measurements:'));
590
+ Object.entries(diagnostics.connectionLatency).forEach(([transport, latency]) => {
591
+ const quality = latency < 100 ? 'Excellent' : latency < 300 ? 'Good' : latency < 1000 ? 'Fair' : 'Poor';
592
+ const color = latency < 100 ? chalk.green : latency < 300 ? chalk.yellow : chalk.red;
593
+ console.log(color(` ${transport.toUpperCase()}: ${latency}ms (${quality})`));
594
+ });
595
+ }
596
+ else {
597
+ console.log(chalk.gray(' - No latency measurements available'));
598
+ }
599
+ // Summary and recommendations
600
+ console.log(chalk.blue.bold('\n📋 MCP Diagnostic Summary'));
601
+ console.log(chalk.cyan('━'.repeat(50)));
602
+ const issues = [];
603
+ const recommendations = [];
604
+ if (!diagnostics.authenticationValid) {
605
+ issues.push('Authentication credentials are invalid or missing');
606
+ recommendations.push('Run: lanonasis auth login');
607
+ }
608
+ if (!diagnostics.endpointsReachable) {
609
+ issues.push('MCP endpoints are not reachable');
610
+ recommendations.push('Check internet connection and firewall settings');
611
+ }
612
+ const workingTransports = Object.values(diagnostics.transportTests).filter(Boolean).length;
613
+ if (workingTransports === 0 && diagnostics.authenticationValid) {
614
+ issues.push('No transport protocols are working');
615
+ recommendations.push('Check network connectivity to mcp.lanonasis.com');
616
+ }
617
+ else if (workingTransports < 3 && diagnostics.authenticationValid) {
618
+ issues.push(`Only ${workingTransports}/3 transport protocols working`);
619
+ recommendations.push('Some MCP features may be limited');
620
+ }
621
+ if (!diagnostics.currentConnection.connected) {
622
+ issues.push('MCP client is not connected');
623
+ recommendations.push('Run: lanonasis mcp connect');
624
+ }
625
+ if (!diagnostics.toolsAvailable && diagnostics.currentConnection.connected) {
626
+ issues.push('No MCP tools are available');
627
+ recommendations.push('Check MCP server configuration');
628
+ }
629
+ // Show results
630
+ if (issues.length === 0) {
631
+ console.log(chalk.green('✅ All MCP connection checks passed!'));
632
+ console.log(chalk.cyan(' Your MCP connection is working correctly.'));
633
+ if (Object.keys(diagnostics.connectionLatency).length > 0) {
634
+ const avgLatency = Object.values(diagnostics.connectionLatency).reduce((a, b) => a + b, 0) / Object.values(diagnostics.connectionLatency).length;
635
+ console.log(chalk.cyan(` Average latency: ${Math.round(avgLatency)}ms`));
636
+ }
637
+ }
638
+ else {
639
+ console.log(chalk.red(`❌ Found ${issues.length} issue(s):`));
640
+ issues.forEach(issue => {
641
+ console.log(chalk.red(` • ${issue}`));
642
+ });
643
+ console.log(chalk.yellow('\n💡 Recommended actions:'));
644
+ recommendations.forEach(rec => {
645
+ console.log(chalk.cyan(` • ${rec}`));
646
+ });
647
+ }
648
+ // Additional troubleshooting info
649
+ if (issues.length > 0) {
650
+ console.log(chalk.gray('\n🔧 Additional troubleshooting:'));
651
+ console.log(chalk.gray(' • Try different connection modes: --mode websocket|remote|local'));
652
+ console.log(chalk.gray(' • Check firewall settings for ports 80, 443, and WebSocket'));
653
+ console.log(chalk.gray(' • Verify your network allows outbound HTTPS connections'));
654
+ console.log(chalk.gray(' • Contact support if issues persist'));
655
+ }
656
+ });
358
657
  }
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import { Command } from 'commander';
3
3
  import chalk from 'chalk';
4
4
  import { config } from 'dotenv';
5
5
  import { initCommand } from './commands/init.js';
6
- import { loginCommand } from './commands/auth.js';
6
+ import { loginCommand, diagnoseCommand } from './commands/auth.js';
7
7
  import { memoryCommands } from './commands/memory.js';
8
8
  import { topicCommands } from './commands/topics.js';
9
9
  import { configCommands } from './commands/config.js';
@@ -252,6 +252,10 @@ authCmd
252
252
  console.log(chalk.white(' lanonasis auth logout && lanonasis auth login'));
253
253
  }
254
254
  });
255
+ authCmd
256
+ .command('diagnose')
257
+ .description('Diagnose authentication issues')
258
+ .action(diagnoseCommand);
255
259
  // MCP Commands (primary interface)
256
260
  mcpCommands(program);
257
261
  // Memory commands (require auth) - now MCP-powered by default
@@ -9,6 +9,25 @@ export interface LanonasisServerOptions {
9
9
  verbose?: boolean;
10
10
  apiUrl?: string;
11
11
  token?: string;
12
+ preferredTransport?: 'stdio' | 'websocket' | 'http';
13
+ enableTransportFallback?: boolean;
14
+ }
15
+ export interface ConnectionHealth {
16
+ clientId: string;
17
+ connectedAt: Date;
18
+ lastActivity: Date;
19
+ transport: 'stdio' | 'websocket' | 'http';
20
+ authenticated: boolean;
21
+ clientInfo?: {
22
+ name?: string;
23
+ version?: string;
24
+ };
25
+ }
26
+ export interface ConnectionPoolStats {
27
+ totalConnections: number;
28
+ activeConnections: number;
29
+ authenticatedConnections: number;
30
+ connectionsByTransport: Record<string, number>;
12
31
  }
13
32
  export declare class LanonasisMCPServer {
14
33
  private server;
@@ -16,6 +35,12 @@ export declare class LanonasisMCPServer {
16
35
  private apiClient;
17
36
  private transport;
18
37
  private options;
38
+ private connectionPool;
39
+ private maxConnections;
40
+ private connectionCleanupInterval;
41
+ private supportedTransports;
42
+ private transportFailures;
43
+ private enableFallback;
19
44
  constructor(options?: LanonasisServerOptions);
20
45
  /**
21
46
  * Initialize the server
@@ -26,15 +51,15 @@ export declare class LanonasisMCPServer {
26
51
  */
27
52
  private registerTools;
28
53
  /**
29
- * Register MCP resources
30
- */
54
+ * Register MCP resources
55
+ */
31
56
  private registerResources;
32
57
  /**
33
58
  * Register MCP prompts
34
59
  */
35
- private registerPrompts;
36
- /**
37
- * Handle tool calls
60
+ private registerPrompts; /**
61
+
62
+ * Handle tool calls
38
63
  */
39
64
  private handleToolCall;
40
65
  /**
@@ -48,7 +73,133 @@ export declare class LanonasisMCPServer {
48
73
  /**
49
74
  * Handle system configuration
50
75
  */
51
- private handleSystemConfig;
76
+ private handleSystemConfig; /**
77
+
78
+ * Connection pool management methods
79
+ */
80
+ /**
81
+ * Add a new connection to the pool
82
+ */
83
+ private addConnection;
84
+ /**
85
+ * Remove a connection from the pool
86
+ */
87
+ private removeConnection;
88
+ /**
89
+ * Update connection activity timestamp
90
+ */
91
+ private updateConnectionActivity;
92
+ /**
93
+ * Mark connection as authenticated
94
+ */
95
+ private authenticateConnection;
96
+ /**
97
+ * Get connection pool statistics
98
+ */
99
+ private getConnectionPoolStats;
100
+ /**
101
+ * Start connection cleanup monitoring
102
+ */
103
+ private startConnectionCleanup;
104
+ /**
105
+ * Stop connection cleanup monitoring
106
+ */
107
+ private stopConnectionCleanup;
108
+ /**
109
+ * Clean up stale connections (no activity for 10 minutes)
110
+ */
111
+ private cleanupStaleConnections;
112
+ /**
113
+ * Generate unique client ID for new connections
114
+ */
115
+ private generateClientId;
116
+ /**
117
+ * Extract client ID from request headers or metadata
118
+ */
119
+ private extractClientId;
120
+ /**
121
+ * Authenticate incoming MCP request
122
+ */
123
+ private authenticateRequest;
124
+ /**
125
+ * Extract authentication information from request
126
+ */
127
+ private extractAuthInfo;
128
+ /**
129
+ * Check if this is a stdio connection
130
+ */
131
+ private isStdioConnection;
132
+ /**
133
+ * Determine transport type from request
134
+ */
135
+ private determineTransport;
136
+ /**
137
+ * Ensure connection exists in pool
138
+ */
139
+ private ensureConnectionExists;
140
+ /**
141
+ * Validate stored CLI credentials
142
+ */
143
+ private validateStoredCredentials;
144
+ /**
145
+ * Validate provided credentials against the API
146
+ */
147
+ private validateCredentials;
148
+ /**
149
+ * Validate connection authentication status
150
+ */
151
+ private validateConnectionAuth;
152
+ /**
153
+ * Get authentication status for all connections
154
+ */
155
+ private getAuthenticationStatus;
156
+ /**
157
+ * Transport protocol management methods
158
+ */
159
+ /**
160
+ * Check if a transport is available and working
161
+ */
162
+ private checkTransportAvailability;
163
+ /**
164
+ * Test WebSocket transport availability
165
+ */
166
+ private testWebSocketAvailability;
167
+ /**
168
+ * Test HTTP transport availability
169
+ */
170
+ private testHttpAvailability;
171
+ /**
172
+ * Record a transport failure
173
+ */
174
+ private recordTransportFailure;
175
+ /**
176
+ * Get the best available transport
177
+ */
178
+ private getBestAvailableTransport;
179
+ /**
180
+ * Handle transport-specific errors with clear messages
181
+ */
182
+ private handleTransportError;
183
+ /**
184
+ * Attempt to start server with transport fallback
185
+ */
186
+ private startWithTransportFallback;
187
+ /**
188
+ * Start a specific transport
189
+ */
190
+ private startTransport;
191
+ /**
192
+ * Get transport status and statistics
193
+ */
194
+ private getTransportStatus;
195
+ /**
196
+ * Check if connection limit allows new connections
197
+ */
198
+ private canAcceptNewConnection;
199
+ /**
200
+ * Get connection by client ID
201
+ */
202
+ private getConnection;
52
203
  /**
53
204
  * Setup error handling
54
205
  */
@@ -65,4 +216,8 @@ export declare class LanonasisMCPServer {
65
216
  * Get server instance (for testing)
66
217
  */
67
218
  getServer(): Server;
219
+ /**
220
+ * Get connection pool for testing/monitoring
221
+ */
222
+ getConnectionPool(): Map<string, ConnectionHealth>;
68
223
  }