Skip to main content
Glama
websocket-authentication-reproduction.test.ts7.68 kB
/** * TDD Reproduction Test: WebSocket Authentication 401 Errors * * CRITICAL BUG: WebSocket connections failing with "Unexpected server response: 401" * Root Cause: verifyClient function in WebSocket server rejecting connections * when SSH sessions don't exist or aren't properly registered. * * This test reproduces the exact failure scenario using real components. */ import { MCPSSHServer } from "../src/mcp-ssh-server.js"; import { WebServerManager } from "../src/web-server-manager.js"; import { SSHConnectionManager } from "../src/ssh-connection-manager.js"; import WebSocket from "ws"; describe("WebSocket Authentication - TDD Reproduction", () => { let mcpServer: MCPSSHServer; let webServer: WebServerManager; let sshManager: SSHConnectionManager; beforeEach(() => { // Create shared SSH connection manager sshManager = new SSHConnectionManager(8080); // Create MCP server with shared manager mcpServer = new MCPSSHServer({}, sshManager); // Create web server with shared manager webServer = new WebServerManager(sshManager); }); afterEach(async () => { // Clean shutdown try { if (mcpServer) await mcpServer.stop(); if (webServer) await webServer.stop(); } catch (error) { console.error("Test cleanup error:", error); } }); describe("FAILING TEST - WebSocket Authentication Issues", () => { it("should reject WebSocket connections for non-existent sessions", async () => { // FAILING TEST: This should properly reject with 401 // Start web server only (no SSH sessions created) await webServer.start(); const webPort = await webServer.getPort(); // Attempt to connect to non-existent session const wsUrl = `ws://localhost:${webPort}/ws/session/non-existent-session`; const connectionPromise = new Promise<{ success: boolean; error?: string }>((resolve) => { const ws = new WebSocket(wsUrl); ws.on('open', () => { ws.close(); resolve({ success: true }); }); ws.on('error', (error: any) => { // Should get 401 Unauthorized error const errorMessage = error.message || String(error); resolve({ success: false, error: errorMessage }); }); ws.on('close', (code) => { if (code === 1002 || code === 1006) { // WebSocket close codes for unauthorized/abnormal closure resolve({ success: false, error: `WebSocket closed with code: ${code}` }); } }); }); const result = await connectionPromise; // ASSERTION: Connection should fail with proper error expect(result.success).toBe(false); expect(result.error).toContain('401'); }, 10000); it("should allow WebSocket connections for existing sessions", async () => { // FAILING TEST: This should work when session exists // Start both servers await webServer.start(); const webPort = await webServer.getPort(); await mcpServer.start(); mcpServer.setWebServerPort(webPort); // Create SSH session through MCP const connectionResult = await mcpServer.callTool('ssh_connect', { name: 'test-session', host: 'localhost', username: 'jsbattig', keyFilePath: '/home/jsbattig/.ssh/id_ed25519' }); // Verify session was created expect(connectionResult).toHaveProperty('success', true); // Now try WebSocket connection to existing session const wsUrl = `ws://localhost:${webPort}/ws/session/test-session`; const connectionPromise = new Promise<{ success: boolean; error?: string }>((resolve) => { const ws = new WebSocket(wsUrl); const timeout = setTimeout(() => { ws.close(); resolve({ success: false, error: 'Connection timeout' }); }, 5000); ws.on('open', () => { clearTimeout(timeout); ws.close(); resolve({ success: true }); }); ws.on('error', (error: any) => { clearTimeout(timeout); const errorMessage = error.message || String(error); resolve({ success: false, error: errorMessage }); }); }); const result = await connectionPromise; // ASSERTION: Connection should succeed for existing session expect(result.success).toBe(true); expect(result.error).toBeUndefined(); }, 15000); it("should handle WebSocket session validation race conditions", async () => { // FAILING TEST: Race condition between session creation and WebSocket connection await webServer.start(); const webPort = await webServer.getPort(); await mcpServer.start(); mcpServer.setWebServerPort(webPort); // Start WebSocket connection attempt immediately after session creation const sessionName = 'race-test-session'; // Create session and immediately try to connect WebSocket const [connectionResult, wsResult] = await Promise.all([ mcpServer.callTool('ssh_connect', { name: sessionName, host: 'localhost', username: 'jsbattig', keyFilePath: '/home/jsbattig/.ssh/id_ed25519' }), new Promise<{ success: boolean; error?: string }>((resolve) => { // Small delay to simulate real timing setTimeout(() => { const wsUrl = `ws://localhost:${webPort}/ws/session/${sessionName}`; const ws = new WebSocket(wsUrl); const timeout = setTimeout(() => { ws.close(); resolve({ success: false, error: 'Connection timeout' }); }, 3000); ws.on('open', () => { clearTimeout(timeout); ws.close(); resolve({ success: true }); }); ws.on('error', (error: any) => { clearTimeout(timeout); const errorMessage = error.message || String(error); resolve({ success: false, error: errorMessage }); }); }, 100); }) ]); // ASSERTIONS: Both should succeed without race conditions expect(connectionResult).toHaveProperty('success', true); expect(wsResult.success).toBe(true); }, 15000); }); describe("WebSocket Session Management", () => { it("should properly register sessions for WebSocket verification", async () => { // Verify that SSH sessions are properly registered for WebSocket access await webServer.start(); const webPort = await webServer.getPort(); await mcpServer.start(); mcpServer.setWebServerPort(webPort); const sessionName = 'verification-test'; // Before session creation - should not exist expect(sshManager.hasSession(sessionName)).toBe(false); // Create session const result = await mcpServer.callTool('ssh_connect', { name: sessionName, host: 'localhost', username: 'jsbattig', keyFilePath: '/home/jsbattig/.ssh/id_ed25519' }); expect(result).toHaveProperty('success', true); // After session creation - should exist expect(sshManager.hasSession(sessionName)).toBe(true); // Cleanup await mcpServer.callTool('ssh_disconnect', { sessionName }); // After cleanup - should not exist expect(sshManager.hasSession(sessionName)).toBe(false); }, 12000); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/LightspeedDMS/ssh-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server