UNPKG

@inso_web/els-mcp

Version:

MCP-сервер поверх INSO Error Logs Service. Read-only tools (search, analytics, fingerprinting, correlations) для подключения Claude Desktop/Code и ChatGPT к логам ошибок. Streamable HTTP transport + stdio для npx-запуска.

124 lines 4.29 kB
import { createHttpApp } from '../http/app.js'; import { ElsClient } from '../elsClient.js'; import { HttpTransportManager } from './http.js'; import { RedisService } from '../cache/redis.js'; import { createAuditService, setAuditServiceForTests, } from '../audit/service.js'; import { createUsageTracker, setUsageTrackerForTests, } from '../billing/tracker.js'; import { createLkResolver } from '../http/lkResolver.js'; export async function startHttpServer(opts) { const { config, log } = opts; // Probe-client для /readyz — использует master/ELS_API_KEY если задан. let probeClient; if (config.elsApiKey) { probeClient = new ElsClient({ baseUrl: config.elsBaseUrl, apiKey: config.elsApiKey, timeoutMs: 5000, log, }); } // Redis для cache layer + readyz + DCR rate-limit. let redis = null; if (config.cacheEnabled && config.redisUrl) { redis = new RedisService({ url: config.redisUrl, log }); } // LK API resolver (sub → apps + appSlug → tier). Если URL не задан — // резолвер сразу fallback'ит на oidcDemoAppSlug / defaultTier. const lkResolver = createLkResolver({ lkApiBaseUrl: config.lkApiBaseUrl ?? null, lkApiToken: config.lkApiToken ?? null, redis: redisAsLkCache(redis), fallbackAppSlug: config.oidcDemoAppSlug ?? '', fallbackTier: config.defaultTier, log, }); const tierResolver = async (ctx) => { if (!ctx.appSlug) return config.defaultTier; const r = await lkResolver.resolveAppTier(ctx.appSlug); return r.tier; }; // Audit + usage. Singletons заменяются на real instances. const audit = createAuditService({ ...(config.mcpDatabaseUrl ? { databaseUrl: config.mcpDatabaseUrl } : {}), log, }); setAuditServiceForTests(audit); const usage = createUsageTracker({ ...(config.mcpDatabaseUrl ? { databaseUrl: config.mcpDatabaseUrl } : {}), log, }); setUsageTrackerForTests(usage); const middlewareDeps = { audit, usage, redactionConfig: { enabled: config.redactionEnabled, ...(config.redactionFields.length > 0 ? { fields: new Set(config.redactionFields) } : {}), }, }; const manager = new HttpTransportManager({ config, log, redis, middlewareDeps, tierResolver, }); const { app } = createHttpApp({ config, log, transportManager: manager, ...(probeClient ? { probeElsClient: probeClient } : {}), redis, middlewareDeps, lkResolver, }); const server = await new Promise((resolve, reject) => { const s = app.listen(config.httpPort, () => resolve(s)); s.on('error', reject); }); log.info({ port: config.httpPort, publicUrl: config.publicUrl, cacheEnabled: config.cacheEnabled && !!redis, metricsEnabled: config.metricsEnabled, }, 'HTTP transport listening'); return { server, manager, redis, async close() { log.info('Shutting down HTTP transport'); await new Promise((resolve) => server.close(() => resolve())); await manager.close(); if (probeClient) await probeClient.close(); if (redis) await redis.close(); }, }; } /** * Адаптирует RedisService к интерфейсу, который ожидает `lkResolver`. * RedisService обёрнут вокруг ioredis Cluster/Single — методы get/set с * совместимой сигнатурой. */ function redisAsLkCache(redis) { if (!redis) return null; const client = redis.raw; if (!client) return null; return { async get(key) { return client.get(key); }, async set(key, value, mode, duration) { // ioredis: client.set(key, value, 'EX', seconds) return client.set(key, value, mode, duration); }, }; } //# sourceMappingURL=http-server.js.map