UNPKG

@sei-js/mcp-server

Version:

Model Context Protocol (MCP) server for interacting with EVM-compatible networks

104 lines (103 loc) 3.87 kB
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'; import cors from 'cors'; import express from 'express'; export class HttpSseTransport { constructor(port, host, path) { this.port = port; this.host = host; this.path = path; this.mode = 'http-sse'; this.httpServer = null; this.connections = new Map(); this.mcpServer = null; this.app = express(); this.setupMiddleware(); this.setupRoutes(); } setupMiddleware() { this.app.use(express.json()); this.app.use(cors({ origin: '*', methods: ['GET', 'POST', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization'], credentials: true, exposedHeaders: ['Content-Type', 'Access-Control-Allow-Origin'] })); this.app.options('*', cors()); } setupRoutes() { // Health check endpoint this.app.get('/health', (req, res) => { res.json({ status: 'ok', timestamp: new Date().toISOString() }); }); this.app.get(this.path, (req, res) => { console.error(`SSE connection from ${req.ip}`); // Create SSE transport - it will handle headers automatically const transport = new SSEServerTransport(`${this.path}/message`, res); const sessionId = Date.now().toString(); this.connections.set(sessionId, transport); // Connect transport to MCP server if (this.mcpServer) { this.mcpServer.connect(transport); } // Clean up on disconnect req.on('close', () => { this.connections.delete(sessionId); console.error(`SSE connection closed for session ${sessionId}`); }); }); // Message endpoint for SSE transport this.app.post(`${this.path}/message`, async (req, res) => { try { // Find the first available transport (simple approach for now) const transport = Array.from(this.connections.values())[0]; if (!transport) { res.status(404).json({ error: 'No active SSE connection' }); return; } await transport.handleMessage(req.body); res.status(200).end(); } catch (error) { console.error('Error handling message:', error); res.status(500).json({ error: 'Internal server error' }); } }); } async start(server) { this.mcpServer = server; return new Promise((resolve, reject) => { this.httpServer = this.app.listen(this.port, this.host, () => { console.error(`MCP Server ready (http-sse transport on ${this.host}:${this.port}${this.path})`); resolve(); }); this.httpServer.on('error', (error) => { console.error('Error starting server:', error); reject(error); }); // Handle graceful shutdown const cleanup = () => { console.error('Shutting down HTTP SSE server...'); this.connections.clear(); if (this.httpServer) { this.httpServer.close(); } }; process.on('SIGINT', cleanup); process.on('SIGTERM', cleanup); }); } async stop() { return new Promise((resolve) => { if (this.httpServer) { this.httpServer.close(() => { console.error('HTTP SSE server stopped'); resolve(); }); } else { resolve(); } }); } }