docs: publish translated docs in twelve languages and the tool that maintains them (#3280)
This commit is contained in:
parent
14aa889992
commit
52ad0a8876
1 changed files with 89737 additions and 54 deletions
123
i18n/ru/pages/advanced/apps.md
Normal file
123
i18n/ru/pages/advanced/apps.md
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
---
|
||||
translation:
|
||||
sections: [0355618e5f4d5fe4, 1821eaf50f2d0b64, 82e0b28ebd3abf5a, 8ac39614c094f2d0, dab6ff945501ab2a, bd5565c3b2d4f959, 96819ce3d63a0487]
|
||||
tool: 1
|
||||
---
|
||||
# MCP Apps {#mcp-apps}
|
||||
|
||||
**MCP App** — это инструмент с собственным лицом: помимо данных, инструмент указывает на HTML-документ, который хост отображает как интерактивную поверхность.
|
||||
|
||||
Две части, всегда две:
|
||||
|
||||
1. **Инструмент**, который делает работу и возвращает данные, как любой другой инструмент.
|
||||
2. **Ресурс `ui://`** с HTML, который хост показывает для этого инструмента.
|
||||
|
||||
Инструмент несёт ссылку на ресурс в `_meta.ui.resourceUri`. Хост получает его через `resources/read`, отображает в **изолированном iframe** (песочнице) и передаёт результат инструмента в этот iframe через `postMessage`. Ваш сервер никогда не отправляет и не принимает сообщений `ui/*`: этот трафик идёт между хостом и iframe. Вы отдаёте инструмент и HTML-документ, а всё представление устраивает хост.
|
||||
|
||||
В SDK это встроенное расширение `Apps` (`io.modelcontextprotocol/ui`). Если [расширения](extensions.md) вам в новинку, сначала пробегите ту страницу. Одна минута — и возвращайтесь.
|
||||
|
||||
## Часы с циферблатом {#a-clock-with-a-face}
|
||||
|
||||
```python title="server.py" hl_lines="19 22 30 32"
|
||||
--8<-- "docs_src/apps/tutorial001.py"
|
||||
```
|
||||
|
||||
Четыре шага:
|
||||
|
||||
* `Apps()`: один экземпляр хранит инструменты, привязанные к UI, и их ресурсы.
|
||||
* `@apps.tool(resource_uri="ui://clock/app.html")`: обычный инструмент плюс отметка `_meta.ui.resourceUri`. Всё, что принимает `@mcp.tool()` (name, title, description, ...), передаётся дальше.
|
||||
* `apps.add_html_resource("ui://clock/app.html", CLOCK_HTML)`: парный ресурс, который отдаётся как `text/html;profile=mcp-app`. Именно этот MIME-тип говорит хосту: «это приложение, отобрази его».
|
||||
* `MCPServer("clock", extensions=[apps])`: подключение расширения. Теперь сервер объявляет `io.modelcontextprotocol/ui` в `capabilities.extensions`.
|
||||
|
||||
Сам HTML слушает `postMessage` от хоста и показывает результат. В настоящих приложениях используйте внутри HTML официальный браузерный SDK [`@modelcontextprotocol/ext-apps`](https://github.com/modelcontextprotocol/ext-apps). Он даёт `ontoolresult`, `callServerTool`, `getHostContext` и `onhostcontextchanged` вместо сырых событий сообщений.
|
||||
|
||||
## Корректная деградация {#graceful-degradation}
|
||||
|
||||
Не каждый клиент отображает приложения. Спецификация прямо говорит, что это значит для вас:
|
||||
|
||||
> Инструменты **ДОЛЖНЫ** возвращать осмысленный массив `content`, даже когда UI доступен.
|
||||
|
||||
Модель читает `content`; iframe — для людей. Хост с поддержкой UI всё равно передаёт текстовый результат модели, а чисто текстовый клиент получает *только* его. Поэтому канонический паттерн — один инструмент, два ответа. Взгляните на `get_time` ещё раз:
|
||||
|
||||
```python title="server.py" hl_lines="23-27"
|
||||
--8<-- "docs_src/apps/tutorial001.py"
|
||||
```
|
||||
|
||||
`client_supports_apps(ctx)` возвращает `True`, только когда клиент объявил расширение `io.modelcontextprotocol/ui` **и** указал `text/html;profile=mcp-app` в настройке `mimeTypes`. Поле обязательное, так что клиент, который его опустил, не считается. Именно это объявляет `main()` в том же файле: клиентскую половину согласования — и в ответ приходит развёрнутый результат.
|
||||
|
||||
!!! warning
|
||||
Никогда не возвращайте заглушку вроде `"[Rendered UI]"` в качестве единственного содержимого. Если запасной текст бесполезен, инструмент бесполезен для любого текстового клиента и для самой модели. Напишите нормальное предложение.
|
||||
|
||||
## Ограничение iframe {#locking-the-iframe-down}
|
||||
|
||||
Метаданные безопасности несёт ресурс: что iframe может загружать, какие разрешения браузера ему нужны, как его хотелось бы встроить:
|
||||
|
||||
```python title="server.py" hl_lines="9 19-22"
|
||||
--8<-- "docs_src/apps/tutorial002.py"
|
||||
```
|
||||
|
||||
`csp` и `permissions` — это **просьбы к хосту**, а не поведение сервера. Хост строит по ним Content-Security-Policy и Permissions-Policy для iframe и может отказать. Проверяйте наличие возможности в своём JS, а не рассчитывайте, что разрешение выдано.
|
||||
|
||||
`ResourceCsp`, поле за полем (имя в Python, ключ в передаваемых данных, что с ним делает хост):
|
||||
|
||||
| Python | В передаваемых данных (`_meta.ui.csp`) | Что контролирует |
|
||||
|---|---|---|
|
||||
| `connect_domains` | `connectDomains` | `connect-src`: куда могут обращаться `fetch`/XHR |
|
||||
| `resource_domains` | `resourceDomains` | `img-src`, `style-src`, ...: статические ресурсы |
|
||||
| `frame_domains` | `frameDomains` | `frame-src`: вложенные iframe |
|
||||
| `base_uri_domains` | `baseUriDomains` | `base-uri`: на что может указывать `<base>` |
|
||||
|
||||
`ResourcePermissions`: каждое поле запрашивает для iframe разрешение браузера.
|
||||
|
||||
| Python | В передаваемых данных (`_meta.ui.permissions`) |
|
||||
|---|---|
|
||||
| `camera` | `camera` |
|
||||
| `microphone` | `microphone` |
|
||||
| `geolocation` | `geolocation` |
|
||||
| `clipboard_write` | `clipboardWrite` |
|
||||
|
||||
!!! note
|
||||
CSP и разрешения живут на **ресурсе**, никогда не на инструменте. В метаданных инструмента по спецификации для них нет места, и хосты их там игнорируют. SDK делает эту ошибку невозможной в принципе: у `@apps.tool()` просто нет параметра `csp`.
|
||||
|
||||
### Видимость {#visibility}
|
||||
|
||||
`visibility=["app"]` на инструменте говорит: «это существует для iframe, а не для модели»:
|
||||
|
||||
* `"model"`: модель может его вызывать.
|
||||
* `"app"`: iframe может его вызывать (через `callServerTool`).
|
||||
* Не указано: и то и другое, это значение по умолчанию.
|
||||
|
||||
Фильтрация — задача **хоста**. Сервер перечисляет инструменты только для приложения в `tools/list`, как и любые другие; хост скрывает их от модели. Не фильтруйте на стороне сервера.
|
||||
|
||||
## Правила, за которыми следит SDK {#the-rules-the-sdk-enforces}
|
||||
|
||||
Всё это падает при запуске, а не в рабочей среде:
|
||||
|
||||
* `resource_uri` или URI ресурса, не начинающийся с `ui://...`, — это `ValueError` в момент декорирования или регистрации.
|
||||
* Инструмент, привязанный к URI **без соответствующего зарегистрированного ресурса**, — это `ValueError`, когда `MCPServer(extensions=[apps])` обрабатывает расширение. Инструмент, объявляющий HTML, который отдаёт 404 на `resources/read`, — это ошибка конфигурации, поэтому сервер отказывается создаваться.
|
||||
* `meta={"ui": ...}` на `@apps.tool()` — это `ValueError`. Ключом `_meta["ui"]` владеет декоратор; выражайте это через `resource_uri=` и `visibility=`. Остальные ключи `meta=` спокойно объединяются рядом.
|
||||
|
||||
Ни TypeScript SDK ext-apps, ни FastMCP сегодня ничего из этого не ловят; лучше узнать об этом раньше, чем узнает хост.
|
||||
|
||||
## Не только встроенный HTML {#beyond-inline-html}
|
||||
|
||||
`add_html_resource` покрывает типичный случай: строку HTML. Для всего остального — HTML на диске или генерируемого содержимого — постройте ресурс сами и передайте его:
|
||||
|
||||
```python title="server.py" hl_lines="12 18"
|
||||
--8<-- "docs_src/apps/tutorial003.py"
|
||||
```
|
||||
|
||||
`add_resource` подставляет MIME-тип `text/html;profile=mcp-app`, когда ресурс не задаёт его явно, и отклоняет явное несоответствие: ресурс `ui://` с любым другим MIME-типом не отобразит ни один хост.
|
||||
|
||||
!!! tip
|
||||
Ориентируетесь на хост, выпущенный до GA, который всё ещё читает устаревший плоский ключ `_meta["ui/resourceUri"]`? Добавьте его сами:
|
||||
`@apps.tool(resource_uri="ui://x", meta={"ui/resourceUri": "ui://x"})`.
|
||||
Вложенный объект `ui` — это форма по спецификации; плоский ключ доживает последние дни.
|
||||
|
||||
## Запуск примера {#see-it-run}
|
||||
|
||||
Сценарий `apps` в `examples/stories/` — это эта страница в виде готовой к запуску пары: сервер с привязанным к UI инструментом-часами и клиент, который согласовывает Apps, читает `_meta.ui.resourceUri` инструмента, получает HTML и вызывает инструмент.
|
||||
|
||||
```bash
|
||||
uv run python -m stories.apps.client
|
||||
```
|
||||
Loading…
Reference in a new issue