@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-запуска.
78 lines • 3.19 kB
JavaScript
/**
* Tier matrix MCP — single source of truth.
*
* Поля:
* - reqPerDay — основной cut-off limit для tool-calls
* - concurrentSse — SSE-сессий per app
* - aiPerDay — лимит на AI-explain (отдельный counter)
* - graceFraction — 10 % grace выше дневного лимита перед hard cut-off
*/
export const TIER_LIMITS = {
FREE: { reqPerDay: 1_000, concurrentSse: 2, aiPerDay: 20, graceFraction: 0.1 },
STANDARD: { reqPerDay: 50_000, concurrentSse: 20, aiPerDay: 500, graceFraction: 0.1 },
PREMIUM: { reqPerDay: 500_000, concurrentSse: 100, aiPerDay: 5_000, graceFraction: 0.1 },
UNLIMITED: {
reqPerDay: Number.POSITIVE_INFINITY,
concurrentSse: 500,
aiPerDay: Number.POSITIVE_INFINITY,
graceFraction: 0,
},
};
/** Секунд до конца текущих UTC-суток. */
export function secondsUntilUtcMidnight(now = new Date()) {
const next = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1, 0, 0, 0, 0);
return Math.max(1, Math.ceil((next - now.getTime()) / 1000));
}
/** Чистое решение: принимает текущий счётчик и tier, возвращает decision. */
export function decideQuota(currentUsed, tier) {
const limits = TIER_LIMITS[tier];
if (!Number.isFinite(limits.reqPerDay)) {
return { allowed: true, remaining: Number.POSITIVE_INFINITY, tier };
}
const cutoff = Math.floor(limits.reqPerDay * (1 + limits.graceFraction));
if (currentUsed < limits.reqPerDay) {
return { allowed: true, remaining: limits.reqPerDay - currentUsed, tier };
}
if (currentUsed < cutoff) {
return {
allowed: true,
remaining: 0,
overage: true,
tier,
};
}
return {
allowed: false,
remaining: 0,
retryAfter: secondsUntilUtcMidnight(),
tier,
};
}
/**
* Чистое решение для AI-quota: принимает текущий AI-счётчик и tier.
* Используется только для tools, помеченных как AI (например, `explain_error`).
*
* Семантика отличается от `decideQuota`: AI-quota — hard cut-off без grace
* (генерация дорогая, нет смысла превышать).
*/
export function decideAiQuota(currentUsed, tier) {
const limits = TIER_LIMITS[tier];
if (!Number.isFinite(limits.aiPerDay)) {
return { allowed: true, remaining: Number.POSITIVE_INFINITY, tier };
}
if (currentUsed < limits.aiPerDay) {
return { allowed: true, remaining: limits.aiPerDay - currentUsed, tier };
}
return {
allowed: false,
remaining: 0,
retryAfter: secondsUntilUtcMidnight(),
tier,
};
}
/** Список tool-имён, считающихся AI (используют отдельную AI-квоту). */
export const AI_TOOL_NAMES = new Set(['explain_error']);
export function isAiTool(toolName) {
return AI_TOOL_NAMES.has(toolName);
}
//# sourceMappingURL=limits.js.map