Объект Context
Машинный перевод
Эта страница переведена с английской документации автоматически, и основной версией остаётся английская страница. Если что-то читается неправильно, на странице Переводы объясняется, как об этом сообщить.
Аргументы инструмента приходят от модели. Всё остальное (запрос, который вы обслуживаете, сервер, внутри которого работаете, способ обратиться к клиенту) приходит из одного объекта: Context.
Его не нужно ни создавать, ни настраивать. Достаточно попросить.
Попросите его
Добавьте в любой инструмент параметр с аннотацией Context:
from mcp.server import MCPServer
from mcp.server.mcpserver import Context
mcp = MCPServer("Bookshop")
@mcp.tool()
def search_books(query: str, ctx: Context) -> str:
"""Search the catalog by title or author."""
return f"[request {ctx.request_id}] Found 3 books matching {query!r}."
- SDK создаёт новый
Contextдля каждого запроса и передаёт его в функцию. - Имя параметра не важно.
ctx,context,c: SDK находит его по аннотации. - Ресурсы и промпты могут объявить такой параметр точно так же.
ctx.request_id— идентификатор запроса, который ваша функция обслуживает прямо сейчас.
Info
Если вы работали с FastAPI, этот приём вам знаком: объявляете параметр с типом самого фреймворка
(там Request, здесь Context), и фреймворк его подставляет. Ничего регистрировать, ничего
настраивать: весь механизм — это аннотация типа.
Невидим для модели
Вот что стоит усвоить. Так выглядит входная схема, которую tools/list сообщает для search_books:
{
"type": "object",
"properties": {
"query": {"title": "Query", "type": "string"}
},
"required": ["query"],
"title": "search_booksArguments"
}
Одно свойство. ctx — не аргумент: он никогда не появляется в схеме, модели о нём не сообщают, и ни один клиент не может его заполнить. Это договорённость между вами и SDK, невидимая в передаваемых данных.
Попробуйте сами
Запустите сервер через MCP Inspector:
uv run mcp dev server.py
В форме для search_books единственное поле — query. Вызовите инструмент со значением dune:
[request 3] Found 3 books matching 'dune'.
Число — номер того запроса, которым оказался этот вызов. Вызовите инструмент ещё раз, и оно изменится: каждый запрос получает свой Context.
Что он даёт
Внедряемый объект невелик. Помимо request_id:
await ctx.read_resource(uri): прочитать один из собственных ресурсов сервера изнутри инструмента. Об этом следующий раздел.await ctx.report_progress(progress, total, message): передавать вызывающей стороне ход выполнения во время долгого вызова. Подробнее — на странице Прогресс.await ctx.elicit(message, schema)иawait ctx.elicit_url(...): приостановить инструмент и задать пользователю вопрос. Это элицитация (elicitation).ctx.session: серверная сторона разговора с этим клиентом. Здесь живут уведомления, которые вы отправляете клиенту; последний раздел её использует.ctx.headers: заголовки запроса, которые передал транспорт, илиNoneна stdio. Прочитать нестандартный заголовок можно так:(ctx.headers or {}).get("x-..."). Заголовки — это данные от клиента: годятся для локали или флага возможности, но никогда для идентификации.ctx.request_context: сырая запись о текущем запросе. Поле, к которому вы будете обращаться, —lifespan_context, объект, который вернул ваш код запуска (см. Жизненный цикл (lifespan)).
Логирования в этом списке нет намеренно. Сервер пишет логи через модуль Python logging, как любая другая программа на Python. Почему так — на короткой странице Логирование.
Tip
Внедрение происходит только для функции, которую вы зарегистрировали. Вспомогательная функция,
которую вызывает ваш инструмент, не получает собственный Context; передавайте ей ctx как
обычный аргумент. Никакого фонового «текущего контекста», который можно достать откуда-то ещё,
не существует.
Чтение собственных ресурсов
Ресурсы сервера предназначены не только для клиентов. Инструмент тоже может их читать:
from mcp.server import MCPServer
from mcp.server.mcpserver import Context
mcp = MCPServer("Bookshop")
@mcp.resource("catalog://genres")
def genres() -> str:
"""The genres the catalog is organised into."""
return "fiction, non-fiction, poetry"
@mcp.tool()
async def describe_catalog(ctx: Context) -> str:
"""Describe how the catalog is organised."""
[contents] = await ctx.read_resource("catalog://genres")
return f"The catalog is organised into: {contents.content}"
ctx.read_resource разрешает URI через тот же реестр, что обслуживает resources/read, поэтому инструмент получает то же, что получил бы клиент: итерируемый набор ReadResourceContents, по одному на блок содержимого. Для этого URI он один:
contents.content # 'fiction, non-fiction, poetry'
contents.mime_type # 'text/plain'
content— ровно то, что вернулаgenres(). Один источник истины: клиент просматривает ресурс, ваши инструменты его потребляют, никто не копирует строку.- Единственный параметр
describe_catalog— этоContext, поэтому в его входной схеме вообще нет свойств. Модель вызывает его с{}.
Сообщите клиенту, что список изменился
То, что предлагает сервер, не зафиксировано на момент импорта. Зарегистрируйте инструмент во время выполнения, а затем сообщите об этом клиенту:
from mcp.server import MCPServer
from mcp.server.mcpserver import Context
mcp = MCPServer("Bookshop")
def recommend_book(genre: str) -> str:
"""Recommend a book in the given genre."""
return f"In {genre}, try 'Dune'."
@mcp.tool()
async def enable_recommendations(ctx: Context) -> str:
"""Switch on the recommendation tool."""
mcp.add_tool(recommend_book)
await ctx.session.send_tool_list_changed()
return "Recommendations are now available."
mcp.add_tool(recommend_book)регистрирует обычную функцию как инструмент: имя, описание и схема выводятся точно так же, как это сделал бы@mcp.tool().await ctx.session.send_tool_list_changed()отправляетnotifications/tools/list_changed. Клиент, получивший его, снова вызываетtools/listи видитrecommend_book.
Родственные методы — send_resource_list_changed(), send_prompt_list_changed() и send_resource_updated(uri) для изменения одного конкретного ресурса.
На подключении 2026-07-28 клиенты получают уведомления об изменениях только в потоке subscriptions/listen, который они открыли, поэтому перечисленные выше методы send_* до этих потоков не доходят. Методы публикации в Context доставляют уведомление сразу во все подписанные потоки: await ctx.notify_tools_changed(), await ctx.notify_prompts_changed(), await ctx.notify_resources_changed() и await ctx.notify_resource_updated(uri). Подробнее, включая масштабирование на несколько реплик, — на странице Подписки.
Check
Пока никто не запустил enable_recommendations, обещанного инструмента не существует. Вызовите
его всё равно, и результатом будет ошибка, которую модель может прочитать:
Unknown tool: recommend_book
Запустите enable_recommendations, и тот же самый вызов проходит успешно. Список инструментов
действительно динамический: tools/list отражает то, что зарегистрировано прямо сейчас.
Итоги
- Аннотируйте параметр типом
Context(в инструменте, ресурсе или промпте), и SDK его внедрит. Имя выбираете вы. - Для модели он невидим: входная схема всегда содержит только ваши настоящие аргументы.
ctx.request_idидентифицирует запрос;ctx.request_context.lifespan_context— то, что вернул ваш код запуска.await ctx.read_resource(uri)позволяет инструменту читать собственные ресурсы сервера.ctx.session— канал обратно к клиенту:send_tool_list_changed()и родственные методы велят ему заново запросить изменённый список.- Отчёты о ходе выполнения и элицитация тоже начинаются с
Context; у каждой темы своя страница.
Параметры, которых модель никогда не видит и которые заполняют ваши собственные функции, — это Зависимости.