Заведи каркас пакета и утилитный слой MCP-сервера

package.json, tsconfig, eslint, генерация типов из OpenAPI-спеки,
модули config/redact/errors/money/sanitize/cache с тестами и
обезличенные фикстуры живых ответов API openrusrouter.ru.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
vasyanskandClaude Opus 5.5 committed 2026-10-24 10:48:00 +07:00
commit ade6eb82df
29 files changed
+11350

No files matched your search

+47
View File
@@ -0,0 +1,47 @@
import { redactError } from "./redact.js";
/** Неуспешный HTTP-ответ API площадки. */
export class ApiError extends Error {
override name = "ApiError";
readonly status: number;
readonly code?: string;
readonly path: string;
constructor(status: number, path: string, message?: string, code?: string) {
super(message ?? `HTTP ${status}`);
this.status = status;
this.path = path;
if (code !== undefined) {
this.code = code;
}
}
}
export interface ToolErrorResult {
[key: string]: unknown;
isError: true;
content: [{ type: "text"; text: string }];
}
function textForStatus(status: number | undefined, fallback: string): string {
if (status === 401) return "ключ недействителен или отозван";
if (status === 403) return "ключу запрещено выполнять это действие";
if (status === 429) return "превышен лимит частоты запросов, повторите позже";
if (status !== undefined && status >= 500 && status <= 599) {
return "сервис openrusrouter.ru временно недоступен";
}
return fallback;
}
/** Ошибку любого вида превращает в результат инструмента MCP; текст всегда проходит redactError. */
export function toToolErrorResult(err: unknown): ToolErrorResult {
const r = redactError(err);
let text = textForStatus(r.status, r.message);
const details = [r.status !== undefined ? `HTTP ${r.status}` : "", r.code ?? "", r.path ?? ""]
.filter((s) => s !== "")
.join(", ");
if (details !== "") {
text += ` (${details})`;
}
return { isError: true, content: [{ type: "text", text }] };
}