Корневые каталоги
MCP 2026-07-28 убрал roots/list как серверный запрос к клиенту, управляемый
возможностями, и перенёс его на MRTR в виде
вида input-запроса
— сразу устаревшего. Client::add_root / add_roots помечены
#[deprecated] и требуют #[allow(deprecated)].
Новым инструментам лучше принимать нужные пути явными аргументами.
Model Context Protocol (MCP) предоставляет стандартизированный способ для клиентов предоставлять серверам файловые «корневые каталоги». Корневые каталоги определяют границы, в пределах которых серверы могут работать в файловой системе, позволяя им понять, к каким директориям и файлам у них есть доступ.
Корневые каталоги — это конфигурируемые данные
Корневые каталоги — не обработчик. Клиент отвечает на input-запрос сервера
roots/list из того списка, с которым он был собран, а непустой список
заставляет его объявить clientCapabilities.roots в каждом запросе:
сервер может запросить только тот вид, который клиент объявил.
Поскольку уведомления notifications/roots/list_changed больше нет, сервер
видит тот список, который есть у клиента в момент прихода запроса.
Подписываться не на что, и возможность roots.listChanged включать не нужно.
Добавление корневых каталогов
use neva::prelude::*;
#[tokio::main]
async fn main() -> Result<(), Error> {
let mut client = Client::new()
.with_options(|opt| opt
.with_http(|http| http.bind("127.0.0.1:3001").with_endpoint("/mcp")));
// Устарело с момента появления, как и весь вид roots.
#[allow(deprecated)]
client
.add_root("file:///home/user/projects/my_project", "My Project")
.add_root("file:///home/user/projects/my_another_project", "My Another Project");
client.connect().await?;
// Раунд MRTR происходит внутри этого единственного вызова.
let result = client.call_tool("scan_workspace", ()).await?;
tracing::info!("Result: {:?}", result.content);
client.disconnect().await
}
Доступ к корневым каталогам на сервере
Внедрите Context
в обработчик инструмента и запросите список со стабильным replay-ключом:
#[tool]
async fn scan_workspace(mut ctx: Context) -> Result<String, Error> {
// Первый раунд разворачивает обработчик с `input_required` и конвертом
// `roots/list`; второй — воспроизводит ответ из `requestState`.
#[allow(deprecated)]
let roots = ctx.list_roots("dirs").await?;
// Каждый корневой каталог содержит URI и человекочитаемое имя
for root in &roots.roots {
tracing::info!(uri = %root.uri, name = %root.name);
}
Ok(format!("корневых каталогов: {}", roots.roots.len()))
}
Весь код выше точки list_roots выполняется на втором раунде заново,
поэтому побочные эффекты оборачивайте в ctx.memo / ctx.once /
ctx.on_commit — те же примитивы, что и для
получения данных.
legacy-specКорневые каталоги работают как push-запрос: ctx.list_roots() не принимает
ключ, их можно добавлять после connect(), а возможность
roots.listChanged (with_roots(|r| r.with_list_changed())) уведомляет
сервер об изменениях. См. Легаси-спецификация.
Обучение на примерах
Полный пример доступен здесь.