Skip to main content
Glama

GitHub Enterprise MCP Server

http.ts7.56 kB
import express, { Request, Response } from 'express'; import cors from 'cors'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'; import { randomUUID } from 'crypto'; // Session-based transport storage const transportMap = new Map<string, SSEServerTransport>(); // Connection status storage const connectionStatus = new Map<string, boolean>(); /** * Run an MCP server through an HTTP server. * * @param server MCP server instance * @param port Server port (default: 3000) * @returns Express app instance */ export async function startHttpServer(server: McpServer, port: number = 3000): Promise<express.Express> { const app = express(); // CORS configuration - Allow all origins for development app.use(cors({ origin: '*', methods: ['GET', 'POST', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization'] })); // JSON parsing with larger payload sizes app.use(express.json({ limit: '10mb' })); // Health check endpoint app.get('/health', (req: Request, res: Response) => { res.json({ status: 'ok', server: 'GitHub Enterprise MCP', version: '1.0.0' }); }); // Server startup message is now handled in server/index.ts with i18n // MCP SSE endpoint app.get('/sse', async (req: Request, res: Response) => { try { // Generate session ID const sessionId = randomUUID(); if (process.env.DEBUG === 'true' || process.argv.includes('--debug')) { console.log(`New SSE connection: ${sessionId}`); } // Create endpoint for message handling const messageEndpoint = `/messages?sessionId=${sessionId}`; try { // Initialize SSE transport - 트랜스포트가 헤더를 알아서 설정하도록 함 const transport = new SSEServerTransport(messageEndpoint, res); // Store for later reference transportMap.set(sessionId, transport); connectionStatus.set(sessionId, true); // Connect server to transport await server.connect(transport); // Handle client disconnection req.on('close', () => { if (process.env.DEBUG === 'true' || process.argv.includes('--debug')) { console.log(`Connection closed: ${sessionId}`); } // Clean up connectionStatus.set(sessionId, false); transportMap.delete(sessionId); // Ensure response is properly ended if (!res.writableEnded) { res.end(); } }); } catch (err: any) { console.error('Failed to initialize SSE transport:', err.message); if (!res.headersSent) { res.status(500).send('Failed to initialize SSE connection'); } else { // 헤더가 이미 전송된 경우 데이터만 전송 // If headers are already sent, only send data res.write(`data: ${JSON.stringify({ error: 'Transport initialization failed' })}\n\n`); res.end(); } } } catch (error: any) { console.error('SSE connection error:', error.message); if (!res.headersSent) { res.status(500).send('Internal Server Error'); } } }); // Messages endpoint app.post('/messages', async (req: Request, res: Response) => { try { // Extract session ID from query string and clean it let sessionId = req.query.sessionId; if (!sessionId || typeof sessionId !== 'string') { console.error('Invalid or no session ID provided'); return res.status(400).json({ jsonrpc: '2.0', id: null, error: { code: -32602, message: 'Valid session ID required' } }); } // 중요: sessionId에서 쿼리스트링 부분 제거 // URL 형식이 포함된 경우(예: "uuid?sessionId=xxx") 파싱 // Important: Remove query string part from sessionId // Parse if URL format is included (e.g., "uuid?sessionId=xxx") if (sessionId.includes('?')) { sessionId = sessionId.split('?')[0]; } if (process.env.DEBUG === 'true' || process.argv.includes('--debug')) { console.log(`Message received for clean session ${sessionId}:`, JSON.stringify(req.body)); } // Validate request body if (!req.body) { console.error('Empty request body'); return res.status(400).json({ jsonrpc: '2.0', id: null, error: { code: -32600, message: 'Invalid request' } }); } // Get transport for this session const transport = transportMap.get(sessionId); if (!transport) { console.error(`Transport not found for session ${sessionId}`); return res.status(404).json({ jsonrpc: '2.0', id: req.body.id || null, error: { code: -32000, message: 'Session not found or expired' } }); } // Verify connection is still active const isConnected = connectionStatus.get(sessionId); if (!isConnected) { console.error(`Connection for session ${sessionId} has been closed`); return res.status(400).json({ jsonrpc: '2.0', id: req.body.id || null, error: { code: -32001, message: 'Connection closed' } }); } // Normalize the JSON-RPC request const normalizedRequest = { jsonrpc: '2.0', id: req.body.id !== undefined ? req.body.id : Math.floor(Math.random() * 1000000), method: req.body.method, params: req.body.params || {} }; if (process.env.DEBUG === 'true' || process.argv.includes('--debug')) { console.log('Normalized request:', JSON.stringify(normalizedRequest)); } // Process the message try { await transport.handlePostMessage(req, res, normalizedRequest); } catch (err: any) { console.error('Message processing error:', err.message); if (!res.headersSent) { res.status(500).json({ jsonrpc: '2.0', id: normalizedRequest.id, error: { code: -32603, message: `Internal error: ${err.message}` } }); } } } catch (error: any) { console.error('Message handling error:', error.message); if (!res.headersSent) { res.status(500).json({ jsonrpc: '2.0', id: null, error: { code: -32603, message: 'Internal JSON-RPC error' } }); } } }); // Start server with fallback ports if the default is already in use const server1 = app.listen(port, () => { console.log(`HTTP server running at http://localhost:${port}`); }).on('error', (err: any) => { if (err.code === 'EADDRINUSE') { console.log(`Port ${port} is already in use, trying ${port + 1}...`); // Start server on next port if current is in use startHttpServer(server, port + 1); } else { console.error('Failed to start HTTP server:', err); } }); // Clean up all connections on server shutdown process.on('SIGINT', () => { console.log('Shutting down server...'); server1.close(); process.exit(0); }); return app; }

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/ddukbg/github-enterprise-mcp'

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