python-sdk/i18n/ru/pages/servers/handling-errors.md

139 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
translation:
sections: [e33d441f12d50535, 7099694c603e0f5f, c1df4cf9673433e6, c9cd294541422e6e, 6cec073617bfd037, efa92b8f99e908c8, 6a22a29e27fb4601]
tool: 1
---
# Обработка ошибок {#handling-errors}
Инструмент может завершиться неудачей двумя способами, и SDK обрабатывает их совершенно по-разному.
Выбросьте обычное исключение — и его увидит **модель**. Выбросьте `MCPError` — и его увидит **протокол**.
Эта страница о том, как выбрать.
## Ошибка, которую модель может исправить {#an-error-the-model-can-fix}
Возьмём инструмент, который что-то ищет, и пусть поиск ничего не найдёт:
```python title="server.py" hl_lines="11-12"
--8<-- "docs_src/handling_errors/tutorial001.py"
```
В этих двух строках нет ничего специфичного для MCP. `get_author` выбрасывает обычный `ValueError`, как любая функция на Python.
Вызовите его с названием, которого нет в каталоге, и посмотрите на результат:
```python
result.is_error # True
result.content # [TextContent(text="Error executing tool get_author: No book titled 'Nothing' in the catalog.")]
result.structured_content # None
```
* Запрос **выполнен успешно**. Результат есть; на вызывающей стороне ничего не выброшено.
* `is_error` равен `True`, а сообщение вашего исключения (с префиксом в виде имени инструмента) лежит в `content` — ровно там, где читает модель.
* `structured_content` равен `None`. У неудачного вызова нет возвращаемого значения, которое можно было бы структурировать.
Это **ошибка инструмента**, и так по умолчанию обрабатывается *любое* исключение, выброшенное инструментом. И почти всегда это именно то, что нужно.
Ваш инструмент вызывает модель. Она же выбрала аргументы. Поэтому ошибка инструмента — это реплика в диалоге: модель читает *«No book titled 'Nothing' in the catalog.»*, понимает, что ошиблась с названием, и вызывает инструмент снова с более подходящим. Вы написали один `raise` и получили агента, который исправляет себя сам.
!!! tip
Никогда не возвращайте сообщение об ошибке из инструмента через `return`. У возвращённой строки
`is_error=False`, поэтому для модели (и для любого клиентского интерфейса) всё выглядит так, будто
инструмент сработал, а эта строка и есть ответ. Используйте `raise`. Сигналом служит флаг.
## Ошибка, которую модель исправить не может {#an-error-the-model-cannot-fix}
Теперь замените `ValueError` на `MCPError`.
```python title="server.py" hl_lines="1 3 14"
--8<-- "docs_src/handling_errors/tutorial002.py"
```
`MCPError` — это **ошибка протокола** в SDK. Это единственное исключение, которое обёртка инструмента *не* перехватывает: оно проходит дальше, и весь запрос `tools/call` завершается ошибкой JSON-RPC вместо результата.
```json
{
"code": -32602,
"message": "No book titled 'Nothing' in the catalog."
}
```
* Результата **нет**. Ни `content`, ни `is_error` — модели нечего читать.
* Вместо этого ошибку получает приложение-**хост** — так же, как если бы инструмента не существовало вовсе.
* `code`, `message` и `data` доходят без изменений. `INVALID_PARAMS` — это `-32602`; модуль `mcp.types` экспортирует его и остальные коды ошибок JSON-RPC (`INVALID_REQUEST`, `INTERNAL_ERROR`, ...) как константы, чтобы никогда не приходилось набирать магическое число.
!!! check
Тот же поиск, тот же промах, но теперь вызов на стороне клиента *выбрасывает исключение* вместо того, чтобы вернуть результат:
```text
mcp.shared.exceptions.MCPError: No book titled 'Nothing' in the catalog.
```
Первая версия передавала модели фразу, на которую та могла отреагировать. Эта не передаёт ничего.
Для `get_author` это однозначно хуже — о чём и следующий раздел.
## Что выбрасывать {#which-one-to-raise}
Два пути отвечают на два разных вопроса.
* **Выбрасывайте любое исключение** при сбое *выполнения*: то, что инструмент пытался сделать, не получилось. Вызов выбрала модель, значит, модель и должна увидеть последствия и получить шанс исправиться. Опечатка в названии, тайм-аут внешнего API, несуществующая строка в таблице — всё это ошибки инструмента.
* **Выбрасывайте `MCPError`**, когда отклонить нужно *сам запрос*: у клиента нет возможности, от которой зависит инструмент, сервер не в состоянии обслуживать кого бы то ни было, вызывающая сторона пропустила обязательный шаг. Никакая повторная попытка модели ничего из этого не исправит, так что передавать ей сообщение бессмысленно.
Решает один вопрос: **могла бы более умная модель этого избежать?** Да -> обычное исключение. Нет -> `MCPError`.
По этому критерию вторая версия `get_author` выбрала неверно: правильное название всё исправляет, значит, модель заслуживала увидеть сообщение. Она здесь, чтобы показать механизм, а не чтобы его рекомендовать.
!!! info
`MCPError` импортируется как `from mcp import MCPError` и принимает `code`, `message` и необязательную
полезную нагрузку `data`. Что бы вы в них ни положили, именно это и получит клиент: SDK передаёт
выброшенный `MCPError` дословно, не очищая его.
## Ресурс, которого не существует {#a-resource-that-doesnt-exist}
Ресурсы проводят ту же границу и для частого случая поставляются с одним именованным исключением.
```python title="server.py" hl_lines="2 13"
--8<-- "docs_src/handling_errors/tutorial003.py"
```
`books://{title}` — это **шаблон**. Он совпадает с *любым* названием, поэтому «URI корректен» и «книга существует» — два разных вопроса, и на второй может ответить только ваша функция.
Когда ответить она не может, выбрасывайте `ResourceNotFoundError`. SDK превращает его в ошибку протокола, которую спецификация назначает отсутствующему ресурсу: `-32602` с запрошенным URI в `data`, чтобы клиент знал, *какое именно* чтение не удалось.
```json
{
"code": -32602,
"message": "No book titled 'Nothing' in the catalog.",
"data": {"uri": "books://Nothing"}
}
```
Обратите внимание: здесь нет полурезультата с `is_error=True`. Чтение ресурса либо возвращает содержимое, либо завершается ошибкой — у ресурсов есть только протокольный путь. Шаблоны и всё остальное о ресурсах — на странице **[Ресурсы](resources.md)**.
## Ошибки, которые вы никогда не выбрасываете {#errors-you-never-raise}
Некорректный аргумент никогда не доходит до вашей функции.
Передайте `get_author` значение `title`, которое не является строкой, и SDK отклонит его по входной схеме **до** вызова функции — в виде такой же ошибки инструмента с `is_error=True`, которую модель может прочитать и исправить. На странице **[Инструменты](tools.md)** показано такое же отклонение с ограничением `Field(le=50)`.
Это целый класс операторов `raise`, которые писать не нужно: не проверяйте повторно собственные аннотации типов.
!!! info
Всё на этой странице — это то, что видит **клиент**, и `Client` в памяти, с которым вы будете
писать тесты, видит ровно то же самое. Даже `raise_exceptions=True` не превращает ошибку инструмента
обратно в трассировку: к моменту, когда этот флаг мог бы сработать, ваше исключение уже стало
результатом с `is_error=True`. Проверяйте результат. Этот приём описан на странице **[Тестирование](../get-started/testing.md)**.
## Итоги {#recap}
* Выбрасываете **любое исключение** в инструменте -> вызов возвращает `is_error=True` с вашим сообщением в `content`. Модель читает его и может повторить попытку. Это поведение по умолчанию.
* Выбрасываете **`MCPError`** -> сам вызов завершается ошибкой JSON-RPC. Модель ничего не видит; разбирается хост. `code`, `message` и `data` доходят без изменений.
* Решающий вопрос: *могла бы более умная модель этого избежать?* Да -> исключение. Нет -> `MCPError`.
* `ResourceNotFoundError` из обработчика ресурса -> протокольный `-32602` с URI в `data`.
* Некорректные аргументы отклоняются по схеме до запуска вашей функции; `raise` для них не нужен.
* `from mcp import MCPError`; константы кодов ошибок — из `mcp.types`.
С ошибками разобрались. Это всё, что сервер *предоставляет*. Что каждый обработчик может прочитать и что сделать в сторону клиента во время выполнения — в следующем разделе: **[Внутри обработчика](../handlers/index.md)**.
Точный текст ошибок SDK, которые встретятся чаще всего, смысл каждой и исправление в одно действие — на странице **[Устранение неполадок](../troubleshooting.md)**.