docs: publish translated docs in twelve languages and the tool that maintains them (#3280)

This commit is contained in:
Max 2026-08-14 17:07:34 +01:00 • committed by GitHub
commit 52ad0a8876
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 89737 additions and 54 deletions

View file

@ -0,0 +1,144 @@
---
translation:
sections: [0d6c05bcbf836bf3, 59a7b14eeefc68c1, 7114d8d6daba203f, e8bbb56a98ba7bc9, 5138010f6159901c, f78da7c7c363d4c6, 220a939cab348686]
tool: 1
---
# Перші кроки {#first-steps}
**[Головна сторінка](../index.md)** рухається швидко: написати сервер, запустити його, викликати інструмент.
Ця сторінка йде повільніше: усі три речі, які може надавати сервер, і назва для всього, що трапиться дорогою.
## Хост, клієнт і сервер {#host-client-and-server}
Три слова, які ви бачитимете на кожній сторінці відтепер:
* **Хост** — це LLM-застосунок: Claude, IDE, середовище виконання агентів. Це те, з чим говорить користувач.
* **Клієнт** живе всередині хоста й говорить мовою MCP. Хост запускає по одному клієнту на кожен сервер, до якого під'єднаний.
* **Сервер** — це те, що ви створюєте за допомогою цього SDK. Він надає речі клієнтам. Він ніколи не говорить із моделлю напряму.
Ви пишете сервер. Хости — це чийсь інший продукт. SDK також дає вам `Client`. Ним ви тестуватимете свої сервери, і він з'явиться далі на цій сторінці.
## Три примітиви {#the-three-primitives}
Сервер надає рівно три види речей. Відрізняє їх те, **хто вирішує їх використати**:
| Примітив | Хто керує | Що це таке | Приклад |
|---------------|-----------------|-----------------------------------------------------------------|------------------------------------|
| **Інструменти** | Модель | Функція, яку модель викликає, щоб виконати дію | Виклик API, запис у базу даних |
| **Ресурси** | Застосунок | Дані, які хост завантажує в контекст моделі | Вміст файлу, відповідь API |
| **Промпти** | Користувач | Багаторазовий шаблон повідомлення, який користувач викликає за назвою | Слеш-команда, пункт меню |
«Хто керує» — у цьому й увесь сенс поділу. Інструмент запускається, бо **модель** вирішила його викликати. Ресурс долучається, бо **застосунок** вирішив, що він потрібен моделі. Промпт запускається, бо його обрав **користувач**.
!!! info
Якщо ви вже створювали веб-API, більша частина інтуїції у вас є: **ресурс** — це `GET`
(завантажує дані й нічого не змінює), а **інструмент** — це `POST` (виконує роботу й може мати
побічні ефекти). **Промпт** не має HTTP-аналога; він ближчий до збереженого запиту, який
користувач запускає за назвою.
## Один сервер, усі три {#one-server-all-three}
```python title="server.py" hl_lines="6 12 18"
--8<-- "docs_src/first_steps/tutorial001.py"
```
Три звичайні функції, три декоратори. Кожен декоратор — це вся реєстрація:
* `@mcp.tool()` робить `add` **інструментом**.
* `@mcp.resource("greeting://{name}")` робить `greeting` **шаблоном ресурсу**: `{name}` в URI — це параметр функції.
* `@mcp.prompt()` робить `summarize` **промптом**. Рядок, який він повертає, стає повідомленням користувача.
Усе інше (назву, опис, схему аргументів) SDK зчитує із самої функції: її назви, докстрингу, анотацій типів. Ви нічого з цього не оголошували окремо.
!!! tip
Дві половини SDK мають два шляхи імпорту: `from mcp import Client` і
`from mcp.server import MCPServer`. Шляху `from mcp import MCPServer` не існує.
### Спробуйте самі {#try-it}
Запустіть його за допомогою MCP Inspector:
```console
uv run mcp dev server.py
```
Відкрийте URL, який він надрукує. В Inspector є по одній вкладці на кожен примітив; пройдіть їх по черзі.
**Tools.** Один запис: `add` з описом *Add two numbers.* Форма має обов'язкове цілочислове поле для `a` і ще одне для `b`. Заповніть їх, викличте інструмент, і результатом буде `3`. Inspector побудував цю форму з `a: int, b: int`. Так само робить і будь-який інший клієнт.
**Resources.** Список *Resources* порожній. `greeting` розташований у **Resource Templates**, бо `greeting://{name}` має параметр: немає жодного конкретного ресурсу, який можна показати в списку, поки хтось не вкаже `name`. Передайте `World` і прочитайте:
```text
Hello, World!
```
**Prompts.** Один запис: `summarize` з єдиним обов'язковим аргументом `text`. Отримайте його з якимось текстом — і повернеться одне повідомлення з `role: user` та вашим відрендереним рядком як вмістом. Оце й увесь промпт: функція, яка будує повідомлення.
Inspector запустив ваш сервер через **stdio**, один із транспортів, якими може говорити MCP-сервер. Поки що обирати транспорт не потрібно; для цього є сторінка **[Запуск сервера](../run/index.md)**.
## Можливості {#capabilities}
В Inspector ви бачили три вкладки. Звідки він знав, що їх три?
Коли клієнт під'єднується, сервер оголошує свої **можливості**: на які сімейства запитів він відповідатиме. Клієнт використовує це оголошення, щоб вирішити, про що взагалі просити. Ви його не писали; `MCPServer` оголошує його за вас.
Подивіться самі. `Client` з SDK приймає об'єкт сервера напряму й під'єднується до нього **в пам'яті** (без підпроцесу, без порту):
```python
import asyncio
from mcp import Client
from server import mcp
async def main() -> None:
async with Client(mcp) as client:
print(client.server_capabilities.model_dump(exclude_none=True))
asyncio.run(main())
```
```text
{'prompts': {'list_changed': True}, 'resources': {'subscribe': True, 'list_changed': True}, 'tools': {'list_changed': True}}
```
Цей словник — оголошені **можливості** вашого сервера. Це перше, про що дізнається кожен клієнт, що під'єднується:
| Можливість | Клієнт тепер може викликати |
|-------------|------------------------------------------------------------|
| `tools` | `tools/list`, `tools/call` |
| `resources` | `resources/list`, `resources/templates/list`, `resources/read` |
| `prompts` | `prompts/list`, `prompts/get` |
`MCPServer` обслуговує всі три примітиви, тож усі три завжди оголошено.
Зверніть увагу на те, чого там немає. `completions` (автодоповнення аргументів для шаблонів ресурсів і промптів) потребує обробника, який пишете ви; у цього сервера його немає, тож можливість відсутня, і коректний клієнт про неї не проситиме. Це правило для всього необов'язкового: зареєструйте річ — і можливість з'явиться; **[Автодоповнення](../servers/completions.md)** це доводить.
!!! info
`Client(mcp)` — той самий клієнт у пам'яті, яким протестовано кожен приклад у цій документації, і
саме ним ви тестуватимете свої. Йому присвячено цілу сторінку: **[Тестування](testing.md)**.
## Чого ви не писали {#what-you-did-not-write}
Озирніться на цю сторінку. Ви написали три невеликі функції Python. Ви **не** писали:
* JSON Schema. `a: int, b: int` *і є* схема для `add`.
* Обробник запитів. `tools/list`, `resources/read`, `prompts/get`: усе обслуговується за вас.
* Оголошення можливостей. `MCPServer` зробив його за вас.
* Жодного рядка протоколу. Узгодження версії, обрамлення JSON-RPC, обмін можливостями: усе це відбулося всередині `mcp dev` і `Client(mcp)`, і ви цього не бачили.
У цьому співвідношенні й увесь сенс SDK.
## Підсумки {#recap}
* **Хост** — це LLM-застосунок, **клієнт** — його половина, що говорить мовою MCP, **сервер** — те, що ви створюєте.
* Інструментами керує **модель**, ресурсами — **застосунок**, промптами — **користувач**.
* Один декоратор на примітив: `@mcp.tool()`, `@mcp.resource(uri)`, `@mcp.prompt()`. Назва, опис і схема беруться з функції.
* URI з `{param}` створює **шаблон** ресурсу, який показується окремо від конкретних ресурсів.
* **Можливості** сервера оголошуються за вас, а клієнт просить лише те, що сервер оголосив.
* `Client(mcp)` під'єднується до об'єкта сервера в пам'яті: ваш тестовий стенд із першого дня.
Далі — **[Під'єднання до справжнього хоста](real-host.md)**: цей сервер усередині Claude Desktop або IDE, по-справжньому. Потім **[Тестування](testing.md)**: одна сторінка, один клієнт у пам'яті — і більше ніколи не доведеться гадати, чи воно працює. Після цього кожен примітив отримує власну сторінку, починаючи з того, яким керує модель: **[Інструменти](../servers/tools.md)**.

View file

@ -0,0 +1,53 @@
---
translation:
sections: [ed4a756b4c53c585, 97e2fb315b7fe398, 4d04f1c6f4bf6c1d, 577d73078fc62baf]
tool: 1
---
# Початок роботи {#get-started}
Уперше маєте справу з MCP або з цим SDK? Почніть звідси. Ці сторінки проведуть від нуля до робочого, протестованого сервера: [встановіть SDK](installation.md), напишіть свій [перший сервер](first-steps.md), [під'єднайте його до справжнього хоста](real-host.md) і [протестуйте](testing.md) за допомогою клієнта в пам'яті.
## Запуск коду {#run-the-code}
Усі блоки коду можна копіювати й використовувати як є: це повні робочі файли.
Щоб іти слідом за викладом, вставте блок у файл `server.py` і відкрийте його в MCP Inspector:
```console
uv run mcp dev server.py
```
**НАПОЛЕГЛИВО радимо** набрати (або скопіювати) код, відредагувати його й запустити локально. Саме робота у власному редакторі показує суть: як мало доводиться писати, автодоповнення, перевірка типів, що ловить помилки ще до запуску.
## Гадати не доведеться {#you-will-not-be-guessing}
Кожен приклад у цій документації — повний файл у каталозі [`docs_src/`](https://github.com/modelcontextprotocol/python-sdk/tree/main/docs_src) репозиторію самого SDK, і кожен із них проганяється тестовим набором SDK через **клієнт у пам'яті**:
```python
import pytest
from mcp import Client
from server import mcp
@pytest.mark.anyio
async def test_add() -> None:
async with Client(mcp) as client:
result = await client.call_tool("add", {"a": 1, "b": 2})
assert result.structured_content == {"result": 3}
```
Ні підпроцесу, ні порту, ні транспорту. `Client(mcp)` під'єднується безпосередньо до об'єкта сервера.
Якщо зміна в SDK зламає приклад на одній із цих сторінок, CI почервоніє раніше, ніж сторінка. Код, який ви тут читаєте, — це код, який виконується.
Ви самі скористаєтеся цим на сторінці [Тестування](testing.md): власні сервери тестують так само.
## Куди далі {#where-to-go-next}
Щойно сервер запрацює, решта цієї документації — довідник, а не курс. Кожна сторінка самодостатня, тож переходьте одразу до потрібного:
* Те, що сервер надає назовні (інструменти, ресурси, промпти), — це **[Сервери](../servers/index.md)**.
* Те, що доступне всередині функцій, які ви реєструєте, — це **[Усередині обробника](../handlers/index.md)**.
* Як донести сервер до клієнтів (stdio, HTTP, ваш наявний FastAPI-застосунок) — це **[Запуск сервера](../run/index.md)**.
* Побудова іншої сторони — застосунку, що *використовує* MCP-сервери, — це **[Клієнти](../client/index.md)**.

View file

@ -0,0 +1,47 @@
---
translation:
sections: [6e2f9bab94d5ed36, 8cf653388f69e28b, 6fd9ea2f65de0df6]
tool: 1
---
# Встановлення {#installation}
Python SDK опубліковано на PyPI як [`mcp`](https://pypi.org/project/mcp/). Потрібен **Python 3.10+**.
Ця документація описує **v2** — поточну стабільну лінійку випусків:
=== "uv"
```bash
uv add "mcp[cli]"
```
=== "pip"
```bash
pip install "mcp[cli]"
```
!!! note "Переходите з v1?"
v2 — мажорна версія з несумісними змінами; **[Посібник з міграції](../migration.md)**
описує кожну з них. Якщо ваш *пакет* залежить від `mcp` і ще не готовий до міграції, залиште
верхню межу `<2` (наприклад, `mcp>=1.28,<2`), щоб розв'язання залежностей без фіксованої версії залишалося на лінійці 1.x.
## Що встановлюється {#what-gets-installed}
Щоб користуватися SDK, нічого з цього знати не потрібно, але якщо цікаво, навіщо кожна залежність:
* `mcp-types`: усі типи протоколу (запити, результати, блоки вмісту) окремим пакетом, версія якого йде в ногу з SDK. Код, що залежить від `mcp`, імпортує його через псевдонім `mcp.types` (кожне `from mcp.types import ...` у цій документації); імпортуйте `mcp_types` напряму лише в проєкті, який встановлює `mcp-types` без SDK.
* [`anyio`](https://anyio.readthedocs.io/): асинхронне середовище виконання. Увесь SDK написано поверх anyio, тож він працює і на `asyncio`, і на `trio`.
* [`pydantic`](https://docs.pydantic.dev/): основа кожної моделі в `mcp.types`, а також уся генерація схем і валідація.
* [`httpx2`](https://pypi.org/project/httpx2/): HTTP-клієнт, на якому працюють *клієнтські* транспорти Streamable HTTP і SSE, із вбудованою підтримкою server-sent events.
* [`starlette`](https://www.starlette.io/), [`uvicorn`](https://www.uvicorn.org/), [`sse-starlette`](https://pypi.org/project/sse-starlette/) і [`python-multipart`](https://pypi.org/project/python-multipart/): *серверні* HTTP-транспорти.
* [`jsonschema`](https://pypi.org/project/jsonschema/): перевіряє структурований вивід інструмента на відповідність оголошеній схемі виводу.
* [`pyjwt[crypto]`](https://pyjwt.readthedocs.io/): робота з OAuth-токенами для авторизації.
* [`opentelemetry-api`](https://opentelemetry-python.readthedocs.io/): лише легкий API, тож middleware трасування в SDK нічого не коштує, доки ви самі не встановите OpenTelemetry SDK і експортер.
* [`typing-extensions`](https://typing-extensions.readthedocs.io/) і [`typing-inspection`](https://pypi.org/project/typing-inspection/): сучасні можливості типізації на Python 3.10.
* [`pywin32`](https://pypi.org/project/pywin32/): лише для Windows, використовується для керування підпроцесами `stdio`.
## Необов'язкові доповнення {#optional-extras}
* `mcp[cli]` додає [`typer`](https://typer.tiangolo.com/) і [`python-dotenv`](https://pypi.org/project/python-dotenv/) для інструмента командного рядка `mcp` (`mcp dev`, `mcp run`, `mcp install`). Під час розробки він знадобиться; на розгорнутому сервері може бути зайвим.
* `mcp[rich]` додає [`rich`](https://rich.readthedocs.io/) для охайніших логів сервера.

View file

@ -0,0 +1,184 @@
---
translation:
sections: [3c4f2f06b4e978b6, 22520eecae3d1961, f4e1709db18d635a, 2eb57992049671d9, 1ba83e9af37cc1b4, 4822586344b08d9e, 1c93afef72478992, b6b448f9eddd51dc, fe55370fd931815b]
tool: 1
---
# Підключення до справжнього хоста {#connect-to-a-real-host}
**Хост** — це застосунок, усередині якого зрештою опиняється ваш сервер: Claude Desktop, Claude Code, IDE. Саме з хостом говорить користувач. Усередині нього MCP-**клієнт** запускає ваш сервер як дочірній процес і спілкується з ним через stdin і stdout цього процесу.
Отже, підключення до хоста — це одна дія: ви повідомляєте йому **команду, яка запускає ваш сервер**. Усе на цій сторінці (дві команди CLI, три JSON-файли) — це різні місця, куди вписати ту саму команду.
## Один сервер, усі хости {#one-server-every-host}
```python title="server.py" hl_lines="3 33-34"
--8<-- "docs_src/real_host/tutorial001.py"
```
Два інструменти й один ресурс в одному файлі. Для кожного хоста нижче в цьому файлі важливі три речі:
* `mcp.run()` без аргументів запускає **stdio**-сервер: він блокує виконання, читає повідомлення протоколу зі stdin і пише їх у stdout. Саме цим транспортом говорить кожен хост на цій сторінці. Хост запускає ваш файл як дочірній процес і володіє цими двома каналами, тому підключення завжди зводиться до «ось команда». Порт обирати не потрібно, і ніщо на порту не слухає.
* `run()` стоїть під `if __name__ == "__main__":`. Усе, що нижче, **імпортує** цей файл, а не виконує його, тож незахищений `run()` запустив би сервер, щойно будь-що завантажить модуль.
* Об'єкт сервера — глобальна змінна рівня модуля з іменем `mcp`. Саме це ім'я шукає `mcp run` (`server` і `app` теж підходять). Назвете інакше — вкажіть ім'я явно: `mcp run server.py:bookshop`.
Це останній рядок Python на цій сторінці. Далі — лише налаштування хостів.
## Команда запуску {#the-launch-command}
Кожен хост нижче отримує ту саму команду:
```bash
uv run --with "mcp[cli]" mcp run /absolute/path/to/server.py
```
Одна команда для всіх, бо `uv run --with` розв'язує SDK у свіже середовище на місці: вона працює з будь-якого каталогу й не потребує ні проєкту, ні віртуального середовища, яке треба активувати. Тут це важливіше, ніж деінде, бо хост запускає ваш сервер зі *свого* робочого каталогу з майже порожнім середовищем, а не з вашої оболонки.
Це також команда, яку `mcp install` записує за вас у конфігурацію Claude Desktop (нижче), тож те, що ви набираєте вручну, і те, що генерує утиліта, збігаються — за винятком точної фіксації версії, яку додає утиліта.
!!! tip "Якщо хост не може знайти `uv`"
Хост породжує ваш сервер із мінімальним `PATH`, і `uv` у ньому може не бути. Замініть
просто `uv` абсолютним шляхом з `which uv` (macOS/Linux) або `where uv` (Windows). Саме це
й записує `mcp install`.
!!! note "Ця сторінка — про локальний сценарій"
Усе тут запускає ваш сервер на тій самій машині, де працює хост: хост запускає ваш
файл через stdio. Для особистого інструмента або інструмента на одній машині це саме те,
що треба. Щоб дати сервер людям, у яких *немає* вашого файлу, роздають **URL**, а не
команду: той самий об'єкт `mcp`, що обслуговується через Streamable HTTP. **[Запуск сервера](../run/index.md)**
зводить це рішення до однієї таблиці, а **[Розгортання й масштабування](../run/deploy.md)** —
це шлях звідти до справжнього імені хоста.
А хост — це не більше ніж застосунок з MCP-клієнтом усередині, тож роль хоста може
зіграти й ваш власний Python: сторінка **[Транспорти клієнта](../client/transports.md)** запускає
цей самий файл як підпроцес через `stdio_client(...)`, а **[Тестування](testing.md)**
підключається до нього в пам'яті взагалі без процесу.
## Claude Desktop {#claude-desktop}
Єдиний хост, який SDK може налаштувати за вас:
```bash
uv run mcp install server.py
```
От і все. `mcp install` імпортує файл, щоб прочитати ім'я сервера, знаходить файл конфігурації Claude Desktop і записує в нього команду запуску. Дорогою вона перетворює ваш шлях на абсолютний, тож робити це самим не потрібно.
Жодної магії тут немає. Ось запис, який вона створює:
```json
{
"mcpServers": {
"Bookshop": {
"command": "/absolute/path/to/uv",
"args": [
"run",
"--frozen",
"--with",
"mcp[cli]==2.0.0",
"mcp",
"run",
"/absolute/path/to/server.py"
]
}
}
}
```
Це команда запуску з розділу вище з трьома доповненнями: абсолютний шлях до `uv`, `--frozen`, щоб `uv` ніколи не переписував lock-файл, який випадково опиниться поруч, і точна фіксація встановленої у вас версії `mcp`. Запис потрапляє в `claude_desktop_config.json`, який лежить тут:
* **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
* **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
Цей файл можна написати й вручну. `mcp install` існує, щоб ви не припустилися класичної помилки (відносного шляху), поки це робите.
Повністю завершіть Claude Desktop (а не лише закрийте вікно) і відкрийте знову.
!!! warning
`mcp install` завершується помилкою `Claude app not found`, якщо *каталогу* конфігурації
Claude Desktop ще немає. Встановіть Claude Desktop і запустіть його один раз: саме це й
створює каталог.
!!! tip
Claude Desktop запускає ваш сервер у власному процесі, тож змінних середовища вашої
оболонки там немає. `uv run mcp install server.py -v API_KEY=abc123` (або `-f .env`) записує
їх у поле `env` запису. `--name` перевизначає ім'я запису; за замовчуванням це `name` сервера.
## Claude Code {#claude-code}
Файлу для редагування немає. Зареєструйте сервер через CLI `claude`; усе після `--` — це команда запуску.
```bash
claude mcp add bookshop -- uv run --with "mcp[cli]" mcp run /absolute/path/to/server.py
```
Виконайте `/mcp` у сесії Claude Code, щоб переконатися, що `bookshop` підключено, а його інструменти перелічено.
## Cursor {#cursor}
Створіть `.cursor/mcp.json` у корені проєкту.
```json
{
"mcpServers": {
"bookshop": {
"command": "uv",
"args": ["run", "--with", "mcp[cli]", "mcp", "run", "/absolute/path/to/server.py"]
}
}
}
```
Ті самі `command` та `args` під тим самим ключем `mcpServers`, що й у Claude Desktop. Сервер з'являється в налаштуваннях MCP у Cursor з обома інструментами в списку.
## VS Code {#vs-code}
Створіть `.vscode/mcp.json` у корені проєкту.
```json
{
"servers": {
"bookshop": {
"type": "stdio",
"command": "uv",
"args": ["run", "--with", "mcp[cli]", "mcp", "run", "/absolute/path/to/server.py"]
}
}
}
```
Дві відмінності від файлу Cursor, і це єдині дві: ключ-обгортка — `servers`, а не `mcpServers`, і кожен запис оголошує свій `type`. Підтвердьте запит про довіру, і команда **MCP: List Servers** у палітрі команд покаже, що `bookshop` працює.
!!! note
Потрібен VS Code 1.99 або новіший із розширенням **GitHub Copilot**, у якому виконано вхід
(достатньо Copilot Free), а Copilot Chat має бути в режимі **Agent**, бо жоден інший режим
не викликає інструменти.
## Сервер не з'являється {#it-doesnt-show-up}
Перш ніж чіпати конфігурацію будь-якого хоста, виконайте команду запуску самі:
```bash
uv run --with "mcp[cli]" mcp run /absolute/path/to/server.py
```
Нічого не виводиться, і команда не завершується. Ця тиша правильна: stdio-сервер чекає, доки хост першим заговорить у stdin (`Ctrl-C`, щоб зупинити). Справжня помилка — це трасування стека або негайний вихід, і тепер її можна прочитати, а не вгадувати через хост.
Коли ця команда сидить і чекає, залишається майже завжди одне з трьох:
* **Відносний шлях.** Хост запускає ваш сервер зі *свого* робочого каталогу, а не з того, з якого ви його реєстрували. `server.py` там, де потрібен `/absolute/path/to/server.py`, — найпоширеніша причина збою. Якщо хост не знаходить ще й `uv`, цей шлях теж має бути абсолютним.
* **Хост досі працює зі старою конфігурацією.** Хости читають конфігурацію під час запуску. Зокрема Claude Desktop треба *повністю завершити* (а не лише закрити вікно) і відкрити знову, перш ніж зміна в `claude_desktop_config.json` набуде чинності.
* **Щось потрапило в stdout поза проміжком перенаправлення.** У stdio stdout — це *і є* протокол. Під час обслуговування SDK перенаправляє скинутий (flushed) сторонній вивід у stderr, але вивід, скинутий у stdout до того (скрипт-обгортка, що робить echo, `print()` на етапі імпорту в небуферизованому процесі), або буферизований `print()`, що зливається під час завершення інтерпретатора, передає хосту зіпсоване повідомлення, і той розриває з'єднання. Пишіть логи зі стандартною конфігурацією `logging`, чий обробник stderr скидає кожен запис; власні обробники теж мають уникати stdout. Докладніше — на сторінці **[Логування](../handlers/logging.md)**.
Claude Desktop веде окремий лог для кожного сервера: `mcp-server-<NAME>.log` — це stderr вашого сервера, поруч із `mcp.log` для з'єднань, у `~/Library/Logs/Claude` на macOS і `%APPDATA%\Claude\logs` на Windows.
Для всього, що виходить за межі цих трьох випадків, є сторінка **[Усунення несправностей](../troubleshooting.md)**.
## Підсумки {#recap}
* **Хост** (Claude Desktop, IDE) виконує MCP-клієнт, який запускає ваш сервер як дочірній процес через stdio. Підключитися означає дати йому одну команду запуску.
* Ця команда — `uv run --with "mcp[cli]" mcp run /absolute/path/to/server.py`: жодного venv для активації, працює з будь-якого каталогу.
* **Claude Desktop** — єдиний хост, який `mcp install` налаштовує за вас. Вона записує ту саму команду (плюс абсолютний шлях до `uv`, `--frozen` і точну фіксацію встановленої у вас версії) у `claude_desktop_config.json`, тож вам цього робити не доведеться.
* **Claude Code** — це `claude mcp add bookshop -- <launch command>`. **Cursor** — `.cursor/mcp.json` з ключем `mcpServers`. **VS Code** — `.vscode/mcp.json` з ключем `servers`, кожен запис із `type`.
* Скрізь абсолютні шляхи, перезапуск хоста після редагування конфігурації, і ніщо, крім SDK, ніколи не пише в stdout.
Кожен хост на цій сторінці підключився до того самого файлу тією самою командою. Про те, що цей файл може *надавати*, — решта цієї документації: **[Інструменти](../servers/tools.md)**, **[Ресурси](../servers/resources.md)** і всі транспорти, крім stdio, на сторінці **[Запуск сервера](../run/index.md)**.

View file

@ -0,0 +1,114 @@
---
translation:
sections: ['4926721070127497', c52a1de2b6b32f40, 2e410b412c25f314, 627195f7159e24ef]
tool: 1
---
# Тестування {#testing}
Python SDK містить клас `Client` із **транспортом у пам'яті**: передайте йому об'єкт сервера — і він під'єднається до нього напряму.
Жодного підпроцесу. Жодного порту. Узагалі жодного транспорту. Та сама ідея, що й `TestClient` у FastAPI.
## Базове використання {#basic-usage}
Припустімо, є простий сервер з одним інструментом:
```python title="server.py"
--8<-- "docs_src/testing/tutorial001.py"
```
Щоб запустити тест нижче, знадобляться дві додаткові залежності (для розробки):
=== "uv"
```bash
uv add --dev pytest inline-snapshot
```
=== "pip"
```bash
pip install pytest inline-snapshot
```
!!! info
Ця документація припускає, що ви вже знайомі з [`pytest`](https://docs.pytest.org/en/stable/).
[`inline-snapshot`](https://15r10nk.github.io/inline-snapshot/latest/) — це те, чим тест нижче
перевіряє весь об'єкт результату одним рядком. Він записує вивід тесту у вигляді
літерала `snapshot(...)`, який ви бачите. Якщо не хочете ним користуватися, приберіть імпорт і
перевіряйте потрібні поля (`result.content[0].text == "3"`), як у будь-якому іншому тесті.
Тепер сам тест:
```python title="test_server.py"
import pytest
from inline_snapshot import snapshot
from mcp import Client
from mcp.types import CallToolResult, TextContent
from server import mcp
@pytest.fixture
def anyio_backend(): # (1)!
return "asyncio"
@pytest.fixture
async def client(): # (2)!
async with Client(mcp, raise_exceptions=True) as c:
yield c
@pytest.mark.anyio
async def test_call_add_tool(client: Client):
result = await client.call_tool("add", {"a": 1, "b": 2})
# Drop the server identity stamp in `_meta`; it is not what this test is about.
result.meta = None
assert result == snapshot(
CallToolResult(
content=[TextContent(type="text", text="3")],
structured_content={"result": 3},
)
)
```
1. Якщо ви використовуєте `trio`, поверніть натомість `"trio"`. Подробиці — у [документації anyio](https://anyio.readthedocs.io/en/stable/testing.html#specifying-the-backends-to-run-on).
2. Фікстура віддає під'єднаного клієнта. Кожен тест, що приймає `client`, отримує нове з'єднання в пам'яті з тим самим сервером.
Готово! Тепер можна розширювати тести, щоб охопити більше сценаріїв.
## Навіщо `raise_exceptions=True`? {#why-raise_exceptionstrue}
Піти не так можуть дві різні речі, і цей прапорець стосується лише однієї з них.
Виняток усередині одного з **ваших інструментів** — це не збій протоколу. Він стає звичайним результатом
з `is_error=True`, і модель читає повідомлення. `raise_exceptions` цього не змінює: з ним чи
без нього `call_tool` повертає той самий результат з `is_error=True`. Про це є ціла сторінка:
**[Обробка помилок](../servers/handling-errors.md)**.
Збій **поза** тілом інструмента — інша річ. На з'єднанні, яке дає `Client(mcp)`, сервер
замінює його загальним `"Internal server error"`, перш ніж його побачить клієнт. Ніколи не слід
розкривати подробиці неочікуваного падіння віддаленій стороні, що викликає. У тесті це саме те,
чого ви *не* хочете, і саме це змінює `raise_exceptions=True`: тест бачить справжнє повідомлення
замість узагальненого.
Залишайте його ввімкненим у тестах. У робочому коді він не має сенсу.
## У тому самому процесі за замовчуванням {#in-process-by-default}
!!! note
`Client(mcp)` під'єднується в межах процесу й за замовчуванням **нейтральний щодо покоління**: він зондує сервер і
обирає відповідний шлях протоколу. Зафіксуйте `mode="legacy"`, якщо тест перевіряє семантику, специфічну для
старого покоління — push семплювання (sampling) чи еліцитації (elicitation), `message_handler`, — і приберіть там `raise_exceptions=True`:
з'єднання старого покоління взагалі нічого не узагальнює, а прапорець повторно викидає
збій усередині завдання сервера, а не у вашому тесті.
Цей один рядок — ще й причина, чому ця документація може обіцяти, що її приклади працюють: кожен
файл прикладу проганяється власним набором тестів SDK, майже всі — саме через цей
клієнт. Ви користуєтеся тим самим інструментом, яким SDK перевіряє сам себе.
У вас є робочий, протестований сервер. Як помістити його в справжній застосунок (Claude Desktop,
IDE) — на сторінці **[Під'єднання до справжнього хоста](real-host.md)**; усі інші способи його запустити —
у розділі **[Запуск сервера](../run/index.md)**.