Skip to main content
Glama
sse-transport.ts3.96 kB
import express from 'express'; import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js"; import { BookStackTools } from "./bookstack-tools.js"; export class SSETransportServer { private app = express(); private transports: Map<string, SSEServerTransport> = new Map(); private bookStackTools: BookStackTools; constructor(bookStackTools: BookStackTools) { this.bookStackTools = bookStackTools; this.setupRoutes(); } private setupRoutes() { this.app.use(express.json()); this.app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization'); if (req.method === 'OPTIONS') { res.sendStatus(200); return; } next(); }); this.app.get("/sse", async (req, res) => { try { console.log("New SSE connection request"); const sessionId = req.query.sessionId as string; if (sessionId && this.transports.has(sessionId)) { console.log(`Session ${sessionId} already exists, closing connection`); res.status(409).send("Session already exists"); return; } const server = new Server( { name: "bookstack-mcp", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: this.bookStackTools.getTools() }; }); server.setRequestHandler(CallToolRequestSchema, async (request) => { return await this.bookStackTools.handleToolCall(request); }); const transport = new SSEServerTransport("/message", res); this.transports.set(transport.sessionId, transport); console.log(`Created new session: ${transport.sessionId}`); transport.onclose = () => { console.log(`Session ${transport.sessionId} closed`); this.transports.delete(transport.sessionId); }; await server.connect(transport); console.log(`Server connected for session: ${transport.sessionId}`); } catch (error) { console.error("Error in SSE endpoint:", error); if (!res.headersSent) { res.status(500).send("Internal server error"); } } }); this.app.post("/message", express.json(), async (req, res) => { try { const sessionId = req.headers['x-session-id'] as string; if (!sessionId) { res.status(400).send("Missing session ID header"); return; } const transport = this.transports.get(sessionId); if (!transport) { res.status(404).send("Session not found"); return; } await transport.handleMessage(req.body); res.sendStatus(200); } catch (error) { console.error("Error in message endpoint:", error); res.status(500).send("Internal server error"); } }); this.app.get("/health", (req, res) => { res.json({ status: "healthy", timestamp: new Date().toISOString(), activeSessions: this.transports.size }); }); } start(port: number = 8007): void { this.app.listen(port, () => { console.log(`BookStack MCP SSE server running on port ${port}`); console.log(`SSE endpoint: http://localhost:${port}/sse`); console.log(`Message endpoint: http://localhost:${port}/message`); console.log(`Health check: http://localhost:${port}/health`); }); } getApp() { return this.app; } }

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/ttpears/bookstack-mcp'

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