Перейти к содержимому
Выберите тему

Rust SDK (rmcp)

Официальный Rust SDK для MCP - пакет rmcp 3.5.0, установка, редакции протокола, режимы жизненного цикла клиента и то, что обычный вызов serve остаётся в эпохе Legacy.

сверено с выпуском 3.5.0 4 октября 2026

Это SDK из репозитория modelcontextprotocol/rust-sdk. В списке официальных SDK он отнесён к первому уровню (Tier 1).

В репозитории два пакета: rmcp - основная реализация протокола, и rmcp-macros - процедурные макросы для описания инструментов. Ветка одна, 3.x. Выпуск 3.5.0 опубликован на crates.io 28 сентября 2026.

Клиент и сервер включаются признаками Cargo. По Cargo.toml пакета, по умолчанию включены base64, macros и server; клиентская часть включается признаком client.

Установка
cargo add rmcp --features server

Команда взята из README выпуска. Нужен Rust 1.88 или новее, редакция языка 2024: так записано в корневом Cargo.toml и в данных crates.io. Основные зависимости по README - tokio и serde; схемы JSON в диалекте 2020-12 строит schemars.

Список версий в model.rs: 2024-11-05, 2025-03-26, 2025-06-18, 2025-11-25, 2026-07-28.

В том же файле две константы, которые нужно различать:

  • ProtocolVersion::LATEST - самая новая известная версия, в этом выпуске 2026-07-28;
  • ProtocolVersion::LATEST_WITH_INITIALIZE - самая новая версия, в которой ещё есть рукопожатие initialize, то есть 2025-11-25.

README формулирует поддержку так: SDK реализует редакцию 2026-07-28 и остаётся совместимым с 2025-11-25 и более ранними версиями. Чем эпохи различаются, описано на странице о версиях протокола.

По README, serve() использует старый жизненный цикл: клиент отправляет initialize, получает согласованные сведения о сервере и отправляет notifications/initialized. Комментарий в service/client.rs подтверждает: поведение ServiceExt::serve осталось старой инициализацией.

Другой порядок выбирается явно, вызовом ClientServiceExt::serve_with_lifecycle с одним из значений ClientLifecycleMode:

Режим Что уходит в сеть Со старым сервером
Initialize рукопожатие initialize; то же, что serve() работает
Discover server/discover, затем данные клиента в каждом запросе ошибка, отката нет
Auto пробный server/discover откат на initialize

В режиме Auto откат происходит, когда сервер сообщает, что он старый, или не отвечает 10 секунд. Версию для отката задаёт поле legacy_version.

Клиент с выбором жизненного цикла
use rmcp::{ClientLifecycleMode, ClientServiceExt, ProtocolVersion, model::ClientConfig};
// Start directly with server/discover and include client metadata on every request.
let client = ClientConfig::default()
.serve_with_lifecycle(
transport,
ClientLifecycleMode::Discover {
preferred_versions: vec![ProtocolVersion::V_2026_07_28],
},
)
.await?;
// Or probe the discover lifecycle and fall back when a legacy server reports
// that server/discover is not implemented or does not respond within 10 seconds.
let client = ClientConfig::default()
.serve_with_lifecycle(
transport,
ClientLifecycleMode::Auto {
preferred_versions: vec![ProtocolVersion::V_2026_07_28],
legacy_version: Some(ProtocolVersion::V_2025_11_25),
},
)
.await?;

Пример взят из документации выпуска 3.5.0 и здесь не запускался.

Это отрывок из README: переменная transport в нём создаётся отдельно. По README, при запуске через server/discover уведомление notifications/initialized не отправляется, а каждый следующий запрос несёт версию протокола, сведения о клиенте и его возможности в _meta.

Серверный обработчик в handler/server.rs содержит метод discover и отвечает на запрос server/discover.

Для Streamable HTTP README описывает правило так: редакцию 2026-07-28 rmcp обслуживает без сеансов сам. В ней нет Mcp-Session-Id, отдельного потока GET и DELETE, возобновления по Last-Event-ID.

Настройка legacy_session_mode в StreamableHttpServerConfig относится только к версиям старше 2026-07-28. По tower.rs, её значение по умолчанию - true: старые клиенты получают сеанс. Вызов with_legacy_session_mode(false) обслуживает без сеансов и старые версии.

Сервер на stdio с одним инструментом
use rmcp::{handler::server::wrapper::Parameters, schemars, tool, tool_router, ServiceExt, transport::stdio};
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
struct AddParams {
a: i32,
b: i32,
}
#[derive(Clone)]
struct Calculator;
#[tool_router(server_handler)]
impl Calculator {
#[tool(description = "Add two numbers")]
fn add(&self, Parameters(AddParams { a, b }): Parameters<AddParams>) -> String {
(a + b).to_string()
}
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let service = Calculator.serve(stdio()).await?;
service.waiting().await?;
Ok(())
}

Пример взят из документации выпуска 3.5.0 и здесь не запускался.

Транспорты подключаются признаками Cargo. По таблице в README: stdio - transport-io, запуск дочернего процесса на стороне клиента - transport-child-process, сервер Streamable HTTP - transport-streamable-http-server (служба Tower, которую можно подключить к любому маршрутизатору), клиент Streamable HTTP - transport-streamable-http-client-reqwest.

Tasks. Реализовано. По README, rmcp реализует расширение MCP Tasks (SEP-2663, io.modelcontextprotocol/tasks). Клиент объявляет расширение в своих возможностях, а сервер для каждого запроса решает, оформлять ли вызов tools/call как задачу. Клиент опрашивает tasks/get, отвечает на запросы ввода через tasks/update и может запросить отмену через tasks/cancel. На стороне сервера задачами управляет rmcp::task_manager::TaskManager.

MCP Apps. Отдельной реализации нет. В выпуске есть только общая карта возможностей extensions, а идентификатор io.modelcontextprotocol/ui встречается как пример ключа в одном тесте в capabilities.rs. Программного интерфейса для Apps и раздела о них в README нет. То есть объявить возможность можно, а всё остальное вы пишете сами.

Что такое расширения и как о них договариваются стороны - на странице о расширениях.

  • Roots, Sampling, Logging. В README каждая из трёх возможностей помечена как устаревшая по SEP-2577 с оговоркой, что пока она полностью работает и будет удалена в одном из будущих выпусков.
  • Сеансы и initialize. Сохранены для версий старой эпохи: legacy_session_mode на сервере и serve() на клиенте.
  • Динамическая регистрация клиента. По документу об OAuth, клиент выбирает способ регистрации по порядку: заранее выданные данные клиента, затем Client ID Metadata Documents, затем динамическая регистрация как запасной путь.
  • HTTP+SSE. Старого транспорта из двух адресов в выпуске нет, и это решение проекта: README выпуска называет его осознанным отказом и сообщает, что ни клиентского, ни серверного транспорта HTTP+SSE SDK не поставляет и возвращать не будет. К серверу, который умеет только HTTP+SSE, клиент на rmcp не подключится. Признак client-side-sse - это разбор потока SSE внутри клиента Streamable HTTP, а не старый транспорт.

Общий список - на странице об устаревших возможностях.

На 4 октября 2026 ветка main опережает метку rmcp-v3.5.0 на четыре изменения: два касаются служебной автоматики репозитория, одно - уведомления хранилищ учётных данных при отклонённом токене обновления, одно - примера. Константы версий протокола совпадают с выпуском, номер версии пакета в main по-прежнему 3.5.0. Пробных выпусков новее 3.5.0 на crates.io нет.

  • Помечена ли динамическая регистрация клиента как устаревшая в коде выпуска: документ об OAuth называет её запасным путём, но об устаревании не говорит.
  • Содержимое опубликованного архива пакета отдельно не распаковывалось: сверялись исходники по метке выпуска и данные crates.io.