Перейти до змісту

Об'єкт Context

Машинний переклад

Цю сторінку перекладено автоматично з англомовної документації, і основною версією є англомовна сторінка. Якщо щось читається неправильно, на сторінці Переклади пояснено, як про це повідомити.

Аргументи інструмента надходять від моделі. Усе інше (запит, який ви обслуговуєте, сервер, у якому живе інструмент, спосіб звернутися назад до клієнта) надходить з одного об'єкта: Context.

Його не потрібно ні створювати, ні налаштовувати. Достатньо попросити.

Як його попросити

Додайте до будь-якого інструмента параметр з анотацією Context:

server.py
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, об'єкт, який ваш код запуску віддав через yield (див. Життєвий цикл (lifespan)).

Логування навмисно немає в цьому списку. Сервер пише логи модулем logging Python, як і будь-яка інша програма на Python. Логування — коротка сторінка про те, чому саме так.

Tip

Впровадження відбувається лише для функції, яку ви зареєстрували. Допоміжна функція, яку викликає ваш інструмент, не отримує власного Context; передавайте ctx далі як звичайний аргумент. Жодного фонового «поточного контексту», який можна було б дістати звідкись іще, немає.

Читання власних ресурсів

Ресурси сервера — не лише для клієнтів. Інструмент теж може їх читати:

server.py
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, тож його схема вхідних даних не має жодної властивості. Модель викликає його з {}.

Сповіщення клієнта про зміну списку

Те, що пропонує сервер, не зафіксовано на момент імпорту. Зареєструйте інструмент під час виконання, а потім повідомте клієнта:

server.py
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 — те, що ваш код запуску віддав через yield.
  • await ctx.read_resource(uri) дає інструменту змогу читати власні ресурси сервера.
  • ctx.session — канал назад до клієнта: send_tool_list_changed() та споріднені методи кажуть йому заново отримати список, який ви змінили.
  • Звітування про перебіг виконання та еліцитація теж починаються з Context; кожному присвячено окрему сторінку.

Параметри, яких модель ніколи не бачить і які заповнюють ваші власні функції, — це Залежності.