mlspace-plugin 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. mlspace_mcp/__init__.py +3 -0
  2. mlspace_mcp/__main__.py +125 -0
  3. mlspace_mcp/_status.py +18 -0
  4. mlspace_mcp/auth.py +109 -0
  5. mlspace_mcp/bodyspec.py +184 -0
  6. mlspace_mcp/client.py +170 -0
  7. mlspace_mcp/client_setup.py +235 -0
  8. mlspace_mcp/config.py +283 -0
  9. mlspace_mcp/errors.py +241 -0
  10. mlspace_mcp/formatting.py +502 -0
  11. mlspace_mcp/init_cli.py +356 -0
  12. mlspace_mcp/job_hooks.py +242 -0
  13. mlspace_mcp/jsonc.py +90 -0
  14. mlspace_mcp/overview.py +421 -0
  15. mlspace_mcp/registry.py +632 -0
  16. mlspace_mcp/resource_refs.py +188 -0
  17. mlspace_mcp/server.py +190 -0
  18. mlspace_mcp/setup_ui.py +103 -0
  19. mlspace_mcp/skills/mlspace-call-deployed-model-predict/SKILL.md +36 -0
  20. mlspace_mcp/skills/mlspace-deploy-inference-service/SKILL.md +42 -0
  21. mlspace_mcp/skills/mlspace-diagnose-data-transfer/SKILL.md +41 -0
  22. mlspace_mcp/skills/mlspace-diagnose-failed-training-job/SKILL.md +64 -0
  23. mlspace_mcp/skills/mlspace-diagnose-job-launch-rejection/SKILL.md +44 -0
  24. mlspace_mcp/skills/mlspace-diagnose-unhealthy-inference/SKILL.md +40 -0
  25. mlspace_mcp/skills/mlspace-discover-launchable-resources/SKILL.md +45 -0
  26. mlspace_mcp/skills/mlspace-launch-training-job/SKILL.md +66 -0
  27. mlspace_mcp/skills/mlspace-list-running-jobs/SKILL.md +43 -0
  28. mlspace_mcp/skills/mlspace-resume-notebook-and-set-autoshutdown/SKILL.md +44 -0
  29. mlspace_mcp/skills/mlspace-stage-s3-to-nfs/SKILL.md +45 -0
  30. mlspace_mcp/skills/mlspace-transfer-compute-node/SKILL.md +63 -0
  31. mlspace_mcp/spec/body_schemas.json +7506 -0
  32. mlspace_mcp/spec/openapi.json +28347 -0
  33. mlspace_mcp/tls.py +46 -0
  34. mlspace_mcp/tools/__init__.py +1 -0
  35. mlspace_mcp/tools/allocations.py +92 -0
  36. mlspace_mcp/tools/async_inference.py +77 -0
  37. mlspace_mcp/tools/build_image.py +76 -0
  38. mlspace_mcp/tools/dalle.py +52 -0
  39. mlspace_mcp/tools/data_transfer.py +260 -0
  40. mlspace_mcp/tools/docker_registry.py +156 -0
  41. mlspace_mcp/tools/inference.py +88 -0
  42. mlspace_mcp/tools/jobs.py +155 -0
  43. mlspace_mcp/tools/notebooks.py +174 -0
  44. mlspace_mcp/tools/queues.py +202 -0
  45. mlspace_mcp/tools/resources.py +86 -0
  46. mlspace_mcp/tools/tensorboards.py +112 -0
  47. mlspace_mcp/tools/workspaces.py +92 -0
  48. mlspace_mcp/workspace_context.py +107 -0
  49. mlspace_plugin-0.2.1.dist-info/METADATA +427 -0
  50. mlspace_plugin-0.2.1.dist-info/RECORD +53 -0
  51. mlspace_plugin-0.2.1.dist-info/WHEEL +4 -0
  52. mlspace_plugin-0.2.1.dist-info/entry_points.txt +2 -0
  53. mlspace_plugin-0.2.1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,427 @@
1
+ Metadata-Version: 2.4
2
+ Name: mlspace-plugin
3
+ Version: 0.2.1
4
+ Summary: Cloud.ru MLSpace plugin: MCP server, agent skills and setup
5
+ Project-URL: Repository, https://github.com/noesskeetit/mlspace-plugin
6
+ Project-URL: Issues, https://github.com/noesskeetit/mlspace-plugin/issues
7
+ Project-URL: Documentation, https://github.com/noesskeetit/mlspace-plugin/blob/main/INSTALL.md
8
+ License: MIT License
9
+
10
+ Copyright (c) 2026 Cloud.ru
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ of this software and associated documentation files (the "Software"), to deal
14
+ in the Software without restriction, including without limitation the rights
15
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ copies of the Software, and to permit persons to whom the Software is
17
+ furnished to do so, subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be included in all
20
+ copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALING IN THE
28
+ SOFTWARE.
29
+ License-File: LICENSE
30
+ Keywords: agent-skills,cloudru,mcp,mlspace
31
+ Classifier: Operating System :: MacOS
32
+ Classifier: Operating System :: POSIX :: Linux
33
+ Classifier: Programming Language :: Python :: 3
34
+ Requires-Python: >=3.10
35
+ Requires-Dist: httpx>=0.27
36
+ Requires-Dist: mcp<2,>=1.9.0
37
+ Requires-Dist: prompt-toolkit<4,>=3.0.48
38
+ Requires-Dist: pydantic-settings>=2.3
39
+ Requires-Dist: pydantic>=2.7
40
+ Requires-Dist: tomli>=2; python_version < '3.11'
41
+ Requires-Dist: truststore<1,>=0.10
42
+ Provides-Extra: dev
43
+ Requires-Dist: mypy>=1.10; extra == 'dev'
44
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
45
+ Requires-Dist: pytest>=8.0; extra == 'dev'
46
+ Requires-Dist: pyyaml>=6; extra == 'dev'
47
+ Requires-Dist: respx>=0.21; extra == 'dev'
48
+ Requires-Dist: ruff>=0.5; extra == 'dev'
49
+ Requires-Dist: trustme>=1.2; extra == 'dev'
50
+ Description-Content-Type: text/markdown
51
+
52
+ # mlspace-plugin
53
+
54
+ **Установка:** [инструкция человеку](https://github.com/noesskeetit/mlspace-plugin/blob/main/INSTALL.md) · [инструкция агенту](https://github.com/noesskeetit/mlspace-plugin/blob/main/INSTALL_AGENT.md).
55
+ Агенту, получившему ссылку на репозиторий для установки: сначала прочитайте INSTALL_AGENT.md.
56
+
57
+ MCP-сервер для **публичного API Cloud.ru MLSpace v2** (`https://api.ai.cloud.ru/public/v2`).
58
+
59
+ Открывает операции MLSpace, не помеченные `deprecated` в поставляемой схеме OpenAPI, — обучение, инференс,
60
+ ноутбуки, TensorBoard'ы, воркспейсы, аллокации, очереди, Docker Registry,
61
+ перенос данных и каталог ресурсов — LLM-агентам в виде **17
62
+ инструментов**, каждый с параметром `action`. Под капотом — 126 операций,
63
+ описанных в поставляемой схеме OpenAPI и сгруппированных для работы LLM-агента.
64
+
65
+ > Одна учётная запись, несколько выбранных workspace; stdio и локальный streamable-HTTP. Запись доступна по
66
+ > умолчанию; необратимые действия — за гейтом `confirm=true`.
67
+
68
+ ---
69
+
70
+ ## Что это и зачем
71
+
72
+ MLSpace отдаёт большой REST-API (142 пути, 185 операций в OpenAPI v2.8.1). Дёргать
73
+ его «голым» из LLM-агента неудобно: разнобой в пагинации, статусах, версиях путей,
74
+ часть параметров обязательны только в рантайме и т.п. Этот сервер прячет всю
75
+ механику (аутентификация, заголовки, шаблоны путей, кодирование query, тела
76
+ запросов, обработка ошибок) и выставляет наружу компактный, предсказуемый,
77
+ дружелюбный к модели интерфейс:
78
+
79
+ - **17 инструментов вместо 185 «тонких»** — модель не тонет в списке тулов; каждый
80
+ инструмент = один домен, конкретное действие выбирается через `action`.
81
+ (Автогенерация по спеке дала бы 185 инструментов с именами по 66 символов вида
82
+ `create_notebook_workspace_autoshutdown_rule_v2_public_v2_notebooks_v2_…`.)
83
+ - **Подсказки прямо в описании** — для каждого `action` указаны обязательные
84
+ параметры и форма тела (required/optional/enum-поля), вытянутые из спеки.
85
+ - **Ступени защиты** — гейт `confirm=true` на необратимые операции, dry-run для
86
+ проверки write-запроса без отправки, режим read-only для наблюдательных сценариев.
87
+ - **Чистый контекст** — компактный JSON, обрезка длинных списков и логов, удаление
88
+ k8s-шума, редактирование секретов в ответах.
89
+
90
+ ---
91
+
92
+ ## Установка и настройка
93
+
94
+ Python 3.10+, macOS или Linux. Установка из PyPI:
95
+
96
+ ```bash
97
+ uvx mlspace-plugin@latest setup
98
+ ```
99
+
100
+ Из каталога скачанного репозитория:
101
+
102
+ ```bash
103
+ uv tool install .
104
+ mlspace-plugin setup
105
+ ```
106
+
107
+ Мастер обнаруживает Claude Code, Codex и OpenCode, просит только Key ID и Key Secret
108
+ с маской `*****`, предлагает поиск воркспейсов по имени/проекту/ID и множественный
109
+ выбор. Проверяет API, сохраняет credentials в пользовательский `.env` с правами
110
+ `0600`, подключает MCP и устанавливает все скиллы из пакета. Откройте новую сессию
111
+ клиента; сервер запускается автоматически.
112
+
113
+ TLS использует системные доверенные CA. Для корпоративного PEM есть `--ca-file`:
114
+ мастер сохраняет копию для последующих запусков. Ошибка TLS/сети останавливает
115
+ настройку без перезаписи прежнего файла и без запроса ручного x-api-key.
116
+
117
+ Повторная настройка — `mlspace-plugin setup --force`: сохранённые ключи на прежнем
118
+ endpoint переиспользуются. Для замены ключей добавьте `--replace-credentials`.
119
+ Для stage укажите `--base-url https://mlspace-stage.example.com`.
120
+ Только credentials без подключения клиентов — `--config-only`.
121
+ Если уже используете native plugin, выбирайте этот режим, чтобы не дублировать MCP.
122
+
123
+ Подробнее: [установка, SSH, сертификаты и повторная настройка](https://github.com/noesskeetit/mlspace-plugin/blob/main/INSTALL.md).
124
+ Для разработки: `uv sync --extra dev` и `uv run pytest`.
125
+
126
+ **Порядок поиска кредов** — от общего к частному, побеждает более конкретное:
127
+
128
+ 1. `~/.config/mlspace-plugin/.env` — пользовательский конфиг, сюда пишет `setup`
129
+ 2. файл из `MLSPACE_ENV_FILE` — выбран явно
130
+ 3. настоящие переменные окружения — бьют любой файл
131
+
132
+ Файл `.env` из текущего каталога автоматически не читается. Для разработки
133
+ укажите его явно через `MLSPACE_ENV_FILE`, чтобы чужой репозиторий не мог
134
+ подменить адрес API при использовании сохранённых ключей.
135
+
136
+ Отсутствующие файлы просто пропускаются, а значения из нижних уровней дополняют
137
+ верхние. Если кредов нет вообще, сервер печатает, где искал, и что запустить.
138
+
139
+ | Переменная | Обяз. | По умолчанию | Назначение |
140
+ |---|:---:|---|---|
141
+ | `MLSPACE_CLIENT_ID` | ✅ | — | Cloud.ru Key ID (личный или сервисного аккаунта) |
142
+ | `MLSPACE_CLIENT_SECRET` | ✅ | — | Cloud.ru Key Secret |
143
+ | `MLSPACE_API_KEY` | | — | `x-api-key` одного воркспейса; без него сервер получает ключ сам |
144
+ | `MLSPACE_WORKSPACE_ID` | Для legacy singleton | — | ID единственного воркспейса |
145
+ | `MLSPACE_WORKSPACES` | Вместо legacy пары | `[]` | Сохранённый JSON-каталог выбранных workspace: `id`, `name`, `project_name`, опциональный `api_key`; пишет мастер |
146
+ | `MLSPACE_ENV_FILE` | | — | Явный путь к файлу с кредами; имеет приоритет над пользовательским конфигом |
147
+ | `MLSPACE_CA_FILE` | | системные CA | Дополнительный доверенный PEM; setup сохраняет копию |
148
+ | `MLSPACE_BASE_URL` | | `https://api.ai.cloud.ru` | Хост API |
149
+ | `MLSPACE_READONLY` | | `false` | `true` убирает все 58 write-действий из перечня `action` |
150
+ | `MLSPACE_DRY_RUN` | | `false` | Валидировать write, но не отправлять (вернуть превью) |
151
+ | `MLSPACE_TRANSPORT` | | `streamable-http` | `streamable-http` или `stdio` |
152
+ | `MLSPACE_HOST` / `MLSPACE_PORT` | | `127.0.0.1` / `8000` | Привязка HTTP (только loopback) |
153
+ | `MLSPACE_ENABLED_DOMAINS` | | _(все)_ | Список через запятую — ограничить набор инструментов |
154
+ | `MLSPACE_TIMEOUT` | | `60` | Таймаут запроса, сек |
155
+ | `MLSPACE_NAMESPACE` | | _(из workspace_id)_ | Закрепить k8s-namespace воркспейса; пусто = резолвится автоматически |
156
+ | `MLSPACE_LIST_DEFAULT_LIMIT` / `MLSPACE_LIST_MAX_LIMIT` | | `50` / `200` | Размер списков по умолчанию / максимум |
157
+ | `MLSPACE_LOG_TAIL_LINES` | | `200` | Сколько последних строк лога показывать |
158
+
159
+ Обмен `service_auth` (токен с TTL 1ч) выполняется внутри — это не инструмент.
160
+
161
+ ## Подключённые воркспейсы и адресация
162
+
163
+ `mlspace_contexts` возвращает сохранённый набор, endpoint и окружение без секретов
164
+ и сетевого обхода. Один, три или десять воркспейсов равноправны: основного/текущего
165
+ нет. `mlspace_workspaces list` показывает доступность учётных данных и не расширяет
166
+ набор. Legacy-конфигурация остаётся одним подключением.
167
+
168
+ Адресные действия принимают `target` (ID или однозначное название) и, для найденных
169
+ jobs/notebooks, `resource_ref`. Ссылка содержит окружение, workspace, тип и адрес
170
+ объекта; конфликтующие аргументы отклоняются. Диагностика и перезапуск сохраняют
171
+ адрес выбранного объекта, а успешный перезапуск получает новую ссылку, когда API
172
+ вернул распознаваемое новое имя. Контекст запроса не доказывает владельца общей очереди.
173
+ При одном workspace выбор не нужен. Явная работа в A сохраняет A для её шагов, а
174
+ отдельное чтение B этого не меняет. Общий запрос «теперь покажи задачи» снова охватывает
175
+ подключённый набор. Делегированное «в любом из A/B с подходящим GPU» позволяет выбрать
176
+ место по проверенным условиям без повторного вопроса; реальная неоднозначность требует
177
+ уточнения. Образец ноутбука сам по себе не определяет место создания.
178
+
179
+ | Read-only helper | Параметры и результат |
180
+ |---|---|
181
+ | `mlspace_jobs_overview` | `targets`, `author`, `status`; динамически обнаруживает MT-регионы и читает страницы jobs |
182
+ | `mlspace_notebooks_overview` | `targets`; читает страницы notebooks, включая paused |
183
+ | `mlspace_queue_inspect` | `queue_id`, `target` исходной очереди, `candidate_nodes`, `targets` областей наблюдения; проверяет доступные jobs/notebooks/pods, все приоритеты pending и факты/нагрузку узлов |
184
+
185
+ У helpers общий набор бюджетов: `page_size=100`, `max_pages=50` на поток,
186
+ `max_requests=200` на логические inventory GET, `max_items=200` на вывод.
187
+ Авторизация, получение ключа и возможный 401 retry — дополнительные транспортные
188
+ запросы. `targets` по умолчанию — весь выбранный набор; эти helpers доступны при
189
+ включённом соответствующем домене. Фильтра `region` у jobs overview нет: для узкого
190
+ регионального чтения доступен `mlspace_jobs list` с пагинацией.
191
+
192
+ Ответ показывает `requested_scope`, `checked_scope`, `failures`, `observed_at` и
193
+ полноту; строки `items` содержат `source` (тип наблюдения), `data`, `provenance` и
194
+ при распознанном адресе `resource_ref`. `complete` требует успешного полного чтения
195
+ и отсутствия усечения вывода. `observed_count` — число наблюдений, не полный итог
196
+ при частичном чтении и не сумма уникальной общей мощности. Частичный поиск не
197
+ доказывает уникальность имени; «все» после сокращённого списка требует понятного
198
+ множества. Явный фильтр `author` можно повторно использовать как настройку пользователя,
199
+ но он не доказывает identity: задачи коллег нельзя называть «моими» по членству в workspace.
200
+
201
+ Перед переносом вычислительного узла используйте `transfer_compute_node`: сначала
202
+ `mlspace_queue_inspect`, затем при запросе на выполнение `mlspace_queues add_nodes`
203
+ в целевую очередь с `{nodes, force_withdrawal:false}`. Узел отличается от Jupyter
204
+ сервера. Настройки GPU у paused notebook не означают занятую мощность; pending —
205
+ ожидающая потребность, а не работающая нагрузка. Даже полное чтение не гарантирует
206
+ видимость всех чужих workload общей аллокации: `cross_workspace_coverage` остаётся
207
+ `unknown`. Не обещайте перенос без влияния по такому обзору. Существующие правила
208
+ подтверждения операций сохраняются; отдельного подтверждения workspace нет.
209
+
210
+ ---
211
+
212
+ ## Запуск
213
+
214
+ ```bash
215
+ mlspace-plugin # streamable HTTP на 127.0.0.1:8000
216
+ MLSPACE_TRANSPORT=stdio mlspace-plugin # stdio
217
+ ```
218
+
219
+ ### Подключение из MCP-клиента
220
+
221
+ **Streamable HTTP** (по умолчанию): запустите `mlspace-plugin`, направьте клиента на
222
+ `http://127.0.0.1:8000/mcp`.
223
+
224
+ **stdio** (Claude Desktop / Cursor / Codex) — пример для `claude_desktop_config.json`:
225
+
226
+ ```json
227
+ {
228
+ "mcpServers": {
229
+ "mlspace": {
230
+ "command": "mlspace-plugin",
231
+ "env": {
232
+ "MLSPACE_TRANSPORT": "stdio",
233
+ "MLSPACE_ENV_FILE": "/absolute/path/to/mlspace-plugin/.env"
234
+ }
235
+ }
236
+ }
237
+ }
238
+ ```
239
+
240
+ ---
241
+
242
+ ## Инструменты
243
+
244
+ Каждый инструмент принимает `action` + параметры этого действия. Полный список
245
+ действий с подсказками по телу виден в описании инструмента у клиента. `[w]` —
246
+ write-операция (скрыта из `action`-перечня при `MLSPACE_READONLY=true`).
247
+
248
+ | Инструмент | Назначение | Действия |
249
+ |---|---|---|
250
+ | `mlspace_jobs` | Задачи обучения | list, get, logs, list_nodes, list_pods, get_params, get_preemptors, run`[w]`, restart`[w]`, delete`[w]` |
251
+ | `mlspace_build_image` | Сборка кастомных образов | list, run`[w]`, get, get_logs, get_pods, delete`[w]` |
252
+ | `mlspace_notebooks` | Jupyter-серверы | list, get, autoshutdown_get, create`[w]`, delete`[w]`, users_list, users_set`[w]`, users_revoke`[w]`, pause`[w]`, resume`[w]`, modify`[w]`, autoshutdown_set`[w]`, autoshutdown_delete`[w]` |
253
+ | `mlspace_tensorboards` | TensorBoard-инстансы | list, get, create`[w]`, modify`[w]`, pause`[w]`, resume`[w]`, delete`[w]` |
254
+ | `mlspace_inference` | Сервисы инференса | list, get, predict, create`[w]`, update`[w]`, delete`[w]` |
255
+ | `mlspace_async_inference` | Асинхронный инференс | list, predict, get_result, get_status |
256
+ | `mlspace_dalle` | DALL-E (async) | predict, result |
257
+ | `mlspace_workspaces` | Воркспейсы | get_api_key, status, users, list, get, allocations, allocation_queues |
258
+ | `mlspace_allocations` | Аллокации ресурсов | list, list_defaults, get, list_assignments, list_instance_types, get_nodes, set_default`[w]`, unset_default`[w]` |
259
+ | `mlspace_queues` | Очереди аллокаций / shared-кластер | list, get, defaults, instance_types, jobs, notebooks, pods, awaiting_launch_resources, queue_defaults, assign_workspace`[w]`, delete`[w]`, create`[w]`, update`[w]`, assign`[w]`, unassign`[w]`, set_default`[w]`, unset_default`[w]`, add_nodes`[w]`, assign_shared`[w]`, unassign_shared`[w]` |
260
+ | `mlspace_docker_registry` | Docker Registry | current_registry, list_repos, get_repo, update_repo`[w]`, delete_repos`[w]`, repo_fav`[w]`, list_images, get_image, delete_images`[w]`, list_tags, create_tag`[w]`, update_tag`[w]`, delete_tags`[w]`, generate_password`[w]` |
261
+ | `mlspace_data_transfer` | Коннекторы / переносы / история | list_connectors, get_connector, create_connector`[w]`, update_connector`[w]`, fav_connector`[w]`, halt_connector`[w]`, try_connector`[w]`, get_connector_logs, delete_connectors`[w]`, list_sources, list_transfers, get_transfer, create_transfer`[w]`, update_transfer`[w]`, fav_transfer`[w]`, switch_transfer`[w]`, delete_transfers`[w]`, list_history, get_history_status, get_event_logs, cancel_history`[w]`, rerun_history`[w]`, fav_history`[w]`, delete_history`[w]` |
262
+ | `mlspace_resources` | Каталог вычислений | configs, instance_types, nodes, nodes_load, rate_limits |
263
+
264
+ Волатильные значения (`region`, статус задачи, `connector_type`, …) — это обычные
265
+ строки, не фиксированные enum'ы: их известные значения перечислены в описании
266
+ параметра, но новые значения, которые API добавит позже, тоже принимаются. Тела
267
+ write-запросов подаются параметром `body: dict`; обязательные/вложенные/enum-поля
268
+ вынесены в описание действия. Локальная валидация тела (`lint`) **fail-open** —
269
+ ловит только явные структурные ошибки (нет обязательного поля, явное несовпадение
270
+ типа) до похода в сеть и никогда не отклонит тело, которое API бы принял.
271
+
272
+ ---
273
+
274
+ ## Плагин: Codex, Claude Code и совместимые агенты
275
+
276
+ [Поставка MLSpace](plugin/README.md) объединяет MCP-сервер и 12 скиллов.
277
+ Внутренний релиз `mlspace-plugin-<версия>.zip` содержит точную версию сервера,
278
+ общие скиллы и манифесты для загрузчиков Codex и Claude Code. Его установка
279
+ не зависит от публикации `mlspace-plugin` в публичном PyPI.
280
+
281
+ После распаковки и однократного `mlspace-plugin setup` добавьте каталог релиза как
282
+ marketplace клиента. [Команды установки и сборки](plugin/README.md).
283
+ Ключи остаются в пользовательском конфиге вне плагина.
284
+
285
+ Для других агентов предусмотрены Agent Plugins 1.0, Agent Skills и обычное
286
+ MCP-подключение; поддержку конкретного загрузчика нужно проверять отдельно.
287
+ Пошаговые сценарии поставляются только скиллами плагина; сервер отдаёт общие правила
288
+ в instructions. `tests/test_skills.py` сверяет скиллы с реальными инструментами.
289
+
290
+ ---
291
+
292
+ ## Безопасность
293
+
294
+ - **Гейт подтверждения**: 16 необратимых действий (удаления, отзыв доступа,
295
+ отмена переноса, ротация пароля реестра) требуют `confirm=true`. Это основной
296
+ рубеж в конфигурации по умолчанию.
297
+ - **Режим read-only** (`MLSPACE_READONLY=true`): все 58 write-действий исчезают
298
+ из `action`-перечня — модель их не видит — и блокируются в рантайме как
299
+ страховка. По умолчанию **выключен**: сервер задуман как рабочий инструмент, а
300
+ не смотровое окно. Включайте для наблюдательных сценариев, общих стендов и
301
+ всего, где агенту незачем ничего менять.
302
+ - **Dry-run** (`MLSPACE_DRY_RUN=true`): write-запрос полностью валидируется (lint
303
+ тела, шаблон пути, гейт confirm), но **не отправляется** — возвращается превью
304
+ запроса, который ушёл бы в API. Удобно дать модели собрать и проверить write
305
+ до реального применения.
306
+ - **Снос воркспейса не выставлен**: `POST/DELETE /workspaces/v3/` (bootstrap /
307
+ debootstrap) намеренно не реализованы — слишком большой радиус поражения для
308
+ общего сервера. Жизненный цикл воркспейса — только через консоль.
309
+ - **Сеть**: HTTP-транспорт отказывается слушать не-loopback адрес (в v1 нет
310
+ серверной аутентификации).
311
+ - **Секреты в ответах** (пароль registry, api-key воркспейса) редактируются.
312
+
313
+ > Важно про `confirm`: этот флаг ставит **сам агент** в том же вызове. Это защита от
314
+ > случайного действия, а **не** human-in-the-loop. Подтверждение человеком обеспечивает
315
+ > окно вашего MCP-клиента, которому мы помогаем аннотацией `destructiveHint`.
316
+ > Единственный рубеж, который агент обойти не может, — `MLSPACE_READONLY=true`;
317
+ > в конфигурации по умолчанию его нет, и решение остаётся за клиентом.
318
+
319
+ ---
320
+
321
+ ## Защиты от дефектов API
322
+
323
+ Обёртка гасит несколько особенностей MLSpace API, на которых агент иначе спотыкается.
324
+ API-ловушки ниже проверялись на живом API: основная часть — 03.08.2026,
325
+ региональность `/home/jovyan` — 12.08.2026. Допустимость binary-команд подтверждена
326
+ [официальной CLI-документацией](https://cloud.ru/docs/aicloud/mlspace-cli/topics/references__job-submit)
327
+ и локальной схемой; проверка доступности команды в образе остаётся отдельным шагом.
328
+
329
+ - **Тип entrypoint и регион проверяются до отправки.** Явный `region` обязателен для
330
+ каждого типа задачи. Для `type=binary` поле `script` — непустая команда или путь к
331
+ исполняемому файлу (`ls -lah`, `python /home/jovyan/launch.py -f script.py`); MCP
332
+ передаёт строку без изменений и не заявляет, что команда существует в выбранном
333
+ образе. Для остальных типов сохраняется проверка абсолютного пути под
334
+ `/home/jovyan`. Это NFS конкретного региона: одинаковый путь в notebook `SR006` и
335
+ job `A100-MT` может указывать на разные данные. Public API не умеет сделать `stat`
336
+ на целевом NFS, поэтому наличие файла подтверждается same-region notebook'ом или
337
+ transfer'ом. Локальная проверка формы пути или dry-run не проверяет наличие файла.
338
+ - **Порядок лога восстанавливается.** `logs?tail=N` возвращает строки в обратном
339
+ порядке — трейсбэк переворачивается, и причина оказывается ниже следствия. MCP
340
+ сортирует обратно и помечает это в ответе, чтобы модель знала, что порядку можно
341
+ доверять. Без `tail` порядок правильный и не трогается.
342
+ - **Состояния вместо прозы.** Строки `"No such job"` (логи протухли по retention) и
343
+ `Job … in queue. Try later` (задача в очереди) приходят внутри `HTTP 200`. MCP
344
+ распознаёт их и выдаёт понятную типизированную пометку, чтобы «логи устарели» не
345
+ читалось как «задачи не существует».
346
+ - **Ретрай не уходит в бесконечность.** Часть ручек отдаёт `500` детерминированно
347
+ (например, assignments на 6 из 7 аллокаций). Совет при `5xx` — повторить один раз,
348
+ дальше считать отказ постоянным.
349
+ - **Каталог образов сжимается.** `configs` отдаёт ~200 КБ (≈51k токенов), дублируя
350
+ образы под каждым `instance_type`; MCP дедуплицирует их без потери данных и умеет
351
+ отфильтровать один регион.
352
+
353
+ ---
354
+
355
+ ## Разработка
356
+
357
+ ```bash
358
+ pytest # юнит- + drift/контракт-тесты (моки HTTP, живые креды не нужны)
359
+ ruff check .
360
+ mypy src
361
+ ```
362
+
363
+ Сервер сверяется с небольшим сгенерированным артефактом
364
+ (`src/mlspace_mcp/spec/body_schemas.json`) ради подсказок по телу запроса и
365
+ мягкого lint'а; полный `openapi.json` нужен только для сборки/тестов.
366
+
367
+ ### Обновление спеки (после смены версии API)
368
+
369
+ 1. Перевыкачать `src/mlspace_mcp/spec/openapi.json` с OpenAPI-эндпоинта MLSpace.
370
+ 2. Перегенерировать схемы тел: `python scripts/gen_body_schemas.py`.
371
+ 3. Прогнать `pytest` — drift/контракт-тест поймает любое действие, чьё
372
+ `(method, path)` исчезло или стало deprecated; поправить соответствующий
373
+ `tools/<domain>.py`.
374
+
375
+ ### После правки скилла
376
+
377
+ Правьте `plugin/skills/<name>/SKILL.md` и запустите `pytest tests/test_skills.py`.
378
+ После смены версии пакета — `python scripts/gen_plugin.py` для манифестов.
379
+
380
+ ---
381
+
382
+ ## Архитектура
383
+
384
+ ```
385
+ LLM ⇄ FastMCP (streamable-HTTP/stdio) ⇄ tools/<domain>.py (зонтичные тулы)
386
+ ⇄ MLSpaceClient ⇄ TokenManager ⇄ api.ai.cloud.ru/public/v2
387
+ ```
388
+
389
+ | Модуль | Ответственность |
390
+ |---|---|
391
+ | `config.py` | Настройки (`Settings`) из окружения: креды, base_url, readonly, dry_run, транспорт, host/port, таймаут, включённые домены. |
392
+ | `auth.py` | `TokenManager`: обмен `service_auth`, кэш токена (TTL 1ч), single-flight рефреш за ~60с до истечения и при 401. |
393
+ | `client.py` | `MLSpaceClient`: единый `httpx.AsyncClient`, инъекция заголовков, шаблоны путей, повторяемые query-ключи, JSON-тело на любом методе (включая DELETE), один прозрачный ретрай на 401. |
394
+ | `errors.py` | Маппинг HTTP/исключений → понятные сообщения (включая разбор `detail[]`). |
395
+ | `formatting.py` | Компактный JSON, обрезка списков/логов, удаление шумовых ключей, редактирование секретов. |
396
+ | `workspace_context.py` / `resource_refs.py` | Выбор фиксированного контекста, отдельные клиенты и проверяемые адреса jobs/notebooks. |
397
+ | `overview.py` | Ограниченный обход инвентаря и очереди с происхождением и полнотой наблюдений. |
398
+ | `job_hooks.py` | Job preflight, fail-closed delete guard и совместимость ответов; leaf без импорта registry. |
399
+ | `registry.py` | `Op`/`Param`/`DomainTool` + единый дженерик-диспетчер: стрип write в readonly, гейт `confirm`, dry-run, clamp пагинации, lint тела, формат ответа. |
400
+ | `tools/<domain>.py` | Чистые декларативные данные: `DOMAIN: DomainTool` (карта `action → Op` + список `Param`). Никакой механики запросов. |
401
+ | `bodyspec.py` | Загрузка `body_schemas.json`; `summarize()` для подсказок + fail-open `lint()`. |
402
+ | `server.py` / `__main__.py` | Сборка `FastMCP`, общие instructions, регистрация доменов, запуск транспорта. |
403
+
404
+ Каждый доменный модуль — независимая единица из чистых данных; вся механика живёт
405
+ в ядре один раз, поэтому домены не могут «разъехаться» по заголовкам/путям/
406
+ кодированию.
407
+
408
+ ---
409
+
410
+ ## Документация
411
+
412
+ - [Установка плагина с MCP и скиллами](plugin/README.md).
413
+
414
+ ## Обратная связь и вклад в проект
415
+
416
+ Нашли ошибку или неудобный сценарий работы с MLSpace? Создайте [Issue](https://github.com/noesskeetit/mlspace-plugin/issues): опишите, что хотели сделать, что получилось, версию плагина,
417
+ ОС и клиент — Claude Code, Codex или OpenCode. Не прикладывайте ключи, токены,
418
+ `.env` или логи с чувствительными данными.
419
+
420
+ Исправления и улучшения присылайте через [Pull Request](https://github.com/noesskeetit/mlspace-plugin/pulls).
421
+ Как запустить проверки и устроен проект — в [CONTRIBUTING.md](https://github.com/noesskeetit/mlspace-plugin/blob/main/CONTRIBUTING.md).
422
+
423
+ **Особенно рады вашим скиллам!** Если вы нашли удобный способ запускать обучение,
424
+ разбирать ошибки, работать с данными или обслуживать инференс — оформите его
425
+ в скилл и поделитесь. Такие сценарии помогают всем проще работать с MLSpace.
426
+ Готовая реализация необязательна: можно начать с Issue с примером задачи и
427
+ ожидаемым результатом.
@@ -0,0 +1,53 @@
1
+ mlspace_mcp/__init__.py,sha256=78w3VpwGvTVaXjn9iTyGvS1s_z_jiGi2VGLf-iGb_T8,96
2
+ mlspace_mcp/__main__.py,sha256=NNUdfKZHNi5lUQDqr1b91adqI83ZwxYqtLtbS1k7b8U,5692
3
+ mlspace_mcp/_status.py,sha256=WAiZwEJtqxif0La1nnpiXFY3ar4IRLiyyNBzqWifoLw,723
4
+ mlspace_mcp/auth.py,sha256=k10AzdkxyIqsT1BlCOb420tEEW-32cslfpn_EPYGwAA,4143
5
+ mlspace_mcp/bodyspec.py,sha256=i9dlQZt6BpIVSVOYUdu9O68hfDh6XWIEm7CwBZhjM8Q,6892
6
+ mlspace_mcp/client.py,sha256=kvhKnewiKEy9yVdzrQJIORYNinIYP8Rkpwy5j2WQ75c,6121
7
+ mlspace_mcp/client_setup.py,sha256=0I_ejsryyXoclqc7g8NwMBKMnlMW9ktaVwshKOTS6hs,12836
8
+ mlspace_mcp/config.py,sha256=V83pMdSQkcd1T-ARwx73_yju93P1FE5fboiuu095BIk,12136
9
+ mlspace_mcp/errors.py,sha256=lLJk21reV95y8duRP3-acSEji73bsrB60GKpmR3W1t8,9876
10
+ mlspace_mcp/formatting.py,sha256=3f8pUdUvw_WCR4ss1StyoR-rdTiV0bRUo089ClhbJcY,21701
11
+ mlspace_mcp/init_cli.py,sha256=Tn9fD9XbniCz1R3Ae0RnITT6gexbcy0NJVxi_vB3QtI,18243
12
+ mlspace_mcp/job_hooks.py,sha256=fYSLWPi0vDh_ZyIqPQGqmOLNcGJzGb_VJnShtzSzGSY,12050
13
+ mlspace_mcp/jsonc.py,sha256=9gQJDIKlm7RoFVYIhl0V3pgEtDVz8sc-Ei3NF9YKSF4,3261
14
+ mlspace_mcp/overview.py,sha256=KeIKKtBEIfpQZpywHyCChQpmP7aN17alni706J6nR8c,23210
15
+ mlspace_mcp/registry.py,sha256=Y2tfy-4Ifzp1VoDc1vQMszWERSAA5H3E3wOKFi5gwTQ,29465
16
+ mlspace_mcp/resource_refs.py,sha256=5H3W0qvlzKnzyJljuuUocPAcP-Ext4P7fLp0LnhDMFA,8032
17
+ mlspace_mcp/server.py,sha256=G9DTE5aAlx-IEVBJfJHGft4B5EBVcZk68hLRXsRSWZ0,8177
18
+ mlspace_mcp/setup_ui.py,sha256=GrXRmSzZdHu1l69wuBwWRods8_aSNdUlVTdjMoPG5To,3949
19
+ mlspace_mcp/tls.py,sha256=0tC8-c3JAP68jkp_Ak6wceRkGpmf1qtaFbIKCWB2HEs,1981
20
+ mlspace_mcp/workspace_context.py,sha256=f4T7YFDK610m6ihMdbk7L7_qWIHYYWjQmyyco4DLkBA,4683
21
+ mlspace_mcp/tools/__init__.py,sha256=2BRMNyON3u99_84K-fpxJPzyIVqbbJLZfB7BR96aTYw,84
22
+ mlspace_mcp/tools/allocations.py,sha256=wczOJU2plyBPRnlk-laaCWdhGdhNGLQShBpEb-Rxg10,3409
23
+ mlspace_mcp/tools/async_inference.py,sha256=NPYxLj8Sel7Sa_EtyqtOdLRSOJ0iucaX9mjTB4DO5d4,2924
24
+ mlspace_mcp/tools/build_image.py,sha256=ejZ9X-OkfZjO9qRv_Iy-lurbTqxsxT_oaUgezik9DpI,2515
25
+ mlspace_mcp/tools/dalle.py,sha256=81u662RAbD60pRlYstkOYaeHA0V3eM0y8v88ArodqK4,2115
26
+ mlspace_mcp/tools/data_transfer.py,sha256=HPi_l7tuYaHJvIsr27fpfAbbr5lWvDWGSfY6kttIfaY,10502
27
+ mlspace_mcp/tools/docker_registry.py,sha256=RPB4jTObTmcs77LwpEYtFPUKtbNNvOw0OtKpqiDrC_c,6073
28
+ mlspace_mcp/tools/inference.py,sha256=KTz5Ib6AhSz0Dz8y9-WHBpyMHSoUEL1aZmstiVwu0g8,3311
29
+ mlspace_mcp/tools/jobs.py,sha256=COCb71oo1DR3zGDg1PDvUO-RGlKO67-LlfRcEbj5i3w,6442
30
+ mlspace_mcp/tools/notebooks.py,sha256=dYDnoBzxfFvyfCVcSnTKChukEoL861hTNaovRSkCDVs,7279
31
+ mlspace_mcp/tools/queues.py,sha256=m9GcgPIIZ_Bm4FkfxpSSK5OGwFywyjahHyl6DIeiiOo,8010
32
+ mlspace_mcp/tools/resources.py,sha256=oHvY4F9jePh-lbvFJm3HFUOsVuuWPLzn0aSOvT5A_vM,3275
33
+ mlspace_mcp/tools/tensorboards.py,sha256=Twj5QSq6qJoebukaO_mjXLCSeran8gitPo-AujkVJdg,4236
34
+ mlspace_mcp/tools/workspaces.py,sha256=l0GD9LtkWY4V-Yt_QpdbfCIhbQ_tI1j3ywTfO-Ztons,3769
35
+ mlspace_mcp/skills/mlspace-call-deployed-model-predict/SKILL.md,sha256=TkKOzfrkfDqmnbGLJw1c5T3USGygeL6toTlT3bywsi0,1847
36
+ mlspace_mcp/skills/mlspace-deploy-inference-service/SKILL.md,sha256=_moM2Rh9Gp3eKMYJoKqElWw5thS-lAUN3jurxD-IXSg,2165
37
+ mlspace_mcp/skills/mlspace-diagnose-data-transfer/SKILL.md,sha256=DLO1TSnWGkM9iyXW1XzcsJFeLh9vzp1cbikuioOZ2D4,2086
38
+ mlspace_mcp/skills/mlspace-diagnose-failed-training-job/SKILL.md,sha256=Swen_XrPY9eZ8EyjZfnhbF2wrrTOdU6sPsGVMoM2xsE,3719
39
+ mlspace_mcp/skills/mlspace-diagnose-job-launch-rejection/SKILL.md,sha256=rPqXdBz7fDRGLz-nQJQU7RIbr4D8w88gKOumr6JtKzk,2671
40
+ mlspace_mcp/skills/mlspace-diagnose-unhealthy-inference/SKILL.md,sha256=pu5fnfW7L3QwbU00Q_oEPqhXT5gaUpKeP6RHHKyOOSk,2080
41
+ mlspace_mcp/skills/mlspace-discover-launchable-resources/SKILL.md,sha256=5ihxKjEJ_j_DMHhGeucp65kHG9X7M0T2IviQtURp9G0,2656
42
+ mlspace_mcp/skills/mlspace-launch-training-job/SKILL.md,sha256=pnV4_f4HkO04xMCahS-zEE9UKBuIVWeIF0lQWOVVN8U,4135
43
+ mlspace_mcp/skills/mlspace-list-running-jobs/SKILL.md,sha256=cU3lemuEgyQhiLf8nQ2iTvaNKGuHPyk8b7ecKvFjvC0,2250
44
+ mlspace_mcp/skills/mlspace-resume-notebook-and-set-autoshutdown/SKILL.md,sha256=m4wvNNWoMJVMzIAHhDgyArNMcft4VqYBGLX4ygBRCvE,2490
45
+ mlspace_mcp/skills/mlspace-stage-s3-to-nfs/SKILL.md,sha256=d9uQK-Yer9s0N6iqiI2DL89RTSkK2giEIWsiiAdTWiY,2425
46
+ mlspace_mcp/skills/mlspace-transfer-compute-node/SKILL.md,sha256=fpNGUSK-cUDpB56GM6o3HyG3SNYdbNOIgJ5Vpt1cxfY,4041
47
+ mlspace_mcp/spec/body_schemas.json,sha256=0RNycpIQzrmdyS_KsOUMi63LgspV1K3R8Ws222Wb-yw,210812
48
+ mlspace_mcp/spec/openapi.json,sha256=_sqK0-Rea9ch-mm5Z9oVUrjBTJYt0asukzlHjMckJMA,727085
49
+ mlspace_plugin-0.2.1.dist-info/METADATA,sha256=dpl-pY-ma46vkluy5C3CyyvxLWGOIiqhrnaGDyG3NjY,34904
50
+ mlspace_plugin-0.2.1.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
51
+ mlspace_plugin-0.2.1.dist-info/entry_points.txt,sha256=FgDWpi2VBn6t_3KiLZbNguyK-xZ6VXmyD3WNO7fFymI,61
52
+ mlspace_plugin-0.2.1.dist-info/licenses/LICENSE,sha256=hm1I98cUKNyLB7u1lsDZaFyJOo4eTzq0-rAVxd3jGDI,1064
53
+ mlspace_plugin-0.2.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ mlspace-plugin = mlspace_mcp.__main__:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Cloud.ru
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALING IN THE
21
+ SOFTWARE.