Все 6 инструментов проверены живым MCP-протоколом против прода реальным ключом: баланс, каталог (включая speech без цены), подбор модели, оценка стоимости, usage, список ключей. Секрет нигде не утёк (stderr пуст). Каталог TTS-моделей на проде изменился со вчерашнего дня — отражается верно, это не баг сервера. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
4.7 KiB
4.7 KiB
Feature: клиентский MCP-сервер для openrusrouter.ru
Дата: 2026-10-24
Задача
Клиентский MCP-сервер (TypeScript/Node, npm-пакет, stdio) для быстрой настройки интеграции и выбора модели прямо из Claude Desktop/Code/Cursor/OpenCode.
Research (консилиум)
- architect/api/security/devops. Ключевая находка security-агента: Bearer-ключ
сам по себе НЕ read-only (доступны платежи/промокоды/тикеты) — периметр
«только чтение» держится ИСКЛЮЧИТЕЛЬНО дисциплиной самого MCP-пакета
(узкий HTTP-клиент на 7 строго заданных GET-методов, без универсального
fetch(url, method)). - Мутации API-ключей (создание/отзыв) физически недоступны Bearer-ключом —
серверный инвариант
cabinet.sessionOnly, не наше решение, а факт из кода бэкенда (cabinet/middleware.go:118). - Решение владельца: список ВСЕХ ключей аккаунта показывать (поля обрезаны, секрет никогда не возвращается).
Plan
Детальный план по файлам (Plan-агент), с уточнениями по факту:
/api/keys и /v1/auth/key оборачивают массив/объект в {keys:[...]}/{data:{...}}
(не так, как в OpenAPI-спеке бэкенда — расхождение зафиксировано в коде и README).
Реализация (3 волны, параллельные exec-агенты)
- Волна 1: репозиторий
pub/openrusrouter-mcp, живые проверки 7 эндпоинтов с фикстурами, package.json/tsconfig/eslint, утилиты (config/redact/errors/ money/sanitize/cache) с тестами. - Волна 2:
client.ts(заморожен, ровно 7 методов, никакого произвольного HTTP),catalog.ts(join/api/models+/v1/models), 6 MCP-инструментов (list_models, pick_model, estimate_cost, get_balance, get_usage, list_api_keys), entry point с перенаправлением console в stderr. - Волна 3: README (быстрый старт для 4 клиентов, явный блок «чего тут нет и
почему»), CHANGELOG, CI (
.gitea/workflows/ci.yaml: test на каждый push, publish только по тегуv*, секретNPM_TOKENвenv:шага).
Validation
npm run typecheck && npm test && npm run build && npm run lint && npm pack --dry-run— чисто, 64/64 теста.- Живой smoke-тест через настоящий MCP-протокол (
StdioClientTransport) противhttps://openrusrouter.ruреальным ключом:get_balance— сходится с кабинетом.list_models(text, speech) — корректные данные, у speechprice_noteбез цифр.pick_model— работает.estimate_cost— верно отказывает для speech-модели и несуществующего id, верно считает для текстовой модели (сверено вручную).get_usage— реальные данные за 7 дней, совпадают со вчерашними TTS- тестами (elevenlabs, whisper).list_api_keys— показывает все ключи аккаунта,is_current_keyверно определяет используемый, секрет нигде не виден.- stderr пуст, ключ нигде не утёк (
KEY_LEAK_IN_STDERR: false).
- Побочно замечено: каталог TTS-моделей на проде изменился со вчера (Gemini/
voxtral пропали из списка speech) — это действие администратора в кабинете,
не баг MCP-сервера; подтверждено, что
estimate_cost/list_modelsчестно отражают актуальный каталог, а не кэш старых данных.
Статус
Done (код + живая проверка). НЕ запушено в удалённый репозиторий, НЕ опубликовано
в npm — оба действия требуют отдельного подтверждения (публикация —
необратимое публичное действие, нужен секрет NPM_TOKEN, которого у агента нет).