Skip to main content
Glama
server.tsβ€’14 kB
import express, { Request, Response } from 'express'; import { env } from '../config/env.js'; import { logger } from '../logging/logger.js'; import { contextManager } from '../context/manager.js'; import { routeRequest } from '../routing/router.js'; import { db } from '../db/postgres.js'; import { redisCache } from '../cache/redis.js'; /** * HTTP API Server for stateless request handling */ import { Server } from 'http'; export class APIServer { private app: express.Application; private server: Server | null = null; constructor() { this.app = express(); this.setupMiddleware(); this.setupRoutes(); } /** * Setup Express middleware */ private setupMiddleware() { // CORS this.app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', env.API_CORS_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(); }); // JSON parsing this.app.use(express.json({ limit: '10mb' })); // Request logging this.app.use((req, _res, next) => { logger.info('API request', { method: req.method, path: req.path, conversationId: req.body?.conversationId, }); next(); }); } /** * Setup API routes */ private setupRoutes() { // Health check this.app.get('/health', (_req, res) => { res.json({ status: 'ok', redis: redisCache.isReady(), database: db.isReady(), timestamp: new Date().toISOString(), }); }); // Route request (intelligent model selection) this.app.post('/v1/route', async (req, res) => { await this.handleRoute(req, res); }); // Code agent endpoint this.app.post('/v1/code-agent', async (req, res) => { await this.handleCodeAgent(req, res); }); // Chat endpoint (general purpose) this.app.post('/v1/chat', async (req, res) => { await this.handleChat(req, res); }); // Context endpoints this.app.get('/v1/context/:conversationId', async (req, res) => { await this.handleGetContext(req, res); }); this.app.post('/v1/context/:conversationId', async (req, res) => { await this.handleUpdateContext(req, res); }); // 404 handler this.app.use((req, res) => { res.status(404).json({ error: 'Not found', path: req.path, }); }); } /** * Handle /v1/route endpoint */ private async handleRoute(req: Request, res: Response): Promise<void> { const startTime = Date.now(); try { const { conversationId, message, userId, projectId, qualityLevel, } = req.body; if (!conversationId || !message) { res.status(400).json({ error: 'Missing required fields: conversationId, message', }); return; } // Ensure conversation exists await contextManager.ensureConversation( conversationId, userId, projectId ); // Ensure conversation context is loaded await contextManager.getSummary(conversationId); // Route request const result = await routeRequest( { prompt: message }, { quality: qualityLevel || 'normal', complexity: 'medium', taskType: 'general', } ); // Save message to context await contextManager.addMessage(conversationId, { role: 'user', content: message, }); await contextManager.addMessage(conversationId, { role: 'assistant', content: result.content, metadata: { modelUsed: result.modelId, provider: result.provider, }, }); // Log to DB await db.insert('llm_calls', { conversation_id: conversationId, model_id: result.modelId, layer: 'L0', input_tokens: result.inputTokens, output_tokens: result.outputTokens, estimated_cost: result.cost, duration_ms: Date.now() - startTime, success: true, }); res.json({ result: { response: result.content, model: result.modelId, provider: result.provider, }, routing: { summary: result.routingSummary, fromCache: false, }, context: { conversationId, }, performance: { durationMs: Date.now() - startTime, tokens: { input: result.inputTokens, output: result.outputTokens, }, cost: result.cost, }, }); } catch (error) { logger.error('Route request error', { error: error instanceof Error ? error.message : 'Unknown', }); res.status(500).json({ error: 'Internal server error', message: error instanceof Error ? error.message : 'Unknown error', }); } } /** * Handle /v1/code-agent endpoint */ private async handleCodeAgent(req: Request, res: Response): Promise<void> { const startTime = Date.now(); try { const { conversationId, task, files, userId, projectId } = req.body; if (!conversationId || !task) { res.status(400).json({ error: 'Missing required fields: conversationId, task', }); return; } // Ensure conversation exists await contextManager.ensureConversation( conversationId, userId, projectId ); // Get context const summary = await contextManager.getSummary(conversationId); // Build prompt for code agent const prompt = `Task: ${task}\n\nFiles: ${files ? JSON.stringify(files) : 'N/A'}\n\nContext: ${summary ? JSON.stringify(summary) : 'New conversation'}`; // Route request const result = await routeRequest( { prompt }, { quality: 'high', complexity: 'high', taskType: 'code', } ); // Save to context await contextManager.addMessage(conversationId, { role: 'user', content: task, metadata: { type: 'code-agent', files }, }); await contextManager.addMessage(conversationId, { role: 'assistant', content: result.content, metadata: { type: 'code-agent', modelUsed: result.modelId, }, }); res.json({ result: { response: result.content, model: result.modelId, provider: result.provider, }, performance: { durationMs: Date.now() - startTime, cost: result.cost, }, }); } catch (error) { logger.error('Code agent error', { error: error instanceof Error ? error.message : 'Unknown', }); res.status(500).json({ error: 'Internal server error', message: error instanceof Error ? error.message : 'Unknown error', }); } } /** * Handle /v1/chat endpoint */ private async handleChat(req: Request, res: Response): Promise<void> { const startTime = Date.now(); try { const { conversationId, message, userId, projectId } = req.body; if (!conversationId || !message) { res.status(400).json({ error: 'Missing required fields: conversationId, message', }); return; } // Ensure conversation exists await contextManager.ensureConversation( conversationId, userId, projectId ); // Get context const summary = await contextManager.getSummary(conversationId); const recentMessages = await contextManager.getRecentMessages(conversationId); // Build context-aware prompt const contextStr = summary ? `Context: ${JSON.stringify(summary)}\n\nRecent messages: ${JSON.stringify(recentMessages)}` : ''; const fullPrompt = contextStr ? `${contextStr}\n\nUser: ${message}` : message; // Route request const result = await routeRequest( { prompt: fullPrompt }, { quality: 'normal', complexity: 'medium', taskType: 'general', } ); // Save messages await contextManager.addMessage(conversationId, { role: 'user', content: message, }); await contextManager.addMessage(conversationId, { role: 'assistant', content: result.content, }); res.json({ result: { response: result.content, model: result.modelId, }, performance: { durationMs: Date.now() - startTime, cost: result.cost, }, }); } catch (error) { logger.error('Chat error', { error: error instanceof Error ? error.message : 'Unknown', }); res.status(500).json({ error: 'Internal server error', message: error instanceof Error ? error.message : 'Unknown error', }); } } /** * Handle GET /v1/context/:conversationId */ private async handleGetContext(req: Request, res: Response) { try { const { conversationId } = req.params; const summary = await contextManager.getSummary(conversationId); const messages = await contextManager.getRecentMessages(conversationId); res.json({ conversationId, summary, recentMessages: messages, }); } catch (error) { logger.error('Get context error', { error: error instanceof Error ? error.message : 'Unknown', }); res.status(500).json({ error: 'Internal server error', }); } } /** * Handle POST /v1/context/:conversationId */ private async handleUpdateContext(req: Request, res: Response): Promise<void> { try { const { conversationId } = req.params; const { summary } = req.body; if (!summary) { res.status(400).json({ error: 'Missing required field: summary', }); return; } await contextManager.updateSummary(conversationId, { ...summary, conversationId, }); res.json({ success: true, conversationId, }); } catch (error) { logger.error('Update context error', { error: error instanceof Error ? error.message : 'Unknown', }); res.status(500).json({ error: 'Internal server error', }); } } /** * Start the API server */ async start(): Promise<void> { const port = parseInt(env.API_PORT); const host = env.API_HOST; // Initialize database schema await db.initSchema(); this.server = this.app.listen(port, host, () => { logger.info('API server started', { host, port, endpoints: [ 'GET /health', 'POST /v1/route', 'POST /v1/code-agent', 'POST /v1/chat', 'GET /v1/context/:conversationId', 'POST /v1/context/:conversationId', ], }); }); } /** * Stop the API server */ async stop(): Promise<void> { if (this.server) { this.server.close(); logger.info('API server stopped'); } } } // Singleton instance export const apiServer = new APIServer();

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/babasida246/ai-mcp-gateway'

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