MCP 2026-07-28
Поколение протокола MCP 2026-07-28 используется по умолчанию. Обычная
сборка neva говорит на нём — опционального флага нет, — и вся остальная
документация описывает именно это поколение, если на странице не сказано иное.
Предыдущее поколение (MCP 2024-11-05 … 2025-11-25) живёт за флагом
legacy-spec — там же записаны и
пути обновления между релизами.
Сама ревизия спецификации ломает обратную совместимость, и neva следует за ней, а не замораживается на старом формате. Эта страница объясняет, что это означает на практике.
Discovery вместо рукопожатия
Рукопожатия initialize / initialized больше нет. Клиент открывает
соединение единственным запросом server/discover:
DiscoverResultобъявляетsupportedVersions: string[]— весь набор версий, который поддерживает сервер, — а клиент выбирает одну из них.serverInfoотсутствует в результате discovery. Сервер представляется в_metaкаждого результата, под ключомio.modelcontextprotocol/serverInfo; neva проставляет его на уровне диспетчеризации, аClient::server_infoчитает его оттуда.Client::connect()выполняет discovery за вас.Client::discover()— явный вызов;Client::init()остаётся как псевдоним для обратной совместимости.
Клиент neva работает в двойном режиме: если server/discover отклонён на
уровне протокола (MethodNotFound, InvalidRequest либо ответ не в формате
JSON-RPC или с неизвестным кодом), он откатывается к легаси-рукопожатию
initialize и до конца соединения говорит с этим узлом на старом протоколе.
Сетевые ошибки отката не вызывают. Переключение происходит один раз на
соединение, необратимо и до любого другого трафика. На стороне клиента
with_mcp_version по-прежнему существует, но выбирает только версию, о
которой договаривается откат, — он никогда не заставит server/discover
отклонить корректный сервер 2026-07-28.
HTTP-транспорт без состояния
Транспорт Streamable HTTP теперь работает только по схеме
«запрос — ответ»: никакого Mcp-Session-Id в протоколе, никакого DELETE
сессии и никакого отдельного SSE-потока GET. Серверные уведомления вместе с
ним не исчезли — они переехали на запрос, который клиент открывает именно для
них, см. Подписки.
Каждый запрос объявляет свой контекст сам — сервер без состояния не должен выводить его из предыдущего трафика:
Ключ _meta | Обязателен | Что содержит |
|---|---|---|
io.modelcontextprotocol/protocolVersion | да | Согласованная версия, дублируется заголовком MCP-Protocol-Version |
io.modelcontextprotocol/clientCapabilities | да | Возможности, на которые опирается этот запрос (пустой объект — тоже корректное объявление) |
io.modelcontextprotocol/logLevel | нет | Подписка на логирование в области запроса |
traceparent / tracestate / baggage | нет | Зарезервированные ключи распространения контекста OpenTelemetry |
Запрос без любого из обязательных ключей отклоняется с InvalidParams
(-32602) и HTTP 400; запросы внутри батча проверяются по одному.
Требование относится к сообщению, а не к транспорту, поэтому
Request::required_meta_error публичен, и диспетчер применяет его и для
stdio — специфичен для HTTP только код 400.
Отмена — это закрытие потока
Без сессии notifications/cancelled через Streamable HTTP некуда адресовать.
Запрос отменяется закрытием его потока ответа: сервер считает закрытый
поток отменой и останавливает обработчик, а клиент никакого уведомления не
шлёт. Клиент neva делает так для любого запроса, которого он перестал ждать, —
по таймауту, при удалении вызова, при брошенном пакете, при отменённой
подписке; см. Таймауты и отмена.
Через stdio и легаси-узлу отмену по-прежнему несёт notifications/cancelled.
Заголовки маршрутизации
Промежуточные узлы маршрутизируют и ограничивают трафик по заголовкам, поэтому заголовки обязаны совпадать с телом запроса:
| Заголовок | Обязателен для | Дублирует |
|---|---|---|
Mcp-Method | каждого запроса | поле method JSON-RPC |
Mcp-Name | tools/call | params.name |
Mcp-Name | resources/read | params.uri |
Mcp-Name | prompts/get | params.name |
Mcp-Name | методов задач | params.taskId |
Mcp-Param-{name} | tools/call | каждый аргумент с аннотацией x-mcp-header |
Отсутствующий или расходящийся заголовок отклоняется с HeaderMismatch
(-32020) и HTTP 400. Значения, не являющиеся безопасным ASCII, — а также
обычные значения, которые можно принять за маркер, — передаются в Base64 за
сентинелом =?base64?...?=, который сервер декодирует перед сравнением.
Уведомление не обязано нести Mcp-Method, но если несёт — должно указать
собственный метод. Заголовки маршрутизации на батче отклоняются целиком:
ни один метод и ни одно имя не описывают батч, поэтому от батча не ждут
дублирования аргументов и не проверяют его на это.
Проверка Origin и Host
Спецификация требует, чтобы локально привязанный сервер проверял эти
заголовки: браузер с готовностью подключится к 127.0.0.1 от имени любой
страницы, чей DNS туда указывает. neva отвечает 403 Forbidden ещё до чтения
тела: при привязке к loopback принимаются только loopback-имена, а
развёртывание за прокси называет свои имена через
HttpServer::with_allowed_origins([...]). См.
Защита от DNS-rebinding.
Обязательный минимум для развёртывания на нескольких экземплярах
Три общих ресурса, как только экземпляров больше одного: первые два — всегда, третий — как только вы обслуживаете подписки:
-
App::with_request_state_secret(<общий секрет>)— без него повторы, попавшие на другой экземпляр, не смогут расшифроватьrequestState. neva предупреждает об этом при старте. neva запечатываетrequestStateчерез ChaCha20-Poly1305, а не просто подписывает: тег AEAD аутентифицирует данные ровно так же, как это сделал бы HMAC, но подписанное состояние осталось бы читаемым, аctx.memoзаписывает в него значения, вычисленные сервером (ответ внешнего сервиса, назначенную цену, токен нижестоящей системы), чтобы следующий раунд их воспроизвёл. Конфиденциальность здесь ничего не стоит, поэтому секрет обеспечивает и её — обращайтесь с ним как с секретом и ротируйте черезApp::with_request_state_keys. -
App::with_request_state_store(<общее хранилище>)— без него повтор из-за потерянного ответа заново выполнит обработчик и продублируетon_commit. Хранилище по умолчаниюInMemoryStateStoreживёт в рамках процесса; для продакшена реализуйтеRequestStateStoreповерх Redis или аналога. Три его метода — обычныеasync fn:use neva::RequestStateStore;
use neva::types::Response;
/// Хранилище, которое ничего не помнит: каждый ретрай выполняет раунд заново.
struct NoCache;
impl RequestStateStore for NoCache {
async fn get(&self, _tag: &str) -> Option<Response> {
None
}
async fn put(&self, _tag: &str, _response: Response, _exp: u64) {}
}reserve— метод, сериализующий одинаковые ретраи финального раунда, — сохраняет реализацию по умолчанию (no-op), а распределённое хранилище переопределяет его настоящей блокировкой. -
App::with_notification_bus(<общая шина>)— без неё потокsubscriptions/listen, живущий на одном экземпляре, никогда не узнает об изменении, случившемся на другом, и потеря выглядит как «сервер никогда не меняется», а не как сбой доставки. См. Сервер → Запуск нескольких экземпляров.
Привязка состояния к сервису
Если один и тот же with_request_state_secret делят несколько сервисов, то
состояние, выпущенное одним, принимается остальными: оно было привязано к своему
запросу и принципалу, но не к сервису.
App::with_request_state_audience(<идентичность этого сервиса>)
это закрывает, и проверка работает в обе стороны: несовпадение — это
InvalidParams, а состояние с указанной аудиторией отклоняется сервером, который
её не настраивает.
App::new()
.with_request_state_secret(std::env::var("MCP_STATE_SECRET").unwrap().as_bytes())
.with_request_state_audience("https://weather.example.com/mcp")
Значение должно быть одинаковым на каждом экземпляре одного сервиса, поскольку повтор может попасть на любой из них.
Протокол: состояние с аудиторией запечатывается под собственной версией
(v2., а не v1.), поэтому бинарник, выпущенный до появления этой опции,
отклоняет его, вместо того чтобы отбросить неизвестное ему поле — что оставило
бы привязку неработающей ровно на том экземпляре, который ещё не обновили.
Развёртывание без аудитории продолжает выпускать v1; декодируются обе версии.
Состояния, находившиеся в полёте в момент включения опции, отклоняются и
истекают в пределах TTL requestState — 5 минут.
Подписки
Без потока GET серверным уведомлениям нужен запрос, на котором они поедут.
Спецификация его даёт: subscriptions/listen — один долгоживущий запрос с
фильтром уведомлений. Он заменяет и поток GET, и пару RPC-методов
resources/subscribe / resources/unsubscribe: подписка на конкретный ресурс
теперь URI внутри фильтра, привязанный к несущему его потоку, а не состояние
на сервере.
--> subscriptions/listen { "notifications": SubscriptionFilter }
<-- notifications/subscriptions/acknowledged { "notifications": …, "_meta": { subscriptionId } }
<-- notifications/tools/list_changed { "_meta": { subscriptionId } }
…
<-- { "id": …, "result": { "resultType": "complete", "_meta": { subscriptionId } } }
SubscriptionFilter целиком построен на явных согласиях —
toolsListChanged, promptsListChanged, resourcesListChanged и
resourceSubscriptions, — а сервер подтверждает запрошенный фильтр,
суженный до объявленных им возможностей, первым же сообщением в потоке.
Каждое сообщение несёт _meta["io.modelcontextprotocol/subscriptionId"],
поэтому по одному каналу может идти несколько подписок.
neva обрабатывает subscriptions/listen сама: серверный обработчик писать не
нужно, а мутирующие методы Context — ctx.tools().add / remove,
ctx.prompts().add / remove, ctx.resources().add / remove и
ctx.resources().notify_updated — рассылают уведомления во все потоки,
которые их просили. На клиенте Client::listen(filter)
возвращает дескриптор Subscription, как только сервер подтвердит подписку, а
сами уведомления идут в обработчики, зарегистрированные через
Client::subscribe и его помощники, — так что существующий клиентский код
менять не нужно.
Логи и прогресс не подписочные: они остаются в области запроса и идут по
потоку ответа того запроса, который их вызвал. Спецификация проводит через
подписку и notifications/tasks, но такой категории в SubscriptionFilter
neva пока нет — статус задачи по-прежнему узнают опросом
tasks/get.
См. Сервер → Подписки и Клиент → Подписки.
Multi Round-Trip Requests (MRTR)
Обработчик может приостановиться посреди выполнения и запросить данные у
клиента: он вызывает ctx.elicit(key, params), ctx.sample(key, params) или
ctx.list_roots(key) и ждёт ответа. Сервер отвечает input_required; клиент
отвечает и повторяет вызов; обработчик выполняется снова.
Прогресс хранится в запечатанном AEAD блобе requestState, который клиент
возвращает при повторе, — поэтому любой запрос может попасть на любой
экземпляр. Так как обработчик выполняется с самого начала на каждом
раунде, побочные эффекты необходимо оборачивать:
| Примитив | Гарантия |
|---|---|
ctx.memo(key, fut) | Вычисляется один раз; на последующих раундах воспроизводится из requestState |
ctx.once(key, fut) | Выполняется не более одного раза за все раунды |
ctx.on_commit(fut) | Выполняется ровно один раз, когда обработчик доходит до финального результата |
#[tool]
async fn place_order(ctx: Context) -> Result<String, Error> {
// Вычисляется один раз; на каждом следующем раунде воспроизводится.
let quote_cents: u32 = ctx.memo("quote", async { Ok(1299) }).await?;
let form = ElicitRequestParams::form(format!(
"Доставка стоит ${:.2}. Укажите данные для отправки:",
quote_cents as f64 / 100.0
))
.with_schema::<Shipping>();
// Первый раунд разворачивает обработчик с `input_required`,
// второй — воспроизводит ответ клиента из `requestState`.
let ship: Shipping = ctx
.elicit("shipping", form.into())
.await?
.content()
.ok_or_else(|| Error::new(ErrorCode::InvalidParams, "доставка отклонена"))?;
// Списание выполнится не более одного раза за все раунды.
ctx.once("charge", async { Ok(()) }).await?;
// Выполнится ровно один раз, на финальном раунде.
let who = ship.full_name.clone();
ctx.on_commit(async move {
tracing::info!("чек отправлен: {who}");
Ok(())
});
Ok(format!("Заказ подтверждён для {}", ship.full_name))
}
На стороне клиента раунды происходят внутри tools().call — вызывающий код
по-прежнему видит один вызов. Число повторов на слот ограничивается через
McpOptions::with_max_mrtr_rounds.
Виды input-запросов: elicitation, sampling, roots
Спецификация не удалила сэмплирование и корневые каталоги. Она убрала их как
серверные запросы к клиенту, управляемые возможностями, и перенесла эту
способность на MRTR в виде видов input-запросов, рядом с elicitation. В
протоколе input-запрос — это по-прежнему конверт { method, params }, где
method играет роль дискриминатора (elicitation/create,
sampling/createMessage, roots/list).
- Elicitation — полноценный, первоклассный вид.
- Сэмплирование и
корневые каталоги — в соответствии с
12-месячным жизненным циклом, заложенным самой спецификацией, —
устарели с момента появления. API помечены
#[deprecated]и существуют для миграции; местам вызова нужен#[allow(deprecated)].
Механика одинакова для всех видов, поэтому once / memo / on_commit
покрывают их бесплатно. ClientMrtrCapabilities содержит elicitation,
sampling и roots; сервер проверяет каждый вид по его собственному
объявлению и отвечает на запрос необъявленного вида ошибкой
MissingRequiredClientCapability (-32021) вместо того, чтобы подвесить
раунд. Объявления аддитивны, поэтому узел, присылающий только
elicitation, по-прежнему разбирается корректно.
Спецификация описывает каждое из них как необязательный объект, а не
булево значение, и содержимое elicitation — это его режимы, так что это
поле — Option<ElicitationModes> с form / url внутри, а не флаг. Клиент,
объявивший {"form": {}}, перечисляет то, что умеет, а голое {} не
называет ни одного режима и потому не исключает ни одного. Что объявил
вызывающий конкретно этого запроса, читайте через
Context::client_capabilities(); см.
Спрашивайте только то, на что вызывающая сторона может ответить.
Обобщённый input-запрос представлен объединением mrtr::InputRequest
(InputRequest::Elicitation(params) / Sampling / Roots), а
mrtr::InputResponses — это HashMap<String, serde_json::Value>: тип
результата зависит от запрошенного вида, поэтому разбирайте свой тип из
значения самостоятельно.
Возможности едут в каждом запросе
Рукопожатия нет, а значит негде объявить возможность один раз на соединение —
поэтому клиент объявляет их в каждом запросе, в его _meta под ключом
io.modelcontextprotocol/clientCapabilities:
{
"_meta": {
"io.modelcontextprotocol/clientCapabilities": {
"elicitation": { "form": {} },
"extensions": {
"io.modelcontextprotocol/ui": {
"mimeTypes": ["text/html;profile=mcp-app"]
}
}
}
}
}
Тип на проводе —
RequestClientCapabilities:
флаги MRTR (elicitation, sampling, roots) плоско, рядом с ними
extensions — ровно так, как спецификация пишет ClientCapabilities.
Обработчик читает то, что объявила вызывающая сторона этого запроса:
| Спрашиваем | Получаем |
|---|---|
ctx.client_capabilities() | Виды MRTR — на какие input-запросы эта сторона умеет отвечать |
ctx.client_extension(id) | Настройки, объявленные этой стороной для расширения id, либо None |
ctx.supports_apps() | Умеет ли эта сторона рендерить MCP Apps — client_extension плюс правило самого расширения |
use neva::prelude::*;
#[tool(descr = "Ищет по корпусу")]
async fn search(ctx: Context, query: String) -> String {
let fuzzy = ctx
.client_extension("com.example/search")
.and_then(|settings| settings["fuzzy"].as_bool())
.unwrap_or(false);
format!("ищем {query} (нечёткий поиск: {fuzzy})")
}
#[tokio::main]
async fn main() {
App::new()
.with_options(|opt| opt.with_stdio())
.run()
.await;
}
Объявлением считается само присутствие ключа, но что считать поддержкой
расширения — дело самого расширения: MCP Apps требует, чтобы настройки называли
типы содержимого, которые клиент рендерит. Это правило применяет
supports_apps(), а client_extension() — нет.
Некорректное значение extensions читается как ничего не объявлено, а не
заваливает запрос. _meta разбирается целиком, поэтому жёсткая ошибка здесь
утянула бы за собой токен прогресса, уровень логирования и все остальные ключи —
ради карты, которую neva до появления расширений вообще игнорировала.
Карту в каждый запрос клиент на neva пишет сам, так что объявление доходит до
сервера без рукопожатия, а Context читает его обратно.
legacy-specТам возможности едут в initialize, а методы доступа для каждого запроса из
сборки вырезаются.
resultType в каждом результате
Дискриминатор обязателен во всех результатах, а не только в продолжениях MRTR. Каждый успешный результат несёт одно из значений:
| Значение | Смысл |
|---|---|
complete | Финальный результат — инструменты, промпты, ресурсы, discovery, автодополнение, … |
input_required | Продолжение MRTR с input-запросами |
task | CreateTaskResult (плоский: Result & Task) |
Значение проставляется централизованно в Response::success, поэтому
покрывает каждую реализацию IntoResponse, включая Json<T> и скалярные.
Уже заданный дискриминатор никогда не перезаписывается — именно так
input_required проходит через ту же воронку; результат, не являющийся
объектом, некуда снабдить полем и передаётся как есть.
Читать его следует через Response::result_type(), который применяет
правило совместимости из спецификации: отсутствующее поле трактуется как
Complete, и так же трактуется любое значение, неизвестное neva.
Кэширование
ttlMs и cacheScope — обязательные члены CacheableResult, а не
опциональные подсказки: они присутствуют в DiscoverResult,
ReadResourceResult и всех четырёх результатах-списках. CacheScope
принимает значения public / private, по умолчанию — private. neva
всегда отправляет оба поля; узел, который их не прислал, всё равно
разбирается.
Инструменты
Tool.input_schema/output_schema— полноценные документы JSON Schema 2020-12 (InputSchemaповерхserde_json::Value); макрос#[tool]формирует их автоматически. Схема публикуется в том виде, в каком её объявили:default(SEP-1034),pattern,examples,$schema,$defs,$ref,additionalProperties,allOf/anyOfиif/then/elseсохраняются дословно (SEP-2106), в том числе ниже корня.- Аргументы извлекаются по имени, поэтому обработчик инструмента и его
опубликованная схема обязаны называть одни и те же аргументы — при
расхождении
App::runотказывается стартовать. ПараметрOption<T>публикуется, но не попадает вrequired. См. Инструменты → Имена аргументов. - Детерминированный порядок списка. Реестры основаны на
BTreeMapи упорядочены по имени, поэтомуtools/listстабилен между вызовами: курсорная пагинация больше не может пропустить или продублировать запись, а промпт-кэши LLM попадают чаще. x-mcp-header. Сервер может аннотировать свойство вinputSchemaинструмента, чтобы аргумент дублировался в заголовкеMcp-Param-{name}. Клиенты обязаны это поддерживать, поэтому клиент neva запоминает аннотации изtools/listи добавляет заголовки приtools/call. Определение, нарушающее ограничения спецификации (имя не является токеном, дубликат, непримитивный тип или свойство, недостижимое статически черезproperties), исключает из списка весь инструмент — так одно неверное определение не сможет изменить то, что отправляет корректное. Работает только для Streamable HTTP; другие транспорты вправе игнорировать аннотацию.
Расширения
Появился трейт Extension: возможность, объявляемая внутри
capabilities.extensions по reverse-DNS идентификатору и приносящая с собой те
методы и метаданные, которые сама определяет. Вместе с neva поставляются два
встроенных потребителя.
Задачи
Задачи появились в neva первыми из двух, объявляются
через capabilities.extensions["io.modelcontextprotocol/tasks"]. Возможность
представлена пустым объектом — само её объявление и есть декларация, —
поэтому opt.with_tasks() не принимает замыкание.
tasks/get — единственный метод опроса, возвращающий DetailedTask;
tasks/update отвечает на input-запросы задачи; tasks/cancel подтверждает
пустым результатом. tasks/list и tasks/result удалены.
MCP Apps
MCP Apps
(SEP-1865) — первое
официальное расширение MCP, объявляется через
capabilities.extensions["io.modelcontextprotocol/ui"] и включается вызовом
opt.with_apps(). Оно даёт инструменту лицо: HTML-документ ui://, который
хост рендерит в песочнице iframe и в который передаёт результат инструмента.
Собственных методов оно не приносит — UI это метаданные на обычных
инструментах и ресурсах. Блок _meta.ui на инструменте называет ресурс, который
его отрисовывает; блок _meta.ui на этом ресурсе несёт его CSP, разрешения и
предпочтения по оформлению. Всё, что называется ui/*, — трафик postMessage
между хостом и его iframe, до сервера он не доходит.
Значение capability отличается по направлениям, и это требование спецификации, а
не асимметрия neva: сервер объявляет {}, а клиент обязан назвать типы
содержимого, которые умеет рендерить (mimeTypes), поэтому with_apps() на
клиенте подставляет text/html;profile=mcp-app. Клиент, не назвавший ни одного,
поддержку не объявил.
Серверная половина существует только в 2026-07-28;
клиентская работает в обоих поколениях. До сервера на
2026-07-28 объявление доходит в
_meta каждого запроса, так что обработчик
может спросить ctx.supports_apps() и подобрать content под ответ.
Авторизация
HTTP MCP-сервер — это защищённый ресурс OAuth 2.1: он публикует документ
Protected Resource Metadata (RFC 9728)
и отвечает на неавторизованный запрос вызовом WWW-Authenticate, который на
него указывает, — так что клиент, знающий только URL конечной точки, находит
сервер авторизации по 401. Токены несут resource indicator по RFC 8707, и
токен, выпущенный для другого ресурса, отклоняется, а не принимается потому,
что он валиден.
Поколение также меняет порядок получения client_id: сначала предварительно
зарегистрированный, затем Client ID Metadata Document — https-URL, который
сервер авторизации разыменовывает, — и только потом Dynamic Client Registration
(RFC 7591), которую эта спецификация объявляет устаревшей.
neva реализует половину сервера-ресурса за фичей server-oauth, а клиентскую —
за client-oauth, плюс два опциональных расширения:
| За пределами текста спецификации | Фича | Что это |
|---|---|---|
Аутентификация клиента private_key_jwt | client-oauth-jwt | Клиент подписывает короткоживущее утверждение собственным ключом вместо общего секрета — то, что РЕКОМЕНДУЕТ расширение client credentials |
| DPoP: токены, привязанные к отправителю | client-oauth-dpop | RFC 9449. SEP-1932 не смержен, а в тексте 2026-07-28 DPoP не встречается, поэтому пакет проверок соответствия относит его к расширениям. Выключено по умолчанию и само не включается |
Неинтерактивные grants — client credentials
(io.modelcontextprotocol/oauth-client-credentials), JWT bearer по RFC 7523 и
корпоративный профиль с identity assertion — покрывают развёртывания, где перед
браузером никого нет.
См. Сервер → OAuth 2.1 и Клиент → OAuth 2.1.
Удалено в этом поколении
ping(а такжеClient::ping,BatchBuilder::ping)logging/setLevel(а такжеwith_logging/set_log_level) — заменено логированием в области запросаtasks/list,tasks/resultnotifications/roots/list_changednotifications/elicitation/complete(а такжеContext::complete_elicitation,Client::on_elicitation_completed,ElicitationCompleteParams)elicitationIdв URL-elicitation — без серверного сигнала о завершении нечего сопоставлятьwith_mcp_versionна сервере (доступен подlegacy-spec)resources/subscribe/resources/unsubscribeкак RPC-методы — свёрнуты вSubscriptionFilter::resource_subscriptions. На сервереctx.resources().subscribe/unsubscribeсуществуют только подlegacy-spec; на клиенте методы остаются скомпилированными для легаси-отката, но на узле 2026-07-28 отвечаютMethodNotFound- Значения
thisServer/allServersуincludeContextпомечены#[deprecated]; не указывайте поле или используйтеnone
Новые коды ошибок
Вариант ErrorCode | Код | HTTP | Полезная нагрузка data |
|---|---|---|---|
HeaderMismatch | -32020 | 400 | — |
MissingRequiredClientCapability | -32021 | 400 | requiredCapabilities |
UnsupportedProtocolVersion | -32022 | 400 | supported / requested |
Error::with_data прикрепляет полезную нагрузку, определённую
спецификацией. См. Обработка ошибок.
Что почитать дальше
- CHANGELOG и заметки о релизах — что изменилось в каждом релизе.
examples/mrtr— сквозной пример MRTR: сервер и клиент.examples/subscriptions—subscriptions/listenпо HTTP: сервер и клиент.examples/sampling/examples/roots— сэмплирование и корневые каталоги на подложке MRTR.examples/tasks— переработанное расширение Tasks.examples/oauth-with-keycloak— авторизация целиком, против реального issuer'а.cargo doc --features full --open— справочник API для сборки по умолчанию в вашем собственном чекауте. Учтите, что--all-featuresвключаетlegacy-spec, а это компилирует данное поколение из сборки прочь.