Содержание
- Четыре способа дать агенту руки
- Один разъём вместо самодельных адаптеров
- Спецификация переехала на запросы без сессий
- Цена протокола, посчитанная по одной оси
- Чужой сервер — это чужой продакшен
- Чего стоят описания инструментов и во что обходится их починка
- Сколько стоит каталог инструментов в токенах
- Свой сервер: фасад бизнес-действий вместо экспорта API
- Что остаётся на вашей стороне
- Чего никто не померил
- Итог
- FAQ
- Источники
«Собери корзину: молоко, яйца, овсянка, бананы» — обычная просьба, написанная человеком в чат. Через минуту агент возвращает ссылку на настоящую корзину в интернет-магазине, где лежат четыре товара примерно на 522 рубля. Оформляет заказ дальше человек, как любой другой. Товары подобрал агент, и под этот магазин никто не писал ни строчки кода. У магазина есть публичный сервер, который сам рассказывает любому пришедшему агенту, что у него можно спросить и что можно сделать.
Со стороны разработчика подключение занимает три строки. В них помещаются адрес сервера, способ связи и перечень инструментов, которые агенту разрешено видеть. Отсюда растёт понятный соблазн: раз это почти бесплатно, подключим тем же движением внутреннюю CRM, базу заявок и биллинг. Дальше обнаруживается, что подключение было самой дешёвой частью работы. От чьего имени агент только что создал заявку, что этому человеку вообще позволено и что записалось в журнал — на эти вопросы Model Context Protocol не отвечает вовсе, и закрывать их придётся вашим кодом ровно в том же объёме, что и до него.
Дневник курса, урок 17. Здесь понадобится разобранное раньше: обрыв точности при росте каталога инструментов (урок 1) — та кривая ниже уточняется по трём пунктам; протокол A2A (урок 10) — парная ось связи: там агент договаривается с агентом, здесь агент дотягивается до инструмента; модель угроз агента (урок 9) — чем опасен чужой компонент в цепочке поставки. Пост читается отдельно: все термины вводятся заново.
Сквозным примером снова идёт чат-бот банка. У него 8000 обращений в сутки, около пятнадцати ходов на диалог и доступ к данным счетов.
1. Четыре способа дать агенту руки
Вопросами про тарифы бот банка занимается давно. Теперь от него хотят действий. Пусть посмотрит статус заявки клиента, создаст новую, проверит лимит по счёту. Дотянуться до внутренней системы можно четырьмя способами, и три из них никаких протоколов не требуют. Речь дальше только про канал вызова, а события, выгрузки данных и формы для человека живут своими путями. Выбирают между четырьмя по границе задачи. Она проходит там, где кончается ваш код и начинается чужой процесс, чужая машина, чужая организация или чужой цикл релизов. Чем дальше проходит эта граница, тем больше смысла в отдельном протоколе; когда её нет совсем, протокол добавляет слой без выигрыша.
| Способ | Что это | Когда уместен | Чем платите |
|---|---|---|---|
| Функция или SDK | код в том же процессе, что и агент | инструмент нужен одному агенту | другим агентам переиспользовать нечего |
| Прямой вызов API | REST или OpenAPI прямо из кода агента | у сервиса зрелая инфраструктура: шлюз, мониторинг | каждый агент делает себе инструменты заново |
| Консольная утилита | команда в терминале, агент её запускает | утилита уже есть и умеет цепочки с пагинацией | в веб- и мобильных агентах не работает |
| Отдельный сервер по общему протоколу | процесс, который сам публикует список инструментов и их схемы | когда сходятся условия окупаемости | ещё один компонент в проде и его сопровождение |
Отсеять лишнее помогают три вопроса. Нужна ли эта интеграция нескольким агентам, командам или клиентам? Проходит ли между агентом и системой граница, за которой начинается удалённая работа, авторизация от имени пользователя или отдельный цикл релизов? Меняется ли набор инструментов так, что агент должен узнавать о новых сам? Три «нет» подряд означают, что отдельный сервер не окупится и хватит функции или прямого вызова.
Те же вопросы, развёрнутые подробнее, дают пять условий окупаемости:
- переиспользование — одну интеграцию подключают разные агенты, клиенты и команды;
- удалённая работа — агент живёт в браузере или песочнице и локально ничего поставить не может;
- авторизация от имени пользователя — агент действует от лица конкретного человека, а секреты и токены остаются вне промпта;
- независимый жизненный цикл — интеграцию обновляет одна команда, промпты правит другая, и релизы у них разъезжаются;
- динамическое обнаружение — набор инструментов зависит от прав, версии или подписки, и агент должен узнавать о нём сам.
Практическое правило простое. Сойтись должны хотя бы два-три условия из пяти. Замера под ним нет, потому что ни в одной проверенной работе окупаемость по числу выполненных условий не считали. Это накопленная эмпирика, и порог каждый двигает под свою ситуацию.
Наш банковский агент набирает три условия сразу: заявки нужны не только ему, но и внутреннему ассистенту операционистов. Живёт он в облаке и до внутренностей системы напрямую не дотягивается. Интеграцию сопровождает своя команда, а промпты правит команда агента.
Ошибаются здесь в обе стороны одинаково. Зрелую CRM с полным и живым описанием методов обычно подключают напрямую, вызовами API и консольной утилитой. В одном описанном случае отдельный сервер завели только под остаток вроде расшифровок встреч. От спора спасает одна формулировка. «Это новый стандарт» аргументом не считается; аргумент — какую конкретную стоимость протокол убирает.
2. Один разъём вместо самодельных адаптеров
Допустим, условия сошлись. Что мы получаем взамен ещё одного компонента в проде? Взамен исчезает комбинаторика адаптеров, и это единственное приобретение. Пока каждый агент ходит в каждую систему по-своему, три агента и три системы дают девять кусков интеграционного кода, и каждый кто-то поддерживает. Общий интерфейс превращает M×N в M+N. Система один раз выставляет сервер, агент один раз обзаводится клиентом, дальше они узнают друг о друге по общим правилам.
У этого общего интерфейса есть имя и спецификация. MCP (Model Context Protocol) — открытый протокол, по которому агент спрашивает у сервера список доступных инструментов вместе со схемами аргументов, а потом вызывает их. Anthropic опубликовала его в ноябре 2024 года, в 2025-м подхватили OpenAI, Google и Microsoft, к 2026-му он разошёлся достаточно широко, чтобы публичные серверы держали магазины и трекеры задач.
Масштаб корпоративного использования иллюстрируют цифрами Atlassian. Миллион с лишним пользователей в месяц, свыше пяти миллионов вызовов в день, около трети из них меняют данные. Это заявление вендора, определений «вызова» и «пользователя» рядом с числами не приводится, независимой проверки под ними нет. Как порядок величины годится, как измерение — нет.
HOST (приложение с моделью: IDE, чат, ваш сервис)
┌────────────────────────────────┐
│ модель ──► MCP-клиент №1 ─────┼──► сервер A ──► CRM (REST API)
│ ──► MCP-клиент №2 ─────┼──► сервер B ──► база заявок
└────────────────────────────────┘
одно соединение — один сервер
без общего интерфейса: M агентов × N систем = M×N адаптеров
с общим интерфейсом: M клиентов + N серверов = M+N подключенийСервер отдаёт клиенту три вида возможностей, и различаются они тем, кто решает пустить их в дело. В tools лежат действия, которые выбирает модель, вроде «найти товар» или «создать заявку». В resources лежат данные, которые кладёт в контекст само приложение, без хода модели. Это каталог, файл, страница из хранилища. В prompts лежат готовые сценарии, которые запускает человек кнопкой или командой со слэшем.
Короче говоря, tools служат модели, resources — приложению, prompts — пользователю. Сервер магазина из вводного примера обошёлся первым видом: восемь инструментов, и сборка корзины прошла целиком через них, а ни resources, ни prompts не понадобились.
Едет всё это поверх JSON-RPC 2.0. Это простой формат вызова процедур, где запрос и ответ представляют собой объекты с полями method, params и id. Транспортов два: stdio запускает сервер локальным процессом и общается с ним через стандартный ввод-вывод. Streamable HTTP работает с удалённым сервером по обычному HTTP. Первый вариант — для инструментов на той же машине, что и агент; второй — для всего остального.
3. Спецификация переехала на запросы без сессий
Если каталог инструментов отдаёт сервер, а протокол при этом меняется, стороны должны как-то договориться о версии. Раньше они так и делали, отдельным рукопожатием в начале разговора. Теперь не договариваются вовсе. Каждый запрос несёт свою версию сам, и сервер принимает или отклоняет его независимо от остальных.
Хронология точная: кандидат в новую спецификацию заморозили 21 мая 2026 года, финальную версию 2026-07-28 опубликовали в назначенный срок, и она помечена как текущая. Десять недель между этими датами отводились сопровождающим SDK и авторам клиентов на то, чтобы прогнать изменения по живым нагрузкам. Срок известен заранее именно потому, что ломающих изменений в ревизии много.
было (2025-11-25 и раньше) стало (2026-07-28)
initialize — рукопожатие → вызова нет; версия протокола
один раз на сессию и возможности клиента едут
в поле _meta каждого запроса
сессия в заголовке → заголовка нет; состояние между
Mcp-Session-Id вызовами сервер отдаёт явным
ключом, который модель передаёт
аргументом следующего вызова
обрыв потока: дослать → запрос потерян; клиент обязан
пропущенное по Last-Event-ID повторить его с новым id
Roots, Sampling, Logging → объявлены устаревшими2026-07-28: рукопожатие initialize убрано, а версия и возможности клиента едут в _meta каждого запроса; заголовок сессии Mcp-Session-Id удалён, а состояние между вызовами переносится явным ключом, который модель передаёт аргументом следующего вызова; возобновление оборванного потока по Last-Event-ID убрано, и клиент обязан повторить запрос с новым идентификатором; примитивы Roots, Sampling и Logging объявлены устаревшими.Зачем понадобилось ломать привычную схему? Мотив прикладной и совершенно не идеологический: любой запрос теперь приземляется на любой инстанс сервера за обычным балансировщиком, который раскидывает нагрузку по кругу. Пока сессия жила в протоколе, горизонтальное масштабирование требовало привязки клиента к одному инстансу, общего хранилища сессий и разбора пакетов на шлюзе — то есть ровно тех вещей, от которых в бэкенде обычно бегут. Сопровождающие формулируют выигрыш именно так.
Три убранных примитива придётся расшифровать, потому что в чужой документации они ещё встречаются. Roots позволяли клиенту сообщить серверу, с какими каталогами и файлами тот работает; вместо них спецификация советует передавать пути обычными параметрами инструмента. Sampling разрешал серверу попросить хоста сходить в модель за него, и на смену ему предлагается прямая работа с API провайдера. Logging гнал логи сервера через протокол клиенту, а теперь их пишут в stderr или отправляют по OpenTelemetry. Отдельно объявлена устаревшей динамическая регистрация клиентов (Dynamic Client Registration) в OAuth. Вместо неё метаданные клиента публикуются документом по известному адресу.
Одно из этих изменений задевает работу агента напрямую — оборванный поток ответа больше не восстанавливается. Запрос, который был в полёте, теряется, и клиент обязан повторить его с новым идентификатором. Требование это нормативное и адресовано клиенту, а попадает оно ровно в тот слой, который держит сервис на плаву при сбоях (урок 16). Повторять безопасно только идемпотентный вызов, иначе заявка создастся дважды.
Экосистема за спецификацией не поспевает, и это нормальное состояние на ближайший год. Публичный сервер магазина из вводного примера отвечает по ревизии 2025-03-26, которой уже полтора года. Клиент у вас свежий, сервер на той стороне — из прошлой эпохи, и уживаться им придётся.
4. Цена протокола, посчитанная по одной оси
Часть цены видна сразу и без всяких атак. В проде появляется лишний компонент со своим деплоем и мониторингом, путь отладки удлиняется до пяти звеньев (модель → host → клиент → сервер → целевая система), добавляются дрейф схем и кэши клиентов. Труднее ответить на другой вопрос: протокол делает систему уязвимее сам по себе или дело в кривых реализациях?
Замер по этой оси нашёлся ровно один. Авторы прогнали 847 сценариев атаки на пяти реализациях сервера и каждый сравнили с эквивалентной интеграцией без протокола, где работали тот же агент, те же данные и та же целевая система, подключённая напрямую. Успех атаки через протокол оказался выше на 23–41%. Авторы называют эти слабости архитектурными и предлагают чинить их на уровне самого протокола.
Цепочка из пяти звеньев, к слову, делает сквозной идентификатор запроса условием того, что инцидент вообще получится разобрать. Без него на вопрос «где сломалось» есть пять равновероятных ответов.
Разница набирается там, где протокол никого не проверяет. Полномочия сервера ничем не подтверждаются, он объявляет о себе что угодно, и сверить объявленное не с чем. Sampling — обратный вызов к модели — не проверял происхождение запроса, так что сервер мог подсунуть модели собственный промпт, и сам Sampling спецификация теперь объявила устаревшим. Наконец, в конфигурации из нескольких серверов доверие распространяется неявно: все подключённые серверы видны одному и тому же контексту модели, и то, что пришло от одного, влияет на вызовы к другому.
Последний пункт полезно сопоставить с протоколом A2A (урок 10). Там разбиралось, как агенты разных владельцев общаются, намеренно закрывая внутренности друг друга, и как исполнитель предъявляет карточку возможностей. Здесь картина обратная. Вертикаль «агент → инструменты» тоже переносит доверие, только вместо закрытых границ она внутренности смешивает, и подписи под заявленными возможностями у сервера нет. В этой части протокол подключения инструментов слабее протокола связи агентов.
Обращаться с числами надо осторожно. Работа опубликована двумя авторами препринтом, площадка публикации не указана, а пять реализаций дают маленькую выборку. Ценность её в другом: остальные найденные замеры безопасности протокола сделаны в вакууме, а здесь есть сравнение с прямой интеграцией при прочих равных. Авторы предлагают и расширение, которое сбивает успех атаки с 52,8% до 12,4% ценой 8,3 мс медианных накладных на сообщение. Но это цена расширения, которого в стандарте нет.
5. Чужой сервер — это чужой продакшен
Архитектурная цена принята, и вопрос переходит на этаж ниже. Насколько хороши сами серверы, которые мы подключаем? Здоровье, безопасность и сопровождаемость серверов померили на выборке из 1 899 открытых проектов. Обычные уязвимости нашлись у 7,2%, отравление описаний инструментов (tool poisoning) — у 5,5%, шаблоны багов, знакомые по прошлым работам, — у 14,4%. Интереснее тут не доли, а состав. Из восьми найденных классов уязвимостей с традиционными пересекаются только три, пять оставшихся ваш существующий контур проверки просто не ищет, и авторы прямо пишут, что под протокол нужны отдельные средства поиска.
Отдельный прогон по 39 884 репозиториям дал 106 подтверждённых дыр, о которых сопровождающие ещё не знали, и 67 из них получили номер в общем реестре уязвимостей (CVE). Это самое твёрдое число из всего, что удалось найти про безопасность экосистемы. Вывод отсюда знакомый по любой сторонней библиотеке — посмотреть, кто ведёт проект, и взять ровно то, что нужно.
Дальше полезно развести два разных механизма отказа, потому что фразой «чужой сервер опасен» описана только половина картины.
Случай первый. Сервер работает как задумано. У пользователя два репозитория на GitHub, публичный и приватный, и агент имеет законный доступ к обоим. Атакующий заводит в публичном репозитории задачу, в текст которой вложена инструкция для агента. Пользователь просит агента посмотреть открытые задачи, агент читает вложенную инструкцию и выносит данные из приватного репозитория в публичный. Дефекта в сервере нет, патчить нечего. Подмена инструкций через чужой текст называется промпт-инъекцией (prompt injection), и здесь она просто проложила маршрут между двумя разрешёнными доступами. Закрывается это только границами прав на своей стороне.
Случай второй. Сервер ваш, а прав у него слишком много. Сервер к базе запускали под служебной ролью, которая обходит построчные ограничения доступа. Инъекция приезжала обычным тикетом в публичную поддержку, агент по просьбе «подведи итоги тикетов» читал приватную таблицу с токенами и дописывал их в таблицу, читаемую всеми.
Здесь в одном агенте сошлись те три условия, которые разбирались в модели угроз (урок 9). Приватные данные, недоверенный текст и канал наружу называют смертельной тройкой (lethal trifecta), и пока все три сходятся на одном агенте, утечка возможна без единой ошибки в коде. Свой сервер под привилегированной учёткой опаснее чужого, потому что чужому вы хотя бы не доверяете по умолчанию.
Отказ, который вы увидите на практике, обычно скучнее и живёт сразу на четырёх уровнях.
вызов инструмента вернулся. на каком уровне искать отказ:
1. HTTP-статус 429, 503 транспорт не довёз запрос
2. ошибка JSON-RPC {"error":{"code":...}} сломан сам вызов
3. поле isError "isError": true инструмент сообщил об отказе
4. тело ответа {"ok": false, статус 200, isError нет,
"code":"rate_limited"} а внутри контента — отказ
проверка одного только HTTP-статуса пропускает три остальных уровняerror протокола JSON-RPC, флаг isError самого протокола и, наконец, отказ внутри формально успешного содержимого ответа. Проверка только первого уровня пропускает три остальных.Живой прогон по публичному серверу магазина добавляет к этому наблюдения, которых нет в его документации. Параллельные вызовы возвращают 429. Сервер терпит только последовательные запросы, и повторять их надо с растущей паузой. Поиск понимает запрос буквально, и строка «бородинский хлеб» приводит к влажным салфеткам «Бородинский хлеб». Прикладной результат приходится проверять, каким бы успешным ни выглядел ответ.
Дальше расхождение схемы с описанием: описание обещает от одной до двадцати позиций, схема того же инструмента разрешает тридцать. Держаться надо строгого предела, потому что модель верит схеме, а человек читает описание. Наконец, поле annotations пустое, сервер не сообщает, какая операция только читает, а какая меняет данные необратимо, так что отличить безопасный вызов от опасного хост не может. Всё это снято в одном прогоне; свойством экосистемы такие находки не назовёшь. Но именно в таком виде чужой сервер вам и достаётся, без предупреждений в документации.
Собирается из этого короткий порядок действий перед тем, как подключать чужое:
- Список разрешённого (allowlist). Агенту видны только нужные инструменты, а не весь каталог. Из восьми инструментов магазина сценарию «собери корзину» хватает двух.
- Лимиты. Вызовы последовательные, на 429 пауза и повтор.
- Проверка прикладного результата. Первый ответ не берётся на веру.
- Все четыре уровня ошибки. Одного HTTP-статуса мало.
- Секреты вне промпта и вне кода, который пишет модель.
6. Чего стоят описания инструментов и во что обходится их починка
Модель выбирает инструмент по его описанию, и другого источника сведений у неё нет. Насколько на эти описания можно рассчитывать, померили. В выборке из 856 инструментов на 103 серверах хотя бы один дефект описания нашёлся у 97,1%, а 56% описаний не формулируют внятно, зачем инструмент нужен. В пересказах, которые попались, число превращается в «дефекты у 97% серверов», и единица счёта подменяется. Доля посчитана по 856 описаниям, а не по 103 серверам, и вывод «почти каждый сервер плох» из работы не следует.
Напрашивается очевидная правка: раз описания плохие, надо их дописать. Те же авторы это и проверили.
| Что происходит, если дописать описания | Величина | Как читать |
|---|---|---|
| растёт успешность задачи | +5,85 п.п. по медиане | абсолютная прибавка к доле решённых задач |
| растёт частичное достижение цели | +15,12% | в работе — просто процент, то есть относительный рост |
| растёт число шагов исполнения | +67,46% | относительный рост к прежнему уровню |
| результат становится хуже | в 16,67% задач | каждая шестая задача |
Механика понятная. Подробное описание перечисляет больше вариантов применения и больше оговорок, модель видит больше зацепок и начинает дробить задачу на дополнительные вызовы, включая те, без которых обошлась бы. Выигрывают, по выводу авторов, компактные описания, которые сохраняют надёжность поведения и не тащат лишних токенов. Совет «напишите описания получше» звучит как улучшение, а работает как размен.
В уроке про контракты инструмента (урок 15) речь шла о качестве схемы, которую пишете вы сами. Строгий режим, сообщение об ошибке, пригодное для самоисправления, контрактные тесты против расползания. Здесь тот же сюжет виден с другой стороны. Речь о качестве того, что вам отдают, и своими средствами оно не чинится.
Контрактный тест ловит расхождение только на своей стороне границы. Спецификация же разрешает описывать вход и выход любыми ключевыми словами JSON Schema 2020-12, а детерминированного порядка выдачи каталога требует лишь на уровне рекомендации, то есть тест, проверяющий порядок каталога, законно поплывёт на чужом сервере. Расхождение «описание обещает двадцать, схема разрешает тридцать» для чужого сервера законно, поломкой оно не считается.
7. Сколько стоит каталог инструментов в токенах
Схемы инструментов приезжают в контекст до того, как модель увидит задачу, значит, за каталог платят на каждом запросе. Сколько именно? Открытый pull request в репозитории github/github-mcp-server приводит замер с опубликованной методикой. Вышло 54 422 токена на 44 инструмента дефолтного набора, то есть около 1 240 токенов на инструмент. Повторить замер можно самому, он укладывается в пять строк. Автор формулирует принцип прямо: числа, которые можно оспорить, лучше чисел, которые пересказывают.
Принцип полезно приложить к двум другим ходовым цифрам про экономию контекста.
| Ходовое число | Что за ним есть | Чего за ним нет |
|---|---|---|
| контекст 150 000 → 2 000 токенов | дословная публикация Anthropic про исполнение кода поверх протокола | модель, число серверов, число инструментов, способ подсчёта — условий замера не приводится вовсе |
| −23 000 токенов схем, −50% | подтверждённая консолидация каталога GitHub: 14 инструментов Actions и 9 инструментов Projects свелись к семи укрупнённым | первоисточника чисел нет; «23 000» и «−50%» пришли из разных вторичных блогов и склеились в одно утверждение |
| 54 422 токена на 44 инструмента | опубликованная методика: захват tools/list плюс токенизатор o200k_base |
— |
Отдельная статья расхода — промежуточный результат, который идёт через модель транзитом. Расшифровка двухчасовой встречи, едущая из одного сервиса в другой через контекст, может добавить к счёту порядка 50 000 токенов. Anthropic пишет именно так, и точнее сказать нельзя. Приём против этого простой — содержимое подменяют идентификаторами на стороне клиента, чтобы через модель шли метки, а настоящие данные ходили между системами напрямую.
Теперь поправка, которую эти замеры вносят в самый первый разбор серии (урок 1). Там опубликована кривая обрыва точности, примерно 95/85/15% на 10/50/100 инструментах, и оценка «полторы тысячи токенов на схему одного инструмента».
Во-первых, ось не та, за какую её обычно принимают. Замер, на который здесь опираемся, — бенчмарк LangChain на планировании встреч (агент по схеме ReAct, февраль 2025). Инструментов там два-три на домен, а растёт число доменов, то есть число конкурирующих задач. Падение от разросшегося каталога и падение от разросшегося числа задач — разные эффекты, и в исходной кривой они слиты.
Во-вторых, на этой оси одной кривой для всех моделей не существует. GPT-4o на семи и более доменах падает до 2%, o1 держит 71%, а Llama-3.3-70B даёт 0%, потому что перестаёт вызывать нужный инструмент. Разброс между моделями шире, чем весь разброс кривой по числу инструментов. Точность зависит от пары «каталог × модель», и без указания модели кривая читается как свойство одного каталога, а это не так.
В-третьих, оценка в полторы тысячи токенов на инструмент завышена примерно на четверть. Порядок она передаёт верно, однако измеренные 1 240 можно пересчитать самому.
Средство против разрастания каталога тоже обзавелось числом. Если вынести описания инструментов во внешний индекс и отдавать модели только найденный под задачу шорт-лист, точность выбора поднимается с 13,62% до 43,13% при сокращении промпт-токенов более чем вдвое. Укрупнение инструментов и поиск по индексу работают в паре. Фасад сокращает каталог, индекс прячет остаток.
8. Свой сервер: фасад бизнес-действий вместо экспорта API
Банковскую систему с заявками наружу выставляем уже мы сами, и первая новость про свой сервер приятная — переписывать под него ничего не нужно. Функции написаны и работают, сервер добавляет к ним несколько строк обёртки и способ связи. Библиотека FastMCP делает это декораторами, и минимальный сервер помещается на экран.
from fastmcp import FastMCP
mcp = FastMCP("bank-support")
@mcp.tool() # действие: вызывает модель
def create_request(kind: str, account: str, amount: float) -> dict:
"""Создаёт заявку клиента. Не больше одной на счёт в сутки."""
return core_banking.new_request(kind, account, amount) # ваш старый код
@mcp.resource("bank://tariffs") # данные: кладёт приложение
def tariffs() -> str:
return load_tariffs_markdown()
@mcp.prompt() # сценарий: запускает человек
def triage_message(text: str) -> str:
return f"Определи тему обращения и подбери ответ по тарифам:\n{text}"
mcp.run(transport="stdio")
Соблазн на этом месте предсказуемый: сгенерировать инструменты из описания API и отдать агенту всё, что есть. Так делать не надо, и цену мы уже посчитали: каждый инструмент обходится примерно в тысячу с лишним токенов на каждом запросе, а точность выбора падает от числа соседей в каталоге. Рабочее правило обратное. Сервер выставляет 3–7 укрупнённых бизнес-действий, названных языком предметной области (проверить_клиента, создать_заявку, статус_заявки). У банковской системы заявок около ста двадцати методов в API, и все они остаются внутри, а вызывает их сервер сам. Устройство старой системы модели для работы знать незачем.
Дальше идут решения, которые отличают рабочий сервер от учебного, и каждое легко пропустить.
- Схема и описание не расходятся. Ограничение, названное словами, обязано стоять и в схеме. Иначе получится тот самый разрыв «до двадцати позиций против тридцати», который мы разбирали на чужом сервере.
- Чтение и изменение живут в разных инструментах. У них разные права и разные политики подтверждения; склеенные, они лишают вас возможности спрашивать человека только на опасном.
- Аннотации заполнены. Клиент по спецификации не обязан верить чужим аннотациям, но пустое поле не оставляет ему даже подсказки.
- Вывод готовится для модели. Markdown вместо разметки страницы, усечение длинного ответа с явным указанием, как получить продолжение, лимиты прямо в схеме.
- Вход от модели считается враждебным. Сервер стоит на границе доверия и проверяет аргументы сам, ловя выход за разрешённые пути, подстановку флагов, обращение к чужим ресурсам. Модель транслирует то, что пришло ей на вход, а туда попадает и внешний текст.
- В
stdioстандартный вывод принадлежит протоколу. Один случайныйprintломает клиенту разбор сообщений, и прилететь мусор может даже не от вашего кода, а от установки зависимости при первом запуске. Логи идут в stderr.
9. Что остаётся на вашей стороне
Сервер работает, инструменты укрупнены, каталог короткий. Интеграция готова? Нет, и здесь мы возвращаемся к тому, с чего начали. Протокол решает задачу подключения и только её. Корпоративной интеграцию делают три вещи, и ни одной из них он не приносит. Надо знать, от чьего имени выполнено действие и что этому человеку позволено. Надо решать, что агент делает сам, а что только после согласия человека. И надо проверять, что действие действительно выполнено, а не просто вернуло успешный ответ.
Начинается всё с личности. Агент действует от имени клиента, значит, где-то появляется токен доступа, и соблазнительно взять тот, с которым пришёл хост, и переслать его дальше в целевой API. Спецификация запрещает это прямым текстом. Сервер не должен принимать никакие токены, выписанные не для него. Сквозная пересылка вынесена в раздел про авторизацию отдельным нормативным запретом.
[host агента] ──токен A──► [MCP-сервер] ──токен B──► [целевой API]
токен A выписан для сервера и дальше не идёт
сервер проверяет, что токен A адресован именно ему,
достаёт из него пользователя,
берёт собственный токен B со своим сроком жизни
API живёт по своей авторизации и своему сроку жизни
✗ переслать токен A дальше в целевой API — запрещено спецификацией
✗ человека в процессе нет — тогда действует личность приложенияЗапрет обоснован четырьмя последствиями, и каждое из них ломает что-то своё. Лимиты запросов, валидация и мониторинг целевого сервиса завязаны на то, кому выписан токен, так что, пересылая чужой, вы обходите собственные же ограничения. Компрометация одного сервиса открывает доступ к соседним, потому что граница доверия между ними стёрлась.
Третье последствие дороже двух первых: журнал целевой системы начинает показывать запросы от лица, которое их не делало. В логах остаётся личность того, кто переслал токен, а инициатор действия там не появляется совсем, и разбор инцидента превращается в гадание. Четвёртое скучное, но тоже платное: будущие версии протокола на такую схему не рассчитаны.
У этого сюжета есть имя — подставленный посредник (confused deputy), и спецификация выделяет под него отдельный нормативный раздел для серверов, работающих как прокси. Общая форма всегда одна. Посредник обладает правами, которых нет у просителя, и действует по чужой просьбе, не различая, чья она. Рядом описан и путь попроще — подделка адресов в метаданных авторизации. Клиент, наткнувшись на требование авторизоваться, идёт за метаданными по адресу, который ему сообщил сам сервер, а адрес этот вредоносный сервер контролирует.
Второе, чего протокол не приносит, — политика подтверждений. Рабочая схема разбивает критичное действие на четыре шага, и самое важное в ней происходит между шагами.
критичное действие, разбитое на четыре шага
prepare → проверяет правила, готовит заявку
preview → показывает человеку, что именно произойдёт
approve → фиксирует решение по конкретным аргументам и сроку
commit → исполняет один раз и пишет в журнал
аргументы изменились после approve
└─► подтверждение сгорает, нужен новый previewprepare проверяет правила и готовит заявку, preview показывает человеку, что именно произойдёт, approve фиксирует решение по конкретным аргументам и сроку, commit исполняет один раз и пишет в журнал. Если аргументы изменились после approve, подтверждение сгорает и нужен новый предпросмотр.Сгорающее подтверждение и есть то, ради чего схему делят на четыре шага. Без него человек соглашается на одно, а исполняется другое.
Ставить такое подтверждение на всё подряд нельзя, и почему именно нельзя, разбиралось на агенте за рулём компьютера (урок 5). Если спрашивать на каждый чих, человек начинает жать «ОК» не читая. Там довод держался на рассуждении, а теперь под ним есть число. По телеметрии Anthropic пользователи Claude Code одобряют около 93% запросов на разрешение.
Читать это число надо ровно так, как оно измерено. Перед нами доля одобрений, а не доля невнимательных нажатий. Часть из этих 93% — осознанные «да» на разумные просьбы. Anthropic приводит эту долю как довод против самого приёма: спрашивать разрешение на каждом ходу теоретически работает, а на практике компания называет такой надзор ненадёжным. Чем больше подтверждений видит человек, тем меньше внимания достаётся каждому.
Отсюда и правило: подтверждать только критичные изменения, а чтение пропускать по политике. Но у Anthropic надзор — лишь первый из двух способов ограничить ущерб, и разбирает компания в основном второй: сузить то, что агент в принципе способен сделать. Развилка «надзор за действием или граница возможностей» проходит через весь этот пост. Токен, который заканчивается на нашем сервере, два разрешённых инструмента из восьми вместо целого каталога, фасад из трёх действий поверх ста двадцати методов — это и есть граница возможностей, проведённая заранее. Протокол её не проводит: он отвечает за подключение, а где пройдёт граница, решаете вы.
Третье — проверка прикладного результата, о которой уже шла речь на чужом сервере. Статус 200 и отсутствие isError означают, что вызов дошёл. Правильно ли выполнено бизнес-действие, они не говорят.
Наш банковский агент к концу этого пути выглядит так. Внутренняя система выставлена фасадом из трёх действий, где ограничения в схеме совпадают с описанием. Внешний сервис подключён по списку из двух разрешённых инструментов, вызовы последовательные, на 429 идёт повтор с растущей паузой. Токен клиента заканчивается на нашем сервере, дальше идёт собственный. Списание проходит через предпросмотр, каждое действие попадает в журнал с идентификатором обращения. Протокол из всего перечисленного отвечал за одну строчку — за то, что подключение заняло три строки кода.
10. Чего никто не померил
Разделы выше опирались на замеры. Дальше — пять пробелов, где замера нет ни у кого.
| Что осталось без замера | Что есть вместо него | Что из этого следует |
|---|---|---|
| задержка протокола против прямого вызова | 8,3 мс медианных накладных на сообщение — но это цена предложенного расширения безопасности, а не самого протокола | мерить у себя; чужого числа тут не будет |
| дрейф чужих схем во времени | наблюдение из разборов: схемы меняются без контракта версий. Сколько серверов и как часто ломают сигнатуру, не считал никто | фиксировать снимок каталога и сравнивать при каждом обновлении: из чужого changelog о поломке не узнать |
| подтверждения в мессенджерах | 93% одобрений сняты в среде разработки, где подтверждения идут одно за другим | в Slack и Telegram другой интерфейс, другая частота и другая цена ошибки; число не переносится |
| интеграции с 1С и учётными системами | приём фасада бизнес-действий работает и здесь, но конкретики по российским учётным системам в источниках нет ни строки | «сто двадцать методов» — порядок величины, за продуктом он не закреплён |
| как прятать задержку модели в интерфейсе | приёмы известны инженерно: промежуточный шаг, стриминг ответа, заявка, нарисованная до подтверждения из целевой системы | правило формулировать не из чего, пока нет замеров поведения пользователя |
Заполнять эти клетки догадками смысла нет: пустая честнее выдуманного числа.
Итог
Вернёмся к той корзине из четырёх товаров. Агент собрал её за минуту, подключение заняло три строки, и это чистая правда. Остальное в той же минуте пришлось бы делать вам: разрешить агенту два инструмента из восьми, повторять запросы с растущей паузой после 429, проверить, что в корзину попал хлеб, а не влажные салфетки с похожим названием, и решить, кто отвечает за оплату.
MCP — это подключение, а не вся интеграция. Права, политика подтверждений и проверка результата остаются на вашей стороне ровно в том объёме, в каком были до него. Что агент вообще способен сделать, очерчиваете вы, и надзор за каждым отдельным действием этой работы не заменяет. Отсюда и способ выбирать: по границе задачи, за которой начинается чужой процесс, чужая машина или чужой цикл релизов.
- Три «нет» — и отдельный сервер не нужен. Интеграция не переиспользуется, границы нет, набор инструментов не меняется, значит, хватит функции или прямого вызова. «Это новый стандарт» аргументом не считается; аргумент — какую конкретную стоимость протокол убирает.
- Спецификация сменила эпоху, экосистема — нет. Рукопожатия больше нет, версия едет в каждом запросе, сессии убраны ради обычного балансировщика, а оборванный поток теперь теряет запрос. Живые серверы при этом остаются на ревизиях полуторагодичной давности.
- Цена протокола измерена по одной оси. Тот же сценарий атаки удаётся на 23–41% чаще, чем при прямой интеграции, и авторы называют причину архитектурной: полномочия сервера не подтверждаются, а доверие между несколькими серверами распространяется неявно.
- Числа про экономию контекста надо делить на два класса. 54 422 токена на 44 инструмента опубликованы с методикой и повторяются за пять строк. «150 000 → 2 000» приведено без условий замера, а «−23 000 и −50%» склеены из двух вторичных блогов.
- Чужой сервер — чужой продакшен. Пять из восьми классов уязвимостей ваш обычный контур проверки не ищет, отказ живёт на четырёх уровнях сразу, а дефект нашёлся у 97,1% описаний инструментов, и дописывать их приходится ценой роста числа шагов на 67,46%.
- Наш агент поддержки после этого урока дотягивается до банковской системы через фасад из трёх действий и ходит во внешний сервис по списку разрешённых инструментов. Договариваться с агентами других владельцев он по-прежнему не умеет, потому что это другая ось связи и другой протокол.
FAQ
Чем MCP отличается от обычного вызова REST API?
Стандартизирует он обнаружение и подключение, а сам вызов остаётся прежним. При работе через API агент должен заранее знать, какие методы существуют, и кто-то должен описать их как инструменты в коде агента. Сервер MCP сам отдаёт каталог инструментов с именами, описаниями и JSON-схемами аргументов, поэтому одна интеграция подключается к любому совместимому клиенту без написания адаптера. Внутри себя сервер при этом обычно вызывает тот же самый REST API и переводит его на язык агента.
Что изменила спецификация 2026-07-28?
Она сделала протокол не зависящим от сессии. Рукопожатие initialize и сессии на уровне транспорта удалены, версия протокола и возможности клиента едут в поле _meta каждого запроса, а состояние между вызовами сервер отдаёт явным ключом, который затем едет аргументом следующего вызова. Возобновление оборванного потока убрано. Запрос, который был в полёте, теряется, и клиент обязан повторить его с новым идентификатором. Мотив прикладной. Любой запрос может обработать любой инстанс сервера за обычным балансировщиком, без привязки клиента к одному инстансу и без общего хранилища сессий.
Сколько инструментов выставлять на своём MCP-сервере?
Ориентир — от трёх до семи укрупнённых действий уровня бизнеса вместо механического экспорта всех методов внутреннего API. Причина считается на пальцах: замер с опубликованной методикой даёт 54 422 токена на 44 инструмента дефолтного набора, то есть около 1 240 токенов на каждый инструмент, и платите вы за них на каждом запросе. Плюс точность выбора падает от числа соседей в каталоге, причём насколько именно, зависит от модели не меньше, чем от числа инструментов.
Можно ли переслать токен пользователя через MCP-сервер в целевой API?
Нет, спецификация запрещает это прямым текстом. Сервер не должен принимать токены, выписанные не для него. Работает схема с двумя токенами, где первый выписан для сервера, а второй сервер выписывает себе для целевого API. Причины перечислены в самом стандарте: сквозная пересылка обходит лимиты и проверки, завязанные на получателя токена, ломает журнал целевой системы (в нём остаётся личность пересылающего вместо инициатора) и превращает компрометацию одного сервиса в доступ к соседним.
Правда ли, что описания инструментов у 97% серверов плохие?
Доля посчитана не по серверам. В выборке из 856 описаний инструментов на 103 серверах хотя бы один дефект нашёлся у 97,1% описаний, и 56% из них не формулируют назначение инструмента внятно. Дописывать описания — не бесплатное улучшение. В том же исследовании это дало прибавку успешности на 5,85 процентного пункта по медиане, но подняло число шагов исполнения на 67,46% и ухудшило результат в 16,67% задач.
Когда MCP не нужен и что вместо него?
Когда интеграция нужна одному агенту, живёт в том же процессе и обновляется вместе с ним, достаточно обычной функции или прямого вызова API. Для агентов, работающих с кодом, часто выигрывает консольная утилита. Она собирается в цепочки средствами шелла, без обращения к модели на каждом шаге. Практика 2026 года гибридная. Протокол берут за подключение, обнаружение инструментов и авторизацию от имени пользователя, а исполнение, циклы и фильтрацию данных отдают коду в песочнице.
Источники
- Спецификация MCP, ревизия 2026-07-28: changelog и анонс релиз-кандидата — хронология (кандидат заморожен 21.05.2026, финал 28.07.2026), удаление сессий и рукопожатия, потеря запроса при обрыве потока, устаревание Roots, Sampling и Logging.
- MCP Security Best Practices — нормативный запрет сквозной пересылки токена, четыре последствия его нарушения, раздел про подставленного посредника и подделку адресов в метаданных авторизации.
- «MCP Tool Descriptions Are Smelly!», arXiv:2602.14878 — 856 описаний на 103 серверах, 97,1% с дефектом, 56% без внятного назначения и цена починки: +5,85 п.п. успеха, +67,46% шагов, ухудшение в 16,67% задач.
- «MCP at First Glance», arXiv:2506.13538 — 1 899 открытых серверов, восемь классов уязвимостей, из которых с традиционными пересекаются три, 7,2% обычных уязвимостей и 5,5% отравления описаний.
- «Breaking the Protocol», arXiv:2601.17549 — 847 сценариев на пяти реализациях, рост успеха атаки на 23–41% против эквивалентной интеграции без протокола, три архитектурные слабости. Препринт двух авторов без указанной площадки публикации.
- Anthropic — Code execution with MCP — первоисточник числа «150 000 → 2 000 токенов», приведённого без условий замера, и 50 000 токенов на промежуточном результате с сохранённой модальностью «может означать».
- «RAG-MCP», arXiv:2505.03275 — вынос описаний инструментов во внешний индекс: точность выбора 13,62% → 43,13%, промпт-токенов более чем вдвое меньше.
- Бенчмарк LangChain на планировании встреч (агент по схеме ReAct, 10.02.2025) — разброс точности по моделям на семи и более доменах: GPT-4o падает до 2%, o1 держит 71%, Llama-3.3-70B даёт 0%. Инструментов там два-три на домен, а растёт число доменов, так что меряет бенчмарк число конкурирующих задач, а не длину каталога.
- «VIPER-MCP», arXiv:2605.21392 — прогон по 39 884 репозиториям с серверами: 106 подтверждённых 0-day, 67 присвоенных CVE.
- Invariant Labs — GitHub MCP Exploited — вынос данных из приватного репозитория через задачу в публичном; сервер при этом работает как задумано.
- General Analysis — Supabase MCP и разбор Саймона Уиллисона — свой сервер под привилегированной ролью в обход построчных ограничений доступа.
- Замер каталога GitHub MCP (54 422 токена на 44 инструмента, захват
tools/listплюс токенизаторo200k_base) и консолидация инструментов Actions и Projects — открытые предложения в репозиторииgithub/github-mcp-server. - Доля одобрений в 93% — телеметрия Anthropic из разбора о том, как компания ограничивает Claude в своих продуктах (25.05.2026). Показатель считает нажатия на «разрешить» и не отличает внимательное согласие от машинального. Сама Anthropic приводит его как довод против надзора на каждом ходу и называет такой приём ненадёжным, а основную часть разбора отводит второму способу — ограничению того, что агент способен сделать.
Наблюдения по публичному серверу магазина (восемь инструментов, ревизия 2025-03-26, 429 на параллельных вызовах, расхождение описания и схемы, пустые аннотации) сняты в одном живом прогоне и независимо не проверялись. Числа Atlassian — заявление вендора без определений «вызова» и «пользователя». Правило «два-три условия из пяти» замера под собой не имеет. Остальные числовые ориентиры зависят от состава каталога, модели и даты замера. Экосистема серверов меняется быстрее, чем выходят измерения по ней.