mlspace-plugin 0.3.1__tar.gz → 0.3.2__tar.gz

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 (133) hide show
  1. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/INSTALL.md +1 -1
  2. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/PKG-INFO +1 -1
  3. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/RELEASE.md +5 -5
  4. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/plugin/.claude-plugin/plugin.json +1 -1
  5. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/plugin/.codex-plugin/plugin.json +1 -1
  6. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/plugin/.mcp.json +1 -1
  7. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/plugin/mcp.json +1 -1
  8. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/plugin/plugin.json +1 -1
  9. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/pyproject.toml +1 -1
  10. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/__init__.py +2 -1
  11. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/init_cli.py +8 -2
  12. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/server.py +5 -1
  13. mlspace_plugin-0.3.2/tests/test_user_agent.py +59 -0
  14. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/.agents/plugins/marketplace.json +0 -0
  15. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/.claude-plugin/marketplace.json +0 -0
  16. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/.env.example +0 -0
  17. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/.gitignore +0 -0
  18. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/AGENTS.md +0 -0
  19. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/CONTRIBUTING.md +0 -0
  20. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/INSTALL_AGENT.md +0 -0
  21. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/LICENSE +0 -0
  22. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/README.md +0 -0
  23. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/plugin/README.md +0 -0
  24. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/plugin/skills/mlspace-call-deployed-model-predict/SKILL.md +0 -0
  25. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/plugin/skills/mlspace-deploy-inference-service/SKILL.md +0 -0
  26. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/plugin/skills/mlspace-diagnose-data-transfer/SKILL.md +0 -0
  27. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/plugin/skills/mlspace-diagnose-failed-training-job/SKILL.md +0 -0
  28. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/plugin/skills/mlspace-diagnose-job-launch-rejection/SKILL.md +0 -0
  29. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/plugin/skills/mlspace-diagnose-unhealthy-inference/SKILL.md +0 -0
  30. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/plugin/skills/mlspace-discover-launchable-resources/SKILL.md +0 -0
  31. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/plugin/skills/mlspace-launch-training-job/SKILL.md +0 -0
  32. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/plugin/skills/mlspace-list-running-jobs/SKILL.md +0 -0
  33. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/plugin/skills/mlspace-resume-notebook-and-set-autoshutdown/SKILL.md +0 -0
  34. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/plugin/skills/mlspace-stage-s3-to-nfs/SKILL.md +0 -0
  35. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/plugin/skills/mlspace-transfer-compute-node/SKILL.md +0 -0
  36. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/scripts/build_plugin.py +0 -0
  37. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/scripts/demo_multi_workspace.py +0 -0
  38. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/scripts/gen_body_schemas.py +0 -0
  39. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/scripts/gen_plugin.py +0 -0
  40. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/scripts/mcp_call.py +0 -0
  41. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/scripts/serve_stdio.sh +0 -0
  42. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/scripts/serve_stdio_dryrun.sh +0 -0
  43. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/__main__.py +0 -0
  44. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/_status.py +0 -0
  45. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/auth.py +0 -0
  46. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/bodyspec.py +0 -0
  47. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/client.py +0 -0
  48. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/client_setup.py +0 -0
  49. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/config.py +0 -0
  50. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/errors.py +0 -0
  51. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/formatting.py +0 -0
  52. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/job_hooks.py +0 -0
  53. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/jsonc.py +0 -0
  54. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/native_plugins.py +0 -0
  55. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/overview.py +0 -0
  56. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/registry.py +0 -0
  57. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/resource_refs.py +0 -0
  58. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/setup_conflicts.py +0 -0
  59. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/setup_ui.py +0 -0
  60. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/setup_verification.py +0 -0
  61. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/spec/body_schemas.json +0 -0
  62. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/spec/openapi.json +0 -0
  63. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/tls.py +0 -0
  64. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/tools/__init__.py +0 -0
  65. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/tools/allocations.py +0 -0
  66. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/tools/async_inference.py +0 -0
  67. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/tools/build_image.py +0 -0
  68. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/tools/dalle.py +0 -0
  69. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/tools/data_transfer.py +0 -0
  70. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/tools/docker_registry.py +0 -0
  71. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/tools/inference.py +0 -0
  72. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/tools/jobs.py +0 -0
  73. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/tools/notebooks.py +0 -0
  74. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/tools/queues.py +0 -0
  75. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/tools/resources.py +0 -0
  76. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/tools/tensorboards.py +0 -0
  77. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/tools/workspaces.py +0 -0
  78. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/src/mlspace_mcp/workspace_context.py +0 -0
  79. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/conftest.py +0 -0
  80. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/specutil.py +0 -0
  81. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_allocations.py +0 -0
  82. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_artifact_sync.py +0 -0
  83. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_async_inference.py +0 -0
  84. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_auth.py +0 -0
  85. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_autofill.py +0 -0
  86. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_bodyspec.py +0 -0
  87. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_build_image.py +0 -0
  88. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_client.py +0 -0
  89. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_client_setup.py +0 -0
  90. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_configs_shrink.py +0 -0
  91. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_credential_pointer.py +0 -0
  92. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_dalle.py +0 -0
  93. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_data_transfer.py +0 -0
  94. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_docker_registry.py +0 -0
  95. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_drift.py +0 -0
  96. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_dry_run.py +0 -0
  97. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_dry_run_fidelity.py +0 -0
  98. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_env_file.py +0 -0
  99. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_errors.py +0 -0
  100. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_false_success.py +0 -0
  101. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_formatting.py +0 -0
  102. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_init_cli.py +0 -0
  103. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_integration.py +0 -0
  104. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_job_status.py +0 -0
  105. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_jobs.py +0 -0
  106. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_log_normalisation.py +0 -0
  107. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_mcp_error_contract.py +0 -0
  108. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_native_cli_integration.py +0 -0
  109. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_native_plugins.py +0 -0
  110. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_notebooks.py +0 -0
  111. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_onboarding_init.py +0 -0
  112. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_onboarding_tls.py +0 -0
  113. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_opencode_journey.py +0 -0
  114. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_overview.py +0 -0
  115. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_plugin_bundle.py +0 -0
  116. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_plugin_cli.py +0 -0
  117. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_plugin_release.py +0 -0
  118. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_preflight.py +0 -0
  119. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_queue_scope.py +0 -0
  120. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_queues.py +0 -0
  121. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_registry.py +0 -0
  122. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_resource_refs.py +0 -0
  123. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_resources.py +0 -0
  124. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_route_confusion.py +0 -0
  125. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_setup_conflicts.py +0 -0
  126. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_setup_native_journey.py +0 -0
  127. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_setup_ui.py +0 -0
  128. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_setup_verification.py +0 -0
  129. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_skills.py +0 -0
  130. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_tensorboards.py +0 -0
  131. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_workspace_context.py +0 -0
  132. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_workspaces.py +0 -0
  133. {mlspace_plugin-0.3.1 → mlspace_plugin-0.3.2}/tests/test_wrapper_hardening.py +0 -0
@@ -21,7 +21,7 @@ mlspace-plugin setup
21
21
  ```
22
22
 
23
23
  Если пакет уже установлен: `uv tool install --force .`.
24
- При установке из wheel: `uv tool install /путь/к/mlspace_plugin-0.3.1-py3-none-any.whl`.
24
+ При установке из wheel: `uv tool install /путь/к/mlspace_plugin-0.3.2-py3-none-any.whl`.
25
25
  Не используйте editable-установку из `/tmp` для постоянной работы.
26
26
  Далее команды показаны для PyPI. После установки исходников или wheel через
27
27
  `uv tool install` заменяйте `uvx mlspace-plugin@latest` на `mlspace-plugin`,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: mlspace-plugin
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: Cloud.ru MLSpace plugin: MCP server, agent skills and setup
5
5
  Project-URL: Repository, https://github.com/noesskeetit/mlspace-plugin
6
6
  Project-URL: Issues, https://github.com/noesskeetit/mlspace-plugin/issues
@@ -7,11 +7,11 @@
7
7
  `uv lock` и `uv run python scripts/gen_plugin.py`. Обновите примеры имён wheel/ZIP
8
8
  в инструкциях, если версия присутствует в имени файла.
9
9
  2. Выполните проверки из `CONTRIBUTING.md` на Linux и macOS.
10
- 3. Соберите пакет (в командах ниже замените 0.3.1 на выпускаемую версию):
10
+ 3. Соберите пакет (в командах ниже замените 0.3.2 на выпускаемую версию):
11
11
 
12
12
  ```sh
13
- uv build --out-dir dist/release-0.3.1
14
- uv run python scripts/build_plugin.py --output dist/plugin-release-0.3.1
13
+ uv build --out-dir dist/release-0.3.2
14
+ uv run python scripts/build_plugin.py --output dist/plugin-release-0.3.2
15
15
  ```
16
16
 
17
17
  4. Просмотрите содержимое wheel, sdist и ZIP. В них не должно быть credentials,
@@ -23,11 +23,11 @@ uv run python scripts/build_plugin.py --output dist/plugin-release-0.3.1
23
23
  `UV_PUBLISH_TOKEN` средствами вашего хранилища секретов, не аргументом команды:
24
24
 
25
25
  ```sh
26
- uv publish --trusted-publishing never dist/release-0.3.1/mlspace_plugin-0.3.1-py3-none-any.whl dist/release-0.3.1/mlspace_plugin-0.3.1.tar.gz
26
+ uv publish --trusted-publishing never dist/release-0.3.2/mlspace_plugin-0.3.2-py3-none-any.whl dist/release-0.3.2/mlspace_plugin-0.3.2.tar.gz
27
27
  ```
28
28
 
29
29
  6. Проверьте версию и SHA256 обоих файлов через PyPI JSON API; проверьте установку
30
- `uvx --from mlspace-plugin==0.3.1 mlspace-plugin --version` из нового кеша.
30
+ `uvx --from mlspace-plugin==0.3.2 mlspace-plugin --version` из нового кеша.
31
31
  7. Только после проверки точной версии в PyPI обновите рабочую ветку GitHub
32
32
  marketplace и создайте тег релиза. До этого проверяйте изменения на отдельной
33
33
  release-ветке или локальном Git marketplace: main не должен ссылаться на
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mlspace",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Cloud.ru MLSpace: training jobs, inference, notebooks, data transfer, registry and compute catalog — an MCP server plus skills.",
5
5
  "author": {
6
6
  "name": "Cloud.ru"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mlspace",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Cloud.ru MLSpace: training jobs, inference, notebooks, data transfer, registry and compute catalog — an MCP server plus skills.",
5
5
  "author": {
6
6
  "name": "Cloud.ru"
@@ -5,7 +5,7 @@
5
5
  "command": "uvx",
6
6
  "args": [
7
7
  "--from",
8
- "mlspace-plugin==0.3.1",
8
+ "mlspace-plugin==0.3.2",
9
9
  "mlspace-plugin",
10
10
  "--transport",
11
11
  "stdio"
@@ -6,7 +6,7 @@
6
6
  "command": "uvx",
7
7
  "args": [
8
8
  "--from",
9
- "mlspace-plugin==0.3.1",
9
+ "mlspace-plugin==0.3.2",
10
10
  "mlspace-plugin",
11
11
  "--transport",
12
12
  "stdio"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
3
  "name": "mlspace",
4
- "version": "0.3.1",
4
+ "version": "0.3.2",
5
5
  "description": "Cloud.ru MLSpace: training jobs, inference, notebooks, data transfer, registry and compute catalog — an MCP server plus skills.",
6
6
  "author": {
7
7
  "name": "Cloud.ru"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mlspace-plugin"
3
- version = "0.3.1"
3
+ version = "0.3.2"
4
4
  description = "Cloud.ru MLSpace plugin: MCP server, agent skills and setup"
5
5
  keywords = ["mlspace", "mcp", "agent-skills", "cloudru"]
6
6
  classifiers = [
@@ -1,3 +1,4 @@
1
1
  """mlspace_mcp — MCP server for the Cloud.ru MLSpace public API v2."""
2
2
 
3
- __version__ = "0.3.1"
3
+ __version__ = "0.3.2"
4
+ USER_AGENT = f"mlspace-plugin/{__version__}"
@@ -15,6 +15,7 @@ from urllib.parse import urlsplit
15
15
 
16
16
  import httpx
17
17
 
18
+ from . import USER_AGENT
18
19
  from .client_setup import (
19
20
  CLIENTS,
20
21
  atomic_write,
@@ -65,7 +66,9 @@ def _fetch_workspaces(key_id: str, key_secret: str, *, base_url: str,
65
66
  from .client import MLSpaceClient
66
67
 
67
68
  async def fetch() -> Any:
68
- async with httpx.AsyncClient(timeout=30, verify=tls_context(ca_file)) as http:
69
+ async with httpx.AsyncClient(
70
+ timeout=30, verify=tls_context(ca_file), headers={"User-Agent": USER_AGENT},
71
+ ) as http:
69
72
  tokens = TokenManager(http, base_url=base_url, client_id=key_id, client_secret=key_secret)
70
73
  client = MLSpaceClient(http, tokens, base_url=base_url, api_key='', workspace_id='')
71
74
  return await client.request('GET', '/public/v2/workspaces/v3/')
@@ -148,7 +151,10 @@ def _verify(values: dict[str, str], *, base_url: str) -> str | None:
148
151
  workspaces=json.loads(values.get("WORKSPACES", "[]")),
149
152
  namespace="",
150
153
  )
151
- async with httpx.AsyncClient(timeout=30, verify=tls_context(values.get("CA_FILE", ""))) as http:
154
+ async with httpx.AsyncClient(
155
+ timeout=30, verify=tls_context(values.get("CA_FILE", "")),
156
+ headers={"User-Agent": USER_AGENT},
157
+ ) as http:
152
158
  tokens = TokenManager(
153
159
  http, base_url=base_url, client_id=values["CLIENT_ID"],
154
160
  client_secret=values["CLIENT_SECRET"],
@@ -14,6 +14,7 @@ from mcp.server.fastmcp import FastMCP
14
14
  from mcp.server.transport_security import TransportSecuritySettings
15
15
  from mcp.types import ToolAnnotations
16
16
 
17
+ from . import USER_AGENT
17
18
  from .auth import TokenManager
18
19
  from .config import Settings
19
20
  from .overview import register_overviews
@@ -149,7 +150,10 @@ def build_server(settings: Settings) -> FastMCP:
149
150
 
150
151
  @asynccontextmanager
151
152
  async def lifespan(_server: FastMCP):
152
- async with httpx.AsyncClient(timeout=settings.timeout, verify=tls_context(settings.ca_file)) as http:
153
+ async with httpx.AsyncClient(
154
+ timeout=settings.timeout, verify=tls_context(settings.ca_file),
155
+ headers={"User-Agent": USER_AGENT},
156
+ ) as http:
153
157
  tokens = TokenManager(
154
158
  http,
155
159
  settings.base_url,
@@ -0,0 +1,59 @@
1
+ """Identify plugin traffic at the outgoing HTTP boundary, including bootstrap."""
2
+ from __future__ import annotations
3
+
4
+ import httpx
5
+ import pytest
6
+ import respx
7
+ from conftest import AUTH_URL, BASE_URL, make_settings
8
+
9
+ from mlspace_mcp import __version__
10
+ from mlspace_mcp.init_cli import _fetch_workspaces, _verify
11
+ from mlspace_mcp.server import build_server
12
+
13
+ TOKEN_BODY = {"token": {"access_token": "test-token", "expires_in": 3600}}
14
+
15
+
16
+ @pytest.mark.parametrize("flow", ["discovery", "verification"])
17
+ @respx.mock
18
+ def test_onboarding_identifies_every_outgoing_request(flow):
19
+ respx.post(AUTH_URL).respond(200, json=TOKEN_BODY)
20
+ if flow == "discovery":
21
+ respx.get(f"{BASE_URL}/public/v2/workspaces/v3/").respond(
22
+ 200, json={"workspaces": [{"id": "ws-1", "name": "test"}]},
23
+ )
24
+ assert _fetch_workspaces("cid", "csecret", base_url=BASE_URL)[0]["id"] == "ws-1"
25
+ else:
26
+ respx.get(f"{BASE_URL}/public/v2/workspaces/v1/x_api_key").respond(
27
+ 200, json={"x-api-key": "test-key"},
28
+ )
29
+ respx.get(f"{BASE_URL}/public/v2/workspaces/v3/ws-1").respond(
30
+ 200, json={"id": "ws-1"},
31
+ )
32
+ assert _verify({
33
+ "CLIENT_ID": "cid", "CLIENT_SECRET": "csecret", "WORKSPACE_ID": "ws-1",
34
+ }, base_url=BASE_URL) is None
35
+ assert respx.calls
36
+ for call in respx.calls:
37
+ assert call.request.headers["user-agent"] == f"mlspace-plugin/{__version__}"
38
+
39
+
40
+ @respx.mock
41
+ async def test_server_identifies_auth_key_lookup_api_and_retry_requests():
42
+ respx.post(AUTH_URL).respond(200, json=TOKEN_BODY)
43
+ respx.get(f"{BASE_URL}/public/v2/workspaces/v1/x_api_key").respond(
44
+ 200, json={"x-api-key": "test-key"},
45
+ )
46
+ jobs = respx.get(f"{BASE_URL}/public/v2/jobs").mock(side_effect=[
47
+ httpx.Response(401, json={"detail": "expired"}),
48
+ httpx.Response(200, json={"jobs": []}),
49
+ ])
50
+ server = build_server(make_settings(api_key=""))
51
+ async with server.settings.lifespan(server) as app:
52
+ client = await app.clients.resolve("ws-1")
53
+ assert await client.request("GET", "/public/v2/jobs") == {"jobs": []}
54
+ assert jobs.call_count == 2
55
+ assert respx.post(AUTH_URL).call_count == 2
56
+ for call in respx.calls:
57
+ assert call.request.headers["user-agent"] == f"mlspace-plugin/{__version__}"
58
+ assert jobs.calls.last.request.headers["x-api-key"] == "test-key"
59
+ assert jobs.calls.last.request.headers["x-workspace-id"] == "ws-1"
File without changes
File without changes
File without changes