import express from 'express'; import cors from 'cors'; import { createServer } from 'http'; import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { getVersion } from '../lib/getVersion.js'; import { WebSocketServerTransport } from '../server/websocket.js'; import { onSignals } from '../lib/onSignals.js'; import { serializeCorsOrigin } from '../lib/serializeCorsOrigin.js'; import { loadConfig } from '../lib/config.js'; import { McpServerManager } from '../lib/mcpServerManager.js'; const setResponseHeaders = ({ res, headers, }) => Object.entries(headers).forEach(([key, value]) => { res.setHeader(key, value); }); export async function configToWs(args) { const { configPath, port, host, messagePath, logger, corsOrigin, healthEndpoints, headers, } = args; logger.info(` - config: ${configPath}`); logger.info(` - Headers: ${Object.keys(headers).length ? JSON.stringify(headers) : '(none)'}`); logger.info(` - host: ${host}`); logger.info(` - port: ${port}`); logger.info(` - messagePath: ${messagePath}`); logger.info(` - CORS: ${corsOrigin ? `enabled (${serializeCorsOrigin({ corsOrigin })})` : 'disabled'}`); logger.info(` - Health endpoints: ${healthEndpoints.length ? healthEndpoints.join(', ') : '(none)'}`); const serverManager = new McpServerManager(logger); let wsTransport = null; let isReady = false; const cleanup = async () => { await serverManager.cleanup(); if (wsTransport) { wsTransport.close().catch((err) => { logger.error(`Error stopping WebSocket server: ${err.message}`); }); } }; onSignals({ logger, cleanup }); let config; try { config = loadConfig(configPath); logger.info(`Loaded config with ${Object.keys(config.mcpServers).length} servers`); } catch (err) { logger.error(`Failed to load config: ${err}`); process.exit(1); } for (const [serverName, serverConfig] of Object.entries(config.mcpServers)) { try { await serverManager.addServer(serverName, serverConfig); logger.info(`Successfully initialized server: ${serverName}`); } catch (err) { logger.error(`Failed to initialize server ${serverName}: ${err}`); process.exit(1); } } try { const server = new Server({ name: 'mcp-superassistant-proxy', version: getVersion() }, { capabilities: {} }); const app = express(); if (corsOrigin) { app.use(cors({ origin: corsOrigin })); } for (const ep of healthEndpoints) { app.get(ep, (_req, res) => { setResponseHeaders({ res, headers, }); if (!isReady) { res.status(500).send('Server is not ready'); } else { res.send('ok'); } }); } const httpServer = createServer(app); wsTransport = new WebSocketServerTransport({ path: messagePath, server: httpServer, }); await server.connect(wsTransport); wsTransport.onmessage = async (message) => { // Extract client ID from the modified message ID const messageId = message.id; let clientId; let originalId; if (typeof messageId === 'string' && messageId.includes(':')) { const parts = messageId.split(':'); clientId = parts[0]; originalId = parts.slice(1).join(':'); message.id = isNaN(Number(originalId)) ? originalId : Number(originalId); } const isRequest = 'method' in message && 'id' in message; if (isRequest) { logger.info(`WebSocket → Servers (client ${clientId}):`, message); try { const response = await serverManager.handleRequest(message); logger.info(`Servers → WebSocket (client ${clientId}):`); logger.debug(`Servers → WebSocket (client ${clientId}):`, response); await wsTransport.send(response, clientId); } catch (err) { logger.error(`Error handling request from client ${clientId}:`, err); const errorResponse = { jsonrpc: '2.0', id: message.id, error: { code: -32000, message: 'Internal error', }, }; try { await wsTransport.send(errorResponse, clientId); } catch (sendErr) { logger.error(`Failed to send error response to client ${clientId}:`, sendErr); } } } else { logger.info(`Notification from client ${clientId}:`, message); } }; wsTransport.onconnection = (clientId) => { logger.info(`New WebSocket connection: ${clientId}`); }; wsTransport.ondisconnection = (clientId) => { logger.info(`WebSocket connection closed: ${clientId}`); }; wsTransport.onerror = (err) => { logger.error(`WebSocket error: ${err.message}`); }; isReady = true; httpServer.listen(port, host, () => { logger.info(`Listening on ${host}:${port}`); logger.info(`WebSocket endpoint: ws://${host}:${port}${messagePath}`); }); logger.info('Config-to-WebSocket gateway ready'); } catch (err) { logger.error(`Failed to start: ${err.message}`); cleanup(); process.exit(1); } } //# sourceMappingURL=configToWs.js.map