Итоги
Курс начинался с вопроса «что происходит под декоратором @mcp.tool()». Теперь вы знаете ответ на трёх уровнях: что сервер может попросить у клиента, как сообщения устроены и доставляются, и какое состояние сервер вправе держать. Соберём это в одну картину.
Обратные возможности
Сервер не только отвечает. Он может попросить модель хоста сгенерировать текст (sampling), сообщить о ходе долгой работы (прогресс), прислать логи и узнать рабочие папки клиента (roots). Всё это в SDK v2 выражается двумя механизмами: ctx.report_progress() для прогресса и резолверы с маркерами Sample() и ListRoots() через Annotated[..., Resolve(...)] для всего, что требует ответа клиента.
Три из четырёх возможностей — sampling, протокольное логирование и roots — устарели с ревизии 2026-07-28. Они работают, SDK их поддерживает, но новый код стоит писать иначе: прямой вызов API модели вместо sampling, logging в stderr вместо ctx.info(), папки из аргументов или конфигурации вместо roots. Прогресс остаётся и остаётся главным способом не выглядеть зависшим.
Общее, что стоит вынести: сервер обязан проверять возможности клиента до того, как что-то у него просить, и должен быть готов, что клиент откажет.
Провод
Всё — JSON-RPC 2.0: запрос, результат, ошибка, уведомление. До 2025-11-25 соединение начиналось с рукопожатия initialize, с 2026-07-28 версия и возможности клиента лежат в _meta каждого запроса, а сервер больше не отправляет клиенту запросов: sampling и roots вкладываются в результат input_required, и клиент повторяет вызов с ответами.
Два транспорта переносят одни и те же сообщения. STDIO — дочерний процесс, строка JSON на сообщение, stdout только для протокола, секреты из окружения, отмена уведомлением. Streamable HTTP — один endpoint, каждый запрос отдельным POST, ответ JSON или SSE-поток с уведомлениями, заголовки-зеркала, отмена закрытием потока, проверка Origin и аутентификация. Предыдущее поколение транспорта с сессиями Mcp-Session-Id, GET-потоком и Last-Event-ID вы теперь узнаете в логах и знаете, почему его упростили.
Состояние
Протокол без состояния не означает сервер без памяти. Он означает, что память лежит в правильных местах: процессные ресурсы — в lifespan, контекст между раундами — в защищённом requestState, долгие задачи — во внешнем хранилище по явному идентификатору, подписки — на общей шине. Экземпляры сервера делят ключ RequestStateSecurity и имя, старые клиенты получают либо липкую маршрутизацию, либо stateless_http. Личность пользователя приходит из проверенного OAuth-токена по HTTP или из окружения по stdio.
Что дальше
Три направления, в которых стоит копать после курса.
Элиситация — способ задать пользователю вопрос из инструмента через форму или ссылку. Она построена на том же MRTR, что sampling и roots, но не устарела и в SDK выражается маркером Elicit(...). Если вам нужно подтверждение опасной операции, это её инструмент.
Расширения — Tasks для долгих операций с долговечными ссылками, MCP Apps для интерактивных элементов в чате. Они необязательные, договариваются через capabilities и описаны на сайте спецификации.
Наблюдаемость — OpenTelemetry-контекст, который ходит в _meta (traceparent), и раздел OpenTelemetry в документации SDK. Когда сервер работает в трёх экземплярах за прокси, трассировка становится единственным способом понять, где потерялся запрос.
И, как всегда, спецификация: modelcontextprotocol.io/specification/latest. Она меняется, и следующая ревизия наверняка что-то ещё уберёт или добавит. Теперь вы умеете это читать.
Попробуйте сами
10–15 мин на рабочем месте- Возьмите сервер, который вы написали до курса, и прогоните его по чек-листу из урока о состоянии. Запишите, что нужно изменить, чтобы запустить его в трёх экземплярах за балансировщиком.
- Добавьте в него прогресс в самый долгий инструмент и замените
print/ctx.infoнаlogging. Это два изменения, которые окупаются в любом сервере.
Коротко
- Обратные возможности в SDK:
report_progress()для прогресса, резолверы сSample()/ListRoots()для запросов к клиенту. - Sampling, протокольные логи и roots устарели с
2026-07-28; прогресс — нет. - JSON-RPC 2.0,
_metaс версией и возможностями в каждом запросе, запросы сервера — черезinput_required. - STDIO для локальных серверов, Streamable HTTP для удалённых; оба переносят одни сообщения.
- Состояние — в lifespan,
requestState, внешнем хранилище и общей шине; личность — из токена или окружения. - Дальше: элиситация, расширения Tasks и Apps, OpenTelemetry.
Видеоверсия
Сценарий озвучки · 322 слова, ≈ 2 мин
Курс начинался с вопроса: что происходит под декоратором, который превращает функцию в инструмент. Теперь вы знаете ответ на трёх уровнях. Соберём их вместе.
Первый уровень — что сервер может попросить у клиента. Сгенерировать текст моделью хоста, это sampling. Сообщить о ходе долгой работы, это прогресс. Прислать логи. Узнать рабочие папки, это roots. В SDK всё это два механизма: метод report_progress для прогресса и резолверы с маркерами для всего, что требует ответа клиента. Три из четырёх возможностей устарели с последней ревизии: sampling, логи через протокол и roots. Они работают, но новый код стоит писать иначе — прямой вызов API вместо sampling, обычный логгер вместо протокольных логов, папки из конфигурации вместо roots. Прогресс остаётся.
Второй уровень — провод. Всё это JSON-RPC: запрос, результат, ошибка, уведомление. В новой ревизии рукопожатия нет, версия и возможности клиента приходят в каждом запросе, а сервер больше не задаёт клиенту вопросов напрямую — он возвращает результат «нужен ввод», и клиент повторяет вызов с ответами. Два транспорта переносят одни и те же сообщения. Стандартный ввод-вывод — для локальных серверов: дочерний процесс, строка джейсона на сообщение, вывод только для протокола. Streamable HTTP — для удалённых: один адрес, каждый запрос отдельным пост-запросом, ответ джейсоном или потоком событий. И вы теперь узнаете в логах предыдущее поколение транспорта с сессиями и понимаете, почему его упростили.
Третий уровень — состояние. Протокол без состояния не значит сервер без памяти. Значит, память лежит в правильных местах: процессные ресурсы при старте, контекст между раундами в защищённой строке состояния, долгие задачи во внешнем хранилище по явному идентификатору, подписки на общей шине. Личность пользователя приходит из проверенного токена или из окружения.
Куда дальше. Элиситация — способ задать пользователю вопрос из инструмента; она построена на том же механизме, что sampling, но не устарела. Расширения — задачи для долгих операций и интерактивные элементы в чате. И наблюдаемость: трассировка через OpenTelemetry, без которой сервер в трёх экземплярах за прокси не отладить.
И спецификация. Она меняется, и следующая ревизия наверняка что-то уберёт или добавит. Теперь вы умеете её читать. Спасибо, что прошли курс.
