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

Параметры в заголовках

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

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

Большинству серверов это не понадобится.

Шлюз или балансировщик нагрузки перед сервером может маршрутизировать запросы только по тем данным, которые читаются без разбора тела. Пометьте аргумент инструмента ключом x-mcp-header, и клиенты, работающие с версией протокола 2026-07-28, будут передавать его значение ещё и в HTTP-заголовке.

Пометка аргумента

Пометка — это один дополнительный ключ в JSON-схеме аргумента. В MCPServer его туда добавляет Field:

server.py
from typing import Annotated

from pydantic import Field

from mcp.server import MCPServer

mcp = MCPServer("Bookshop")


@mcp.tool()
def check_stock(
    title: str,
    region: Annotated[str, Field(json_schema_extra={"x-mcp-header": "Region"})],
) -> str:
    """Count the copies of a book in one region's warehouses."""
    return f"{title}: 3 copies in {region}."
  • При работе через Streamable HTTP с версией 2026-07-28 клиент отправляет заголовок Mcp-Param-Region вместе с телом запроса, а сервер отклоняет вызов, в котором они расходятся.
  • Клиент, который не запрашивал список инструментов, пометки не видел: заголовок он не отправляет, и вызов отклоняется. Класс Client из этого SDK в таком случае запрашивает список инструментов и один раз повторяет вызов, так что предварительный запрос списка лишь экономит один цикл «запрос — ответ».
  • Все остальные подключения эту аннотацию игнорируют.

Сама функция не меняется: region по-прежнему приходит как аргумент.

Что можно пометить

Аргументы типов str, int и bool. Всё остальное отклоняется при регистрации инструмента с исключением InvalidSignature.

Это касается и str | None, у которого нет единственного типа. Для необязательного аргумента схему нужно задать явно, с помощью WithJsonSchema из Pydantic:

region: Annotated[str | None, WithJsonSchema({"type": "string", "x-mcp-header": "Region"})] = None

В низкоуровневом классе Server

Там input_schema пишется вручную, поэтому ключ добавляется прямо в схему:

server.py
from mcp.server import Server, ServerRequestContext
from mcp.types import (
    CallToolRequestParams,
    CallToolResult,
    ListToolsResult,
    PaginatedRequestParams,
    TextContent,
    Tool,
)

CHECK_STOCK = Tool(
    name="check_stock",
    description="Count the copies of a book in one region's warehouses.",
    input_schema={
        "type": "object",
        "properties": {
            "title": {"type": "string"},
            "region": {"type": "string", "x-mcp-header": "Region"},
        },
        "required": ["title", "region"],
    },
)


async def list_tools(ctx: ServerRequestContext, params: PaginatedRequestParams | None) -> ListToolsResult:
    return ListToolsResult(tools=[CHECK_STOCK])


async def call_tool(ctx: ServerRequestContext, params: CallToolRequestParams) -> CallToolResult:
    args = params.arguments or {}
    text = f"{args['title']}: 3 copies in {args['region']}."
    return CallToolResult(content=[TextContent(type="text", text=text)])


server = Server("Bookshop", on_list_tools=list_tools, on_call_tool=call_tool)
app = server.streamable_http_app()
  • Аннотацию здесь никто не проверяет: некорректная будет отдана как есть, а клиенты версии 2026-07-28 исключат такой инструмент из своего списка.

Схемы по имени

Чтобы проверить заголовок, SDK нужна входная схема инструмента ещё до того, как вызов будет передан обработчику. Без get_tool_input_schema SDK получает её, запуская обработчик on_list_tools при каждом вызове с аргументами — независимо от того, помечен ли хоть один инструмент.

server.py
from typing import Any

from mcp.server import Server, ServerRequestContext
from mcp.types import (
    CallToolRequestParams,
    CallToolResult,
    ListToolsResult,
    PaginatedRequestParams,
    TextContent,
    Tool,
)

CHECK_STOCK = Tool(
    name="check_stock",
    description="Count the copies of a book in one region's warehouses.",
    input_schema={
        "type": "object",
        "properties": {
            "title": {"type": "string"},
            "region": {"type": "string", "x-mcp-header": "Region"},
        },
        "required": ["title", "region"],
    },
)

TOOLS = {CHECK_STOCK.name: CHECK_STOCK}


async def list_tools(ctx: ServerRequestContext, params: PaginatedRequestParams | None) -> ListToolsResult:
    return ListToolsResult(tools=list(TOOLS.values()))


async def call_tool(ctx: ServerRequestContext, params: CallToolRequestParams) -> CallToolResult:
    args = params.arguments or {}
    text = f"{args['title']}: 3 copies in {args['region']}."
    return CallToolResult(content=[TextContent(type="text", text=text)])


def tool_input_schema(name: str) -> dict[str, Any] | None:
    tool = TOOLS.get(name)
    return tool.input_schema if tool else None


server = Server(
    "Bookshop",
    on_list_tools=list_tools,
    on_call_tool=call_tool,
    get_tool_input_schema=tool_input_schema,
)
app = server.streamable_http_app()
  • Передайте функцию, чтобы отвечать на основе уже имеющихся данных.
  • Для инструмента, в котором нечего проверять, верните None.

Итоги

  • Ключ x-mcp-header у аргумента инструмента заставляет клиенты версии 2026-07-28 дублировать этот аргумент в HTTP-заголовке Mcp-Param-*.
  • Сервер отклоняет вызов, в котором заголовок и тело расходятся.
  • Пометить можно только аргументы типов str, int и bool. Для всего остального MCPServer выбрасывает исключение InvalidSignature.
  • Низкоуровневый класс Server ничего не проверяет, а клиенты отбрасывают инструмент с некорректной аннотацией.
  • С get_tool_input_schema низкоуровневому классу Server не приходится запускать on_list_tools при каждом вызове.

Остальная часть API класса Server, в котором всё пишется вручную, описана на странице Низкоуровневый Server.