python-sdk/i18n/uk/pages/client/transports.md

128 lines
11 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: [9cac816674181eb0, 0700f337babcd4dd, 2bde0dd58cdf00f5, 40b4916d82eaf1d4, 3d0832f39b0d7059, dfa4446556badef0, 5bd93be2ab2ecb9c]
tool: 1
---
# Транспорти клієнта {#client-transports}
Кожен `Client` спілкується зі своїм сервером через **транспорт** — те, що власне й переносить повідомлення.
Окремо його налаштовувати не доводиться. `Client` приймає один позиційний аргумент і визначає транспорт за його типом.
*Серверний* бік кожного з них (що робить `mcp.run()` і що ви розгортаєте) описано на сторінці **[Запуск сервера](../run/index.md)**.
## У пам'яті {#in-memory}
Передайте сам об'єкт сервера:
```python title="client.py" hl_lines="14"
--8<-- "docs_src/client_transports/tutorial001.py"
```
Жодного підпроцесу, жодного порту, жодних байтів у мережі. Клієнт і сервер — це два об'єкти в одному процесі, а виклик усе одно проходить через справжній протокольний рівень: `search_books` перелічується, валідується й викликається точнісінько так само, як це було б через HTTP.
Тож це одразу дві речі:
* **Тестовий стенд.** Кожен приклад у цій документації перевіряється саме так, а сторінка **[Тестування](../get-started/testing.md)** будує довкола цього весь підхід.
* **API для вбудовування.** Застосунку, який сам створює сервер, не потрібен мережевий перехід, щоб викликати його інструменти.
## Streamable HTTP {#streamable-http}
Передайте рядок з URL — і отримаєте **Streamable HTTP**, транспорт, за яким розгортають сервер:
```python title="client.py" hl_lines="5"
--8<-- "docs_src/client_transports/tutorial002.py"
```
Оце й увесь продакшен-клієнт. `Client` сам загортає URL у `streamable_http_client(...)` поверх `httpx2.AsyncClient`, налаштованого так, як потрібно MCP: `follow_redirects=True`, 30-секундний тайм-аут на connect/write/pool і 300-секундний тайм-аут на читання, бо сервер може тримати потік відповіді відкритим.
!!! check
Створений `Client` ще **не** під'єднаний. Конструктор лише обирає транспорт;
відкриває його `async with`. Спробуйте звернутися до з'єднання до входу в блок — і SDK про це скаже:
```text
RuntimeError: Client must be used within an async context manager
```
Коли ви написали `Client("http://...")`, нічого не розв'язувалося, не завантажувалося й не запускалося. Цей рядок нічого не коштує.
### Власний `httpx2.AsyncClient` {#bring-your-own-httpx2asyncclient}
Щойно знадобиться заголовок `Authorization`, cookie, проксі, mTLS чи інший тайм-аут — створіть `httpx2.AsyncClient` самостійно й передайте його в `streamable_http_client`:
```python title="client.py" hl_lines="8-14"
--8<-- "docs_src/client_transports/tutorial003.py"
```
Зверніть увагу на дві речі:
* `httpx2.AsyncClient` належить вам, тож саме **ви** входите в нього й виходите з нього. SDK ніколи не закриває клієнт, якого не створював.
* `streamable_http_client(url, http_client=...)` повертає транспорт, а `Client(transport)` приймає його, як і будь-що інше.
Одне зауваження щодо TLS: `httpx2` перевіряє сертифікати за сховищем довіри операційної системи (через
[`truststore`](https://pypi.org/project/truststore/)), а не за вбудованим списком CA. У середовищі
без придатного системного сховища CA (деякі мінімальні контейнери) задайте стандартні змінні середовища `SSL_CERT_FILE`/`SSL_CERT_DIR`
або передайте явний `verify=ssl_context` у свій `httpx2.AsyncClient`
(подробиці — у розділі
[`httpx` і `httpx-sse` замінено на `httpx2`](../migration.md#httpx-and-httpx-sse-replaced-by-httpx2)).
!!! warning
Раніше `streamable_http_client` приймав `headers=` і `timeout=` напряму. Більше ні:
його єдині параметри — `url`, `http_client` і `terminate_on_close`. Напишете `headers=`
за звичкою — і отримаєте:
```text
TypeError: streamable_http_client() got an unexpected keyword argument 'headers'
```
Усе, що стосується HTTP, тепер живе на тому єдиному `httpx2.AsyncClient`, який ви передаєте.
!!! info
`httpx2` зберігає знайомий API `httpx`, тож якщо ви знаєте `httpx`, то вже вмієте робити тут автентифікацію,
проксі, хуки подій, повторні спроби й обмеження з'єднань. SDK нічого не додає зверху й нічого не
забирає. Саме сюди під'єднується й OAuth:
`httpx2.AsyncClient(auth=OAuthClientProvider(...))`. Увесь цей процес описано на сторінці **[OAuth-клієнти](oauth-clients.md)**.
## stdio {#stdio}
Сервер **stdio** — це підпроцес. Клієнт запускає його, пише JSON-RPC в його stdin і читає JSON-RPC з його stdout. Саме так десктопний хост запускає сервер на вашій машині: хост і *є* цим кодом плюс UI, а сторінка **[Під'єднання до справжнього хоста](../get-started/real-host.md)** показує ті самі стосунки з боку хоста — як конфігураційний файл.
Опишіть процес за допомогою `StdioServerParameters` і передайте його в `Client`:
```python title="client.py" hl_lines="3-7 11"
--8<-- "docs_src/client_transports/tutorial004.py"
```
Вхід у блок запускає процес. Вихід із нього завершує підпроцес: закриває stdin, чекає, вбиває, якщо той затримується. Прибирати за ним самостійно ніколи не доведеться.
stderr дочірнього процесу йде у ваш. Щоб спрямувати його деінде, зберіть транспорт самостійно через `stdio_client` (з `mcp`) і передайте натомість його: `Client(stdio_client(server, errlog=log_file))`.
!!! warning
Дочірній процес **не** успадковує ваше середовище. Він отримує мінімальний список дозволених змінних (`HOME`, `LOGNAME`,
`PATH`, `SHELL`, `TERM` і `USER` на POSIX), щоб нічого чутливого не просочилося в процес,
який, можливо, писали не ви.
Сервер, якому потрібен ключ API, там його не знайде. Передайте його явно через `env=`; ці
змінні накладаються поверх списку дозволених. Саме це й робить `BOOKSHOP_API_KEY` вище.
## SSE {#sse}
`sse_client(url)` з `mcp.client.sse` — це HTTP-транспорт, на зміну якому прийшов Streamable HTTP. Загортайте його так само, `Client(sse_client("http://localhost:8000/sse"))`, щоб говорити із сервером, який досі ним користується, — і не будуйте на ньому нічого нового.
## Протокол `Transport` {#the-transport-protocol}
Для `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(StdioServerParameters(...))`. Загортайте його в `stdio_client(...)` самостійно лише для того, щоб перенаправити stderr дочірнього процесу.
* Підпроцес отримує середовище зі списку дозволених, а не ваше; `env=` його доповнює.
* Транспорт — це будь-що, з чим можна зробити `async with x as (read, write)`. Усе, що не є об'єктом сервера, URL чи `StdioServerParameters`, `Client` передає прямо цьому протоколу.
* Створення `Client` обирає транспорт. `async with` його відкриває.
Щойно транспорт відкрито, обидві сторони мають домовитися про версію протоколу. Зазвичай про це й не згадуєте; а коли все ж доведеться — є сторінка **[Версії протоколу](../protocol-versions.md)**.