remnawave-mcp
remnawave-mcp
Автономный MCP-сервер (Model Context Protocol) для панели Remnawave. Один
инструмент — на каждую операцию method + path из OpenAPI-спеки панели
(217 операций), плюс несколько служебных инструментов. Сервер сгенерирован
из спеки, но не зависит от неё в рантайме — сгенерированный файл
src/generated/tools.ts закоммитен в репозиторий.
Пакет самодостаточен: копия спеки лежит в spec/remnawave_openapi.json,
внешних рантайм-зависимостей кроме @modelcontextprotocol/sdk и zod нет.
Что это
Полное покрытие API Remnawave 3.4.3: пользователи, ноды, хосты, подписки, инбаунды, шаблоны конфигов, bulk-операции и т.д. — 217 инструментов.
Слой безопасности в рантайме: режимы
ro/rw, deny-листы, подтверждение мутирующих операций (confirm), dry-run, лимит на bulk-операции, редактирование секретов в ответах, аудит-журнал вызовов.Служебные инструменты:
api_search,api_describe,api_status,api_audit_tail,system_metrics.Транспорт: stdio (по умолчанию) и опционально Streamable HTTP.
Установка
npm ci
npm run buildnpm ci ставит зависимости строго по package-lock.json. Для разработки:
npm test, npm run typecheck.
Конфигурация
Все настройки — через переменные окружения. Скопируйте .env.example в .env
и заполните нужные поля (для локального запуска сервера через npm start;
.env в .gitignore, секреты никуда не коммитятся).
Ключевые переменные (полный список с комментариями — в .env.example):
Переменная | Назначение | По умолчанию |
| URL панели, без завершающего слэша | — (обязателен) |
| Bearer JWT-токен панели (или путь к файлу с токеном) | — |
| Basic-auth для | — |
|
|
|
| Доп. правила | пусто |
| Включить встроенный deny-лист Remnawave |
|
| Требовать |
|
| Не-GET никогда не отправляются, всегда превью |
|
| Путь к JSONL-журналу аудита |
|
| Таймаут HTTP-запроса к панели |
|
| Лимит размера ответа инструмента |
|
| Regex по имени инструмента — публикует подмножество | пусто (все) |
| Редактирование секретов в ответах |
|
| Доп. редактирование полей подписки (trojanPassword и т.д.) |
|
| Лимит длины массивов |
|
| Опциональный HTTP-транспорт (см. ниже) | — |
Режимы безопасности
ro (по умолчанию)
Разрешены только GET-запросы. Любая попытка вызвать не-GET инструмент
возвращает отказ (isError: true, denied: true).
rw
Разрешены все операции, кроме тех, что попадают в deny-лист. Для не-GET
операций по умолчанию (MCP_CONFIRM=1) первый вызов без confirm: true
возвращает превью запроса (метод, URL, тело) и ничего не отправляет.
Повторный вызов с confirm: true выполняет запрос.
// 1) без confirm — превью
{ "name": "users_create_user", "arguments": { "body": { "username": "bob", "expireAt": "2030-01-01T00:00:00Z" } } }
// -> { "preview": true, "method": "POST", "url": ".../api/users", "body": {...} }
// 2) с confirm — выполняется
{ "name": "users_create_user", "arguments": { "body": { "username": "bob", "expireAt": "2030-01-01T00:00:00Z" }, "confirm": true } }
// -> { "status": 201, "ok": true, "data": {...}, "truncated": false }Deny-лист
Действует независимо от режима. Встроенный дефолт для Remnawave
(MCP_DENY_DEFAULTS=1):
/api/keygenPOST /api/tokens/api/auth/api/passkeysPOST /api/nodes/actions/restart-allPOST /api/users/bulk/delete-by-status
Эти операции выдают постоянные админ-креды, генерируют ключи или уничтожают
пользователей — они запрещены, даже если модель попросит confirm: true.
Отключить дефолт: MCP_DENY_DEFAULTS=0. Добавить свои правила:
MCP_DENY="DELETE /api/users,POST /api/hosts/bulk".
Dry-run
MCP_DRY_RUN=1 — ни одна не-GET операция не отправляется на панель, всегда
возвращается превью, даже с confirm: true. Полезно для проверки, что делает
модель, без реального изменения данных.
Bulk-лимит
Для bulk-инструментов (/api/users/bulk/*, /api/hosts/bulk/*) массивы
uuids/ids в теле не могут превышать MCP_MAX_BULK_ITEMS (по умолчанию
100) — при превышении вызов отклоняется без обращения к панели.
Редактирование секретов
По умолчанию (MCP_REDACT=1) в ответах инструментов заменяются на
<redacted> ключи, подходящие под
/(password|passwd|secret|token|api_key|apikey|private_key|privateKey|authorization|cookie)/i.
Поля, специфичные для подписок Remnawave (trojanPassword, vlessUuid,
ssPassword, subscriptionUrl, links, happ.cryptoLink) — это рабочие
данные, нужные клиентам, поэтому по умолчанию не редактируются, даже если
формально подпадают под базовый паттерн (например trojanPassword). Включить
их редактирование: MCP_REDACT_SUBSCRIPTION=1. Полностью выключить
редактирование: MCP_REDACT=0.
Заголовок Authorization никогда не попадает ни в ответы, ни в аудит-журнал.
Подключение к Claude Code
При старте сервер сам читает
.envиз корня своего каталога (уже заданные переменные окружения имеют приоритет). Поэтому проще всего заполнить.envи подключать без--env, чтобы секреты не попали в конфиг MCP-клиента:claude mcp add remnawave -- node /абсолютный/путь/до/remnawave-mcp/dist/index.js
claude mcp add remnawave \
--env REMNA_BASE_URL=https://panel.example.com \
--env REMNA_API_TOKEN=xxxxx \
--env MCP_MODE=ro \
-- node /абсолютный/путь/до/remnawave-mcp/dist/index.jsДля rw-режима добавьте --env MCP_MODE=rw (и, по вкусу, MCP_CONFIRM,
MCP_DENY). Не забудьте npm run build перед подключением — сервер
запускается из dist/index.js.
Подключение через generic JSON-конфиг (любой MCP-клиент)
{
"mcpServers": {
"remnawave": {
"command": "node",
"args": ["/абсолютный/путь/до/remnawave-mcp/dist/index.js"],
"env": {
"REMNA_BASE_URL": "https://panel.example.com",
"REMNA_API_TOKEN": "xxxxx",
"MCP_MODE": "ro"
}
}
}
}Опциональный HTTP-транспорт
npm run start:http поднимает Streamable HTTP на 127.0.0.1:$MCP_HTTP_PORT.
Обязателен MCP_HTTP_TOKEN — без него сервер откажется стартовать. Каждый
HTTP-запрос должен нести заголовок Authorization: Bearer <MCP_HTTP_TOKEN>
(это отдельный токен транспорта, не токен панели).
Имена инструментов
snake_case, ASCII, ≤ 60 символов, уникальные. Правило: из operationId
вида XxxController_yyyZzz отрезается Controller, обе части переводятся
в snake_case и склеиваются через _. Например:
UsersController_getUsers → users_get_users,
SystemController_getRemnawaveHealth → system_get_remnawave_health.
Слишком длинные имена (после конвертации > 60 символов) обрезаются с
добавлением короткого хэша для сохранения уникальности.
Примеры вызова часто используемых инструментов
Список пользователей (users_get_users, GET /api/users, пагинация
через start/size):
{ "name": "users_get_users", "arguments": { "size": 20, "start": 0 } }Пользователь по ID (users_get_user_by_id, GET /api/users/{userId}):
{ "name": "users_get_user_by_id", "arguments": { "userId": 42 } }Список нод (nodes_get_nodes, GET /api/nodes):
{ "name": "nodes_get_nodes", "arguments": {} }Здоровье панели (system_get_remnawave_health,
GET /api/system/health; это же дёргает служебный api_status):
{ "name": "system_get_remnawave_health", "arguments": {} }Список хостов (hosts_get_hosts, GET /api/hosts):
{ "name": "hosts_get_hosts", "arguments": {} }Найти нужный инструмент, если имя не угадывается:
{ "name": "api_search", "arguments": { "query": "subscription" } }Посмотреть полную входную схему конкретного инструмента:
{ "name": "api_describe", "arguments": { "tool": "users_create_user" } }Как перегенерировать при обновлении панели
Скопируйте актуальный
openapi.jsonиз контейнера панели, например:docker cp <remnawave-container>:/opt/app/openapi.json ./new_openapi.jsonЗамените файл спеки в пакете:
cp new_openapi.json spec/remnawave_openapi.jsonПерегенерируйте инструменты и пересоберите:
npm run generate npm run buildПрогоните тесты (число инструментов в тестах жёстко зашито — 217; если панель добавила/убрала операции, обновите ожидаемое число в
test/generator.test.ts):npm testПроверьте
git diff src/generated/tools.ts— новые/изменённые инструменты, изменившиеся deny-правила (если появились новые опасные операции — добавьте их вDEFAULT_DENY_RULESвsrc/config.tsи вMCP_DENYв.env.example).
Разработка
npm run typecheck— проверка типов без сборки.npm test— vitest, все сетевые вызовы замоканы (vi.stubGlobal("fetch", ...)).npm run smoke— живая проверкаapi_status+ одинGETк реальной панели; выполняется только если в корне пакета есть заполненный.env, иначе печатаетskippedи завершается с кодом 0. Использует только GET.
Примечания к реализации
/metrics(Basic-auth, схемаPrometheus) отсутствует как отдельныйpathв OpenAPI-спеке панели, поэтому не генерируется автоматически. Вместо этого добавлен вручную написанный служебный инструментsystem_metrics, который используетREMNA_METRICS_USER/REMNA_METRICS_PASSи Basic-auth (а не Bearer-токен панели). Без этих переменных возвращает понятный отказ, не пытаясь ничего вызвать.Ответы панели, обёрнутые в
{ "response": ... }, возвращаются как есть — сервер не разворачивает эту обёртку.