docs: refresh translations for recent English changes (#3379)

This commit is contained in:
Max 2026-08-24 19:29:54 +01:00 • committed by GitHub
commit c5d7d0b6d0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 3149 additions and 1047 deletions

View file

@ -1,13 +1,13 @@
---
translation:
sections: [e33d441f12d50535, 7099694c603e0f5f, c1df4cf9673433e6, c9cd294541422e6e, 6cec073617bfd037, efa92b8f99e908c8, 6a22a29e27fb4601]
sections: [7be05607887e6853, e7375894888d9750, c36f73fc7e3af13b, 2fec2d7e129e62fe, 809b0e0a7c27295a, b4395a04d2a5d906, 1a436007f5f54779, c6b2078ed1e63ba5]
tool: 1
---
# Обработка ошибок {#handling-errors}
Инструмент может завершиться неудачей двумя способами, и SDK обрабатывает их совершенно по-разному.
Инструмент может завершиться неудачей тремя способами, и SDK обрабатывает каждый из них по-своему.
Выбросьте обычное исключение — и его увидит **модель**. Выбросьте `MCPError` — и его увидит **протокол**.
Выбросьте `ToolError` — и ваше сообщение увидит **модель**. Выбросьте `MCPError` — и его увидит **протокол**. Выбросьте что-то другое — и это уже сбой: модель узнает только, что вызов не удался, а трассировка попадёт в ваш лог.
Эта страница о том, как выбрать.
@ -15,11 +15,11 @@ translation:
Возьмём инструмент, который что-то ищет, и пусть поиск ничего не найдёт:
```python title="server.py" hl_lines="11-12"
```python title="server.py" hl_lines="2 12-13"
--8<-- "docs_src/handling_errors/tutorial001.py"
```
В этих двух строках нет ничего специфичного для MCP. `get_author` выбрасывает обычный `ValueError`, как любая функция на Python.
`ToolError` из `mcp.server.mcpserver.exceptions` — это способ, которым инструмент сообщает модели, что что-то пошло не так.
Вызовите его с названием, которого нет в каталоге, и посмотрите на результат:
@ -30,12 +30,14 @@ result.structured_content # None
```
* Запрос **выполнен успешно**. Результат есть; на вызывающей стороне ничего не выброшено.
* `is_error` равен `True`, а сообщение вашего исключения (с префиксом в виде имени инструмента) лежит в `content` — ровно там, где читает модель.
* `is_error` равен `True`, а ваше сообщение (с префиксом в виде имени инструмента) лежит в `content` — ровно там, где читает модель.
* `structured_content` равен `None`. У неудачного вызова нет возвращаемого значения, которое можно было бы структурировать.
Это **ошибка инструмента**, и так по умолчанию обрабатывается *любое* исключение, выброшенное инструментом. И почти всегда это именно то, что нужно.
Это **ошибка инструмента**, и почти всегда это именно то, что нужно.
Ваш инструмент вызывает модель. Она же выбрала аргументы. Поэтому ошибка инструмента — это реплика в диалоге: модель читает *«No book titled 'Nothing' in the catalog.»*, понимает, что ошиблась с названием, и вызывает инструмент снова с более подходящим. Вы написали один `raise` и получили агента, который исправляет себя сам.
Вызывает ваш инструмент именно модель. Она же выбрала аргументы. Поэтому ошибка инструмента — это реплика в диалоге: модель читает *«No book titled 'Nothing' in the catalog.»*, понимает, что ошиблась с названием, и вызывает инструмент снова с более подходящим. Вы написали один `raise` и получили агента, который исправляет себя сам.
На сервере `ToolError` — это одна строка уровня `INFO` в логе, без трассировки. Вы её предвидели, так что расследовать нечего.
!!! tip
Никогда не возвращайте сообщение об ошибке из инструмента через `return`. У возвращённой строки
@ -44,7 +46,7 @@ result.structured_content # None
## Ошибка, которую модель исправить не может {#an-error-the-model-cannot-fix}
Теперь замените `ValueError` на `MCPError`.
Теперь замените `ToolError` на `MCPError`.
```python title="server.py" hl_lines="1 3 14"
--8<-- "docs_src/handling_errors/tutorial002.py"
@ -77,10 +79,10 @@ result.structured_content # None
Два пути отвечают на два разных вопроса.
* **Выбрасывайте любое исключение** при сбое *выполнения*: то, что инструмент пытался сделать, не получилось. Вызов выбрала модель, значит, модель и должна увидеть последствия и получить шанс исправиться. Опечатка в названии, тайм-аут внешнего API, несуществующая строка в таблице — всё это ошибки инструмента.
* **Выбрасывайте `ToolError`** при сбое *выполнения*: то, что инструмент пытался сделать, не получилось. Вызов выбрала модель, значит, модель и должна увидеть последствия и получить шанс исправиться. Опечатка в названии, тайм-аут внешнего API, несуществующая строка в таблице — всё это ошибки инструмента.
* **Выбрасывайте `MCPError`**, когда отклонить нужно *сам запрос*: у клиента нет возможности, от которой зависит инструмент, сервер не в состоянии обслуживать кого бы то ни было, вызывающая сторона пропустила обязательный шаг. Никакая повторная попытка модели ничего из этого не исправит, так что передавать ей сообщение бессмысленно.
Решает один вопрос: **могла бы более умная модель этого избежать?** Да -> обычное исключение. Нет -> `MCPError`.
Решает один вопрос: **могла бы более умная модель этого избежать?** Да -> `ToolError`. Нет -> `MCPError`.
По этому критерию вторая версия `get_author` выбрала неверно: правильное название всё исправляет, значит, модель заслуживала увидеть сообщение. Она здесь, чтобы показать механизм, а не чтобы его рекомендовать.
@ -89,6 +91,25 @@ result.structured_content # None
полезную нагрузку `data`. Что бы вы в них ни положили, именно это и получит клиент: SDK передаёт
выброшенный `MCPError` дословно, не очищая его.
## Любое другое исключение {#any-other-exception}
Теперь уберите проверку и дайте поиску по словарю упасть самому:
```python title="server.py" hl_lines="11"
--8<-- "docs_src/handling_errors/tutorial004.py"
```
`CATALOG[title]` выбрасывает `KeyError`. Вы этого не предусмотрели, поэтому SDK считает это сбоем:
```python
result.is_error # True
result.content # [TextContent(text="Error executing tool get_author")]
```
Вызов по-прежнему возвращает `is_error=True`, так что модель знает, что он не удался, и может двигаться дальше. Чего она не получает, так это текста исключения: `KeyError` из вашего кода или гора SQL из драйвера тремя библиотеками ниже могут описывать внутреннее устройство сервера, поэтому за его пределы этот текст не выходит.
Вместо модели его получаете вы. Сервер пишет сбой в лог на уровне `ERROR` с полной трассировкой — как `Tool 'get_author' raised an unexpected exception`. Поэтому в эксплуатации лог на уровне `WARNING` молчит при каждом `ToolError` и подаёт голос в тот момент, когда что-то действительно сломалось.
## Ресурс, которого не существует {#a-resource-that-doesnt-exist}
Ресурсы проводят ту же границу и для частого случая поставляются с одним именованным исключением.
@ -109,7 +130,7 @@ result.structured_content # None
}
```
Обратите внимание: здесь нет полурезультата с `is_error=True`. Чтение ресурса либо возвращает содержимое, либо завершается ошибкой — у ресурсов есть только протокольный путь. Шаблоны и всё остальное о ресурсах — на странице **[Ресурсы](resources.md)**.
Обратите внимание: здесь нет полурезультата с `is_error=True`. Чтение ресурса либо возвращает содержимое, либо завершается ошибкой — у ресурсов есть только протокольный путь. `ResourceError` — то же самое для сбоя, который не сводится к «не найдено» (`-32603`, ваше сообщение), и оба оставляют в вашем логе одну строку уровня `INFO`. Любое другое исключение, кроме `MCPError`, — это сбой: клиент получает `-32603` с указанием одного лишь URI, а трассировка уходит в ваш лог на уровне `ERROR`. Шаблоны и всё остальное о ресурсах — на странице **[Ресурсы](resources.md)**.
## Ошибки, которые вы никогда не выбрасываете {#errors-you-never-raise}
@ -120,19 +141,21 @@ result.structured_content # None
Это целый класс операторов `raise`, которые писать не нужно: не проверяйте повторно собственные аннотации типов.
!!! info
Всё на этой странице — это то, что видит **клиент**, и `Client` в памяти, с которым вы будете
писать тесты, видит ровно то же самое. Даже `raise_exceptions=True` не превращает ошибку инструмента
обратно в трассировку: к моменту, когда этот флаг мог бы сработать, ваше исключение уже стало
результатом с `is_error=True`. Проверяйте результат. Этот приём описан на странице **[Тестирование](../get-started/testing.md)**.
Всё, что на этой странице видит **клиент**, видит и `Client` в памяти, с которым вы будете
писать тесты. Даже `raise_exceptions=True` не возвращает исключение упавшего
инструмента вызывающей стороне: к моменту, когда этот флаг мог бы сработать, ваше исключение уже стало
результатом с `is_error=True`. Проверяйте результат. Если нужна трассировка сбоя, она в логе
сервера, и `caplog` из pytest её перехватывает. Этот приём описан на странице **[Тестирование](../get-started/testing.md)**.
## Итоги {#recap}
* Выбрасываете **любое исключение** в инструменте -> вызов возвращает `is_error=True` с вашим сообщением в `content`. Модель читает его и может повторить попытку. Это поведение по умолчанию.
* Выбрасываете **`ToolError`** в инструменте -> вызов возвращает `is_error=True` с вашим сообщением в `content`. Модель читает его и может повторить попытку.
* Выбрасываете **`MCPError`** -> сам вызов завершается ошибкой JSON-RPC. Модель ничего не видит; разбирается хост. `code`, `message` и `data` доходят без изменений.
* Решающий вопрос: *могла бы более умная модель этого избежать?* Да -> исключение. Нет -> `MCPError`.
* Решающий вопрос: *могла бы более умная модель этого избежать?* Да -> `ToolError`. Нет -> `MCPError`.
* Любое **другое исключение** — это сбой -> `is_error=True`, где для модели только `Error executing tool <name>`, а для вас — запись уровня `ERROR` с трассировкой.
* `ResourceNotFoundError` из обработчика ресурса -> протокольный `-32602` с URI в `data`.
* Некорректные аргументы отклоняются по схеме до запуска вашей функции; `raise` для них не нужен.
* `from mcp import MCPError`; константы кодов ошибок — из `mcp.types`.
* Импорты: `from mcp import MCPError`, `from mcp.server.mcpserver.exceptions import ToolError, ResourceError, ResourceNotFoundError`, а константы кодов ошибок — из `mcp.types`.
С ошибками разобрались. Это всё, что сервер *предоставляет*. Что каждый обработчик может прочитать и что сделать в сторону клиента во время выполнения — в следующем разделе: **[Внутри обработчика](../handlers/index.md)**.

View file

@ -1,6 +1,6 @@
---
translation:
sections: [496394d24d221bf1, 4ceb4591180dc6c3, 0fd63e4682d02e0c, 969ede0bd3686a16, 043f526230dd243d, 6ee3e9bcfd24047a]
sections: [496394d24d221bf1, 4ceb4591180dc6c3, 0fd63e4682d02e0c, 969ede0bd3686a16, 864137b5e9c61e91, 043f526230dd243d, db1ef91db7d6b3f3]
tool: 1
---
# Медиа {#media}
@ -86,6 +86,24 @@ result.structured_content # None
`Audio` из байтов MP3 — и клиенту сообщат `mime_type="audio/wav"`, после чего
он честно не сможет это декодировать. Передаёте `data=` — передавайте и `format=`.
## Встраивание ресурса {#embedding-a-resource}
Инструмент может вернуть и документ: текст или байты вместе с URI, по которому он находится, и MIME-типом. Это **`EmbeddedResource`**, ещё один вид блока содержимого. В отличие от обычной `str` он сообщает клиенту, что это за содержимое, и клиент может показать его как вложение или узнать ресурс, который ему уже знаком.
```python title="server.py" hl_lines="7 14 16-18"
--8<-- "docs_src/media/tutorial005.py"
```
* `brand://guidelines` — обычный ресурс (о них — на странице **[Ресурсы](resources.md)**). Инструмент по запросу отдаёт модели тот же документ, а прямой вызов `guidelines()` сохраняет единый источник истины.
* `EmbeddedResource` и `TextResourceContents` берутся из `mcp.types`. Вспомогательного класса, как для изображений, нет: собранный вами блок попадает в результат как есть, а `structured_content` отсутствует.
* Используйте тот URI, под которым ресурс зарегистрирован, чтобы клиент мог понять, что вложение и `brand://guidelines` — один и тот же документ. Допустим любой URI, зарегистрированный или нет.
```python
result.content # [EmbeddedResource(type="resource", resource=TextResourceContents(uri="brand://guidelines", mime_type="text/markdown", text="# Brand guidelines\n\n..."))]
```
Для двоичного содержимого вместо `TextResourceContents` используйте `BlobResourceContents(uri=..., mime_type=..., blob=...)` с байтами, закодированными в base64 в поле `blob`. Чтобы отправить только указатель, по которому клиент позже сможет выполнить `resources/read`, верните вместо этого `ResourceLink(name=..., uri=...)` — это тоже блок содержимого.
## Иконки {#icons}
`Icon` — это метаданные, а не содержимое. Изображение он не несёт: он указывает на него через URI, а клиент может загрузить его и показать рядом с именем сервера, инструментом, ресурсом или промптом.
@ -115,6 +133,7 @@ client.server_info.icons # [Icon(src="https://example.com/brand-kit.png", mime_
* Верните `Image` или `Audio` из инструмента — и клиент получит блок `ImageContent` / `AudioContent`: ваши байты в кодировке base64 с MIME-типом.
* Собирайте их из `path=`, и тогда MIME-тип определит расширение, или из данных в памяти через `data=` с явным `format=`.
* Верните `EmbeddedResource`, чтобы поместить в результат документ (текст или blob в base64 вместе с его URI и MIME-типом), или `ResourceLink`, чтобы отправить только указатель.
* У медиарезультатов нет ни `structured_content`, ни схемы выходных данных.
* `Icon` — это указатель: URI в `src` плюс необязательные `mime_type`, `sizes` и `theme`.
* `icons=[...]` работает на сервере, инструментах, ресурсах и промптах, а клиенты находят их в соответствующих объектах.

View file

@ -1,6 +1,6 @@
---
translation:
sections: [d65c098f37f5b6c3, dd0c2724d6f2877e, 6835bb3570c6714c, ffe823cb0fedd488, f33651add1b59094]
sections: [d65c098f37f5b6c3, dd0c2724d6f2877e, 6835bb3570c6714c, d30d3c20168b88b2, f5ef38dad59d6f76, 6e38a699ba57fbdf, 2b984a3bf37a0ddd]
tool: 1
---
# Промпты {#prompts}
@ -140,9 +140,54 @@ uv run mcp dev server.py
```
!!! info
Если вы читали страницу **[Инструменты](tools.md)**, то уже знаете всё, что здесь написано. Тот же декоратор,
Если вы читали страницу **[Инструменты](tools.md)**, всё сказанное до этого места вам уже знакомо. Тот же декоратор,
та же строка документации в роли описания, те же `Annotated`/`Field`. Меняется только то, кто
запускает промпт (пользователь) и куда идёт результат (в диалог).
запускает промпт (пользователь), и куда идёт результат (в диалог).
## Больше, чем текст {#more-than-text}
`UserMessage` и `AssistantMessage` везде, где принимают `str`, принимают также блок содержимого или вспомогательный объект `Image` / `Audio`. В промптах встречаются два случая: вложить документ и вложить картинку.
### Встраивание файла {#embedding-a-file}
```python title="server.py" hl_lines="5 12 21 23"
--8<-- "docs_src/prompts/tutorial004.py"
```
* Руководство по стилю — это ресурс по адресу `style://python` (о ресурсах — на странице **[Ресурсы](resources.md)**), который читается из файла `style-guide.md` рядом с `server.py`. Положите туда любой файл Markdown.
* `EmbeddedResource(resource=TextResourceContents(...))` (оба из `mcp.types`) несёт файл вместе с его URI и MIME-типом первым сообщением; запрос, который на него ссылается, идёт следом обычным текстом.
* Встраивание, в отличие от вставки руководства прямо в f-строку, позволяет клиенту показать его как вложение и позже снова открыть `style://python`, а модель получает файл дословно. Для двоичного файла используйте `BlobResourceContents` с `blob` в base64.
После рендеринга `content` первого сообщения — это блок `resource`:
```json
{"type": "resource", "resource": {"uri": "style://python", "mimeType": "text/markdown", "text": "* Prefer early returns.\n..."}}
```
### Вложение изображения {#attaching-an-image}
```python title="server.py" hl_lines="4 15"
--8<-- "docs_src/prompts/tutorial005.py"
```
* `Image` — вспомогательный класс со страницы **[Изображения, аудио и иконки](media.md)**. `UserMessage` преобразует его в блок `ImageContent` (файл в base64, MIME-тип угадывается по `.png`) при рендеринге промпта; `Audio` точно так же становится `AudioContent`.
* Положите рядом с `server.py` любой PNG с именем `architecture.png`. Аргументы промпта — строки, поэтому картинка всегда берётся с сервера; `component` даёт только слова.
```json
{"type": "image", "data": "iVBORw0KGgoAAAANSUhEUg...", "mimeType": "image/png"}
```
## Изменение списка во время работы {#changing-the-list-at-runtime}
Промпты можно добавлять, пока клиенты подключены, — например, чтобы пользователь мог сохранить инструкцию как собственный пункт меню. Зарегистрируйте промпт, затем отправьте уведомление:
```python title="server.py" hl_lines="5 23-27"
--8<-- "docs_src/prompts/tutorial006.py"
```
* `mcp.add_prompt(Prompt.from_function(fn, name=..., description=...))` регистрирует функцию ровно так же, как это сделал бы `@mcp.prompt()`, а `mcp.remove_prompt(name)` — обратная операция. `add_prompt` сохраняет существующую запись с тем же именем, а не перезаписывает её, поэтому инструмент сначала удаляет старую, чтобы сохранение работало как замена. `prompts/list` отражает изменение сразу.
* `await ctx.notify_prompts_changed()` отправляет `notifications/prompts/list_changed` каждому клиенту `2026-07-28`, который слушает поток `subscriptions/listen` (**[Подписки](../handlers/subscriptions.md)**). `await ctx.session.send_prompt_list_changed()` отправляет его вызывающему клиенту, если тот старше поколения 2026 (**[Обслуживание клиентов старого поколения](../run/legacy-clients.md)**). Вызывайте оба; каждый ничего не делает, когда сообщать некому.
* Клиент, получивший уведомление, снова вызывает `prompts/list`. В классе `Client` на Python это `async with client.listen(prompts_list_changed=True) as sub:`, который выдаёт событие `PromptsListChanged`.
## Итоги {#recap}
@ -152,5 +197,7 @@ uv run mcp dev server.py
* Верните `str` — и она станет одним сообщением пользователя. Верните список `UserMessage` / `AssistantMessage`, чтобы задать многоходовой диалог.
* `title=` и `Field(description=...)` — это то, что клиент показывает в интерфейсе.
* Отсутствующий обязательный аргумент проваливает весь запрос. Отдельного результата с ошибкой у промпта нет.
* Оберните `EmbeddedResource` или `Image` в `UserMessage`, чтобы вложить документ или картинку.
* Добавляйте и удаляйте промпты во время работы через `mcp.add_prompt(...)` / `mcp.remove_prompt(...)`, затем вызывайте `await ctx.notify_prompts_changed()` и `await ctx.session.send_prompt_list_changed()`.
Автодополнение аргументов промпта (или шаблона ресурса) на стороне сервера — на странице **[Автодополнение](completions.md)**.

View file

@ -1,6 +1,6 @@
---
translation:
sections: [a838d57f003aed44, 857d03886a0137ed, 42d9efcb9f542867, 2290ff08435b5573, e866c192e11d1c14, 6cdbad079f7b47f0, d4b607372fb28b51, 18dbf726ac45e0b7, c6f7d2a148aa49f4, c851964bb3301907, d715db6f8dccc9cc, ef86634aa70498a7]
sections: [a838d57f003aed44, 857d03886a0137ed, 42d9efcb9f542867, 2290ff08435b5573, 91be9b73602abcf1, 6cdbad079f7b47f0, d4b607372fb28b51, 18dbf726ac45e0b7, c7eff2a5698225fa, c851964bb3301907, 8f296f1f09e4c400, d715db6f8dccc9cc, a0c344a48450dbe4]
tool: 1
---
# Структурированный вывод {#structured-output}
@ -105,7 +105,7 @@ result.structured_content # {"temperature": 16.2, "humidity": 0.83, "conditions
--8<-- "docs_src/structured_output/tutorial003.py"
```
Во время выполнения `TypedDict` — обычный `dict`, его вы и собираете и возвращаете. Схема, валидация и `structured_content` идентичны варианту с `BaseModel` (за вычетом описаний, которые в `TypedDict` разместить негде).
Во время выполнения `TypedDict` — обычный `dict`, его вы и собираете и возвращаете. Схема, валидация и `structured_content` подчиняются тем же правилам, что и в варианте с `BaseModel`: добавьте docstring класса или `Annotated[..., Field(description=...)]` — и они станут описаниями, а ключ `NotRequired`, который вы не включили в словарь, не попадёт и в `structured_content`.
## Dataclass {#a-dataclass}
@ -187,18 +187,19 @@ result.structured_content # {"London": 16.2, "Reykjavik": 4.4}
Аннотация обещает `WeatherData`. Ответ вышестоящего сервиса перестал присылать `humidity`.
!!! check
Вызовите `get_weather` — и он не передаст клиенту молча полупустой объект. Вызов завершается ошибкой,
и первые же строки ошибки называют поле:
Вызовите `get_weather` — и он не передаст клиенту молча полупустой объект. Вызов завершается ошибкой:
клиент получает `is_error=True` с текстом `Error executing tool get_weather`, так что модель знает, что
вызов не удался, а не уверенно читает погоду, которой нет. Имя поля — для вас, в логе сервера
на уровне `ERROR`:
```text
Error executing tool get_weather: 1 validation error for WeatherData
Tool 'get_weather' raised an unexpected exception
...
pydantic_core._pydantic_core.ValidationError: 1 validation error for WeatherData
humidity
Field required [type=missing, input_value={'temperature': 16.2, 'conditions': 'Overcast'}, input_type=dict]
```
Этот текст возвращается как результат инструмента с `is_error=True`, так что модель знает, что вызов
не удался, а не уверенно читает погоду, которой нет.
Кстати, вернуть обычный `dict` из инструмента с `-> WeatherData` вполне допустимо. Именно это и выдал `json.loads`. Проверяется значение, а не тип Python.
## Отказ от структурированного вывода {#opting-out}
@ -213,6 +214,10 @@ result.structured_content # {"London": 16.2, "Reykjavik": 4.4}
Обратный вариант, `structured_output=True`, превращает автоматическое определение в требование: инструмент, по возвращаемому типу которого нельзя построить схему, выбрасывает исключение при импорте, а не переключается на текст.
## Блоки контента и медиа {#content-blocks-and-media}
Блоки контента и медиа (`TextContent`, `EmbeddedResource`, `Image`, `Audio` и им подобные — сами по себе, как элементы `list`, `tuple` или `Sequence` либо как варианты объединения) исключаются из структурированного вывода за вас: они предназначены для чтения моделью, поэтому автоматическое определение не выводит по ним схему (об `Image` и `Audio` — на странице **[Изображения, аудио и значки](media.md)**). `structured_output=True` по-прежнему принудительно строит схему для классов блоков контента.
## Класс без аннотаций типов {#a-class-without-type-hints}
Есть один способ оказаться без структурированного вывода, не прося об этом: вернуть класс, в **теле которого нет аннотаций**.
@ -242,9 +247,9 @@ result.structured_content # {"London": 16.2, "Reykjavik": 4.4}
## Итоги {#recap}
* **Аннотация возвращаемого типа** — это выходная схема. Она публикуется в `tools/list` как `output_schema`.
* Скаляры, списки, кортежи и объединения оборачиваются в `{"result": ...}`. Модели, `TypedDict`, dataclass, классы с аннотациями и `dict[str, ...]` уже являются объектами и остаются как есть.
* Скаляры, списки, кортежи и объединения оборачиваются в `{"result": ...}`. Модели, `TypedDict`, dataclass, классы с аннотациями и `dict[str, ...]` — уже объекты и остаются как есть.
* Каждый результат несёт `content` (текст, для модели) **и** `structured_content` (данные, для приложения).
* Возвращаемое значение проверяется на соответствие схеме. Несоответствие — это ошибка инструмента, а не испорченный результат.
* `structured_output=False` отключает структурированный вывод для инструмента. Класс без аннотаций типов отключает его молча — следите за этим.
* `structured_output=False` отключает структурированный вывод для инструмента. Блоки контента, `Image` и `Audio` отключают его по умолчанию; класс без аннотаций типов отключает его молча — следите за этим.
Теперь вы владеете всем, что инструмент может сказать в ответ. Дальше — второй примитив: **[Ресурсы](resources.md)**.

View file

@ -1,6 +1,6 @@
---
translation:
sections: [e4cc390d56573409, 8566e2b68594e9ad, 2c97b9f888398951, 048e5471dfa71aea, 3076b1e16ad95950, edbedf2a16e71311, 3d8ef8da89fa87c1, f6c0e02e6ea5a363]
sections: [e4cc390d56573409, f30cf8103a6e918c, 2c97b9f888398951, 048e5471dfa71aea, 3076b1e16ad95950, edbedf2a16e71311, 3d8ef8da89fa87c1, f6c0e02e6ea5a363]
tool: 1
---
# Инструменты {#tools}
@ -39,6 +39,8 @@ translation:
Оба аргумента попали в `required`, потому что ни у одного нет значения по умолчанию. Сейчас это исправим. (Ключи `title` — артефакты Pydantic; контракт составляют свойства, их типы и `required`.)
Ключа `$schema` тоже нет: схему без него MCP рассматривает как **JSON Schema 2020-12**, а Pydantic генерирует именно её, так что выбирать нечего — пока не придётся писать схемы вручную для **[низкоуровневого Server](../advanced/low-level-server.md#the-dialect-is-json-schema-2020-12)**.
!!! tip
Аннотации типов здесь не документация. Это и есть **контракт**. Если клиент пришлёт `"limit": "ten"`,
SDK отклонит вызов ещё до того, как запустится функция.

View file

@ -1,6 +1,6 @@
---
translation:
sections: [4a7033e1ed8ad602, 55dcbfff0c6271bf, 101ef9d14bf4ec46, 4b6c4a845438abc7, f98b46bafbee4acd]
sections: [4a7033e1ed8ad602, 55dcbfff0c6271bf, 317f4256a650cab6, 4b6c4a845438abc7, f98b46bafbee4acd]
tool: 1
---
# Шаблоны URI и безопасность путей {#uri-templates-and-path-safety}
@ -170,14 +170,14 @@ SDK поддерживает подмножество, подобранное д
песочницы. Для доступа к файловой системе используйте `safe_join`, чтобы
разрешить путь и убедиться, что он остаётся внутри базового каталога:
```python title="server.py" hl_lines="4 14"
```python title="server.py" hl_lines="5 15"
--8<-- "docs_src/uri_templates/tutorial002.py"
```
`safe_join` ловит выход через символические ссылки, последовательности
`..` и трюки с абсолютными путями, которые простая строковая проверка
пропустила бы. Если разрешённый путь выходит за `DOCS_ROOT`, функция
выбрасывает `PathEscapeError`, которое доходит до клиента как
выбрасывает исключение `PathEscapeError`, которое доходит до клиента как
`ResourceError`.
### Когда настройки по умолчанию мешают {#when-the-defaults-get-in-the-way}
@ -213,9 +213,10 @@ SDK поддерживает подмножество, подобранное д
!!! tip
Если обработчик не может выполнить запрос (файла нет, идентификатор
неизвестен), выбросьте исключение. SDK превратит его в ответ с
ошибкой. О разнице между ошибкой протокола и ошибкой инструмента
см. **[Обработка ошибок](handling-errors.md)**.
неизвестен), выбросьте `ResourceNotFoundError`, как делает `read_manual`
выше. Клиент получит `-32602` с вашим сообщением и URI. Непредвиденное
исключение вместо этого превращается в общую ошибку `-32603`. См.
**[Обработка ошибок](handling-errors.md#a-resource-that-doesnt-exist)**.
## Ресурсы на низкоуровневом Server {#resources-on-the-low-level-server}