Skip to main content
Glama

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 build

npm ci ставит зависимости строго по package-lock.json. Для разработки: npm test, npm run typecheck.

Конфигурация

Все настройки — через переменные окружения. Скопируйте .env.example в .env и заполните нужные поля (для локального запуска сервера через npm start; .env в .gitignore, секреты никуда не коммитятся).

Ключевые переменные (полный список с комментариями — в .env.example):

Переменная

Назначение

По умолчанию

REMNA_BASE_URL

URL панели, без завершающего слэша

— (обязателен)

REMNA_API_TOKEN / REMNA_API_TOKEN_FILE

Bearer JWT-токен панели (или путь к файлу с токеном)

REMNA_METRICS_USER / REMNA_METRICS_PASS

Basic-auth для /metrics (схема Prometheus в спеке)

MCP_MODE

ro — только GET, rw — всё, кроме deny-листа

ro

MCP_DENY

Доп. правила METHOD /prefix или /prefix, через запятую

пусто

MCP_DENY_DEFAULTS

Включить встроенный deny-лист Remnawave

1

MCP_CONFIRM

Требовать confirm:true для не-GET

1 в rw, 0 в ro (не имеет значения — там всё и так denied)

MCP_DRY_RUN

Не-GET никогда не отправляются, всегда превью

0

MCP_AUDIT_LOG

Путь к JSONL-журналу аудита

./.audit/remnawave-mcp.jsonl

MCP_TIMEOUT_MS

Таймаут HTTP-запроса к панели

30000

MCP_MAX_RESPONSE_BYTES

Лимит размера ответа инструмента

200000

MCP_TOOL_FILTER

Regex по имени инструмента — публикует подмножество

пусто (все)

MCP_REDACT

Редактирование секретов в ответах

1

MCP_REDACT_SUBSCRIPTION

Доп. редактирование полей подписки (trojanPassword и т.д.)

0

MCP_MAX_BULK_ITEMS

Лимит длины массивов uuids/ids в bulk-запросах

100

MCP_HTTP_PORT / MCP_HTTP_TOKEN

Опциональный 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/keygen

  • POST /api/tokens

  • /api/auth

  • /api/passkeys

  • POST /api/nodes/actions/restart-all

  • POST /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_getUsersusers_get_users, SystemController_getRemnawaveHealthsystem_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" } }

Как перегенерировать при обновлении панели

  1. Скопируйте актуальный openapi.json из контейнера панели, например:

    docker cp <remnawave-container>:/opt/app/openapi.json ./new_openapi.json
  2. Замените файл спеки в пакете:

    cp new_openapi.json spec/remnawave_openapi.json
  3. Перегенерируйте инструменты и пересоберите:

    npm run generate
    npm run build
  4. Прогоните тесты (число инструментов в тестах жёстко зашито — 217; если панель добавила/убрала операции, обновите ожидаемое число в test/generator.test.ts):

    npm test
  5. Проверьте 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": ... }, возвращаются как есть — сервер не разворачивает эту обёртку.