~/potatohd.ruEN
← Все статьи
28 июня 2026·8 мин

Как я подключил ИИ-агентов к своей инфраструктуре через MCP

MCPAI toolingself-hosted

Из 8+ MCP-серверов, которые в итоге прижились в моей инфраструктуре, один сначала наотрез отказывался работать: TLS-терминатор перед базой не анонсировал нужное расширение согласования протокола, и клиент с прямым SSL не мог завершить хендшейк. Это оказалась самая частая история во всей затее: почти каждый MCP-сервер из коробки рассчитан на облачный продукт вендора, а трекер задач, платформа деплоя, аналитика, observability и биллинг у меня свои, self-hosted. Почти всё пришлось перепрошивать вручную, сервер за сервером, прежде чем агенты смогли реально что-то через них делать.

#Заточено под чужое облако

Схема повторялась почти везде: MCP-сервер зашит под OAuth-обмен с облачным эндпоинтом вендора, а до self-hosted инстанса так просто не достучаться. Логика понятна со стороны вендора: у них один облачный OAuth-провайдер на всех клиентов, и клиент по умолчанию собран именно под него. Self-hosted инстанс в эту схему не укладывается — регистрировать там OAuth-приложение попросту не у кого. Трекер задач — самый наглядный случай. Вместо OAuth-потока ему хватило трёх простых вещей: URL моего собственного API, идентификатор workspace и обычный API-ключ в заголовке запроса — тот же ключ, что уже стоит в остальных интеграциях. Сами поля оказались очевидны, как только до них добрались; путь к ним лежал через документацию, которая по умолчанию описывает только облачный сценарий и про self-hosted режим молчит вообще.

#Поднять чужой MCP-сервер самому

Аналитика оказалась самым долгим квестом во всей подборке. Первая попытка — официальный MCP от самого вендора — отвалилась сразу: он умеет говорить только с их облачным продуктом и до self-hosted инстанса не дотягивается никак, в конфиге просто нет поля под свой хост. Вторая попытка выглядела многообещающе: отдельный npm-пакет, в описании которого прямо заявлена поддержка self-hosted. Установка прошла гладко, а внутри пакета не нашлось ни одного исполняемого бинарника — библиотека без сервера внутри, годная только на то, чтобы обернуть её во что-то ещё самому.

Сработал третий заход: тот же вендор публикует готовый, уже собранный Docker-образ своего MCP-сервера — тот же образ, что использует у себя. Я поднял его как ещё один маленький сервис в своей инфраструктуре, рядом со всем остальным. Первый запуск тут же не задался: контейнер поднимался и почти сразу останавливался, без единой попытки повторить, а в логах читалось одно и то же — сервер тянется к Redis и не находит его. Зависимость оказалась жёсткой: без доступного Redis сервер не поднимается вообще, ни в каком урезанном режиме. Указал ему на уже существующий в инфраструктуре Redis — и он поднялся с первой попытки. Аутентификация в итоге оказалась самой простой частью всей истории: персональный API-ключ, который летит bearer-токеном в заголовке каждого запроса.

#Мелочи, что съели время

Пара историй помельче, но каждая съела времени непропорционально своему размеру. У платформы деплоя MCP-сервер аутентифицируется обычным API-ключом в заголовке — простейшая часть настройки. Её CLI на первом запуске временами репортит, что не может подключиться, и первый порыв — перепроверять ключ, URL, права доступа. Причина почти всегда куда скучнее: холодный старт платформы не укладывается в таймаут health-check, с которым CLI пытается достучаться до неё в первые секунды после запуска. Правильная реакция — просто повторить попытку через несколько секунд, прежде чем перепроверять конфиг с нуля.

Со стороны observability-платформы сюрприз был другого рода. Почти все MCP-серверы в этой подборке приходят npm-пакетом — npx и готово. Этот пришёл Go-бинарником, единственный во всей подборке: файл под свою архитектуру, права на выполнение, путь в конфиге агента — процедура на порядок отличается от привычного npx имя-пакета. Аутентификация держится на заголовке, чьё имя легко набрать неточно: ошибиться в регистре символов или в формате самого имени — и сервер просто не аутентифицирует запрос, без ошибки, которая указывала бы, в чём именно дело.

#Дашборд и API на разных хостах

С биллингом ошибка напрашивалась сама: в MCP-конфиг хотелось вписать тот же хост, что открыт в браузере для входа в панель биллинга. Хост оказался неподходящим. Настоящий API биллинга живёт на отдельном домене — похожем по названию, но другом, — и именно туда MCP-серверу нужен URL вместе со своим API-ключом в заголовке; хост дашборда, за которым стоит логин, для этого не годится, потому что MCP обращается к API напрямую, минуя браузерную сессию.

Это самая обычная форма self-hosted UI в этой подборке: отдельный SSO-шлюз закрывает вход во фронтенд логином и сессией, а API под тем же продуктом стоит у себя, на другом хосте, вообще без отношения к SSO-шлюзу. Из одной вкладки браузера эта граница не видна: дашборд открывается, всё работает, и только когда MCP-сервер пытается достучаться до API напрямую, выясняется, что нужный хост — не тот, что в адресной строке.

#Базы напрямую

К остальным базам MCP-серверы подключены без посредников: колоночная аналитическая база, кэш и векторная база, у каждой свой способ аутентификации и свой клиент. Набор переменных подключения на каждую — и всё работает сразу, без сюрпризов.

Реляционная база, Postgres, — тот самый случай из открытия — оказалась единственным местом во всей истории, где пришлось чинить инфраструктуру уровнем ниже: TLS-прокси перед базой, который об MCP вообще ничего не знает. MCP-клиент подключался напрямую по SSL, и хендшейк обрывался на согласовании протокола — раньше того момента, когда Postgres вообще успел бы увидеть запрос и что-то на нём проверить. Ошибка на этом уровне ничего не говорит ни о правах доступа, ни о самой базе: она значит только то, что два конца TLS-соединения не смогли договориться, на каком протоколе говорить дальше. Причина нашлась в конфиге TLS-прокси: не хватало ровно одного имени протокола в списке того, что прокси готов согласовывать при подключении. Добавил недостающее имя, перезапустил прокси — и хендшейк наконец прошёл.

#Один шлюз на всё

Ещё один слой стоит ниже всего перечисленного. Self-hosted кластерный LLM-шлюз, поднятый поверх open-source LLM-роутера, задеплоен на той же платформе, что и всё остальное здесь — просто ещё один сервис в общей инфраструктуре, без отдельного облака под него. Смысл в том, чтобы каждый внутренний сервис и каждый агент ходил к языковым моделям через одну и ту же точку входа, вместо того чтобы каждый держал свой набор ключей от своего провайдера.

Роль этого слоя проще всего понять через то, что стоит рядом с ним. MCP-серверы — это то, как агенты дотягиваются до моих систем: трекера задач, деплоя, баз. Шлюз — то, как те же агенты дотягиваются до самих языковых моделей. Один слой отвечает за то, куда агент может пойти внутри инфраструктуры, второй — за то, к каким моделям он может обратиться снаружи неё.

#Короткий поводок для агентов

На последнем шаге под ограничение попали сами агентские CLI-инструменты. Два форкнутых open-source CLI для ИИ-кодинга, которыми я пользуюсь, собраны так, что каждый физически может достучаться ровно до одного эндпоинта и одного креда, зашитых на этапе сборки, — до того самого внутреннего LLM-шлюза, и больше никуда. Любой другой исходящий путь блокируется на уровне сети: правило стоит в файрволе, снаружи процесса, и сам инструмент до него не дотягивается, а значит и обойти его не может.

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

// свободен для заказов

Нужен сайт или настройка под ключ?