tryaii-mcp-server
Version:
TryAII MCP Server - 15+ AI models with comparison, cost tracking, and collective intelligence
182 lines • 7.32 kB
JavaScript
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