ГлавнаяБлогMCP cacheScope: изоляция приватных ответов по кэш-партициям
AI / Нейросети

MCP cacheScope: изоляция приватных ответов по кэш-партициям

Как избежать утечки данных между пользователями в MCP: используйте cachePartition для приватных ответов. Научитесь тестировать изоляцию кэша.

Al
Редакция Algolitalgolit.ru
8 мин чтения15 августа 2026 г.

Проблема: свежий ответ может быть небезопасен для другого пользователя

В спецификации MCP от 28 июля 2026 года кэш-подсказки (cache hints) определяют, как долго результат может считаться свежим, но не учитывают, кто именно запрашивает данные. Если Алиса нагревает общий кэш, а Боб затем использует тот же самый стор, неполный ключ кэша может вернуть Бобу результат Алисы. Это нарушение границы безопасности, которое стоит тестировать.

В этой статье мы разберем, как cacheScope и cachePartition помогают изолировать приватные ответы, и покажем рабочий пример на TypeScript, который воспроизводит утечку и исправляет её.

Что такое cacheScope в MCP и почему он не решает проблему

Спецификация MCP покрывает кэширование для discovery, списков инструментов и промптов, списков ресурсов, шаблонов ресурсов и чтения ресурсов. Значения cacheScope имеют разный смысл:

  • public — результат можно переиспользовать между разными контекстами авторизации, даже если эндпоинт требует аутентификацию.
  • private — результат можно переиспользовать только в пределах одного и того же контекста авторизации.

Для пользовательского каталога инструментов сервер может описать результат так:

const server = new McpServer({
  name: "private-catalog",
  version: "1.0.0"
}, {
  cacheHints: {
    "tools/list": {
      ttlMs: 60_000,
      cacheScope: "private"
    }
  }
});

Эта подсказка сообщает о намерениях сервера, но не говорит общему кэшу, какой вызывающий сделал запрос. Ключ кэша уже должен включать метод и все параметры, которые могут изменить результат. Идентичность авторизации обычно передаётся вне параметров tools/list, поэтому клиенту нужна отдельная партиция для неё.

Воспроизведение утечки: общий стор без партиций

Опасная конфигурация выглядит просто: два контекста авторизации, один кэш ответов, и никакой партиции.

const sharedCache = new InMemoryResponseCacheStore();

function unpartitionedClient() {
  return new Client({
    name: "shared-gateway",
    version: "1.0.0"
  }, {
    responseCacheStore: sharedCache
  });
}

Демонстрация использует два эндпоинта в одном процессе с одинаковой идентичностью сервера. Один эндпоинт отдаёт инструмент только для Алисы, другой — только для Боба. Эти эндпоинты имитируют разные результаты, которые реальный аутентифицированный сервер мог бы производить.

Алиса вызывает tools/list первой, помещая свой приватный результат в общий стор. Затем Боб вызывает тот же метод с теми же параметрами и идентичностью сервера. Без cachePartition поиск не различает контексты авторизации. Боб получает кэшированный список инструментов Алисы, а его эндпоинт не получает ни одного запроса tools/list. Официальное руководство по кэшированию TypeScript SDK предупреждает, что такая конфигурация может отдать одному пользователю приватное тело ответа другого.

Тест намеренно считается пройденным, когда воспроизводит небезопасный результат. Это делает видимым сбой без учётных данных, сетевого сервиса, модели или платного вызова API.

Исправление: стабильная партиция для каждого контекста

Решение — дать каждому контексту авторизации стабильную партицию кэша:

const sharedCache = new InMemoryResponseCacheStore();

function clientFor(cachePartition: string) {
  return new Client({
    name: "shared-gateway",
    version: "1.0.0"
  }, {
    responseCacheStore: sharedCache,
    cachePartition
  });
}

const alice = clientFor("subject:alice");
const bob = clientFor("subject:bob");

Теперь приватные записи Алисы хранятся отдельно от записей Боба. Тот же метод, параметры и идентичность сервера больше не приводят к одному и тому же месту в приватном кэше. Второй тест подтверждает, что оба эндпоинта получают по одному запросу, и каждый клиент видит только свои инструменты.

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

SDK обрабатывает публичные записи иначе: они остаются общими между партициями, потому что сервер явно объявил их безопасными для переиспользования между контекстами. Это сохраняет преимущество производительности, не ослабляя приватную изоляцию.

Практический вывод: что делать прямо сейчас

Мой чек-лист краток:

  1. Помечайте результаты, зависящие от авторизации, как private.
  2. Настройте cachePartition всегда, когда один стор обслуживает несколько субъектов.
  3. Тестируйте два субъекта против одного метода, параметров и идентичности сервера.

Рабочий пример на TypeScript включает небезопасное воспроизведение и исправление с партициями. Ограничения: cacheScope не является авторизацией. Он управляет переиспользованием кэша, но не предоставляет доступ. Сервер должен аутентифицировать вызывающего и авторизовать каждый некэшированный запрос.

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

Другие границы протокола тоже важны. Многоэтапные повторные запросы с inputResponses или requestState не должны кэшироваться. Результат input_required неполный и не подлежит кэшированию. Пагинация кэшируется по одной странице за раз, использует ту же область между страницами и не гарантирует согласованность снимков.

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

Знает ли ваш MCP-клиент, какому контексту авторизации принадлежит каждый приватный результат? Если нет — исправьте это сегодня.

#MCP#кэширование#безопасность#cacheScope#TypeScript
Al
Редакция Algolit

Пишем про алгоритмы, подготовку к собеседованиям и карьеру в IT — так, чтобы было понятно и полезно.

Хочешь закрепить знания на практике?

Решай задачи на Algolit — интерактивная платформа для обучения

Начать бесплатно →