Написано 5 августа 2026 года. Эта часть устаревает быстрее всех: она про то, как устроено внутри, а внутри всё и переписала редакция от 28 июля. Всё написанное ниже сверено с ней — и, значит, всё, что вы читали про внутренности MCP раньше, описывает предыдущую конструкцию.

Как это устроено изнутри

Как это устроено изнутри

Уберите словарь — и MCP-сервер окажется тремя вещами: список инструментов, описание каждого, написанное для машины, а не для человека, и один из двух транспортов, которым едут сообщения: stdio — для сервера на вашей машине, Streamable HTTP — для того, что работает не у вас. Всё остальное в Model Context Protocol навешено на эти три — JSON-RPC, авторизация по OAuth 2.1 и три ступени ошибок, третья из которых стоила мне двенадцати опубликованных статей и невидима для протокола по построению.

Третья часть — про устройство. Она подробнее двух первых и по-прежнему без кода, потому что в коде не видно ни одного решения, о котором стоит спорить: что описание должно сказать, чтобы инструмент выбрали; чему в самоописании инструмента нельзя верить никогда; и что проверять после того, как вызов уже вернул успех.

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

Что вообще передаётся по проводу

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

Между ними ходит JSON-RPC — формат сообщений, вторая версия которого вышла в 2010 году и не менялась с 2013-го, за полтора десятилетия до всей этой истории с искусственным интеллектом, и примерно такой же захватывающий, как почтовая наклейка. В запросе три части: имя метода, объект с аргументами и идентификатор, по которому ответы сопоставляются с вопросами, когда в полёте сразу несколько. Взять готовое было правильным решением: спорить не о чем, парсер есть в любом языке, а авторы Model Context Protocol — название стоит развернуть хотя бы раз, аббревиатура его проглотила — освободили внимание для того, что действительно было новым.

Почему вызов MCP совсем не похож на REST

На REST это не похоже совсем. В REST смысл несёт адрес: GET /articles/17 кладёт существительное в путь, глагол — в метод, и кэш посередине может что-то с этим сделать, не заглядывая в тело. В MCP любой вызов идёт по одному и тому же адресу и говорит «tools/call». Какой инструмент, с какими аргументами, что он сейчас сделает с вашими данными — всё внутри тела, и любой промежуточный узел видит одинаковые конверты. Поэтому редакция июля 2026-го и добавила заголовки, по которым посредник может маршрутизировать и кэшировать запрос, не вскрывая содержимое: протокол выкупал обратно то, что HTTP получает даром задолго до того, как этот стиль записали.

Два транспорта: stdio и Streamable HTTP

Протокол описывает, как выглядит сообщение. Про то, как байты попадают из одного процесса в другой, он не говорит ничего — это отдельная работа, и у неё есть своё название: транспорт. Труба, а не язык. Транспортов в MCP два, и выбор между ними следует из того, где живёт сервер, а не из чьих-то достоинств. Если вы когда-нибудь прописывали сервер в Claude Desktop, Cursor или VS Code, вы уже выбрали один, не заметив: эти клиенты держат список серверов в конфигурационном файле, и локальная запись в нём — просто командная строка. Мой — stdio-сервер, и поэтому же ничего здесь не расскажет, каково эксплуатировать Streamable HTTP. Эту часть спецификации я читал, но не жил в ней.

    • stdio: сервер запускается соседним процессом на той же машине и общается через свои стандартные ввод и вывод — те самые две трубы, которыми программы командной строки пользуются с семидесятых. Ни портов, ни сети. Так работает большинство локальных серверов, и поэтому небрежно сделанный успевает заработать раньше, чем кто-нибудь вспомнит про аутентификацию.
    • Streamable HTTP: один сетевой адрес, на который и пишут, и с которого читают, — и он же умеет держать соединение открытым и досылать сообщения. Это транспорт для всего, что работает не на вашей машине.
    • Старый транспорт HTTP+SSE, у которого адресов было два вместо одного, объявлен устаревшим ещё в марте 2025-го. Если он встретился вам в инструкции, инструкция несвежая и в остальном тоже.
    • Что убрали в июле 2026-го: восстановление оборванного потока. Раньше клиент мог переподключиться и попросить пропущенное; теперь запрашивает заново с начала. Протокол проще, работы больше — этот размен редакция делает раз за разом.
Два транспорта

Два транспорта

Безопасность локального сервера

Сервер на собственной машине кажется безопасным и является самым небезопасным местом во всей картине. Он работает от вашего имени, держит ваши ключи, и его никто не укреплял — предполагалось, что до него не добраться. Добраться может одна вещь: открытая в браузере страница. Мой в том числе: он работает от моего имени и держит ключи ко всем площадкам, куда я публикуюсь. Три требования ниже к нему до сих пор не относились только по одной причине: он говорит по stdio и не слушает вообще ничего — а это свойство моей конфигурации, а не моего сервера, и оно перестанет быть верным в тот день, когда я унесу его с этой машины.

Спецификация называет здесь три требования и пишет их капслоком, а капслок в стандарте — термин, а не крик: проверять заголовок Origin, слушать только локальный адрес, требовать аутентификацию. (RFC 2119 от 1997 года закрепил значения; RFC 8174 двадцатью годами позже — правило, по которому считается только капслок. Пропустив MUST, вы получаете не реализацию спецификации, а что-то другое; SHOULD можно пропустить, если понимаете, чем платите. Заглавные нужны, чтобы читающий по диагонали не принял требование за совет.)

Перепривязка DNS: атака, которую закрывают эти три правила

У атаки, которую эти три строчки закрывают, неудачное имя — перепривязка DNS — и простое устройство. Вы открываете страницу; ничего необычного в ней нет. Она принадлежит имени, которым распоряжается атакующий, и когда браузер это имя разрешал, он получил адрес атакующего — ровно как и должен был. Через несколько секунд атакующий меняет ответ, и то же самое имя разрешается теперь в 127.0.0.1, то есть в вашу машину. Браузер разрешает имя заново, видит то же имя и заключает, что ничего не изменилось: вся его модель безопасности построена на именах, а не на адресах. Страница сохраняет все свои права. И вот она уже разговаривает с тем, что слушает на вашем ноутбуке, изнутри вашего ноутбука, и читает ответы.

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

Как описывают инструмент

Первая часть цикла сказала, что сервер может предложить три вещи, и дальше — как почти всё, что пишут про MCP, — говорила только про первую. Инструменты (tools) — это действия: модель просит, что-то происходит, приходит ответ. Ресурсы (resources) — данные, которые сервер даёт прочитать: файл, таблица, страница; у каждого есть адрес, побочных эффектов нет, и есть важное отличие — втянуть ресурс в диалог решает клиент или человек, а не модель, которая до него дотянулась. Промпты (prompts) — заготовленные запросы, которые сервер предлагает в виде меню; в клиенте они всплывают как слэш-команды. На практике инструменты доминируют до состояния монополии. Большинство серверов ничего другого и не предлагает, мой в том числе.

Из чего состоит описание инструмента

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

Статус не вычитывают из текста

Из этого различия следует правило, которое я поставил бы выше всех остальных в этой статье. Статус не вычитывают из текста.

Я выучил его обычным способом. Мой публикующий сервер кладёт черновики на Medium, управляя настоящим браузером, и адаптеру нужно было понимать, не сорвалось ли сохранение. Он искал в тексте страницы фразы, которые Medium показывает при сбое, — одна из них «something is wrong». И нашёл её в тексте публикуемой статьи, во фразе «if something is wrong you flip back». Успешный прогон был объявлен провалившимся, отработал повтор, появился дубль черновика. Починка состояла в том, чтобы перестать читать страницу как текст и смотреть только на те элементы, которыми страница объявляет статус. Урок обобщается далеко за пределы браузеров: если единственный способ узнать исход — искать слова в тексте, вы его не узнали, а угадали, и рано или поздно ваше ключевое слово встретится в тексте случайно.

Пометки о поведении — утверждение, а не гарантия

Инструмент может нести и пометки о собственном поведении: только чтение, разрушающий, идемпотентный, работающий с внешним миром. Тут же спецификация предупреждает: верить им нельзя, если сервер не ваш. Я скажу резче. Пометки о поведении — самая опасная часть спецификации: «только чтение» выглядит как гарантия, а является обещанием чужого кода. Гарантию обеспечивает тот, кому будет плохо, если она нарушится. Эту — тот, кому будет хорошо.

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

Три ступени ошибок

У большинства систем есть один способ сломаться. У MCP их два по замыслу и третий, которого замысел не видит.

Первая ступень — ошибка протокола: инструмента с таким именем нет, аргументы не подошли под схему, запрос кривой. Ничего не выполнялось. Она возвращается так, как JSON-RPC возвращает ошибки всю свою жизнь, с кодом, и говорит о разговоре, а не о ваших данных. Разбирается с ней обвязка клиента.

Вторая — ошибка выполнения: инструмент отработал, и новости плохие. Файла не было, API ответил 403, ветка уже существует. Здесь MCP делает то, что с первого взгляда выглядит недоразумением: плохая новость приезжает успешным ответом с пометкой, а сама новость написана в тексте. Транспорт сработал — он об этом и сообщает.

Это замысел, а не небрежность. В REST-сервисе ошибка адресована программисту, которого рядом нет: вернули 500, записали в лог, а код, написанный когда-то давно, справится или не справится. В MCP получатель — модель посреди задачи, и она может что-то предпринять. Узнав, что ветка уже есть, она переключится на неё; узнав, что файла нет, посмотрит каталог. Ошибка протокола спрятала бы новость от единственного участника, способного отреагировать: такие ошибки разбирает обвязка клиента, до модели они не доходят.

Третья ступень: успех, которого не было

Третья ступень — та, которую не поймает никакая спецификация. Мне она стоила двенадцати статей.

Вторая часть рассказывает эту историю целиком: аудит всех двадцати пяти записей с адресом публикации, двенадцать живых постов на Medium, в которых осталось от 41 до 72% текста, и адаптер, закрывавший вкладку, пока Medium досохранял тело фоновыми запросами. Сюда относится другая половина — протокольная. Все двенадцать вернулись валидным URL и ничем не примечательным «ok», и выдал его тот самый инструмент, который только что не доделал работу.

Пометку о неудаче ставит тот же код, который ошибся насчёт происходящего. В этом всё дело. Протокол может донести вердикт, но не может его проверить. Эта ступень невидима для него по построению.

Из тех трёх, что доехали целиком, двое едут через тот же браузер, что и Medium; отличала их от Medium форма отправки, а не машинерия. Не замечали неделями потому, что мониторинг спрашивал, открывается ли ссылка, — она открывалась. Обрезанная статья — совершенно здоровая веб-страница.

Вывод неприятный и неизбежный. Для всего, что имеет значение, проверка «работа сделана» должна быть отделена от инструмента, который её делал, и задавать другой вопрос: не «вернулся ли вызов», а «совпадает ли то, что сейчас лежит на сервере, с тем, что я отправлял». У меня теперь есть скрипт, который вычитывает каждую опубликованную статью обратно и сверяет с исходником. Он существует потому, что успешный ответ — это мнение.

Кто кого пускает

Скрипту, который вычитывает эти статьи обратно, нужен для этого ключ — и это вторая половина сервера, которую никто не закладывает заранее: не что он умеет, а кому позволено спрашивать. (Сначала предупреждение про слово. Во второй части, про стоимость MCP в токенах, «токен» означал единицу, в которой считают текст и выставляют счёт. Здесь это совсем другое: удостоверение, строка, доказывающая, кто вы и что вам можно. Одно слово, два несвязанных значения, и оба общеприняты в своём углу.)

Авторизация в MCP тяжелее, чем в REST, по структурной причине. Ключ в REST удостоверяет одну программу, которая зовёт другую. В MCP участников трое — человек, клиент, действующий от его имени, и сервер, у которого могут быть собственные ключи к четвёртой системе, — и всё интересное происходит в зазорах между ними.

    • В REST обычно: ключ в заголовке, и на этом всё. Работает, потому что обе стороны пишет обычно одна команда или хотя бы читает одну документацию.
    • В MCP: OAuth 2.1, обязательный PKCE и отдельные стандарты на то, как клиент вообще узнаёт, у кого спрашивать разрешение. PKCE заслуживает простых слов. Ответ авторизационного потока возвращается через браузер или через операционную систему, обрабатывающую ссылки, и несёт короткоживущий код, а путь этот не приватный: подслушать может другое приложение на той же машине. Поэтому клиент в самом начале придумывает случайный секрет, отправляет только его отпечаток и обязан предъявить оригинал, когда меняет код на токен. Украденный код становится бесполезен тому, кто его украл: код у него есть, секрета нет, обмен не проходит.
    • Привязка токена к получателю: сервер обязан проверить, что токен перед ним выписан именно ему, а не просто что он действителен. Токен — не пароль, а записка, адресованная кому-то; принять записку, адресованную другому, — это и есть способ превратить сервер во вход.
    • Прямой запрет пробрасывать чужой токен дальше, и спецификация называет по имени то, что запрет предотвращает: проблема запутанного посредника. Посредник — это компонент с бо́льшими правами, чем у того, кто его зовёт: ваш сервер с админским ключом. Запутайте его в том, кто спрашивает, — и он потратит собственные права в пользу спрашивающего.
    • Что изменилось в 2026-м: динамическую регистрацию клиента, когда клиент сам прописывается на авторизационном сервере на лету, объявили устаревшей. Это была самая удобная часть потока и самая злоупотребляемая.

Главный практический вопрос: обёртка

Всё выше — про протокол. Этот раздел — про то, на что у вас реально уйдёт время.

Почти всегда MCP-сервер — это переводчик, стоящий перед REST API, который у вас уже есть. Это не новая система, своих данных у неё нет, и, если она исчезнет, нижележащий сервис даже не заметит. Мой ходит в собственный админский API по HTTP с API-ключом — в те же эндпоинты, которыми пользуется админка. У API есть описание OpenAPI; MCP-сервер его не читает — описание, написанное для программиста на этапе интеграции, это не то, что нужно модели во время работы. Это норма, а не срезанный угол.

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

Не делайте инструмент на каждый эндпоинт

Дальше начинается соблазн, и он ловит почти всех. У вас двести эндпоинтов; сгенерировать из них двести инструментов — работа на вечер, и результат не просто бесполезен, а дорог и бесполезен. Каждое описание оплачивается в каждом запросе, список уже достаточно длинный, чтобы регулярно выбиралось не то, и форма неправильная на более глубоком уровне: эндпоинты устроены как база данных, а задачи — как человеческая голова. «Закрыть задачу» — одно действие для человека и три вызова для API.

Выборка из пятнадцати работает лучше полного списка, и вся работа — именно в отборе. Самый наглядный пример в моём сервере — набор инструментов, которым в API не соответствует ничего: три верификатора. Двое смотрят, переживёт ли статья площадку, до того как её туда положат: никаких markdown-таблиц там, где в редакторе нет блока таблицы; никакого заголовка, дублирующего название; никаких незакрытых блоков кода. А третий вычитывает опубликованную страницу обратно и жалуется. Такого эндпоинта в API нет и не нужно. Человеку он тоже не понадобился бы. Они существуют потому, что зовёт модель, а мне нужен был ограничитель, который она не сможет обойти.

Раз уж я раздаю советы, назову собственную цифру. В моём сервере календаря публикаций 20 инструментов. Это на пять больше, чем я рекомендую, и я хорошо знаю, как так вышло: каждый в день добавления был очевидно оправдан. Никто не решает завести двадцать инструментов. У вас одиннадцать, потом появляется сокращение, экономящее шаг, потом парный листинг к уже имеющемуся, и в одно утро список перестаёт помещаться на экран. Отбор — не решение, принимаемое однажды. Это то, что приходится делать постоянно и против самого себя.

Обёртка

Обёртка

Переезд на редакцию 2026-07-28

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

    • Сессий больше нет: состояние переезжает в аргументы, которые сервер сам же и выдаёт. Клиент возвращает то, что ему дали, и ответить может любой экземпляр сервера.
    • Рукопожатия больше нет: никаких стартовых переговоров, версия и возможности едут в каждом запросе. Запрос стал самодостаточным — протокол стал stateless, в этом весь смысл.
    • Появился отдельный вызов, чтобы спросить сервер, что он умеет: раньше это было побочным эффектом подключения, теперь — вопрос, который задают, когда нужен ответ.
    • Сервер, которому не хватает данных, отвечает «нужны данные», а клиент повторяет запрос, приложив ответ. Пауза больше не живёт внутри открытого соединения.
    • Три возможности устарели, и у каждой есть замена: доступ к папкам — обычные аргументы; обращение сервера к модели — прямой вызов её собственного API, где ему и место; логирование на уровне протокола — поток ошибок или ваша телеметрия, где место ему.

С чего начать, если решились

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

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

Держите человека в контуре для всего необратимого. Спецификация просит об этом прямо, а после разбора трёх ступеней ошибок понятно, почему я держусь этой позиции твёрже, чем она сформулирована: инструмент, отчитавшийся об успехе, высказывает утверждение, а не предъявляет доказательство.

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

Большинству команд MCP-сервер сейчас не нужен. Правильный первый шаг — не написать его, а посчитать, во сколько токенов обойдутся описания инструментов, в каждом запросе, на той нагрузке, которая у вас есть на самом деле. У половины на этом всё и закончится, и вечер будет потрачен отлично. Сервер окупается там, где набор действий действительно нельзя знать заранее: когда он зависит от того, что подключил пользователь, куда повернёт задача, что решили в разговоре. У меня — тот самый случай, и он всё равно стоил мне двенадцати статей, прежде чем я научился проверять работу, а не ответ. Если список вызовов можно выписать заранее — выпишите список. Это было верно в 2000 году, и редакция 2026-го тихо сделала это ещё вернее.

Это последняя статья из трёх. Первая — откуда взялись MCP и REST и почему июльская редакция 2026-го развернула MCP обратно к REST; вторая — сколько MCP стоит в токенах.

Источники

Спецификация MCP публикуется на modelcontextprotocol.io; здесь использованы редакции 2025-06-18 и 2026-07-28: разделы о транспортах, инструментах и авторизации; официальный список изменений последней редакции; предложения SEP-2567, SEP-2575, SEP-2322, SEP-2549. Стандарты OAuth 2.1, RFC 8707, RFC 9728, RFC 7591, а также RFC 2119 и RFC 8174 — про капслок. Описанные выше инциденты — из моего собственного публикующего сервера, разбор от 25 июля 2026 года.