Разработка ИИ-агентов · Модуль 4 · Урок 4.3

Подключение агента к MCP-серверам

Хост, клиент и подключение

С другой стороны протокола — хост: ваше приложение-агент. Внутри него работает MCP-клиент, который устанавливает соединение с сервером. Для stdio это значит запустить сервер как подпроцесс и говорить через его stdin/stdout; для удалённого — открыть HTTP-соединение. Часто хост держит несколько клиентов — по одному на каждый подключённый сервер.

После соединения клиент выполняет рукопожатие initialize и запрашивает tools/list — узнаёт, какие инструменты предлагает сервер. Эти описания приходят уже в виде «имя + описание + JSON Schema» — то есть в точности том формате, который понимает модель.

Мост между MCP и tool calling модели

Ключевая идея подключения: инструменты MCP-серверов превращаются в обычные tool-схемы для модели. Хост собирает инструменты со всех серверов, объединяет их (следя за уникальностью имён — например, префиксуя именем сервера) и передаёт модели как её tools. Дальше работает знакомый агентный цикл из Модуля 1:

  1. Модель в ответ запрашивает вызов инструмента (tool_calls).
  2. Хост по имени понимает, какому серверу он принадлежит, и шлёт туда tools/call с аргументами.
  3. Сервер выполняет и возвращает результат; хост дозаписывает его как tool-сообщение модели.

То есть MCP не меняет цикл агента — он лишь источник инструментов. Модель по-прежнему не знает о JSON-RPC; для неё это просто инструменты.

Безопасность и жизненный цикл

Подключать чужой MCP-сервер — это давать агенту новые возможности, в том числе потенциально опасные. Правила те же, что в Модуле 2–3, но усиленные: подключайте только доверенные серверы; помните про prompt injection — описание инструмента или его результат могут содержать вредоносные инструкции, поэтому к выводу MCP-инструмента относитесь как к недоверенным данным; держите программные gate'ы и human-in-the-loop на необратимых действиях, откуда бы инструмент ни пришёл.

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

Инструменты MCP-сервера → tool-схемы модели; маршрутизация вызова
// mcpClient — упрощённый клиент: умеет ListTools и CallTool по JSON-RPC.
type mcpClient interface {
    ListTools() ([]Tool, error)
    CallTool(name string, argsJSON string) (string, error)
}

// collectTools собирает инструменты всех серверов и помнит, кто кому принадлежит.
func collectTools(clients map[string]mcpClient) ([]Tool, map[string]string, error) {
    var all []Tool
    owner := map[string]string{} // имя инструмента -> id сервера
    for serverID, c := range clients {
        tools, err := c.ListTools()
        if err != nil {
            return nil, nil, err
        }
        for _, t := range tools {
            name := serverID + "__" + t.Function.Name // префикс для уникальности
            t.Function.Name = name
            owner[name] = serverID
            all = append(all, t)
        }
    }
    return all, owner, nil // all уходит модели как её tools
}

// При tool_calls от модели маршрутизируем вызов нужному серверу:
//   serverID := owner[call.Name]
//   result, _ := clients[serverID].CallTool(strings.TrimPrefix(call.Name, serverID+"__"), call.ArgsJSON)
//   // дозаписываем result как сообщение роли "tool" (агентный цикл Модуля 1)

Anti-patterns

Анти-паттернПочему плохоКак правильно
Подключать любой сервер без проверки доверияЧужой код/инструкции получают доступ к агентуТолько доверенные серверы; принцип наименьших привилегий
Доверять выводу MCP-инструмента как фактуPrompt injection через описание/результатОтноситься к выводу как к недоверенным данным; gate на действия
Коллизии имён инструментов с разных серверовМаршрутизация вызова ломаетсяПрефиксовать имена сервером, хранить карту owner
Игнорировать падения/таймауты сервераЗависший подпроцесс держит весь агентТаймауты, обработка падения и переподключение, корректное завершение

Практическое задание

  • Подключите к агенту свой MCP-сервер из урока 4.2 (запуск подпроцессом, рукопожатие, tools/list).
  • Преобразуйте полученные MCP-инструменты в tool-схемы модели, префиксуя имена сервером.
  • В агентном цикле маршрутизируйте tool_calls нужному серверу через tools/call и дозаписывайте результат.
  • Поставьте таймаут на вызов инструмента и обработайте падение/недоступность сервера.
  • Оставьте программный gate на необратимом действии независимо от того, с какого сервера пришёл инструмент.

Проверка знаний

Как инструменты MCP-сервера попадают к модели?

  • A Модель сама подключается к серверу по JSON-RPC
  • B Хост получает их через tools/list и передаёт модели как обычные tool-схемы; вызовы маршрутизируются обратно в tools/call
  • C Они зашиваются в веса модели
  • D Никак — MCP не связан с tool calling

Зачем префиксовать имена инструментов идентификатором сервера?

  • A Для красоты
  • B Чтобы избежать коллизий имён между серверами и правильно маршрутизировать вызов нужному серверу
  • C Это требование модели
  • D Чтобы скрыть инструменты от пользователя

Как относиться к результату, который вернул MCP-инструмент?

  • A Как к абсолютно достоверному факту
  • B Как к недоверенным данным: он может содержать prompt injection; критичные действия защищать программными gate'ами
  • C Его можно не передавать модели
  • D Его нужно выполнять как код