Как научить ИИ управлять сетевым оборудованием: собираем первый MCP-сервер
Вторая часть цикла о том, как дать ИИ-ассистенту доступ к сетевым устройствам через MCP — на этот раз разбираемся, как устроен минимально полезный сервер: от инвентаризации устройств до безопасного чтения конфигураций.
Первый MCP-сервер для сетевого оборудования — это не инструмент «из коробки», а семя: минимальный каркас, который растёт по мере реальных запросов инженеров. Задача — не автоматизировать всё сразу, а создать безопасную читающую основу, на которую потом можно наращивать слои.
Это вторая часть семисерийного цикла про то, как связать ИИ-ассистентов с сетевым оборудованием через протокол Model Context Protocol (MCP). Первая часть объясняла, зачем сетевым устройствам вообще нужен MCP-сервер. Теперь автор цикла, Узи Голан, переходит от теории к практике: строит первую рабочую версию.
Что такое MCP и зачем это сетевику
Если совсем коротко: MCP — это протокол, который позволяет ИИ-модели (Claude, Copilot, ChatGPT и т.д.) вызывать внешние инструменты. Не просто генерировать текст, а реально выполнять действия — подключаться к устройствам, запрашивать данные, проверять статус.
Для сетевого инженера это означает: можно написать на естественном языке «покажи активные аварии на устройстве sf-163-187», и ассистент подключится по SSH, выполнит нужные команды, получит результат и вернёт его в удобном виде. Без того, чтобы вручную заходить на консоль, помнить синтаксис команд для каждого типа оборудования и копаться в сыром выводе.
Но прежде чем это заработает, нужно построить сервер, который умеет это делать. И вот тут начинается самое интересное.
Минимально полезная версия: что входит, а что — нет
Автор сразу задаёт рамки. Первый сервер умеет:
- вести инвентарь устройств (добавлять, удалять, обновлять);
- подключаться к оборудованию по SSH или telnet;
- выполнять разрешённые команды чтения (те самые
show); - проверять «здоровье» устройства (идентификация, активные аварии);
- сохранять бэкапы конфигураций.
При этом сервер не умеет менять конфигурацию, не содержит базы знаний об оборудовании, не поддерживает SNMP и не распределяется между несколькими пользователями. Всё это — темы следующих статей серии.
Подход честный и прагматичный: не пытаться сделать всё сразу, а выстроить каркас, который потом наращивается. Каждый новый слой появляется тогда, когда инженер задаёт вопрос, на который текущие инструменты не могут ответить.
Инвентарь: знаем, что подключено
Первый инструмент — не команда к оборудованию, а реестр устройств. Прежде чем ИИ обратится к оборудованию, он должен знать, что оно существует.
Добавление устройства требует ровно шести данных: имя, IP-адрес, семейство (тип устройства), группа (например, «lab» или «production»), логин и пароль. Если хоть одно поле пропущено, ассистент спросит всё недостающее за один раз, а не по очереди — важный нюанс для удобства работы.
Разделение паролей и данных — не обсуждается
Самое важное архитектурное решение: учётные данные никогда не хранятся в файле инвентаря. Файл инвентаря содержит только имя, адрес, тип и группу — его можно безопасно сохранять в Git, показывать коллегам, выводить в ответах ассистента. Пароли уходят в отдельный файл .env, который исключён из Git и виден только серверу.
Это не просто «хорошая практика» — это фундаментальная граница безопасности. Данные инвентаря и данные аутентификации живут в разных мирах.
Драйверы: один инструмент — много устройств
Технический центр всей конструкции — это абстракция драйверов. Ключевая идея: транспорт (как подключиться) и диалект команд (что писать в консоли) меняются независимо друг от друга.
Представьте, что вы работаете с разными автомобилями. Транспорт — это то, как вы садитесь в машину (через дверь, через люк, через задний багажник — зависит от модели). Диалект — это то, как вы управляете: рычаг, кнопка, сенсорный экран. Два разных измерения, и они не привязаны друг к другу жёстко.
В сетевом мире: SSH и telnet — это транспорт. А вот формат приглашения, навигация по контексту команд, именование портов, способ сохранения конфигурации — это уже «диалект», который зависит от семейства устройства. SecFlow использует ethernet 3, ETX-2 — ethernet 0/2, а Megaplex-4100 — ethernet 1/1/1. Попытка прописать все эти различия в одном скрипте приводит к тому, что к третьему семейству у вас три копии одного и того же кода, которые постепенно разъезжаются.
Абстракция драйверов решает это элегантно: общий диалект живёт в базовом классе, а различия между семействами — в наследниках, которые переопределяют только то, что реально отличается. Инструменту (например, run_show) всё равно, с каким конкретно оборудованием он работает: он вызывает драйвер, а драйвер уже знает, какую команду выполнить и как распарсить ответ.
Netmiko: транспортный слой
Для управления SSH-сессиями используется библиотека Netmiko — зрелый инструмент для Python, который поддерживают десятки типов оборудования. Если у вас уже есть автоматизация на Netmiko или NAPALM, MCP-сервер просто оборачивает ваши существующие примитивы в типизированные инструменты, которые может вызвать ИИ.
Однако Netmiko — это только транспорт. Политику (какие команды разрешены, что подлежит подтверждению, что логируется) задаёт сам MCP-сервер. Netmiko доставляет вас до устройства, а сервер решает, что там безопасно делать.
Постоянные сессии
Подключение по SSH занимает 5–7 секунд, и многие устройства отказывают в новой сессии, пока не завершится старая. Поэтому сессии кешируются: одна постоянная SSH-сессия на устройство, которая проверяется на «живость» после 60 секунд простоя и прозрачно пересоздаётся при необходимости.
Это и решение о производительности, и решение о безопасности: ассистент не открывает новое SSH-соединение на каждый вызов инструмента, не забивает таблицу сессий на оборудовании и может выполнять цепочки команд без накладных расходов на переподключение.
Чтение по приглашению, а не по таймеру
Ещё один важный технический момент: каждое чтение завершается не по таймауту тишины, а в момент, когда на экране снова появляется приглашение устройства (prompt). Это кажется мелочью, но на практике решает серьёзную проблему.
Например, SecFlow-1p детерминированно делает паузу больше трёх секунд в середине вывода info. Если ориентироваться на таймер тишины, вывод обрезается — и часть конфигурации просто теряется. Ориентировка на приглашение устройства — детерминированна, а ориентировка на таймер — вероятностна. В сетевых операциях детерминированность побеждает всегда.
Первые инструменты: только чтение
Каждый инструмент следует одной модели: пользователь спрашивает на естественном языке, ассистент вызывает инструмент, инструмент выполняется на устройстве и возвращает результат.
add_device— регистрация устройства в инвентаре (шесть обязательных полей, пароли сразу уходят в защищённое хранилище).list_devices— вывод списка устройств (без паролей, с фильтрацией по семейству или группе).test_connectivity— проверка доступности по SSH (только проверяет, что сессия устанавливается, не выполняет команд).run_show— выполнение разрешённых команд чтения. Ассистент сам навигирует по нужному контексту на устройстве и выполняет команду. Важно: принимаются только «белые» команды, а не произвольные строки — защита от инъекции.run_show_in_context— вариант для устройств, где команды нужно выполнять из определённого контекста (например,configure reporting→show active-alarms).cli_help— отправка?на устройство и получение дерева доступных команд. Ничего не выполняет — только читает встроенную справку.get_config— экспорт полной конфигурации в локальный архив, с диффами относительно предыдущих бэкапов.health_check— проверка «здоровья» устройства: идентификация, активные аварии, базовая проверка связности. Результат возвращается в структурированном виде, а не как сырой текст.
Каждый инструмент защищён: белые списки команд, валидация контекстов, строгая проверка символов, отсутствие паролей в ответах и логах.
Почему важно не возвращать всё подряд
Принципиальный момент в архитектуре: инструменты чтения должны возвращать нужное количество данных в нужной форме, а не всё, что прислало устройство.
Команда show может вернуть 20 строк на одном оборудовании и 2000 — на другом. Экспорт конфигурации может занять 15 тысяч строк. Отправить всё это в контекст модели — значит потратить огромное количество токенов, ухудшить качество рассуждений и получить худший ответ, а не лучший.
Автор описывает три направления фильтрации:
- Фильтрация по объёму — не любая команда, а только разрешённые. Белый список формируется для каждого семейства.
- Фильтрация по форме — структурированные данные вместо сырого текста CLI. Модель работает с полями быстрее и точнее, чем с потоком символов.
- Фильтрация по детализации — разный уровень глубины для разных запросов. «Покажи аварии» — одна детализация, «покажи справочник аварий с описаниями и рекомендациями» — совсем другая.
Для больших наборов данных используется паттерн «сначала поиск, потом чтение»: сначала compact-выдача с отрывками и указателями, и только если ответа нет в отрывке — запрос конкретного фрагмента по стабильному идентификатору.
Чего сервер пока не умеет
Автор честно перечисляет ограничения, и это важная часть материала:
- Нет записи конфигураций. Изменения на оборудовании требуют модели безопасности: staged commits, предпросмотр диффов, явное одобрение, аудит. Это тема третьей статьи.
- Нет базы знаний. Сервер может получить вывод команды, но не знает, что она означает, что говорит справочник аварий или какие функции доступны на конкретной прошивке. Слои знаний — тема пятой статьи.
- Нет SNMP. CLI — единственный путь доступа к устройству. SNMP даёт второе