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

Обробка помилок

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

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

Інструмент може завершитися невдачею двома способами, і SDK обробляє їх зовсім по-різному.

Викиньте звичайний виняток — і його побачить модель. Викиньте MCPError — і його побачить протокол.

Ця сторінка — про те, як вибрати.

Помилка, яку модель може виправити

Візьмімо інструмент, який щось шукає, і нехай пошук нічого не знайде:

server.py
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."""
    if title not in CATALOG:
        raise ValueError(f"No book titled {title!r} in the catalog.")
    return CATALOG[title]

У цих двох рядках немає нічого специфічного для MCP. get_author викидає звичайний ValueError, як це зробила б будь-яка функція 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. Сигнал — саме прапорець.

Помилка, яку модель не може виправити

Тепер замініть ValueError на MCPError.

server.py
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 це однозначно гірше — і саме про це наступний розділ.

Який із них викидати

Ці два шляхи відповідають на два різні запитання.

  • Викидайте будь-який виняток у разі збою виконання: те, що інструмент намагався зробити, не вдалося. Виклик обрала модель, тож саме модель має побачити наслідок і отримати шанс виправитися. Назва з помилкою, зовнішній API, що не відповів вчасно, рядок, якого не існує, — усе це помилки інструмента.
  • Викидайте MCPError, коли слід відхилити сам запит: клієнтові бракує можливості, від якої залежить інструмент, сервер не в тому стані, щоб обслуговувати будь-кого, той, хто викликає, пропустив обов'язковий крок. Жодна повторна спроба моделі нічого з цього не виправить, тож передавати їй повідомлення немає сенсу.

Вирішує одне запитання: чи могла б розумніша модель цього уникнути? Так -> звичайний виняток. Ні -> MCPError.

За цим критерієм друга версія get_author зробила хибний вибір: краща назва все виправляє, тож модель заслуговувала побачити повідомлення. Ця версія тут, щоб показати механізм, а не щоб його рекомендувати.

Info

MCPError імпортується як from mcp import MCPError і приймає code, message та необов'язкове корисне навантаження data. Усе, що ви в них покладете, клієнт і отримає: SDK пересилає викинутий MCPError дослівно, не очищуючи його.

Ресурс, якого не існує

Ресурси проводять ту саму межу й мають один іменований виняток для типового випадку.

server.py
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. Читання ресурсу або повертає вміст, або завершується помилкою: у ресурсів є лише протокольний шлях. Про шаблони й усе інше, що стосується ресурсів, — на сторінці Ресурси.

Помилки, які ви ніколи не викидаєте

Некоректний аргумент ніколи не доходить до вашої функції.

Надішліть get_author параметр title, що не є рядком, — і SDK відхилить його за вхідною схемою ще до виклику вашої функції, як таку саму помилку інструмента з is_error=True, яку модель може прочитати й виправити. На сторінці Інструменти показано таке саме відхилення з обмеженням Field(le=50).

Це означає цілий клас інструкцій raise, які писати не треба: не перевіряйте повторно власні анотації типів.

Info

Усе на цій сторінці — те, що бачить клієнт, і Client у пам'яті, з яким ви писатимете тести, бачить рівно те саме. Навіть raise_exceptions=True не перетворює помилку інструмента назад на трасування: до моменту, коли цей прапорець міг би спрацювати, ваш виняток уже став результатом з is_error=True. Перевіряйте результат через assert. Цей підхід описано на сторінці Тестування.

Підсумки

  • Викиньте будь-який виняток в інструменті -> виклик повертає is_error=True з вашим повідомленням у content. Модель читає його й може повторити спробу. Це поведінка за замовчуванням.
  • Викиньте MCPError -> сам виклик завершується помилкою JSON-RPC. Модель нічого не бачить; розбирається хост. code, message і data доходять без змін.
  • Вирішальне запитання: чи могла б розумніша модель цього уникнути? Так -> виняток. Ні -> MCPError.
  • ResourceNotFoundError з обробника ресурсу -> протокольний -32602 з URI в data.
  • Некоректні аргументи відхиляються за схемою ще до запуску вашої функції; для них raise не потрібен.
  • from mcp import MCPError; константи кодів помилок — з mcp.types.

З помилками розібралися. Це все, що сервер надає назовні. Про те, що кожен обробник може читати і що робити у відповідь клієнтові під час роботи, — наступний розділ: Усередині обробника.

Точний текст помилок SDK, з якими ви найімовірніше зіткнетеся, що кожна з них означає і як виправити кожну одним рухом, — на сторінці Усунення несправностей.