Skip to main content
Glama

nautobot-mcp

CI Python License

MCP-сервер для Nautobot, созданный для инстансов, чей API слишком велик для перечисления: Nautobot 3.2 поставляет 1 673 операции REST по 477 путям, и каждое установленное приложение добавляет ещё. Этот сервер предоставляет 15 инструментов, управляемых схемой, вместо одного инструмента на конечную точку, поэтому весь API — ядро и плагины — доступен без переполнения контекста агента.

Что делает его рабочим

Шаг сборки, а не разбор во время выполнения. Документ OpenAPI Nautobot весит 18 МБ, а его GraphQL-интроспекция — ещё 10 МБ. Скрипт сборки объединяет их в индекс SQLite размером ~1,1 МБ с таблицей поиска FTS5. Сервер открывает его только для чтения и отвечает на запросы за микросекунды; запуск не зависит от размера API.

Внешние ключи, восстановленные из GraphQL. OpenAPI сам по себе не может описать связи Nautobot — каждое связанное поле сериализуется как идентичный непрозрачный объект:

// dcim.device: device_type, role, status and location are indistinguishable here
"device_type": { "id": {...}, "object_type": {"pattern": "^[a-z]+\\.[a-z]+$"}, "url": {...} }

Система типов GraphQL прямо называет цели (device_type → DeviceTypeType), поэтому они объединяются по имени компонента OpenAPI для восстановления 441 типизированного FK-ребра. Именно этот граф делает возможным планирование зависимостей.

Сжатие фильтров. dcim.device предоставляет 250 параметров фильтрации, которые на самом деле представляют собой ~74 базовых поля, умноженных на семейство суффиксов поиска (__ic, __n, __isnull, __gte, …). Индекс хранит базовые поля плюс их наборы суффиксов и описывает словарь один раз.

Related MCP server: mcp-graphql

Установка

uv venv && uv pip install -e ".[dev]"
cp .env.example .env        # then set NAUTOBOT_URL and NAUTOBOT_TOKEN
cp .mcp.json.example .mcp.json   # optional: for stdio-based clients
python -m nautobot_mcp.schema.build --probe

Или пропустите клонирование и запустите в контейнере — см. Docker.

Шаг сборки загружает схемы и записывает var/index.sqlite. Повторно запустите его после установки или обновления приложения Nautobot — или вызовите инструмент nautobot_refresh_schema.

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

Переменная

По умолчанию

Назначение

NAUTOBOT_URL

—

Базовый URL, например http://nautobot.example.com:8080

NAUTOBOT_TOKEN

—

API-токен

NAUTOBOT_ALLOW_WRITE

false

Главный переключатель для create/update/delete

NAUTOBOT_VERIFY_SSL

true

Проверка TLS

NAUTOBOT_TIMEOUT

30

Таймаут на запрос (секунды)

NAUTOBOT_CACHE_DIR

./var

Где хранятся исходники схем и индекс

NAUTOBOT_MAX_PAGE

1000

Потолок для пагинации fetch_all

Только для контейнера, читаются точкой входа, а не сервером:

Переменная

По умолчанию

Назначение

MCP_TRANSPORT

streamable-http

Транспорт, который обслуживает контейнер (stdio для контейнера, запускаемого клиентом)

MCP_HOST

0.0.0.0

Адрес привязки для HTTP-транспортов

MCP_PORT

8000

Порт привязки для HTTP-транспортов

NAUTOBOT_AUTO_INDEX

true

Строить отсутствующий индекс схемы при запуске вместо отказа от работы

Регистрация у клиента

{
  "mcpServers": {
    "nautobot": {
      "command": "/path/to/nautobot-mcp/.venv/bin/python",
      "args": ["-m", "nautobot_mcp"],
      "env": {
        "NAUTOBOT_URL": "http://nautobot.example.com:8080",
        "NAUTOBOT_TOKEN": "...",
        "NAUTOBOT_CACHE_DIR": "/path/to/nautobot-mcp/var"
      }
    }
  }
}

HTTP-транспорты также доступны: python -m nautobot_mcp --transport streamable-http --port 8000.

Docker

cp .env.example .env        # then set NAUTOBOT_URL and NAUTOBOT_TOKEN
docker compose up -d        # or: make docker-up

Первый запуск строит индекс схемы для вашего инстанса и сохраняет его на томе index; последующие запуски используют его повторно. Сервер слушает 127.0.0.1:8000/mcp.

Индекс не встроен в образ и не может быть встроен: он создаётся из схем конкретного инстанса Nautobot, включая все установленные в нём приложения. Пересоберите его после установки или обновления приложения — make docker-index или инструмент nautobot_refresh_schema, который пишет в тот же том.

make docker-index                 # rebuild the index in place
make docker-logs                  # follow the server log
make docker-down                  # stop; VOLUMES=1 also drops the index
docker compose run --rm server index --offline   # rebuild from cached sources only

Регистрация контейнера у клиента

По HTTP укажите клиенту опубликованный порт:

{
  "mcpServers": {
    "nautobot": { "url": "http://127.0.0.1:8000/mcp" }
  }
}

Или позвольте клиенту запускать контейнер на каждую сессию через stdio, повторно используя тот же том индекса:

{
  "mcpServers": {
    "nautobot": {
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "--env-file", "/path/to/nautobot-mcp/.env",
        "-e", "MCP_TRANSPORT=stdio",
        "-v", "nautobot-mcp_index:/data",
        "nautobot-mcp:latest"
      ]
    }
  }
}

Всё, что передано после имени образа, идёт напрямую в python -m nautobot_mcp, так что docker run ... nautobot-mcp:latest --transport sse --host 0.0.0.0 --port 8000 тоже работает.

Что предполагает compose-файл

  • Порт публикуется только на loopback. Раздел Безопасность применяется полностью: это неаутентифицированный прокси, держащий токен с вашими правами, поэтому доступ с другого хоста означает размещение аутентификации перед ним, а не расширение проброса порта.

  • Запись остаётся выключенной, если в вашем .env нет NAUTOBOT_ALLOW_WRITE=true.

  • Контейнер укреплён по умолчанию — не-root (uid 1000), read-only корневая файловая система, все capabilities сброшены, no-new-privileges. Единственный путь для записи — том /data, где находятся индекс и его кэшированные источники.

  • Здоровье — это TCP-соединение, а не MCP-запрос: запрос без сессии к /mcp заставляет менеджер сессий выделить транспорт, который никто не освобождает, поэтому проверка протокола каждые 30 секунд приводила бы к утечке сессии на каждую проверку.

  • .env читается compose дословно. Держите комментарии на отдельной строке; завершающий # comment не всегда надёжно отделяется от значения.

Инструменты

Инструмент

Назначение

nautobot_search_schema

Поиск моделей по имени, описанию или имени поля

nautobot_describe_model

Поля, обязательные поля, цели FK, фильтры, действия

nautobot_list_apps

Пространства имён приложений (ядро и плагины), версии, состояние индекса

nautobot_plan_create

Упорядоченные предпосылки для создания объекта

nautobot_resolve

Человеческое имя → UUID, ограничено ссылающейся моделью

nautobot_list / nautobot_get

Чтение любой модели, урезанной или спроецированной

nautobot_create / nautobot_update / nautobot_delete

Запись с ограничениями

nautobot_graphql

Произвольные GraphQL-запросы

nautobot_graphql_schema

Интроспекция, по одному типу за раз

nautobot_model_actions

Не-CRUD конечные точки (trace, napalm, notes, …)

nautobot_call

Любая REST-конечная точка — плагины, массовые операции, пользовательские действия

nautobot_refresh_schema

Повторная загрузка схем и пересборка индекса

Ссылки на модели прощают ошибки: dcim.device, device, devices, Device, /dcim/devices/ и DeviceType — всё разрешается, а опечатки получают подсказки (dvice → "Возможно, вы имели в виду: dcim.device?").

Планирование зависимостей

Создание Device на пустом инстансе означает создание четырёх других объектов сначала. nautobot_plan_create("dcim.device") обходит FK-граф, проверяет живой инстанс на предмет уже существующего и возвращает их по порядку:

dcim.manufacturer → dcim.devicetype → dcim.locationtype → dcim.location → extras.role → dcim.device

Он также обрабатывает ограничение по content-type Nautobot. Role, Status и Tag могут быть назначены только моделям, перечисленным в их content_types. Глобальный подсчёт — неправильный вопрос: инстанс может содержать 20 ролей, в то время как ни одна не применима к Device:

{
  "model": "extras.role",
  "action": "create",                      // not "use_existing", despite 20 existing
  "content_type_scoped": true,
  "by_referrer": { "dcim.device": { "valid_count": 0 } },
  "note": "No extras.role is assignable to dcim.device yet. Create one with
           content_types including ['dcim.device'] ..."
}

Какие модели ограничены таким образом, обнаруживается, а не зашивается: content_types означает "что может здесь находиться" для LocationType и "кто может ссылаться на меня" для Role. Планировщик пробует ограниченный запрос и считает 400 доказательством того, что ограничение не применяется — поэтому модели плагинов ведут себя корректно без дополнительного кода.

Запись

Запись выключена, пока не установлено NAUTOBOT_ALLOW_WRITE=true. Даже тогда мутации двухэтапные: первый вызов возвращает предпросмотр и confirm_token, и вызов повторяется с этим токеном для применения. Токены выводятся из полезной нагрузки, поэтому токен, выданный для одного тела, не может быть воспроизведён для другого. nautobot_update показывает построчный diff; nautobot_delete показывает объект и всё, что на него ссылается.

Безопасность

Этот сервер — неаутентифицированный привилегированный прокси к Nautobot. Он держит API-токен и не выполняет собственной аутентификации: любой клиент, который может до него добраться, действует с полными правами этого токена, никогда не владея самим токеном.

Значения по умолчанию намеренно безопасны — --host привязывается к 127.0.0.1, а NAUTOBOT_ALLOW_WRITE равен false. Рискованная конфигурация — это сочетание не-loopback привязки с включённой записью, что даёт неаутентифицированное создание/обновление/удаление вашего источника истины всему, что может маршрутизировать к порту.

Поток с confirm-токеном — это защита от случайности, а не контроль доступа — любой клиент может прочитать токен из ответа предпросмотра и немедленно подтвердить.

Если сервер должен быть доступен другим хостам, поместите аутентификацию перед ним (обратный прокси с mTLS, шлюз с OAuth или SSH-туннель) и дайте ему токен Nautobot, ограниченный только тем, что нужно агенту. См. SECURITY.md.

Отзывчивость

  • Один объединённый HTTP/2-клиент используется всеми инструментами; планировщик параллельно выполняет проверки существования.

  • Ответы урезаются до того, как попадают к агенту. Nautobot не поддерживает разреженные наборы полей (?fields= отклоняется как неизвестный фильтр), поэтому url, natural_slug, notes_url, временные метки и пустые блоки пользовательских полей отбрасываются на стороне клиента, а вложенные связанные объекты сводятся к идентичности. Передайте fields=[...] для проекции или full=true для отказа от урезания.

Расширение

Каждый набор инструментов — это модуль, экспортирующий register(server, ctx), перечисленный в tools/__init__.py::TOOLSETS. Регистрация обёрнута так, что каждый инструмент возвращает структурированную ошибку вместо исключения — неперехваченное исключение достигло бы агента как непрозрачное "Error executing tool X".

Конечные точки плагинов не требуют кода: они появляются в /api/swagger.json, поэтому пересборка индекса делает их доступными для всех инструментов.

Тесты

pytest

82 теста запускаются на фикстурах, вырезанных из живой схемы, с HTTP, замоканным через respx. Они фиксируют ловушки, найденные при создании: коллизию slug, которая отображает virtualization.vminterface на InterfaceType из DCIM, ограничение content_types, которое молча даёт непригодные планы, и эвристику FK, которая разрешает DynamicGroupMembership.group в auth.Group Django вместо extras.DynamicGroup.

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

AGENT.md содержит готовый к использованию системный промпт и описание реестра для агента, управляющего этим сервером, включая протокол записи и правило ограничения по content-type, которое чаще всего вызывает сбой создания.

Вклад

Приветствуются issues и pull request'ы. pytest должен проходить, а ruff check / ruff format --check должны быть чистыми; CI проверяет оба на Python 3.11-3.13. Набору не нужен инстанс Nautobot и сеть — он работает на фикстурах схем в tests/fixtures с HTTP, замоканным через respx.

Лицензия

Apache 2.0 — см. LICENSE.

Структура

src/nautobot_mcp/
  schema/build.py     fuses OpenAPI + GraphQL + content types into the index
  schema/index.py     read-only query layer (lookup, FTS search, graph)
  client.py           pooled async HTTP, slimming, error normalisation
  depgraph.py         creation planning and reference resolution
  safety.py           write gate, confirm tokens, diffs
  tools/              one module per toolset, registered through a guard
  server.py           MCP server assembly

Related MCP Connectors

Related MCP Servers