UNPKG

tryaii-mcp-server

Version:

TryAII MCP Server - 15+ AI models with comparison, cost tracking, and collective intelligence

182 lines 7.32 kB
import express from 'express'; import cors from 'cors'; import helmet from 'helmet'; import compression from 'compression'; import path from 'path'; import { fileURLToPath } from 'url'; import { BaseTransport } from './BaseTransport.js'; import { config } from '../utils/config.js'; import { logger } from '../utils/logger.js'; import { errorHandler } from '../middleware/errorHandler.js'; // Import routes import healthRoutes from '../routes/health.js'; import enhancedApiRoutes from '../routes/enhancedApi.js'; import mcpRoutes from '../routes/mcp.js'; export class HttpTransport extends BaseTransport { app; server; constructor() { super(); this.app = express(); this.setupMiddleware(); this.setupRoutes(); this.setupErrorHandling(); } setupMiddleware() { // Security middleware this.app.use(helmet({ contentSecurityPolicy: { directives: { ...helmet.contentSecurityPolicy.getDefaultDirectives(), "script-src": ["'self'", "'unsafe-inline'"], "script-src-attr": ["'self'", "'unsafe-inline'"], }, }, })); // CORS this.app.use(cors({ origin: config.server.corsOrigins, credentials: true, methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization', 'X-Request-ID'] })); // Compression this.app.use(compression()); // Body parsing this.app.use(express.json({ limit: '10mb' })); this.app.use(express.urlencoded({ extended: true, limit: '10mb' })); // Request logging and timing this.app.use((req, res, next) => { const startTime = Date.now(); const requestId = req.headers['x-request-id'] || `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; // Add request metadata req.startTime = startTime; req.requestId = requestId; // Log request logger.info('Incoming HTTP request', { requestId, method: req.method, url: req.originalUrl, ip: req.ip, userAgent: req.get('User-Agent') }); // Log response when finished res.on('finish', () => { const processingTime = Date.now() - startTime; logger.info('HTTP request completed', { requestId, method: req.method, url: req.originalUrl, statusCode: res.statusCode, processingTime }); }); next(); }); } setupRoutes() { // Health check routes (no auth required) try { this.app.use('/health', healthRoutes); logger.info('✅ Health routes mounted successfully'); } catch (error) { logger.error('❌ Failed to mount health routes', { error: error instanceof Error ? error.message : String(error) }); throw new Error(`Health routes mounting failed: ${error instanceof Error ? error.message : String(error)}`); } // MCP protocol routes (public access for MCP clients) try { this.app.use('/mcp', mcpRoutes); logger.info('✅ MCP routes mounted successfully'); } catch (error) { logger.error('❌ Failed to mount MCP routes', { error: error instanceof Error ? error.message : String(error) }); throw new Error(`MCP routes mounting failed: ${error instanceof Error ? error.message : String(error)}`); } // Enhanced API routes with full integration (auth + balance + session tracking) try { this.app.use('/api', enhancedApiRoutes); logger.info('✅ Enhanced API routes mounted successfully'); } catch (error) { logger.error('❌ Failed to mount Enhanced API routes', { error: error instanceof Error ? error.message : String(error) }); throw new Error(`Enhanced API routes mounting failed: ${error instanceof Error ? error.message : String(error)}`); } // Static file serving for output HTML files try { const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const outputPath = path.resolve(__dirname, '../../../output'); // Serve static files from the output directory this.app.use('/output', express.static(outputPath, { setHeaders: (res, path) => { if (path.endsWith('.html')) { res.setHeader('Content-Type', 'text/html'); } } })); logger.info('✅ Static output files serving enabled at /output', { outputPath }); } catch (error) { logger.error('❌ Failed to setup static file serving', { error: error instanceof Error ? error.message : String(error) }); // Don't throw here - static files are not critical for basic operation } // 404 handler this.app.use((req, res) => { res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: 'Endpoint not found' }, metadata: { requestId: req.requestId, timestamp: new Date().toISOString() } }); }); } setupErrorHandling() { this.app.use(errorHandler); } async start() { return new Promise((resolve, reject) => { logger.info(`Starting HTTP transport on ${config.server.host}:${config.server.port}...`); this.server = this.app.listen(config.server.port, config.server.host, () => { logger.info(`🚀 HTTP transport started successfully`, { port: config.server.port, host: config.server.host, nodeEnv: config.server.nodeEnv }); resolve(); }); // Handle server errors this.server.on('error', (error) => { logger.error('HTTP server error', { error }); reject(error); }); }); } async stop() { return new Promise((resolve) => { if (this.server) { this.server.close(() => { logger.info('HTTP transport stopped'); resolve(); }); } else { resolve(); } }); } async sendResponse(id, response) { // HTTP transport doesn't use the sendResponse method since Express handles responses directly // This method is here to satisfy the abstract class interface logger.debug('HTTP transport sendResponse called (no-op)', { id, hasResponse: !!response }); } isHealthy() { return this.server && this.server.listening; } } //# sourceMappingURL=HttpTransport.js.map