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
18 changed files with 3149 additions and 1047 deletions

View file

@ -1,6 +1,6 @@
---
translation:
sections: [2c79b6338e09b7ac, 7edc43b3fae11314, 1086e77ce561cd7f, a3f71823df5efc31, 9fc7109f72201cae, 7bf25983df655b66, 6330e1f4c6029683, 2f1749c8c133fa1c, b3530fcf4d11fd56, ebc33704fbd74262, cd0e9c933350390e]
sections: [2c79b6338e09b7ac, 7edc43b3fae11314, 1086e77ce561cd7f, a3f71823df5efc31, 9fc7109f72201cae, d50fe7faead8cf68, 7bf25983df655b66, 6330e1f4c6029683, 2f1749c8c133fa1c, 8db7116fc8ddd0ee, ebc33704fbd74262, cd0e9c933350390e]
tool: 1
---
# Низькорівневий Server {#the-low-level-server}
@ -116,6 +116,17 @@ asyncio.run(main())
Сервер ніколи не порівнює ці два поля. `Client` цього SDK — порівнює: поверніть `structured_content`, що не відповідає оголошеній вами `output_schema`, і `call_tool` викине `RuntimeError`, який починається з `Invalid structured content returned by tool search_books` і далі цитує помилку `jsonschema`. Пообіцяти схему легко; дотримати її — ваша справа. Уся драбина типів повернення та схем — на сторінці **[Структурований вивід](../servers/structured-output.md)**.
## Діалект — JSON Schema 2020-12 {#the-dialect-is-json-schema-2020-12}
`input_schema` і `output_schema` — це JSON Schema, а [специфікація MCP](https://modelcontextprotocol.io/specification/latest/basic#json-schema-usage) фіксує діалект: схема без ключа `$schema` — це **JSON Schema 2020-12**. Схеми, які генерує `MCPServer`, покладаються на це типове значення (Pydantic пише 2020-12 і пропускає ключ), і написаний вручну словник теж має його дотримуватися, тож доступний увесь набір ключових слів 2020-12:
```python title="server.py" hl_lines="8 14-15"
--8<-- "docs_src/lowlevel/tutorial007.py"
```
* Корінь `input_schema` мусить бути `"type": "object"`. Поруч із ним `oneOf`, `additionalProperties`, `anyOf`, `if`/`then`/`else`, `prefixItems`, `$defs` з локальними `$ref` та решта ключових слів 2020-12 доходять до клієнта точно так, як написано.
* Ключ `$schema` не потрібен. Додавайте його лише щоб перейти на давнішу чернетку стандарту: `Client` цього SDK, який перевіряє `structured_content` за `output_schema` інструмента, обирає валідатор за `$schema` і використовує 2020-12, коли ключа немає.
## `_meta`: для застосунку, а не для моделі {#\_meta-for-the-application-not-the-model}
`content` — це частина відповіді, яку читає модель. `structured_content` — та сама відповідь у вигляді типізованих даних. `_meta` — третій канал: дані, що їдуть разом із результатом для **клієнтського застосунку** і взагалі не є частиною відповіді.
@ -167,7 +178,7 @@ asyncio.run(main())
--8<-- "docs_src/lowlevel/tutorial006.py"
```
* Перший аргумент — рядок методу. Для сповіщень є близнюк — `add_notification_handler`.
* Перший аргумент — рядок методу. Для сповіщень є близнюк — `add_notification_handler`. Його обробники спрацьовують на stdio та на HTTP-з'єднаннях покоління рукостискання; на шляху Streamable HTTP версії `2026-07-28` POST-запит клієнта зі сповіщенням підтверджується кодом `202` і не диспетчеризується, бо ця редакція не визначає сповіщень від клієнта до сервера через HTTP.
* `params_type` — це модель, за якою вхідні `params` перевіряються **до** запуску вашого обробника, тож власні методи *отримують* перевірку, якої інструменти не мають. Успадковуйтеся від `RequestParams`, щоб поле `_meta` розбиралося так само, як у кожного іншого методу.
* Обробник повертає `BaseModel`, `dict` або `None`. SDK серіалізує це в результат JSON-RPC.

View file

@ -1,6 +1,6 @@
---
translation:
sections: [6048b4f308edbb8c, 068bda0f21ee9c1b, c3e565b61acd75c5, c62422b159c6ed09, 47204fab253cc45c]
sections: [6048b4f308edbb8c, 46056f318ef205e4, c3e565b61acd75c5, c62422b159c6ed09, 420968f514138f43]
tool: 1
---
# Middleware {#middleware}
@ -33,8 +33,8 @@ translation:
рядок методу; `ctx.params` — сирі параметри, **до** будь-якої валідації.
* `call_next(ctx)` запускає решту ланцюжка: валідацію, пошук обробника, сам обробник.
Поверніть те, що він повернув, — і відповідь залишиться недоторканою.
* `try`/`finally` тут навмисно: обробник, що викидає виняток, однаково буде заміряно, бо збій
доходить до middleware як виняток із `call_next`.
* `try`/`finally` тут навмисно: для обробника, що викидає виняток, час однаково буде заміряно,
бо збій доходить до middleware як виняток із `call_next`.
* `server.middleware.append(...)` реєструє її. Список виконується від зовнішнього до
внутрішнього, тож `middleware[0]` — найближча до мережі.
@ -55,8 +55,11 @@ tools/call took 0.1 ms
* Установлення з'єднання: `server/discover`, або `initialize` і `notifications/initialized`
у сесії старого покоління.
* Кожен запит і кожне сповіщення. Для сповіщення `ctx.request_id is None`,
`call_next(ctx)` повертає `None`, а все, що повернете ви, відкидається.
* Кожен запит і кожне сповіщення, що доходить до сервера. Для сповіщення
`ctx.request_id is None`, `call_next(ctx)` повертає `None`, а все, що повернете ви,
відкидається. (На шляху Streamable HTTP версії `2026-07-28` POST зі сповіщенням від клієнта
транспорт підтверджує кодом `202` і ніколи не передає далі, тож до middleware воно теж не
доходить; ця ревізія не визначає жодних сповіщень від клієнта до сервера через HTTP.)
* Навіть метод, для якого сервер не має обробника: `call_next` викидає
`MCPError(-32601, "Method not found")` *крізь* ваш middleware на шляху до клієнта.
@ -115,8 +118,9 @@ SDK постачає рівно один шар middleware, і він уже є
* Middleware — це `async (ctx, call_next) -> result`; його передають як
`MCPServer(middleware=[...])` (або додають до `mcp.middleware`), а в низькорівневому `Server`
додають до `server.middleware`.
* Він огортає **кожне** вхідне повідомлення (`server/discover`, `initialize`, запити,
сповіщення, невідомі методи) і виконується від зовнішнього до внутрішнього.
* Він огортає **кожне** вхідне повідомлення, що доходить до сервера (`server/discover`,
`initialize`, запити, сповіщення, невідомі методи), і виконується від зовнішнього до
внутрішнього.
* `ctx.request_id is None` — так відрізняють сповіщення від запиту.
* Викиньте виняток замість виклику `call_next`, щоб відхилити одне повідомлення; з'єднання
вціліє.

View file

@ -1,6 +1,6 @@
---
translation:
sections: [ebef1e7a0df854f4, a4c687d3d627d516, 8e79141fc2985342, b345dd05b9c3c7ab, 80ce41579825a6fa, 5f0fa90494de8f65, 83d10514eaa62fa5, 9190555aa39a5d28, 84a4c9d8bf14dddb, 927d71cf40b58c30]
sections: [ebef1e7a0df854f4, 8355cfaf1f76c9d5, 8e79141fc2985342, 46bdb07c7537e8a5, 80ce41579825a6fa, 5f0fa90494de8f65, 83d10514eaa62fa5, 9190555aa39a5d28, 84a4c9d8bf14dddb, 927d71cf40b58c30]
tool: 1
---
# Клієнт {#the-client}
@ -27,9 +27,10 @@ translation:
* Екземпляр `MCPServer` (або низькорівневого `Server`): під'єднання **в межах процесу**.
* Рядок з URL (`Client("http://localhost:8000/mcp")`): Streamable HTTP, шлях для робочого розгортання.
* **Транспорт**: будь-що, що можна використати як `async with ... as (read, write)`, наприклад `stdio_client(...)`, що обгортає підпроцес.
* `StdioServerParameters`: команда, яку буде запущено як **підпроцес**; спілкування з ним іде через його stdin і stdout.
* **Транспорт**: будь-що, що можна використати як `async with ... as (read, write)`, наприклад `streamable_http_client(url, http_client=...)` навколо вашого власного HTTP-клієнта.
Усе інше на цій сторінці однакове для всіх трьох. Заголовки, підпроцеси, тайм-аути та протокол `Transport` мають власну сторінку: **[Транспорти клієнта](transports.md)**.
Усе інше на цій сторінці однакове для всіх чотирьох. Заголовки, підпроцеси, тайм-аути та протокол `Transport` мають власну сторінку: **[Транспорти клієнта](transports.md)**.
### Що є в під'єднаного клієнта {#whats-on-a-connected-client}
@ -85,7 +86,7 @@ tool.description # 'Search the catalog by title or author.'
`call_tool(name, arguments)` запускає інструмент і повертає `CallToolResult`.
```python title="client.py" hl_lines="26-33"
```python title="client.py" hl_lines="27-34"
--8<-- "docs_src/client/tutorial003.py"
```
@ -117,7 +118,7 @@ result.is_error # False
!!! check
Попросіть у `lookup_book` `"Solaris"` (назву, якої немає в каталозі) — і функція викине
`ValueError`. Виклик усе одно повернеться нормально:
`ToolError`. Виклик усе одно повернеться нормально:
```python
result.is_error # True
@ -125,9 +126,10 @@ result.is_error # False
result.structured_content # None
```
Повідомлення винятку потрапило в `content`, де його може прочитати **модель** і спробувати ще раз. Це
навмисно: помилка інструмента — частина розмови, а не аварія. Завжди дивіться на `is_error`,
перш ніж довіряти `structured_content`.
Повідомлення `ToolError` потрапило в `content`, де його може прочитати **модель** і спробувати ще раз. Це
навмисно: помилка інструмента — частина розмови, а не аварія. (Якби інструмент упав
з якимось іншим винятком, у `content` було б лише `Error executing tool lookup_book`.) Завжди дивіться на
`is_error`, перш ніж довіряти `structured_content`.
!!! warning
`is_error=True` охоплює більше, ніж ваш власний `raise`. Попросіть інструмент, якого в сервера

View file

@ -1,6 +1,6 @@
---
translation:
sections: [9cac816674181eb0, 0700f337babcd4dd, 2bde0dd58cdf00f5, ff7401df479af877, 3d0832f39b0d7059, d4bf7e4479637768, 05e20c0a798860e7]
sections: [9cac816674181eb0, 0700f337babcd4dd, 2bde0dd58cdf00f5, 40b4916d82eaf1d4, 3d0832f39b0d7059, dfa4446556badef0, 5bd93be2ab2ecb9c]
tool: 1
---
# Транспорти клієнта {#client-transports}
@ -87,15 +87,15 @@ translation:
Сервер **stdio** — це підпроцес. Клієнт запускає його, пише JSON-RPC в його stdin і читає JSON-RPC з його stdout. Саме так десктопний хост запускає сервер на вашій машині: хост і *є* цим кодом плюс UI, а сторінка **[Під'єднання до справжнього хоста](../get-started/real-host.md)** показує ті самі стосунки з боку хоста — як конфігураційний файл.
Опишіть процес за допомогою `StdioServerParameters`, перетворіть його на транспорт через `stdio_client` і передайте *це* в `Client`:
Опишіть процес за допомогою `StdioServerParameters` і передайте його в `Client`:
```python title="client.py" hl_lines="4-8 12"
```python title="client.py" hl_lines="3-7 11"
--8<-- "docs_src/client_transports/tutorial004.py"
```
`Client` не приймає сам об'єкт параметрів. `StdioServerParameters` — це конфігурація; `stdio_client(server)` — транспорт, який уміє запустити з неї процес. Завжди загортайте.
Вхід у блок запускає процес. Вихід із нього завершує підпроцес: закриває stdin, чекає, вбиває, якщо той затримується. Прибирати за ним самостійно ніколи не доведеться.
Вихід із блоку `async with` також завершує підпроцес: закриває stdin, чекає, вбиває, якщо той затримується. Прибирати за ним самостійно ніколи не доведеться.
stderr дочірнього процесу йде у ваш. Щоб спрямувати його деінде, зберіть транспорт самостійно через `stdio_client` (з `mcp`) і передайте натомість його: `Client(stdio_client(server, errlog=log_file))`.
!!! warning
Дочірній процес **не** успадковує ваше середовище. Він отримує мінімальний список дозволених змінних (`HOME`, `LOGNAME`,
@ -113,16 +113,16 @@ translation:
Для `Client` усе перелічене вище — одне й те саме.
**Транспорт** — це будь-який асинхронний контекстний менеджер, що повертає пару потоків повідомлень `(read, write)`: формально — протокол `Transport` у `mcp.client`. `Client` розв'язує свій аргумент за типом: об'єкт сервера під'єднується в межах процесу, `str` стає `streamable_http_client(url)`, а в будь-що інше він входить безпосередньо як у транспорт. Саме завдяки останньому правилу `stdio_client(...)`, `streamable_http_client(...)` і `sse_client(...)` стають на одне й те саме місце — і саме тому можна написати власний.
**Транспорт** — це будь-який асинхронний контекстний менеджер, що повертає пару потоків повідомлень `(read, write)`: формально — протокол `Transport` у `mcp.client`. `Client` розв'язує свій аргумент за типом: об'єкт сервера під'єднується в межах процесу, `str` стає `streamable_http_client(url)`, `StdioServerParameters` стає `stdio_client(params)`, а в будь-що інше він входить безпосередньо як у транспорт. Саме завдяки останньому правилу `stdio_client(...)`, `streamable_http_client(...)` і `sse_client(...)` стають на одне й те саме місце — і саме тому можна написати власний.
## Підсумки {#recap}
* `Client(mcp)` (об'єкт сервера) під'єднується в пам'яті. Використовуйте для тестів і для вбудовування.
* `Client("http://.../mcp")` (URL) під'єднується через Streamable HTTP, продакшен-транспорт.
* Заголовки, автентифікація, проксі й тайм-аути належать `httpx2.AsyncClient`, який ви передаєте в `streamable_http_client(url, http_client=...)`. Іменованого аргументу `headers=` немає.
* stdio — це `Client(stdio_client(StdioServerParameters(...)))`, ніколи не сам об'єкт параметрів.
* stdio — це `Client(StdioServerParameters(...))`. Загортайте його в `stdio_client(...)` самостійно лише для того, щоб перенаправити stderr дочірнього процесу.
* Підпроцес отримує середовище зі списку дозволених, а не ваше; `env=` його доповнює.
* Транспорт — це будь-що, з чим можна зробити `async with x as (read, write)`. Усе, що не є об'єктом сервера чи URL, `Client` передає прямо цьому протоколу.
* Транспорт — це будь-що, з чим можна зробити `async with x as (read, write)`. Усе, що не є об'єктом сервера, URL чи `StdioServerParameters`, `Client` передає прямо цьому протоколу.
* Створення `Client` обирає транспорт. `async with` його відкриває.
Щойно транспорт відкрито, обидві сторони мають домовитися про версію протоколу. Зазвичай про це не думаєш; а коли доводиться — є сторінка **[Версії протоколу](../protocol-versions.md)**.
Щойно транспорт відкрито, обидві сторони мають домовитися про версію протоколу. Зазвичай про це й не згадуєте; а коли все ж доведеться — є сторінка **[Версії протоколу](../protocol-versions.md)**.

View file

@ -1,11 +1,11 @@
---
translation:
sections: [20541a40dbdd5980, 01262a123ad9501d, 429db5b574a2ac08, 56b2d49da412cb28, 6a1717123fe4513c]
sections: [490237e61c3a7a44, 01262a123ad9501d, 429db5b574a2ac08, e2d0d273fbd2d74b, 64ab0331e868f3d4, 6c8878ce2d1f6d56, 4068f23e371bf0b3, eaef75b8725bc931]
tool: 1
---
# Застарілі можливості {#deprecated-features}
Специфікація 2026-07-28 виводить з ужитку п'ять речей. SDK і далі реалізує кожну з них, і кожна тепер супроводжується **попередженням про застарілість**.
Специфікація 2026-07-28 виводить з ужитку п'ять речей. SDK і далі реалізує кожну з них, і кожна тепер супроводжується **попередженням про застарілість**. Один допоміжний метод SDK оголошено застарілим окремо, і його наведено [наприкінці сторінки](#deprecated-sdk-helpers).
Таблиця нижче називає кожну застарілу можливість, пояснює, чому вона зникає, і вказує заміну, на яку варто спиратися.
@ -56,6 +56,55 @@ MCPDeprecationWarning: The logging capability is deprecated as of 2026-07-28 (SE
потім намагається надіслати запит. Ці два методи працюють від початку до кінця лише на
з'єднанні з `mode="legacy"`, клієнт якого зареєстрував відповідний колбек.
## `ping` у сесії старого покоління {#ping-on-a-legacy-session}
**Ping** — це порожній запит, який може надіслати будь-яка сторона, щоб перевірити, чи інша ще відповідає. Специфікація 2026-07-28 його вилучає ([SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575)): кожен запит, який надсилає сучасний клієнт, уже доводить, що сервер на місці, а сучасний сервер не має каналу, яким міг би надіслати свій. Обидва методи SDK і далі працюють у сесії покоління з рукостисканням. З боку клієнта:
```python
async def main() -> None:
async with Client("http://localhost:8000/mcp", mode="legacy") as client:
await client.send_ping() # warns; returns an EmptyResult
```
А з боку сервера, усередині будь-якого обробника:
```python
@mcp.tool()
async def check_client(ctx: Context) -> str:
"""A tool that still pings the client mid-call."""
await ctx.session.send_ping() # no warning; an EmptyResult while the client is connected
return "client answered"
```
* `client.send_ping()` попереджає через `MCPDeprecationWarning` під час кожного виклику. На з'єднанні за замовчуванням (`2026-07-28`) сервер натомість відповідає `MCPError: Method not found`.
* `ctx.session.send_ping()` попередження не має. На сучасному з'єднанні він викидає ту саму помилку про відсутність зворотного каналу (back-channel), що й будь-який інший запит з ініціативи сервера.
* Жодна зі сторін нічого не реєструє, щоб відповідати на ping.
## Сповіщення про зміну кореневих каталогів {#roots-change-notifications}
Клієнт покоління 2025, який оголосив можливість кореневих каталогів, може повідомити серверу, що теки його робочого простору змінилися, надіславши `notifications/roots/list_changed`; сервер у відповідь знову запитує `roots/list`. Специфікація 2026-07-28 вилучає це сповіщення разом із рештою push-варіанту роботи з кореневими каталогами. На клієнті саме передавання `list_roots_callback=` (**[Колбеки клієнта](client/callbacks.md)**) оголошує `"roots": {"listChanged": true}`, а один виклик дотримує цієї обіцянки:
```python
async def open_folder(client: Client, uri: str, name: str) -> None:
"""The user opened another folder: expose it through the roots callback, then tell the server."""
workspace.append(Root(uri=FileUrl(uri), name=name))
await client.send_roots_list_changed()
```
На боці сервера обробник, що приймає це сповіщення, передають у низькорівневий `Server`:
```python
async def roots_changed(ctx: ServerRequestContext, params: NotificationParams | None) -> None:
"""The client's roots changed: ask for the new list."""
roots = (await ctx.session.list_roots()).roots
server = Server("Bookshop", on_roots_list_changed=roots_changed)
```
* `workspace` — це список, який повертає ваш `list_roots_callback`. `client.send_roots_list_changed()` попереджає і потребує клієнта з `mode="legacy"`: на сучасному з'єднанні сповіщення мовчки відкидається. Після цього не закривайте сесію, бо наступний запит сервера `roots/list` надходить саме нею.
* `MCPServer` не має гачка для цього сповіщення. У низькорівневому `Server` обробник реєструє параметр `on_roots_list_changed=` (теж застарілий, він попереджає під час створення екземпляра). Сповіщення не несе корисного навантаження, тож обробник викликає `ctx.session.list_roots()`, щоб отримати новий список.
## Приглушення попередження {#silencing-the-warning}
У новому коді — не робіть цього.
@ -76,23 +125,33 @@ warnings.filterwarnings("ignore", category=MCPDeprecationWarning)
Розверніть фільтр у зворотний бік — і отримаєте безкоштовний регресійний тест. Додайте
`"error::mcp.MCPDeprecationWarning"` до налаштування `filterwarnings` у конфігурації
pytest — і застарілий виклик **викидатиме виняток** замість попередження. Інструмент
з назвою `old_log`, який досі викликає `ctx.info()`, перестає проходити тест і починає
повідомляти:
з назвою `old_log`, який досі викликає `ctx.info()`, перестає проходити тест: виклик
повертається з `is_error=True` і текстом `Error executing tool old_log`, а захоплений
лог сервера називає винуватця:
```text
Error executing tool old_log: The logging capability is deprecated as of 2026-07-28 (SEP-2577).
mcp.shared.exceptions.MCPDeprecationWarning: The logging capability is deprecated as of 2026-07-28 (SEP-2577).
```
Один рядок конфігурації pytest — і застарілий виклик більше ніколи не прокрадеться назад
у вашу кодову базу, не проваливши тест.
## Застарілі допоміжні методи SDK {#deprecated-sdk-helpers}
Це не зміни специфікації, а лише внутрішні частини SDK, для яких є краща заміна. Вони попереджають тим самим `MCPDeprecationWarning` і будуть вилучені у версії 3.0.
| Застаріле | Що робити натомість |
|---|---|
| `FuncMetadata.call_fn_with_arg_validation()` | `FuncMetadata.validate_arguments()`, а потім `FuncMetadata.call_fn()`. Його викликав лише код, що працює з `FuncMetadata` безпосередньо (скажімо, власний підклас `Tool`). |
## Підсумки {#recap}
* Специфікація 2026-07-28 оголошує застарілими **кореневі каталоги**, **семплювання** з ініціативи сервера та протокольне **логування** (усе — [SEP-2577](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577)), обмежує **перебіг виконання** напрямком від сервера до клієнта й вилучає **`ping`**.
* Стовпець із замінами вказує, куди рухатися далі: **[Багатораундові запити](handlers/multi-round-trip.md)** для семплювання й кореневих каталогів, **[Логування](handlers/logging.md)** для логування, **[Перебіг виконання](handlers/progress.md)** для перебігу виконання. `ping` не потребує взагалі нічого.
* Застарілість має рекомендаційний характер: жодних змін у переданих даних, усе й далі працює із сесіями до 2026, а ви отримуєте помітне попередження `MCPDeprecationWarning` (це `UserWarning`, тож воно ввімкнене за замовчуванням).
* Семплювання й кореневі каталоги додатково потребують зворотного каналу (back-channel), якого сесія 2026-07-28 не має. На сучасному з'єднанні вони попереджають, а потім викидають виняток.
* Семплювання й кореневі каталоги додатково потребують зворотного каналу, якого сесія 2026-07-28 не має. На сучасному з'єднанні вони попереджають, а потім викидають виняток.
* `warnings.filterwarnings("ignore", category=MCPDeprecationWarning)` приглушує всю категорію; `"error::mcp.MCPDeprecationWarning"` у pytest перетворює її на провал тесту.
* Один допоміжний метод SDK, `FuncMetadata.call_fn_with_arg_validation()`, оголошено застарілим окремо — його вилучать у версії 3.0.
* Новий код не варто будувати на жодній із цих можливостей.
Усі інші сторінки цієї документації навчають чинного API.

View file

@ -1,6 +1,6 @@
---
translation:
sections: [3c4f2f06b4e978b6, 22520eecae3d1961, f4e1709db18d635a, 2eb57992049671d9, 1ba83e9af37cc1b4, 4822586344b08d9e, 1c93afef72478992, b6b448f9eddd51dc, fe55370fd931815b]
sections: [3c4f2f06b4e978b6, 51ea5fbcb0e93563, 32d8808606ffdae0, 2eb57992049671d9, 1ba83e9af37cc1b4, 4822586344b08d9e, 1c93afef72478992, b6b448f9eddd51dc, fe55370fd931815b]
tool: 1
---
# Підключення до справжнього хоста {#connect-to-a-real-host}
@ -11,7 +11,7 @@ translation:
## Один сервер, усі хости {#one-server-every-host}
```python title="server.py" hl_lines="3 33-34"
```python title="server.py" hl_lines="4 34-35"
--8<-- "docs_src/real_host/tutorial001.py"
```
@ -50,7 +50,7 @@ uv run --with "mcp[cli]" mcp run /absolute/path/to/server.py
А хост — це не більше ніж застосунок з MCP-клієнтом усередині, тож роль хоста може
зіграти й ваш власний Python: сторінка **[Транспорти клієнта](../client/transports.md)** запускає
цей самий файл як підпроцес через `stdio_client(...)`, а **[Тестування](testing.md)**
цей самий файл як підпроцес через `Client(StdioServerParameters(...))`, а **[Тестування](testing.md)**
підключається до нього в пам'яті взагалі без процесу.
## Claude Desktop {#claude-desktop}

View file

@ -1,6 +1,6 @@
---
translation:
sections: ['4926721070127497', c52a1de2b6b32f40, 2e410b412c25f314, 627195f7159e24ef]
sections: ['4926721070127497', c52a1de2b6b32f40, 8e792bf8c7489ec6, 627195f7159e24ef]
tool: 1
---
# Тестування {#testing}
@ -84,13 +84,13 @@ async def test_call_add_tool(client: Client):
Піти не так можуть дві різні речі, і цей прапорець стосується лише однієї з них.
Виняток усередині одного з **ваших інструментів** — це не збій протоколу. Він стає звичайним результатом
з `is_error=True`, і модель читає повідомлення. `raise_exceptions` цього не змінює: з ним чи
без нього `call_tool` повертає той самий результат з `is_error=True`. Про це є ціла сторінка:
з `is_error=True` (а якщо це був `ToolError`, модель прочитає ваше повідомлення). `raise_exceptions` цього не
змінює: з ним чи без нього `call_tool` повертає той самий результат з `is_error=True`. Про це є ціла сторінка:
**[Обробка помилок](../servers/handling-errors.md)**.
Збій **поза** тілом інструмента — інша річ. На з'єднанні, яке дає `Client(mcp)`, сервер
замінює його загальним `"Internal server error"`, перш ніж його побачить клієнт. Ніколи не слід
розкривати подробиці неочікуваного падіння віддаленій стороні, що викликає. У тесті це саме те,
замінює його загальним `"Internal server error"`, перш ніж його побачить клієнт. Ніколи не
розкривайте подробиці неочікуваного падіння віддаленій стороні, що викликає. У тесті це саме те,
чого ви *не* хочете, і саме це змінює `raise_exceptions=True`: тест бачить справжнє повідомлення
замість узагальненого.

View file

@ -1,6 +1,6 @@
---
translation:
sections: [335ca2a0b266f003, d1ad562d3fe87bc0, 0bb1396c86daeba4, d1cb1235bb9ee267, 833179c09d239c83, e5d6dec2d2e655e8]
sections: [335ca2a0b266f003, d1ad562d3fe87bc0, 25b49f89c9e6f9d0, d1cb1235bb9ee267, 833179c09d239c83, e5d6dec2d2e655e8]
tool: 1
---
# Еліцитація {#elicitation}
@ -89,7 +89,8 @@ translation:
!!! warning
Схема еліцитації не така виразна, як вхідна схема інструмента. Лише пласкі примітивні поля:
`str`, `int`, `float`, `bool` або `Literal` з рядків (він стає `enum`).
Покладіть модель усередину моделі — і `ctx.elicit` викине виняток ще до того, як щось буде надіслано клієнтові:
Покладіть модель усередину моделі — і `ctx.elicit` викине виняток ще до того, як щось буде надіслано клієнтові.
Виклик інструмента завершується помилкою `Error executing tool <name>`, а причина — у лозі сервера:
```text
TypeError: Elicitation schema field 'address' rendered as {'$ref': '#/$defs/Address'}, which is not a valid PrimitiveSchemaDefinition
@ -112,8 +113,8 @@ translation:
!!! tip
Відповідь валідується за вашою моделлю, перш ніж її побачить ваш код. Клієнт, що надсилає
`"maybe"` для `bool`, не зіпсує бронювання: виклик завершується помилкою
невідповідності схемі, а ваш `if` так і не виконується.
`"maybe"` для `bool`, не зіпсує бронювання: `ctx.elicit` викидає `ValueError`, виклик
завершується помилкою, а ваш `if` так і не виконується.
## Перенаправлення користувача на URL {#send-the-user-to-a-url}

View file

@ -1,6 +1,6 @@
---
translation:
sections: [c93a3e1aefd77955, 7851abd5ec54393b, f49d1ca2f330f9cd, c03764bd9dfeef7b, 4a0391691a674ae4, 2df5cd279eabf9f5]
sections: [c93a3e1aefd77955, 7851abd5ec54393b, f49d1ca2f330f9cd, 4cc0a00347c3f534, 4a0391691a674ae4, 2df5cd279eabf9f5]
tool: 1
---
# Логування {#logging}
@ -55,6 +55,8 @@ translation:
`logging.basicConfig()` ніколи не замінює обробники, що вже існують. Якщо налаштувати логування самостійно до створення сервера, ваше налаштування має перевагу.
Так само не потрібен `try`/`except` у кожному обробнику лише для того, щоб фіксувати збої. Коли функція інструмента чи ресурсу викидає виняток, SDK записує його в лог за вас. Що саме логується і на якому рівні, пояснено на сторінці **[Обробка помилок](../servers/handling-errors.md#any-other-exception)**.
## Спробуйте самі {#try-it}
Запустіть сервер з MCP Inspector:

View file

@ -1,6 +1,6 @@
---
translation:
sections: [fea8d769ff9edeba, ce8e2ad42f29ef71, 0d705efb19cf99c2, 7a53ead3e704a7f0, 9adc400e8c88e854, 318893ad8e2e9924, 6b63ab96b34476c0]
sections: [fea8d769ff9edeba, ce8e2ad42f29ef71, 0d705efb19cf99c2, 2fd7cf825e6d2b2c, 9adc400e8c88e854, 318893ad8e2e9924, 6b63ab96b34476c0]
tool: 1
---
# Запуск сервера {#running-your-server}
@ -70,9 +70,9 @@ Inspector робить рівно те саме, що й справжній хо
* `host` / `port`: де слухати. За замовчуванням `127.0.0.1` і `8000`.
* `streamable_http_path`: де розташована кінцева точка MCP. За замовчуванням `/mcp`.
* `json_response=True`: відповідати на кожен POST одним JSON-тілом замість SSE-потоку. У такому тілі є місце для відповіді й ні для чого іншого, тож інструмент, який посеред запиту звертається назад до клієнта (`ctx.elicit()`, семплювання (sampling)), на цьому відрізку викидає `NoBackChannelError`, а сповіщення, прив'язані до поточного виклику (перебіг виконання від `ctx.report_progress()`, повідомлення журналу окремого виклику), відкидаються; окремий потік `GET` і далі несе не пов'язані з ним.
* `json_response=True`: відповідати на кожен POST одним JSON-тілом замість SSE-потоку. У такому тілі є місце для відповіді й ні для чого іншого, тож інструмент, який посеред запиту звертається назад до клієнта (`ctx.elicit()`, семплювання (sampling)), на цьому відрізку викидає `NoBackChannelError`, а сповіщення, прив'язані до поточного виклику (перебіг виконання від `ctx.report_progress()`, лог-повідомлення окремого виклику), відкидаються; окремий потік `GET` і далі несе не пов'язані з ним.
* `stateless_http=True`: новий транспорт на кожен запит, без відстеження сесій.
* `max_request_body_size`: найбільший прийнятний розмір тіла POST у байтах. За замовчуванням 4 МіБ; більші запити
* `max_request_body_size`: найбільший прийнятний розмір тіла запиту в байтах. За замовчуванням 4 МіБ; більші запити
отримують HTTP 413 ще до розбору чи створення сесії. Збільшуйте його лише тоді, коли легітимні MCP-повідомлення
перевищують цей розмір.
* `event_store`, `retry_interval`, `transport_security`: відновлюваність і захист від DNS-rebinding. Вони можуть зачекати, доки ви не розгорнете сервер деінде, крім localhost; `transport_security` описано на сторінці **[Розгортання та масштабування](deploy.md)**.

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,13 +30,15 @@ result.structured_content # None
```
* Запит **виконався успішно**. Результат є; на боці того, хто викликав, нічого не викинуто.
* `is_error` дорівнює `True`, а повідомлення вашого винятку (з назвою інструмента на початку) лежить у `content` — саме там, де читає модель.
* `is_error` дорівнює `True`, а ваше повідомлення (з назвою інструмента на початку) лежить у `content` — саме там, де читає модель.
* `structured_content` дорівнює `None`. У невдалого виклику немає значення, яке можна було б структурувати.
Це **помилка інструмента**, і так за замовчуванням обробляється *будь-який* виняток, який викидає ваш інструмент. І майже завжди це саме те, що потрібно.
Це **помилка інструмента**, і майже завжди це саме те, що потрібно.
Ваш інструмент викликає саме модель. Це вона обрала аргументи. Тож помилка інструмента — це репліка в розмові: модель читає *«No book titled 'Nothing' in the catalog.»*, розуміє, що не вгадала назву, і викликає знову з кращою. Один `raise` — і маєте агента, що сам виправляє свої помилки.
На сервері `ToolError` — це один рядок рівня `INFO` у лозі, без трасування. Ви цього очікували, тож розслідувати нічого.
!!! tip
Ніколи не повертайте повідомлення про помилку з інструмента через `return`. Повернутий рядок має `is_error=False`, тож для
моделі (і для кожного клієнтського інтерфейсу) це виглядає так, ніби інструмент спрацював і цей рядок і є відповіддю.
@ -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`. Перевіряйте результат через assert. Цей підхід описано на сторінці **[Тестування](../get-started/testing.md)**.
Усе, що на цій сторінці бачить **клієнт**, бачить і `Client` у пам'яті, з яким ви писатимете
тести. Навіть `raise_exceptions=True` не повертає виняток інструмента, що впав,
тому, хто викликав: до моменту, коли цей прапорець міг би спрацювати, ваш виняток уже став
результатом з `is_error=True`. Перевіряйте результат через assert. Якщо потрібне трасування збою, воно
в лозі сервера, і `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}
@ -139,10 +139,55 @@ uv run mcp dev server.py
```
!!! info
Якщо ви читали сторінку **[Інструменти](tools.md)**, то вже знаєте все, що є на цій. Той самий декоратор, той самий
Якщо ви читали сторінку **[Інструменти](tools.md)**, то вже знаєте все, про що йшлося досі. Той самий декоратор, той самий
docstring як опис, ті самі `Annotated`/`Field`. Змінюється лише те, хто
його запускає (користувач) і куди йде результат (у розмову).
## Більше ніж текст {#more-than-text}
`UserMessage` і `AssistantMessage` також приймають блок вмісту або допоміжний об'єкт `Image` / `Audio` всюди, де приймають `str`. У промптах трапляються два випадки: прикріпити документ і прикріпити зображення.
### Вбудовування файлу {#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`.
* Покладіть будь-який PNG з іменем `architecture.png` поруч із `server.py`. Аргументи промпту — рядки, тому зображення завжди надходить із сервера; `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`. У Python-класі `Client` це `async with client.listen(prompts_list_changed=True) as sub:`, що видає подію `PromptsListChanged`.
## Підсумки {#recap}
* `@mcp.prompt()` над функцією робить її промптом. Ім'я — з функції, опис — з docstring.
@ -151,5 +196,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}
Є один спосіб опинитися без структурованого виводу, не просивши про це: повернути клас, у **тілі якого немає анотацій**.
@ -245,6 +250,6 @@ result.structured_content # {"London": 16.2, "Reykjavik": 4.4}
* Скаляри, списки, кортежі й об'єднання типів загортаються в `{"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,7 +170,7 @@ 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"
```
@ -211,9 +211,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}

View file

@ -1,6 +1,6 @@
---
translation:
sections: [2efaecdef109a5c5, fcacd3e66b8635a4, 25323d737dcf0261, 4835ed1772f1d113, 137454d469c867f5, 6392596bd6df54f0, 41126fa9c4fe432f, 480b6d7897e30ab4, d83bb682e708dde0, ebbed3449c499db4, 323ef84f6b4bebde, 30fd31be74169d9a, 656943c6cb567218, c2dc3b1007d2e987, 7cf5386b997d04e9, 0b59feed8384456e, 0cba47bae78d04eb, 954dc21efdb532a3]
sections: [2efaecdef109a5c5, fcacd3e66b8635a4, 25323d737dcf0261, 8a6e351ec756904d, 137454d469c867f5, 6392596bd6df54f0, 41126fa9c4fe432f, 480b6d7897e30ab4, d83bb682e708dde0, ebbed3449c499db4, 323ef84f6b4bebde, 30fd31be74169d9a, 656943c6cb567218, c2dc3b1007d2e987, 7cf5386b997d04e9, 0b59feed8384456e, 0cba47bae78d04eb, e4355f4c7cf4fb2e]
tool: 1
---
# Усунення несправностей {#troubleshooting}
@ -81,11 +81,11 @@ async def main() -> None:
`__aexit__` — це від'єднання, тому й немає `client.close()`, про який можна забути. Сторінка **[Тестування](get-started/testing.md)** побудована саме на цьому шаблоні.
## `Error executing tool <name>: <message>` і `Unknown tool: <name>` {#error-executing-tool-name-message-and-unknown-tool-name}
## `Error executing tool <name>: <message>`, `Error executing tool <name>` і `Unknown tool: <name>` {#error-executing-tool-name-message-error-executing-tool-name-and-unknown-tool-name}
Перед вами **результат**, а не виняток. `call_tool` нічого не викинув і ніколи не викине для інструмента, що завершився збоєм.
Викличте `forecast` для міста, якого сервер не знає, — і виняток, який він викидає, повертається із запитом, позначеним як *успішний*:
Викличте `forecast` для міста, якого сервер не знає, — і `ToolError`, який він викидає, повертається із запитом, позначеним як *успішний*:
```python
result.is_error # True
@ -97,6 +97,8 @@ result.structured_content # None
Виправлення — на боці клієнта: **перевіряйте `result.is_error`**. `try/except` навколо `call_tool` не перехопить жодного з цих випадків, бо перехоплювати нічого. Це зроблено навмисно, і це найкорисніше, що варто засвоїти з цієї сторінки: виклик обрала *модель*, тож саме модель отримує повідомлення й шанс спробувати знову. Докладніше — на сторінці **[Обробка помилок](servers/handling-errors.md)**, зокрема про шлях через `MCPError`, який *таки* викидає виняток.
Коротка форма, `Error executing tool <name>` без повідомлення, означає, що інструмент **упав**: з нього вийшов виняток, якого він не передбачав (або його повернене значення не пройшло вихідну схему), і текст цього винятку в передані дані не потрапляє. Трасування — у **лозі сервера** на рівні `ERROR`, як `Tool '<name>' raised an unexpected exception`.
## `TypeError: The @tool decorator was used incorrectly. Did you forget to call it? Use @tool() instead of @tool` {#typeerror-the-tool-decorator-was-used-incorrectly-did-you-forget-to-call-it-use-tool-instead-of-tool}
Ви написали `@mcp.tool` замість `@mcp.tool()`. `tool()` — це *фабрика* декораторів: без дужок Python передає вашу функцію в її параметр `name=`.
@ -410,7 +412,7 @@ mcp = MCPServer("Weather", request_state_security=RequestStateSecurity(keys=[key
## Підсумки {#recap}
* `ExceptionGroup: unhandled errors in a TaskGroup` ніколи не є самою помилкою. Читайте **останній рядок**; перехоплення `MCPError` *всередині* блоку `async with Client(...)` повністю оминає обгортання.
* `call_tool` не викидає виняток для інструмента, що завершився збоєм. `Error executing tool ...` і `Unknown tool: ...` — це результати: перевіряйте `result.is_error`.
* `call_tool` не викидає виняток для інструмента, що завершився збоєм. `Error executing tool ...` і `Unknown tool: ...` — це результати: перевіряйте `result.is_error`. Якщо після імені інструмента немає повідомлення, він упав, а трасування — у лозі сервера.
* `Client must be used within an async context manager` -> використовуйте `async with`. `Use @tool() instead of @tool` -> додайте дужки.
* `Tool already exists:` у лозі сервера — єдина ознака того, що два однойменні інструменти злилися в один.
* Один 421, три написання: `Server returned an error response` (python `Client`), `421 Misdirected Request` / `Invalid Host header` (усе інше), `Invalid Host header: <host>` (лог сервера). Виправлення: `transport_security=TransportSecuritySettings(allowed_hosts=[...])`.

View file

@ -1,6 +1,6 @@
---
translation:
sections: [cfe01c0c5863dfa2, 11d93f1fa09eadf5, a7392996acf1ad8f, 875eb2889263424e]
sections: [cfe01c0c5863dfa2, 1c58c5cfcc37d455, a7392996acf1ad8f, 875eb2889263424e]
tool: 1
---
# Що нового у v2 {#whats-new-in-v2}
@ -46,9 +46,9 @@ v1 давав три вкладені шари: контекстний мене
--8<-- "docs_src/client/tutorial001.py"
```
`Client` приймає об'єкт сервера (у пам'яті, без транспорту: це сценарій тестування), URL (Streamable HTTP) або будь-який контекстний менеджер транспорту, як-от `stdio_client(...)`. Вхід в `async with` під'єднує та узгоджує версію протоколу, хай яким поколінням говорить сервер; після цього `client.server_capabilities` і `client.protocol_version` просто є, як і `client.server_info`, коли сервер себе ідентифікує (тепер це `Implementation | None`, бо ідентичність у поколінні 2026 необов'язкова). Колбеки семплювання й еліцитації, зареєстровані у v1, і далі працюють (їхні тіла зазнають того самого перейменування атрибутів у snake_case, що й усе інше на цій сторінці), тепер вони також відповідають на запити всередині результатів у стилі 2026 (нижче) і виконуються паралельно, а не по одному. `ClientSession` досі лежить під сподом для тих, кому потрібна низькорівнева поверхня, і `client.session` її віддає; вона теж змінилася (працює на новому рушії диспетчера, і деякі її власні сигнатури змінилися), тож прочитайте **[Посібник з міграції](migration.md#clientsession-now-runs-on-jsonrpcdispatcher-basesession-removed)**, перш ніж спускатися нижче.
`Client` приймає об'єкт сервера (у пам'яті, без транспорту: це сценарій тестування), URL (Streamable HTTP), `StdioServerParameters` (підпроцес stdio) або будь-який інший контекстний менеджер транспорту, як-от `sse_client(...)`. Вхід в `async with` під'єднує та узгоджує версію протоколу, хай яким поколінням говорить сервер; після цього `client.server_capabilities` і `client.protocol_version` просто є, як і `client.server_info`, коли сервер себе ідентифікує (тепер це `Implementation | None`, бо ідентичність у поколінні 2026 необов'язкова). Колбеки семплювання й еліцитації, зареєстровані у v1, і далі працюють (їхні тіла зазнають того самого перейменування атрибутів у snake_case, що й усе інше на цій сторінці), тепер вони також відповідають на запити всередині результатів у стилі 2026 (нижче) і виконуються паралельно, а не по одному. `ClientSession` досі лежить під сподом для тих, кому потрібна низькорівнева поверхня, і `client.session` її віддає; вона теж змінилася (працює на новому рушії диспетчера, і деякі її власні сигнатури змінилися), тож прочитайте **[Посібник з міграції](migration.md#clientsession-now-runs-on-jsonrpcdispatcher-basesession-removed)**, перш ніж спускатися нижче.
**[Клієнт](client/index.md)** знайомить із ним, **[Транспорти клієнта](client/transports.md)** описує три форми під'єднання, **[Колбеки клієнта](client/callbacks.md)** — самі колбеки, а **[Тестування](get-started/testing.md)** показує шаблон роботи в пам'яті, що замінює допоміжну функцію `create_connected_server_and_client_session()` з v1.
**[Клієнт](client/index.md)** знайомить із ним, **[Транспорти клієнта](client/transports.md)** описує чотири форми під'єднання, **[Колбеки клієнта](client/callbacks.md)** — самі колбеки, а **[Тестування](get-started/testing.md)** показує шаблон роботи в пам'яті, що замінює допоміжну функцію `create_connected_server_and_client_session()` з v1.
### Низькорівневий `Server` перебудовано, а не перейменовано {#the-low-level-server-was-rebuilt-not-renamed}
@ -134,7 +134,7 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[types.ContentB
Перейменування заявляють про себе самі. А оце — ні:
* **Синхронні функції виконуються в робочому потоці.** Інструмент, оголошений через `def` (або ресурс, промпт чи резолвер), більше не блокує цикл подій; плата за це — його тіло більше не виконується *в* потоці циклу подій, що важливо для коду, прив'язаного до потоку. Обробники `async def` не зачеплено. **[Посібник з міграції](migration.md#sync-handler-functions-now-run-on-a-worker-thread)**.
* **`MCPError` (`McpError` у v1), викинутий усередині інструмента, тепер є помилкою протоколу.** Модель його ніколи не бачить. Будь-який інший виняток, як і раніше, стає результатом з `is_error=True`, який модель може прочитати й на який може відреагувати. Розмежування — на сторінці **[Обробка помилок](servers/handling-errors.md)**.
* **`MCPError` (`McpError` у v1), викинутий усередині інструмента, тепер є помилкою протоколу.** Модель його ніколи не бачить. Будь-який інший виняток, як і раніше, стає результатом з `is_error=True`, але до моделі доходить лише повідомлення `ToolError`: будь-який інший виняток тепер читається як `Error executing tool <name>`, а трасування стека лишається в лозі вашого сервера. Розмежування — на сторінці **[Обробка помилок](servers/handling-errors.md)**.
* **Результати перевіряються перед відправленням.** Зібраний вручну `Tool`, у якого `input_schema` дорівнює `{}`, тепер провалює `tools/list` (специфікація вимагає `"type": "object"`). Сервери, побудовані на `@mcp.tool()`, цього ніколи не бачать: їхні схеми пише SDK.
* **Ваш клієнт перевіряє те, що отримує.** `list_tools()` і `call_tool()` звіряють відповідь сервера з узгодженою версією протоколу, тож не зовсім валідний сервер, який поблажливий розбір v1 терпів, тепер викидає `pydantic.ValidationError`. Якщо ви під'єднуєтеся до серверів, яких не контролюєте, готуйтеся бути тим, хто їх знайде; подробиці — у **[Посібнику з міграції](migration.md#client-validates-inbound-traffic-against-the-protocol-schema)**.
* **URI-шаблони тепер — справжній RFC 6570.** `{+path}`, `{?query}` та подібні працюють, зіставлення точне, а не приблизне за регулярним виразом, а обхід шляху у видобутих значеннях за замовчуванням відхиляється. Суворіші шаблони падають під час декорування, а не на першому запиті. **[URI-шаблони](servers/uri-templates.md)**.