@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
JavaScript
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