Перейти к содержанию

Сэмплирование и корневые каталоги

Машинный перевод

Эта страница переведена с английской документации автоматически, и основной версией остаётся английская страница. Если что-то читается неправильно, на странице Переводы объясняется, как об этом сообщить.

Обработчик может попросить у подключённого клиента ещё две вещи: завершение (completion) от собственной модели клиента — это сэмплирование (sampling), и рабочие папки клиента — это корневые каталоги (roots).

И то и другое по-прежнему работает, на каждой версии протокола, которую поддерживает SDK. Но прежде чем строить на них архитектуру, прочтите предупреждение:

Объявлено устаревшим в спецификации 2026-07-28

Сэмплирование и корневые каталоги объявлены устаревшими начиная с 2026-07-28 (SEP-2577). Они остаются полностью работоспособными и сохраняются в спецификации как минимум двенадцать месяцев, прежде чем их можно будет удалить, но новые реализации не должны на них опираться. Предлагаемые пути миграции: вместо сэмплирования интегрируйтесь напрямую с API вашего поставщика LLM, а вместо корневых каталогов передавайте каталоги через параметры инструментов, URI ресурсов или конфигурацию сервера. Общий для SDK список — на странице Устаревшие возможности.

Сэмплирование: одолжить модель клиента

Резолвер возвращает Sample(...), и инструмент получает завершение — через тот же механизм зависимостей, который выполняет Elicit на странице Зависимости:

server.py
from typing import Annotated

from mcp.server import MCPServer
from mcp.server.mcpserver import Resolve, Sample
from mcp.types import CreateMessageResult, SamplingMessage, TextContent

mcp = MCPServer("Bookshop")


def draft_blurb(title: str) -> Sample:
    prompt = f"Write a one-sentence blurb for the book {title!r}."
    return Sample(
        [SamplingMessage(role="user", content=TextContent(type="text", text=prompt))],
        max_tokens=60,
    )


@mcp.tool()
async def blurb(title: str, draft: Annotated[CreateMessageResult, Resolve(draft_blurb)]) -> str:
    """Draft a blurb for a book."""
    return draft.content.text if draft.content.type == "text" else "No blurb."
  • Sample(messages, max_tokens=...) повторяет параметры sampling/createMessage. Внедряемое значение — CreateMessageResult клиента; передайте tools или tool_choice, и вместо него придёт CreateMessageResultWithTools.
  • Клиент должен был объявить возможность sampling (sampling.tools, если передаёте tools или tool_choice). Если он этого не сделал, вызов завершается ошибкой протокола -32021, а не отправкой запроса, который клиент не сможет обработать. Сессия до 2026 года без обратного канала (back-channel) завершается своей обычной ошибкой об отсутствии обратного канала, поскольку отправлять запрос попросту некуда.
  • На 2026-07-28 запрос доставляется внутри многораундового потока (Многораундовые запросы); на 2025-11-25 это самостоятельный запрос к клиенту. Код в обоих случаях один и тот же, но помните о правиле многораундовых запросов: запрос должен выглядеть одинаково во всех раундах повтора, поэтому стройте его только из аргументов инструмента и других стабильных данных.
  • Не трогайте include_context: значения, отличные от "none", сами объявлены устаревшими (SEP-2596) и требуют возможности, которую почти ни один клиент не объявляет.

Корневые каталоги: куда это положить?

Корневые каталоги — это папки, с которыми, по словам клиента, серверу разрешено работать. Это справочная подсказка, а не механизм контроля доступа. Резолвер возвращает ListRoots():

server.py
from typing import Annotated

from mcp.server import MCPServer
from mcp.server.mcpserver import ListRoots, Resolve
from mcp.types import ListRootsResult

mcp = MCPServer("Bookshop")


def workspace_roots() -> ListRoots:
    return ListRoots()


@mcp.tool()
async def catalog_folder(roots: Annotated[ListRootsResult, Resolve(workspace_roots)]) -> str:
    """Pick the folder the catalog export should go to."""
    if not roots.roots:
        return "No workspace folders shared."
    return str(roots.roots[0].uri)
  • Внедряемый ListRootsResult содержит список объектов Root: URI вида file:// и необязательное отображаемое имя.
  • Проверка та же, что и для сэмплирования: без объявленной возможности roots вызов завершается ошибкой -32021, а не отправкой запроса.

На другой стороне соединения клиент отвечает на оба запроса уже имеющимися у него колбэками: sampling_callback и list_roots_callback, описанными на странице Колбэки клиента.

На подключениях поколения 2025

ctx.session.create_message(...) и ctx.session.list_roots() по-прежнему существуют для кода, который управляет сессией напрямую. Они работают только там, где есть обратный канал (подключения поколения 2025, не stateless), а их вызов выдаёт предупреждение об устаревании. Маркеры резолверов, показанные выше, — поддерживаемая форма: они выбирают способ доставки по согласованной версии и не выдают предупреждений.

Итоги

  • Возвращайте Sample(...) или ListRoots() из резолвера; инструмент получает CreateMessageResult или ListRootsResult как любую другую зависимость.
  • Клиент должен объявить соответствующую возможность, иначе вызов завершится ошибкой -32021 вместо отправки запроса.
  • Обе возможности объявлены устаревшими в 2026-07-28: пока полностью работоспособны, но для новых проектов не годятся. Предпочитайте API поставщика сэмплированию, а явные параметры — корневым каталогам.

Как сообщать, насколько продвинулся медленный инструмент: Ход выполнения.