Skip to main content
Glama
mcp-webserver-integration-fix.test.ts7.7 kB
/** * TDD Implementation Test: MCP WebServerManager Integration Fix * * This test demonstrates the proper TDD red-green-refactor cycle for the * MCP browser blocking fix by showing the minimal change needed. * * ISSUE: MCPSSHServer constructor accepts webServerManager parameter but * mcp-server.ts was not passing it, causing browser blocking behavior. * * FIX: Pass webServerManager parameter during MCPSSHServer instantiation. */ import { describe, test, expect, beforeEach, afterEach } from '@jest/globals'; 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 { TerminalSessionStateManager } from '../src/terminal-session-state-manager.js'; import WebSocket from 'ws'; describe('MCP WebServerManager Integration Fix (TDD)', () => { let sshManager: SSHConnectionManager; let webServerManager: WebServerManager; let terminalStateManager: TerminalSessionStateManager; let webServerPort: number; beforeEach(async () => { // Setup shared components (mirrors mcp-server.ts structure) sshManager = new SSHConnectionManager(); terminalStateManager = new TerminalSessionStateManager(); webServerManager = new WebServerManager(sshManager, {}, terminalStateManager); await webServerManager.start(); webServerPort = await webServerManager.getPort(); }); afterEach(async () => { if (webServerManager) { await webServerManager.stop(); } if (sshManager) { sshManager.cleanup(); } }); test('RED: MCPSSHServer without webServerManager blocks on browser connections', async () => { // Simulate the BROKEN state - MCPSSHServer created without webServerManager const brokenMcpServer = new MCPSSHServer( { logLevel: 'debug' }, sshManager, terminalStateManager // MISSING: webServerManager parameter (this is the bug) ); brokenMcpServer.setWebServerPort(webServerPort); // Setup SSH connection await brokenMcpServer.callTool('ssh_connect', { name: 'red-test-session', host: 'localhost', username: 'jsbattig', keyFilePath: '/home/jsbattig/.ssh/id_ed25519' }); // Create browser connection const wsUrl = `ws://localhost:${webServerPort}/ws/session/red-test-session`; const ws = new WebSocket(wsUrl); await new Promise(resolve => ws.on('open', resolve)); // Execute command - should block (RED test) const startTime = Date.now(); const result = await brokenMcpServer.callTool('ssh_exec', { sessionName: 'red-test-session', command: 'echo "red test - should block"' }) as any; const executionTime = Date.now() - startTime; // RED assertions - demonstrate the problem expect(result.success).toBe(true); expect(result.queued).toBeUndefined(); // BUG: should be true expect(result.result).toBeDefined(); // BUG: should not have immediate result expect(executionTime).toBeGreaterThan(50); // BUG: should return immediately ws.close(); }); test('GREEN: MCPSSHServer with webServerManager returns immediately on browser connections', async () => { // Simulate the FIXED state - MCPSSHServer created WITH webServerManager const fixedMcpServer = new MCPSSHServer( { logLevel: 'debug' }, sshManager, terminalStateManager, webServerManager // FIXED: webServerManager parameter included ); fixedMcpServer.setWebServerPort(webServerPort); // Setup SSH connection await fixedMcpServer.callTool('ssh_connect', { name: 'green-test-session', host: 'localhost', username: 'jsbattig', keyFilePath: '/home/jsbattig/.ssh/id_ed25519' }); // Create browser connection const wsUrl = `ws://localhost:${webServerPort}/ws/session/green-test-session`; const ws = new WebSocket(wsUrl); await new Promise(resolve => ws.on('open', resolve)); // Execute command - should return immediately (GREEN test) const startTime = Date.now(); const result = await fixedMcpServer.callTool('ssh_exec', { sessionName: 'green-test-session', command: 'sleep 1; echo "green test - should queue"' }) as any; const executionTime = Date.now() - startTime; // GREEN assertions - demonstrate the fix expect(result.success).toBe(true); expect(result.queued).toBe(true); // FIXED: properly queued expect(result.commandId).toBeDefined(); // FIXED: has polling ID expect(executionTime).toBeLessThan(1000); // FIXED: returns immediately ws.close(); }); test('REFACTOR: Validate polling interface works with queued commands', async () => { // Test the complete workflow including polling const mcpServer = new MCPSSHServer( { logLevel: 'debug' }, sshManager, terminalStateManager, webServerManager ); mcpServer.setWebServerPort(webServerPort); // Setup await mcpServer.callTool('ssh_connect', { name: 'refactor-test-session', host: 'localhost', username: 'jsbattig', keyFilePath: '/home/jsbattig/.ssh/id_ed25519' }); const wsUrl = `ws://localhost:${webServerPort}/ws/session/refactor-test-session`; const ws = new WebSocket(wsUrl); await new Promise(resolve => ws.on('open', resolve)); // Queue command const execResult = await mcpServer.callTool('ssh_exec', { sessionName: 'refactor-test-session', command: 'echo "refactor test - polling workflow"' }) as any; expect(execResult.queued).toBe(true); const taskId = execResult.commandId; // Poll for completion let attempts = 0; let pollResult: any; do { await new Promise(resolve => setTimeout(resolve, 100)); pollResult = await mcpServer.callTool('ssh_poll_task', { sessionName: 'refactor-test-session', taskId: taskId }) as any; attempts++; } while (pollResult.state === 'running' && attempts < 20); // REFACTOR assertions - complete workflow validation expect(pollResult.success).toBe(true); expect(pollResult.state).toBe('completed'); expect(pollResult.result.stdout).toContain('refactor test - polling workflow'); ws.close(); }); test('MINIMAL FIX VERIFICATION: Only webServerManager parameter addition needed', async () => { // This test verifies the fix is minimal - just adding one parameter // Demonstrate that the fix is simply adding webServerManager to constructor call function simulateBrokenMcpServerCreation() { // OLD CODE (broken): return new MCPSSHServer( { logLevel: 'debug' }, sshManager, terminalStateManager // Missing webServerManager ); } function simulateFixedMcpServerCreation() { // NEW CODE (fixed): return new MCPSSHServer( { logLevel: 'debug' }, sshManager, terminalStateManager, webServerManager // Added webServerManager - this is the ENTIRE fix ); } const brokenServer = simulateBrokenMcpServerCreation(); const fixedServer = simulateFixedMcpServerCreation(); // Verify the fix is minimal - only difference is webServerManager presence expect((brokenServer as any).webServerManager).toBeUndefined(); expect((fixedServer as any).webServerManager).toBeDefined(); expect((fixedServer as any).webServerManager).toBe(webServerManager); // All other properties should be identical expect((brokenServer as any).sshManager).toBe((fixedServer as any).sshManager); expect((brokenServer as any).terminalStateManager).toBe((fixedServer as any).terminalStateManager); }); });

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