Skip to main content
Glama
http.ts4.96 kB
import { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js'; import express from 'express'; import { randomUUID } from 'node:crypto'; interface HttpTransportOptions { port?: number; hostname?: string; allowedOrigins?: string[]; allowedHosts?: string[]; } export class HttpServerTransport { private app: express.Express; private server: any; private messageHandler?: (message: JSONRPCMessage) => Promise<void>; private closeHandler?: () => void; private _sessionId: string | undefined; private options: HttpTransportOptions; constructor(options: HttpTransportOptions = {}) { this.options = { port: 3000, hostname: 'localhost', allowedOrigins: ['http://localhost:*', 'https://localhost:*'], allowedHosts: ['127.0.0.1', 'localhost'], ...options }; this.app = express(); this.setupMiddleware(); this.setupRoutes(); } onmessage(handler: (message: JSONRPCMessage) => Promise<void>): void { this.messageHandler = handler; } onclose(handler: () => void): void { this.closeHandler = handler; } private setupMiddleware() { this.app.use(express.json({ limit: '10mb' })); // CORS middleware this.app.use((req, res, next) => { const origin = req.headers.origin as string; const allowedOrigins = this.options.allowedOrigins || []; // Check if origin is allowed (simple pattern matching) const isAllowed = allowedOrigins.some(allowed => { if (allowed.includes('*')) { const pattern = allowed.replace('*', '.*'); return new RegExp(pattern).test(origin); } return allowed === origin; }); if (isAllowed || !origin) { res.header('Access-Control-Allow-Origin', origin || '*'); } res.header('Access-Control-Allow-Headers', 'Content-Type, MCP-Session-Id, MCP-Protocol-Version, Last-Event-ID'); res.header('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS'); if (req.method === 'OPTIONS') { res.sendStatus(200); return; } next(); }); } private setupRoutes() { // MCP endpoint this.app.all('/mcp', async (req, res) => { try { const protocolVersion = req.headers['mcp-protocol-version'] as string; // Validate protocol version if (!protocolVersion || protocolVersion !== '2025-11-25') { return res.status(400).json({ jsonrpc: '2.0', error: { code: -32602, message: 'Unsupported protocol version', data: { supported: ['2025-11-25'], requested: protocolVersion } }, id: null }); } // Handle the message if (req.body && this.messageHandler) { // Handle POST requests with JSON-RPC messages await this.messageHandler(req.body); // For requests, we expect a response to be sent back // The response will be handled by the message handler res.status(202).json({ status: 'accepted' }); } else if (req.method === 'GET') { // Handle GET requests for SSE or polling res.status(200).json({ status: 'connected' }); } else { res.status(400).json({ error: 'Invalid request' }); } } catch (error) { console.error('HTTP transport error:', error); res.status(500).json({ jsonrpc: '2.0', error: { code: -32603, message: 'Internal error' }, id: null }); } }); // Session termination this.app.delete('/mcp', (req, res) => { const sessionId = req.headers['mcp-session-id'] as string; if (sessionId === this.sessionId) { this.close(); res.status(204).send(); } else { res.status(404).send(); } }); } async start(): Promise<void> { return new Promise((resolve, reject) => { try { this.server = this.app.listen(this.options.port!, this.options.hostname!, () => { console.log(`MCP HTTP server running on http://${this.options.hostname}:${this.options.port}/mcp`); this._sessionId = randomUUID(); resolve(); }); this.server.on('error', reject); } catch (error) { reject(error); } }); } async send(message: JSONRPCMessage): Promise<void> { // For HTTP transport, we can't send unsolicited messages // Messages are sent in response to requests // This is a limitation of HTTP vs persistent connections console.log('HTTP transport received message to send:', message); } async close(): Promise<void> { if (this.server) { this.server.close(); this.server = null; } if (this.closeHandler) { this.closeHandler(); } } get sessionId(): string | undefined { return this._sessionId; } }

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/ssdeanx/ssd-ai'

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