mcpdog
Version: 
MCPDog - Universal MCP Server Manager with Web Interface
144 lines • 5.87 kB
JavaScript
import { spawn } from 'child_process';
export class ProxyServerManager {
    activeServers = new Map();
    serverTimeout = 30000; // 30 second timeout
    async ensureServerRunning(serverName, config) {
        // If server is already running
        if (this.activeServers.has(serverName)) {
            const server = this.activeServers.get(serverName);
            if (server.isReady) {
                return true;
            }
        }
        console.log(`[MCPDog] Starting ${serverName} server...`);
        try {
            const serverProcess = spawn(config.command, config.args, {
                stdio: ['pipe', 'pipe', 'pipe']
            });
            const activeServer = {
                process: serverProcess,
                config,
                isReady: false,
                tools: []
            };
            this.activeServers.set(serverName, activeServer);
            // Listen to server output, wait for server to be ready
            return new Promise((resolve, reject) => {
                const timeout = setTimeout(() => {
                    console.log(`[MCPDog] ${serverName} server startup timeout`);
                    this.stopServer(serverName);
                    reject(new Error(`Server ${serverName} startup timeout`));
                }, this.serverTimeout);
                let initMessages = '';
                serverProcess.stdout?.on('data', (data) => {
                    const message = data.toString();
                    initMessages += message;
                    // Simple readiness detection - consider server started when any output is received
                    if (!activeServer.isReady) {
                        activeServer.isReady = true;
                        clearTimeout(timeout);
                        console.log(`[MCPDog] ${serverName} server is ready`);
                        resolve(true);
                    }
                });
                serverProcess.stderr?.on('data', (data) => {
                    console.error(`[MCPDog] ${serverName} error:`, data.toString());
                });
                serverProcess.on('error', (error) => {
                    clearTimeout(timeout);
                    console.error(`[MCPDog] ${serverName} startup failed:`, error);
                    this.stopServer(serverName);
                    reject(error);
                });
                serverProcess.on('exit', (code) => {
                    console.log(`[MCPDog] ${serverName} server exited with code: ${code}`);
                    this.activeServers.delete(serverName);
                });
                // Send initial handshake request
                const handshakeRequest = {
                    jsonrpc: "2.0",
                    id: 1,
                    method: "initialize",
                    params: {
                        protocolVersion: "2024-11-05",
                        capabilities: {
                            roots: {
                                listChanged: true
                            }
                        },
                        clientInfo: {
                            name: "mcpdog-proxy",
                            version: "2.0.0"
                        }
                    }
                };
                serverProcess.stdin?.write(JSON.stringify(handshakeRequest) + '\n');
            });
        }
        catch (error) {
            console.error(`[MCPDog] Failed to start ${serverName}:`, error);
            return false;
        }
    }
    async callTool(serverName, toolName, parameters) {
        const server = this.activeServers.get(serverName);
        if (!server || !server.isReady) {
            throw new Error(`Server ${serverName} is not ready`);
        }
        return new Promise((resolve, reject) => {
            const callId = Date.now();
            const toolCallRequest = {
                jsonrpc: "2.0",
                id: callId,
                method: "tools/call",
                params: {
                    name: toolName,
                    arguments: parameters
                }
            };
            // Listen for response
            const responseHandler = (data) => {
                try {
                    const response = JSON.parse(data.toString());
                    if (response.id === callId) {
                        server.process.stdout?.off('data', responseHandler);
                        if (response.error) {
                            reject(new Error(response.error.message));
                        }
                        else {
                            resolve(response.result);
                        }
                    }
                }
                catch (error) {
                    // Ignore parsing errors, wait for correct response
                }
            };
            server.process.stdout?.on('data', responseHandler);
            // Send tool call request
            server.process.stdin?.write(JSON.stringify(toolCallRequest) + '\n');
            // Set timeout
            setTimeout(() => {
                server.process.stdout?.off('data', responseHandler);
                reject(new Error(`Tool call timeout for ${toolName}`));
            }, 10000);
        });
    }
    stopServer(serverName) {
        const server = this.activeServers.get(serverName);
        if (server) {
            server.process.kill();
            this.activeServers.delete(serverName);
            console.log(`[MCPDog] ${serverName} server stopped`);
        }
    }
    stopAllServers() {
        for (const [serverName] of this.activeServers) {
            this.stopServer(serverName);
        }
    }
    getActiveServers() {
        return Array.from(this.activeServers.keys()).filter(name => this.activeServers.get(name)?.isReady);
    }
}
//# sourceMappingURL=proxy-server-manager.js.map