Skip to main content
Glama

Slack MCP Server

by lbeatu
mcp.server.ts•7.05 kB
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; import express, { Express, RequestHandler } from "express"; import { Server } from "http"; import { resolve } from "path"; export interface McpServerOptions { appName?: string; } export class McpServerConfig { private transportMap = new Map<string, SSEServerTransport>(); private app: Express; constructor() { this.app = express(); this.setupErrorHandlers(); this.setupRequestLogging(); } private setupErrorHandlers(): void { process.on("uncaughtException", (error) => { console.error("Uncaught Exception:", error.message); console.error(error.stack); setTimeout(() => process.exit(1), 1000); }); process.on("unhandledRejection", (reason) => { console.error("Unhandled Rejection:", reason); }); } private setupRequestLogging(): void { this.app.use((req, res, next) => { const start = Date.now(); res.on("finish", () => { const duration = Date.now() - start; console.log( `${req.method} ${req.path} - ${res.statusCode} (${duration}ms)` ); }); next(); }); } private createSseHandler(serverPath: string): RequestHandler { return async (req, res) => { let createServer; try { const callerDir = process.cwd(); const absoluteServerPath = resolve(callerDir, serverPath); console.log("Loading server configuration from:", absoluteServerPath); const serverModule = await import(absoluteServerPath); createServer = serverModule.default; } catch (error) { console.error("Failed to import server file:", serverPath); console.error( "Error:", error instanceof Error ? error.message : "Unknown error" ); res.status(500).json({ error: "Failed to load server configuration" }); return; } const transport = new SSEServerTransport("/messages", res); const server = createServer(); this.setupResponseHeaders(res); const heartbeatInterval = this.setupHeartbeat(res, transport.sessionId); try { this.transportMap.set(transport.sessionId, transport); res.on("close", async () => { console.log("SSE connection closed:", transport.sessionId); clearInterval(heartbeatInterval); await server.close(); this.transportMap.delete(transport.sessionId); }); await server.connect(transport); console.log( "SSE connection established successfully:", transport.sessionId ); } catch (error) { clearInterval(heartbeatInterval); console.error( "Failed to establish SSE connection:", transport.sessionId ); console.error( "Error:", error instanceof Error ? error.message : "Unknown error" ); this.transportMap.delete(transport.sessionId); res.status(500).end(); } }; } private setupResponseHeaders(res: express.Response): void { res.setHeader("X-Accel-Buffering", "no"); res.setHeader("Cache-Control", "no-cache"); res.setHeader("Connection", "keep-alive"); } private setupHeartbeat( res: express.Response, sessionId: string ): NodeJS.Timeout { return setInterval(() => { try { res.write(": heartbeat\n\n"); } catch (error) { console.error( "Error sending heartbeat:", error instanceof Error ? error.message : "Unknown error" ); } }, 30000); } private createMessageHandler(): RequestHandler { return async (req, res) => { const sessionId = req.query.sessionId as string; res.setHeader("X-Accel-Buffering", "no"); if (!sessionId) { console.error("Message received without sessionId"); res.status(400).json({ error: "sessionId is required" }); return; } const transport = this.transportMap.get(sessionId); if (!transport) { console.error("No active transport found for session:", sessionId); res .status(404) .json({ error: "No active connection found for this session" }); return; } try { await transport.handlePostMessage(req, res); } catch (error) { console.error("Error handling message for session:", sessionId); console.error( "Error:", error instanceof Error ? error.message : "Unknown error" ); res.status(500).json({ error: "Internal server error" }); } }; } private setupShutdownHandlers(httpServer: Server): void { process.on("SIGTERM", () => { console.log("SIGTERM received, shutting down gracefully"); httpServer.close(() => { console.log("Server closed"); process.exit(0); }); setTimeout(() => { console.error("Could not close server gracefully, forcing shutdown"); process.exit(1); }, 10000); }); process.on("SIGINT", () => { console.log("SIGINT received, shutting down gracefully"); httpServer.close(() => { console.log("Server closed"); process.exit(0); }); setTimeout(() => { console.error("Could not close server gracefully, forcing shutdown"); process.exit(1); }, 10000); }); } public async start( serverPath: string, options: McpServerOptions = {} ): Promise<Server> { try { // Configure routes this.app.get("/sse", this.createSseHandler(serverPath)); this.app.post("/messages", this.createMessageHandler()); const port = process.env.PORT || 3001; const httpServer = this.app.listen(port, () => { console.log(`šŸš€ Server started on port ${port}`); console.log(`šŸ“” MCP SSE endpoint: http://localhost:${port}/sse`); console.log(`šŸ”§ Messages endpoint: http://localhost:${port}/messages`); }); // Expose the Express app on the HTTP server for external access (httpServer as any).app = this.app; httpServer.on("error", (error: Error) => { console.error("Server startup error:", error.message); console.error(error.stack); if ((error as any).code === "EADDRINUSE") { console.error("Port is already in use, exiting process"); process.exit(1); } }); this.setupShutdownHandlers(httpServer); return httpServer; } catch (error) { console.error( "Failed to start server:", error instanceof Error ? error.message : "Unknown error" ); console.error(error instanceof Error ? error.stack : undefined); throw error; } } } // Factory function to create and start the server export async function startServer( serverPath: string = "./server.js", options: McpServerOptions = {} ): Promise<Server> { const mcpServer = new McpServerConfig(); return mcpServer.start(serverPath, options); }

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/lbeatu/slack-mcp'

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