docs: publish translated docs in twelve languages and the tool that maintains them (#3280)
This commit is contained in:
parent
14aa889992
commit
52ad0a8876
5 changed files with 89737 additions and 54 deletions
144
i18n/uk/pages/get-started/first-steps.md
Normal file
144
i18n/uk/pages/get-started/first-steps.md
Normal 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)**.
|
||||
53
i18n/uk/pages/get-started/index.md
Normal file
53
i18n/uk/pages/get-started/index.md
Normal 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)**.
|
||||
47
i18n/uk/pages/get-started/installation.md
Normal file
47
i18n/uk/pages/get-started/installation.md
Normal 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/) для охайніших логів сервера.
|
||||
184
i18n/uk/pages/get-started/real-host.md
Normal file
184
i18n/uk/pages/get-started/real-host.md
Normal 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)**.
|
||||
114
i18n/uk/pages/get-started/testing.md
Normal file
114
i18n/uk/pages/get-started/testing.md
Normal 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)**.
|
||||
Loading…
Reference in a new issue