npm-mcp
npm-mcp
Сервер Model Context Protocol для Nginx Proxy Manager
Управляйте reverse-proxy маршрутизацией, TLS-сертификатами, списками доступа и stream-пересылкой в диалоговом режиме — с защитными механизмами, которые предполагают, что вы в конечном итоге направите его на продакшен.
Содержание
Related MCP server: npm-mcp
Зачем это существует
У Nginx Proxy Manager есть полноценный REST API и нет MCP-сервера. Этот сервер — именно он, но интересна не техническая часть, а ограничения.
Reverse proxy — это единая точка отказа для всего, что за ним стоит. Агент с правом записи может вывести из строя сервисы, к которым его не просили прикасаться. Поэтому дизайн исходит из этого:
Инструменты генерируются из собственного OpenAPI-документа API, а не пишутся вручную. Документ закреплён в репозитории, и drift-тест проваливает CI, если вышестоящая поверхность меняется — вместо того чтобы инструменты молча возвращали 404 во время выполнения.
Каждый результат проходит через одну границу редактирования, которая закрывается по умолчанию. Она вызывает исключение на всём, что не может проверить, вместо того чтобы пропускать это дальше.
Защитные механизмы проходят mutation-тестирование. Для каждого контроля безопасности есть тест, который, как доказано, становится красным, когда контроль отключён.
Как это работает
flowchart LR
C["MCP Client"] -->|"Bearer (optional)"| S
subgraph S["npm-mcp"]
direction TB
A["Bearer verifier<br/><i>hmac.compare_digest</i>"] --> G["Guardrails<br/><i>S1 · S2 · S6 · S7 · S8</i>"]
G --> T["66 generated tools"]
T --> R["serialize_result()<br/><i>redact + cap</i>"]
end
S -->|"JWT, auto-refreshed"| N["Nginx Proxy Manager"]
P["npm-openapi.json<br/><i>pinned, in-package</i>"] -.->|generates| TСигнатуры инструментов строятся из закреплённого документа во время импорта, поэтому
create_proxy_host предоставляет 18 типизированных аргументов с настоящими перечислениями —
а не непрозрачный проход через **kwargs.
Быстрый старт
uv sync
cp .env.example .env # then fill in NPM_URL / NPM_IDENTITY / NPM_SECRET
uv run npm-mcp{
"mcpServers": {
"npm": {
"command": "uv",
"args": ["run", "npm-mcp"],
"env": {
"NPM_URL": "https://nginx-proxy-manager.example.net",
"NPM_IDENTITY": "npm-mcp@example.net",
"NPM_SECRET": "…",
"NPM_MCP_TRANSPORT": "stdio"
}
}
}
}{
"mcpServers": {
"npm": {
"type": "http",
"url": "https://npm-mcp.example.net/mcp",
"headers": { "Authorization": "Bearer <NPM_MCP_BEARER_TOKEN>" }
}
}
}FastMCP обслуживает запросы по адресу /mcp. Завершающий слэш вызывает 307-редирект,
который некоторые клиенты обрабатывают некорректно — не позволяйте прокси переписывать путь.
[!TIP] Сначала вызовите
get_guidance. Он сообщает о формах ответов, различии между отключением и удалением, о том, какие защёлки в данный момент открыты, и о действующем списке защищённых доменов.
Аутентификация
Два уровня, которые легко перепутать:
Направление | Механизм | |
Входящая | клиент → npm-mcp | Необязательный |
Исходящая | npm-mcp → NPM | Учётные данные аккаунта → кратковременный JWT, обновляется автоматически. Вызывающие его никогда не видят и не предоставляют. |
NPM не выдаёт долгоживущих API-ключей, поэтому сервер хранит учётные данные, а не принимает токен.
[!IMPORTANT] У
POST /tokensдва возможных ответа: токен или 2FA-вызов. Если в аккаунте включена 2FA, задайтеNPM_TOTP_SECRET— в противном случае сервер завершится с ошибкой при запуске, указав оба способа решения, вместо того чтобы подняться здоровым и сломаться на первом вызове инструмента.
Каталог инструментов
66 инструментов = 65 операций API + get_guidance.
Семейство | # | Представительные инструменты |
🔀 Proxy-хосты | 7 |
|
↪️ Хосты-редиректы | 7 |
|
🚫 Хосты 404 | 7 |
|
🔌 Streams | 7 |
|
🔐 Списки доступа | 5 |
|
📜 Сертификаты | 10 |
|
👤 Пользователи | 8 |
|
🔑 2FA пользователей | 5 |
|
⚙️ Настройки | 3 |
|
📋 Журнал аудита | 2 |
|
ℹ️ Мета | 4 |
|
🧭 Руководство | 1 |
|
Названия происходят из operationId в OpenAPI, поэтому операции списка называются get_*,
а не list_*.
[!WARNING] Три операции намеренно не предоставляются:
requestToken,refreshToken,loginWith2FA. Это собственная аутентификационная обвязка сервера, аrequestTokenпринимает произвольную идентичность и секрет — регистрация превратила бы этот сервер в оракул для проверки учётных данных против NPM, при этом каждая попытка приписывалась бы служебному аккаунту.
Пагинации не существует. Ни одна конечная точка не принимает
limit/offset. Инструменты принимают их и обрезают на стороне клиента; в описаниях инструментов об этом сказано.expand— это перечисление для каждой конечной точки, а не сквозной параметр — proxy-хосты принимаютaccess_list,owner,certificate; сертификаты принимают толькоowner. Значения вне перечисления отклоняются до отправки запроса.
Модель безопасности
[!CAUTION] Запись включена по умолчанию. Этот сервер может переписать таблицу маршрутизации для каждого сервиса за прокси. Установите
NPM_READ_ONLY=1, чтобы отключить все изменения.
Контроль | Переопределение | |
| Отклоняет каждый изменяющий инструмент, проверяется до любого чтения защитных механизмов | — |
S1 | Отказывает в |
|
S2 | Каждый | при каждом вызове |
S5 | Каждое изменение порождает одну строку аудита; собственный журнал аудита NPM доступен для запросов | — |
S6 | Каждая изменяющая операция в |
|
S7 | Отказывается изменять, отключать, удалять или выполнять | нет |
S8 |
|
|
S1 сопоставляет ЛЮБОЙ защищённый домен, а не ВСЕ. ВСЕ позволило бы обезвредить защитный механизм через инструменты, которые он защищает: добавьте один посторонний домен к хосту — и защита испарится.
S1 покрывает
update, а не только delete/disable. Иначе вы удаляете защищённое имя изdomain_names, а затем чисто удаляете — тот же самый простой.S1 сопоставляется с текущим состоянием вышестоящей системы, никогда с отправленным телом. Проверка запроса позволила бы пути «удалить-затем-обновить» пройти напрямую.
Подстановочные знаки S1 сопоставляются в обоих направлениях.
NPM_PROTECTED_DOMAINS=*.example.netдолжен защищатьapp.example.net. Однажды он не сопоставлял ничего и подавлял предупреждение «не защищено», потому что значение было явно задано.S2 ограничивается HTTP-методом, а не префиксом имени. Правило
delete_*пропускаетdisable_user_2fa—DELETE, который снимает второй фактор у человека.S6 — это правило, а не список. Перечисляемая версия молча пропускала
update_user, поэтому защёлка оставалась закрытой, покаis_disabled: trueблокировал администратора.У S7 нет переопределения. Сервер, который может удалить собственные учётные данные, навсегда блокирует сам себя.
Конфигурация
Переменная | Значение |
| Базовый URL экземпляра NPM |
| Email аккаунта |
| Пароль аккаунта |
Переменная | По умолчанию | Значение |
| не задан | Входящий токен. Не задан ⇒ нет входящей аутентификации |
|
|
|
|
| Адрес привязки |
|
| Порт привязки |
Переменная | По умолчанию | Снятие ограничений |
|
| — ( |
| производное от | S1 денylist, через запятую |
|
| S1 |
|
| S6 |
|
| S8 |
Переменная | По умолчанию | Значение |
| не задано | Base32-сид; только если у аккаунта включена 2FA |
|
| Проверять сертификат NPM |
|
| Таймаут вышестоящего сервиса, секунды |
|
| Лимит ответа до усечения |
|
| Подсказка к |
|
|
Развёртывание
docker build -t npm-mcp:latest .
docker compose up -dКонтейнер подключается к существующей Docker-сети вместе с NPM и не публикует никаких портов. NPM обращается к нему по DNS-имени контейнера и завершает TLS, поэтому Bearer-токен никогда не передаётся по сети в открытом виде.
Никакого ключа
build:в compose-файле. Развёртывание из compose-строки (например, Portainer) не передаёт контекст сборки, поэтому образ сначала собирается, а затем указывается по тегу.Healthcheck резолвит хост привязки вместо жёстко заданного
127.0.0.1. С нестандартнымNPM_MCP_HTTP_HOSTнаивная версия навсегда помечает полностью здоровый контейнер как нездоровый. Он также срабатывает вхолостую приstdio, где вообще ничего не слушает.Аутентификационный прогрев выполняется в течение жизни сервера, поэтому неверная конфигурация приводит к провалу healthcheck, а не к зелёному статусу с последующей поломкой при первом использовании.
Тестирование
uv run pytest # 420 tests
uv run ruff check
uv run ruff format --checkПримерно 4 700 строк тестов на 3 300 строк исходного кода, но количество важнее формы:
🧬 Мутационно-проверенные предохранители — для каждого элемента контроля безопасности есть тест, который, как доказано, падает при отключении этого контроля. Написано после обнаружения
asyncio.Lock, удаление которого оставляло набор тестов зелёным.🌐 Нулевой доступ к сети — каждый вызов вышестоящего сервиса замокан через
respx. Тест, которому нужна сеть, — это сломанный тест.🔍 Проход A7 — все 65 инструментов вызываются против вышестоящего сервиса, возвращающего секреты на четырёх уровнях вложенности, с отрицательным контролем, подтверждающим, что фикстура действительно их содержит, чтобы проход не мог пройти впустую.
📐 Защита от дрейфа схемы — количество операций, формы полезной нагрузки и упакованный файл данных — всё проверяется, так что обновление вышестоящего сервиса падает здесь, а не в продакшене.
Заметки по дизайну
Документ | Содержимое |
Контракт продукта — решения D1–D13, контроли S1–S8, критерии приёмки A1–A10 | |
Все 68 операций с полями тела и обязательностью | |
Внутренние интерфейсы модулей | |
Две вещи, которые OpenAPI-документ описывает неверно, проверенные на живом экземпляре | |
Дословная копия |
This server cannot be deployed
Maintenance
Related MCP Connectors
Manage hosts, redirects, SSL, and traffic analytics from Claude and other AI assistants.
Inspect and control your Northflank projects, services, jobs, and builds from your AI assistant.
Remote streamable-HTTP MCP server running on a single Cloudflare Worker. Your assistant gets live Airbnb, Amazon, Booking.com, Google Flights, Maps and Reddit data, social search on X, Instagram and TikTok, the Meta Ad Library, and image/video generation without any keys. Connect your own accounts to let it send WhatsApp or Telegram messages, work an IMAP inbox, manage Meta Ads campaigns and publish to X and LinkedIn. OAuth 2.1 with PKCE; stored credentials are AES-256-GCM encrypted.
Deploy and manage your apps, databases, storage, and scheduled jobs from your AI agent
Related MCP Servers
- AlicenseBqualityCmaintenanceEnables management of Nginx Proxy Manager instances for configuring proxy hosts, requesting Let's Encrypt SSL certificates, and managing access lists. It allows users to control their web proxy infrastructure through natural language commands in MCP-compatible environments.503MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to manage Nginx Proxy Manager instances through natural language, covering 28 tools for proxy hosts, certificates, streams, and more.MIT
- AlicenseCqualityCmaintenanceMCP server that abstracts the Nginx Proxy Manager API, enabling management of proxy hosts, redirections, streams, certificates, access lists, and users through natural language.5414 npm1AGPL 3.0
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to manage Nginx Proxy Manager, including proxy hosts, SSL certificates, streams, and more.5 npmISC