Обробка помилок
Машинний переклад
Цю сторінку перекладено автоматично з англомовної документації, і основною версією є англомовна сторінка. Якщо щось читається неправильно, на сторінці Переклади пояснено, як про це повідомити.
Інструмент може завершитися невдачею трьома способами, і SDK обробляє кожен по-різному.
Викиньте ToolError — і ваше повідомлення побачить модель. Викиньте MCPError — і його побачить протокол. Викиньте будь-що інше — і це збій: модель дізнається лише, що виклик не вдався, а трасування потрапить у ваш лог.
Ця сторінка — про те, як вибрати.
Помилка, яку модель може виправити
Візьмімо інструмент, який щось шукає, і нехай пошук нічого не знайде:
from mcp.server import MCPServer
from mcp.server.mcpserver.exceptions import ToolError
mcp = MCPServer("Bookshop")
CATALOG = {"Dune": "Frank Herbert", "Neuromancer": "William Gibson"}
@mcp.tool()
def get_author(title: str) -> str:
"""Look up the author of a book in the catalog."""
if title not in CATALOG:
raise ToolError(f"No book titled {title!r} in the catalog.")
return CATALOG[title]
ToolError з mcp.server.mcpserver.exceptions — це спосіб, яким інструмент повідомляє моделі, що щось пішло не так.
Викличте його з назвою, якої немає в каталозі, і подивіться на результат:
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 — і маєте агента, що сам виправляє свої помилки.
На сервері ToolError — це один рядок рівня INFO у лозі, без трасування. Ви цього очікували, тож розслідувати нічого.
Tip
Ніколи не повертайте повідомлення про помилку з інструмента через return. Повернутий рядок має is_error=False, тож для
моделі (і для кожного клієнтського інтерфейсу) це виглядає так, ніби інструмент спрацював і цей рядок і є відповіддю.
Пишіть raise. Сигнал — саме прапорець.
Помилка, яку модель не може виправити
Тепер замініть ToolError на MCPError.
from mcp import MCPError
from mcp.server import MCPServer
from mcp.types import INVALID_PARAMS
mcp = MCPServer("Bookshop")
CATALOG = {"Dune": "Frank Herbert", "Neuromancer": "William Gibson"}
@mcp.tool()
def get_author(title: str) -> str:
"""Look up the author of a book in the catalog."""
if title not in CATALOG:
raise MCPError(code=INVALID_PARAMS, message=f"No book titled {title!r} in the catalog.")
return CATALOG[title]
MCPError — це помилка протоколу в SDK. Це єдиний виняток, який обгортка інструмента не перехоплює: він поширюється далі, і весь запит tools/call завершується помилкою JSON-RPC замість результату.
{
"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
Той самий пошук, той самий промах, але тепер виклик на боці клієнта викидає виняток замість того, щоб повернути результат:
mcp.shared.exceptions.MCPError: No book titled 'Nothing' in the catalog.
Перша версія давала моделі речення, на яке вона могла відреагувати. Ця не дає їй нічого.
Для get_author це однозначно гірше — і саме про це наступний розділ.
Який із них викидати
Ці два шляхи відповідають на два різні запитання.
- Викидайте
ToolErrorу разі збою виконання: те, що інструмент намагався зробити, не вдалося. Виклик обрала модель, тож саме модель має побачити наслідок і отримати шанс виправитися. Назва з помилкою, зовнішній API, що не відповів вчасно, рядок, якого не існує, — усе це помилки інструмента. - Викидайте
MCPError, коли слід відхилити сам запит: клієнтові бракує можливості, від якої залежить інструмент, сервер не в тому стані, щоб обслуговувати будь-кого, той, хто викликає, пропустив обов'язковий крок. Жодна повторна спроба моделі нічого з цього не виправить, тож передавати їй повідомлення немає сенсу.
Вирішує одне запитання: чи могла б розумніша модель цього уникнути? Так -> ToolError. Ні -> MCPError.
За цим критерієм друга версія get_author зробила хибний вибір: краща назва все виправляє, тож модель заслуговувала побачити повідомлення. Ця версія тут, щоб показати механізм, а не щоб його рекомендувати.
Info
MCPError імпортується як from mcp import MCPError і приймає code, message та необов'язкове
корисне навантаження data. Усе, що ви в них покладете, клієнт і отримає: SDK пересилає викинутий
MCPError дослівно, не очищуючи його.
Будь-який інший виняток
Тепер приберіть перевірку й дайте пошуку в словнику завершитися невдачею самому по собі:
from mcp.server import MCPServer
mcp = MCPServer("Bookshop")
CATALOG = {"Dune": "Frank Herbert", "Neuromancer": "William Gibson"}
@mcp.tool()
def get_author(title: str) -> str:
"""Look up the author of a book in the catalog."""
return CATALOG[title]
CATALOG[title] викидає KeyError. Ви його не передбачали, тож SDK вважає це збоєм:
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 і озивається, щойно щось справді зламалося.
Ресурс, якого не існує
Ресурси проводять ту саму межу й мають один іменований виняток для типового випадку.
from mcp.server import MCPServer
from mcp.server.mcpserver.exceptions import ResourceNotFoundError
mcp = MCPServer("Bookshop")
CATALOG = {"Dune": "Frank Herbert", "Neuromancer": "William Gibson"}
@mcp.resource("books://{title}")
def book(title: str) -> str:
"""The catalog entry for one book."""
if title not in CATALOG:
raise ResourceNotFoundError(f"No book titled {title!r} in the catalog.")
return f"{title} by {CATALOG[title]}"
books://{title} — це шаблон. Він збігається з будь-якою назвою, тому «URI коректний» і «книга існує» — два різні запитання, і відповісти на друге може лише ваша функція.
Коли не може — викиньте ResourceNotFoundError. SDK перетворює його на помилку протоколу, яку специфікація призначає для відсутнього ресурсу: -32602 із запитаним URI в data, тож клієнт знає, яке саме читання не вдалося.
{
"code": -32602,
"message": "No book titled 'Nothing' in the catalog.",
"data": {"uri": "books://Nothing"}
}
Зверніть увагу: тут немає напіврезультату з is_error=True. Читання ресурсу або повертає вміст, або завершується помилкою: у ресурсів є лише протокольний шлях. ResourceError — те саме для збою, який не є «не знайдено» (-32603, ваше повідомлення), і обидва — це один рядок рівня INFO у вашому лозі. Будь-який інший виняток, окрім MCPError, — це збій: клієнт отримує -32603, де вказано лише URI, а трасування йде у ваш лог на рівні ERROR. Про шаблони й усе інше, що стосується ресурсів, — на сторінці Ресурси.
Помилки, які ви ніколи не викидаєте
Некоректний аргумент ніколи не доходить до вашої функції.
Надішліть get_author параметр title, що не є рядком, — і SDK відхилить його за вхідною схемою ще до виклику вашої функції, як таку саму помилку інструмента з is_error=True, яку модель може прочитати й виправити. На сторінці Інструменти показано таке саме відхилення з обмеженням Field(le=50).
Це означає цілий клас інструкцій raise, які писати не треба: не перевіряйте повторно власні анотації типів.
Info
Усе, що на цій сторінці бачить клієнт, бачить і Client у пам'яті, з яким ви писатимете
тести. Навіть raise_exceptions=True не повертає виняток інструмента, що впав,
тому, хто викликав: до моменту, коли цей прапорець міг би спрацювати, ваш виняток уже став
результатом з is_error=True. Перевіряйте результат через assert. Якщо потрібне трасування збою, воно
в лозі сервера, і caplog з pytest його перехоплює. Цей підхід описано на сторінці Тестування.
Підсумки
- Викиньте
ToolErrorв інструменті -> виклик повертаєis_error=Trueз вашим повідомленням уcontent. Модель читає його й може повторити спробу. - Викиньте
MCPError-> сам виклик завершується помилкою JSON-RPC. Модель нічого не бачить; розбирається хост.code,messageіdataдоходять без змін. - Вирішальне запитання: чи могла б розумніша модель цього уникнути? Так ->
ToolError. Ні ->MCPError. - Будь-який інший виняток — це збій ->
is_error=Trueлише зError executing tool <name>для моделі та запис рівняERRORіз трасуванням для вас. ResourceNotFoundErrorз обробника ресурсу -> протокольний-32602з URI вdata.- Некоректні аргументи відхиляються за схемою ще до запуску вашої функції; для них
raiseне потрібен. - Імпорти:
from mcp import MCPError,from mcp.server.mcpserver.exceptions import ToolError, ResourceError, ResourceNotFoundErrorі константи кодів помилок зmcp.types.
З помилками розібралися. Це все, що сервер надає назовні. Про те, що кожен обробник може читати і що робити у відповідь клієнтові під час роботи, — наступний розділ: Усередині обробника.
Точний текст помилок SDK, з якими ви найімовірніше зіткнетеся, що кожна з них означає і як виправити кожну одним рухом, — на сторінці Усунення несправностей.