vmware-storage 2.4.2__tar.gz → 2.5.0__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 (124) hide show
  1. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/.claude-plugin/plugin.json +2 -2
  2. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/.mcp.json +1 -1
  3. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/PKG-INFO +9 -7
  4. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/README-CN.md +9 -6
  5. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/README.md +8 -6
  6. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/RELEASE_NOTES.md +7 -3
  7. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/openclaw.plugin.json +2 -2
  8. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/package.json +2 -2
  9. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/pyproject.toml +1 -1
  10. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/server.json +3 -3
  11. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/skills/vmware-storage/SKILL.md +10 -7
  12. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/skills/vmware-storage/references/agent-guardrails.md +1 -1
  13. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/skills/vmware-storage/references/capabilities.md +5 -4
  14. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/skills/vmware-storage/references/setup-guide.md +3 -3
  15. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_audit_isolation.py +11 -4
  16. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_datastore_list_renders.py +3 -1
  17. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_storage_hardening_fixes.py +12 -12
  18. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_vsan_efficiency.py +17 -0
  19. vmware_storage-2.5.0/tests/test_datastore_vm_usage.py +106 -0
  20. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/test_iscsi_gate.py +25 -17
  21. vmware_storage-2.5.0/tests/test_request_error_hints.py +29 -0
  22. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/uv.lock +7 -7
  23. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/vmware_storage/__init__.py +1 -1
  24. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/vmware_storage/access_requirements.py +1 -0
  25. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/vmware_storage/mcp_server/server.py +70 -56
  26. vmware_storage-2.5.0/vmware_storage/ops/datastore_usage.py +136 -0
  27. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/vmware_storage/ops/iscsi_gate.py +8 -13
  28. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/vmware_storage/ops/vsan.py +6 -2
  29. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/vmware_storage/ops/vsan_efficiency.py +13 -0
  30. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/.clawhubignore +0 -0
  31. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/.env.example +0 -0
  32. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/.gitignore +0 -0
  33. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/Dockerfile +0 -0
  34. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/LICENSE +0 -0
  35. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/SECURITY.md +0 -0
  36. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/config.example.yaml +0 -0
  37. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/docker-compose.yml +0 -0
  38. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/examples/mcp-configs/README.md +0 -0
  39. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/examples/mcp-configs/claude-code.json +0 -0
  40. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/examples/mcp-configs/continue.yaml +0 -0
  41. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/examples/mcp-configs/cursor.json +0 -0
  42. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/examples/mcp-configs/goose.json +0 -0
  43. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/examples/mcp-configs/localcowork.json +0 -0
  44. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/examples/mcp-configs/mcp-agent.yaml +0 -0
  45. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/examples/mcp-configs/vscode-copilot.json +0 -0
  46. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/patterns/README.md +0 -0
  47. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/patterns/iscsi-target-stale-rescan.yaml +0 -0
  48. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/skills/vmware-storage/evals/evals.json +0 -0
  49. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/skills/vmware-storage/references/cli-reference.md +0 -0
  50. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/smithery.yaml +0 -0
  51. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/__init__.py +0 -0
  52. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/conftest.py +0 -0
  53. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/__init__.py +0 -0
  54. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/capability/__init__.py +0 -0
  55. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/capability/_family.py +0 -0
  56. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/capability/_scores.json +0 -0
  57. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/capability/_scoring.py +0 -0
  58. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/capability/_skill.py +0 -0
  59. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/capability/conftest.py +0 -0
  60. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/capability/test_entity_reachability.py +0 -0
  61. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/capability/test_error_actionability.py +0 -0
  62. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/capability/test_tool_description_quality.py +0 -0
  63. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/capability/test_tool_manifest_budget.py +0 -0
  64. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/__init__.py +0 -0
  65. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_atexit_handlers_do_not_accumulate.py +0 -0
  66. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_browse_ds_path_join.py +0 -0
  67. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_browse_sees_every_file.py +0 -0
  68. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_cached_loader_agrees_with_the_resolver.py +0 -0
  69. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_capability_grader.py +0 -0
  70. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_cli_writes_guarded.py +0 -0
  71. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_credential_env_override.py +0 -0
  72. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_doctor_authenticates_every_target.py +0 -0
  73. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_doctor_uses_the_config_the_tools_use.py +0 -0
  74. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_env_secret_obfuscation.py +0 -0
  75. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_error_hints_fit_the_surface.py +0 -0
  76. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_fc_multipath.py +0 -0
  77. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_image_registry_cross_process.py +0 -0
  78. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_instructions_name_targets.py +0 -0
  79. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_inventory_scale_n1.py +0 -0
  80. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_iscsi_unreachable_host.py +0 -0
  81. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_list_cached_images_target.py +0 -0
  82. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_login_without_a_role.py +0 -0
  83. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_release_blockers.py +0 -0
  84. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_remedy_survives_a_long_home.py +0 -0
  85. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_result_envelope.py +0 -0
  86. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_returned_failure_is_audited.py +0 -0
  87. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_scan_populates_image_cache.py +0 -0
  88. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_session_probe_eviction.py +0 -0
  89. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_storage_ops_behavior.py +0 -0
  90. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_tool_names_2_0.py +0 -0
  91. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_vim_attribute_conformance.py +0 -0
  92. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_vsan_health_is_actually_queried.py +0 -0
  93. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/regression/test_vsan_reports_what_it_could_not_read.py +0 -0
  94. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/spec/__init__.py +0 -0
  95. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/spec/vsan_efficiency_endpoints.py +0 -0
  96. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/eval/spec/vsan_health_endpoints.py +0 -0
  97. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/test_access_info.py +0 -0
  98. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/test_filelock.py +0 -0
  99. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/test_init_and_onboarding.py +0 -0
  100. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/test_no_destructive_ops.py +0 -0
  101. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/test_no_destructive_vm_code.py +0 -0
  102. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/test_policy_roles.py +0 -0
  103. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/test_safe_error_passthrough.py +0 -0
  104. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/test_session_pool.py +0 -0
  105. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/tests/test_stop_signal_runs_atexit.py +0 -0
  106. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/vmware_storage/_filelock.py +0 -0
  107. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/vmware_storage/cli.py +0 -0
  108. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/vmware_storage/config.py +0 -0
  109. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/vmware_storage/connection.py +0 -0
  110. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/vmware_storage/doctor.py +0 -0
  111. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/vmware_storage/init_wizard.py +0 -0
  112. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/vmware_storage/mcp_server/__init__.py +0 -0
  113. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/vmware_storage/mcp_server/__main__.py +0 -0
  114. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/vmware_storage/notify/__init__.py +0 -0
  115. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/vmware_storage/notify/audit.py +0 -0
  116. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/vmware_storage/ops/__init__.py +0 -0
  117. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/vmware_storage/ops/datastore_browser.py +0 -0
  118. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/vmware_storage/ops/inventory.py +0 -0
  119. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/vmware_storage/ops/iscsi_config.py +0 -0
  120. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/vmware_storage/ops/multipath.py +0 -0
  121. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/vmware_storage/ops/storage_paths.py +0 -0
  122. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/vmware_storage/ops/vsan_sdk.py +0 -0
  123. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/vmware_storage/policy_environment.py +0 -0
  124. {vmware_storage-2.4.2 → vmware_storage-2.5.0}/vmware_storage/policy_roles.py +0 -0
@@ -2,8 +2,8 @@
2
2
  "$schema": "https://anthropic.com/claude-code/plugin.schema.json",
3
3
  "name": "vmware-storage",
4
4
  "displayName": "VMware Storage",
5
- "version": "2.4.2",
6
- "description": "VMware vSphere storage with 15 MCP tools: datastores, iSCSI, vSAN, FC multipath.",
5
+ "version": "2.5.0",
6
+ "description": "VMware vSphere storage with 16 MCP tools: datastores, iSCSI, vSAN, FC multipath.",
7
7
  "author": {
8
8
  "name": "Wei Zhou",
9
9
  "email": "zhouwei008@gmail.com",
@@ -5,7 +5,7 @@
5
5
  "command": "uvx",
6
6
  "args": [
7
7
  "--from",
8
- "vmware-storage==2.4.2",
8
+ "vmware-storage==2.5.0",
9
9
  "vmware-storage-mcp"
10
10
  ]
11
11
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: vmware-storage
3
- Version: 2.4.2
3
+ Version: 2.5.0
4
4
  Summary: VMware vSphere storage management: datastores, iSCSI, vSAN. Domain-focused MCP skill.
5
5
  Project-URL: Homepage, https://github.com/vmware-skills/VMware-Storage
6
6
  Project-URL: Repository, https://github.com/vmware-skills/VMware-Storage
@@ -22,13 +22,15 @@ Description-Content-Type: text/markdown
22
22
  <!-- mcp-name: io.github.vmware-skills/vmware-storage -->
23
23
  # VMware Storage
24
24
 
25
+ For a read-only check of this skill with your model and agent host, use the shared [model-fit self-test](https://github.com/vmware-skills/marketplace/blob/main/MODEL_FIT_SELF_TEST.md) in the marketplace root.
26
+
25
27
  > **Author**: Wei Zhou, VMware by Broadcom — wei-wz.zhou@broadcom.com
26
28
  > This is a community-driven project by a VMware engineer, not an official VMware product.
27
29
  > For official VMware developer tools see [developer.broadcom.com](https://developer.broadcom.com).
28
30
 
29
31
  [English](README.md) | [中文](README-CN.md)
30
32
 
31
- VMware vSphere storage management: datastores, iSCSI, vSAN, and read-only Fibre Channel / multipath diagnostics — 15 MCP tools, domain-focused and lightweight.
33
+ VMware vSphere storage management: datastores, iSCSI, vSAN, and read-only Fibre Channel / multipath diagnostics — 16 MCP tools, domain-focused and lightweight.
32
34
 
33
35
  > Split from vmware-aiops for lighter context and local model compatibility.
34
36
 
@@ -109,11 +111,11 @@ chmod 600 ~/.vmware-storage/.env
109
111
  vmware-storage doctor
110
112
  ```
111
113
 
112
- ## MCP Tools (15)
114
+ ## MCP Tools (16)
113
115
 
114
116
  | Category | Tools | Type |
115
117
  |----------|-------|------|
116
- | Datastore | `list_all_datastores`, `browse_datastore`, `scan_datastore_images`, `list_cached_images` | Read |
118
+ | Datastore | `list_all_datastores`, `get_datastore_vm_usage`, `browse_datastore`, `scan_datastore_images`, `list_cached_images` | Read |
117
119
  | iSCSI | `enable_iscsi`, `get_iscsi_status`, `add_iscsi_target`, `remove_iscsi_target`, `rescan_storage` | Read/Write |
118
120
  | vSAN | `get_vsan_health`, `get_vsan_capacity`, `get_vsan_efficiency` | Read |
119
121
  | FC / multipath | `list_fc_adapters`, `list_storage_device_paths` | Read |
@@ -233,7 +235,7 @@ vmware-storage-mcp
233
235
 
234
236
  `vmware-aiops` has 61 MCP tools — too heavy for local LLMs (7B-14B). By splitting storage into its own skill:
235
237
 
236
- - **15 tools** — fits comfortably in small model context windows
238
+ - **16 tools** — fits comfortably in small model context windows
237
239
  - **Domain-focused** — storage admins get only what they need
238
240
  - **Least privilege** — can run under a vCenter service account with storage read-only permissions
239
241
  - **Composable** — use alongside vmware-monitor or vmware-aiops as needed
@@ -260,8 +262,8 @@ vmware-storage-mcp
260
262
 
261
263
  | Feature | Description |
262
264
  |---------|-------------|
263
- | Read-heavy | 11/15 tools are read-only |
264
- | Preview by default (MCP) | The 4 write tools take `confirm` (default `false`): a call without it changes nothing and returns `blast_radius` (host, adapters, and for target removal the paths, devices and datastores behind the target). `confirm=true` is refused when a datastore would lose every path or part of the blast radius could not be read. `dry_run` is a deprecated alias |
265
+ | Read-heavy | 12/16 tools are read-only |
266
+ | Preview by default (MCP) | The 4 write tools take `confirm` (default `false`): a call without it changes nothing and returns `blast_radius` (host, adapters, and for target removal the paths, devices and datastores behind the target). `confirm=true` is refused when a datastore would lose every path or part of the blast radius could not be read. the old `dry_run` alias was removed in 2.4.3 and is refused as an unknown argument |
265
267
  | Input validation | IP addresses and ports validated before iSCSI operations |
266
268
  | Audit logging | All operations logged to `~/.vmware-storage/audit.log` |
267
269
  | No VM operations | Cannot create, delete, or modify VMs |
@@ -1,13 +1,15 @@
1
1
  <!-- mcp-name: io.github.vmware-skills/vmware-storage -->
2
2
  # VMware Storage
3
3
 
4
+ 要用自己的模型和 Agent 平台进行只读自测,请看 marketplace 根目录的[共享模型适配自测任务](https://github.com/vmware-skills/marketplace/blob/main/MODEL_FIT_SELF_TEST.md)。
5
+
4
6
  > **作者**: Wei Zhou, VMware by Broadcom — wei-wz.zhou@broadcom.com
5
7
  > 本项目由 VMware 工程师维护的社区项目,非 VMware 官方产品。
6
8
  > VMware 官方开发者工具请访问 [developer.broadcom.com](https://developer.broadcom.com)。
7
9
 
8
10
  [English](README.md) | [中文](README-CN.md)
9
11
 
10
- VMware vSphere 存储管理:数据存储、iSCSI、vSAN,以及只读的光纤通道(FC)/ 多路径诊断 — 15 个 MCP 工具,领域专注、轻量级。
12
+ VMware vSphere 存储管理:数据存储、iSCSI、vSAN,以及只读的光纤通道(FC)/ 多路径诊断 — 16 个 MCP 工具,领域专注、轻量级。
11
13
 
12
14
  > 从 vmware-aiops 拆分,更轻量的上下文,兼容本地小模型。
13
15
 
@@ -99,11 +101,11 @@ targets:
99
101
  password_env: VMWARE_ESXIPROD_PASSWORD
100
102
  ```
101
103
 
102
- ## MCP 工具(15 个)
104
+ ## MCP 工具(16 个)
103
105
 
104
106
  | 类别 | 工具 | 类型 |
105
107
  |------|------|------|
106
- | 数据存储 | `list_all_datastores`、`browse_datastore`、`scan_datastore_images`、`list_cached_images` | 只读 |
108
+ | 数据存储 | `list_all_datastores`、`get_datastore_vm_usage`、`browse_datastore`、`scan_datastore_images`、`list_cached_images` | 只读 |
107
109
  | iSCSI | `enable_iscsi`、`get_iscsi_status`、`add_iscsi_target`、`remove_iscsi_target`、`rescan_storage` | 读/写 |
108
110
  | vSAN | `get_vsan_health`、`get_vsan_capacity`、`get_vsan_efficiency` | 只读 |
109
111
  | FC / 多路径 | `list_fc_adapters`、`list_storage_device_paths` | 只读 |
@@ -113,6 +115,7 @@ targets:
113
115
 
114
116
  **数据存储**
115
117
  - `list_all_datastores` — 列出所有数据存储,含容量、使用率、可达性
118
+ - `get_datastore_vm_usage` — 一个数据存储上哪些虚拟机占得最多(实际占用 GB,从大到小),以及不属于任何虚拟机的空间
116
119
  - `browse_datastore` — 浏览数据存储目录下的文件(支持 glob 过滤)
117
120
  - `scan_datastore_images` — 扫描可部署镜像(OVA、ISO、OVF、VMDK)
118
121
  - `list_cached_images` — 查询本地镜像注册表(支持按类型/数据存储/target 过滤)
@@ -251,7 +254,7 @@ vmware-storage-mcp
251
254
 
252
255
  `vmware-aiops` 有 61 个 MCP 工具——对本地小模型(7B-14B)来说上下文占用太重。独立拆分后:
253
256
 
254
- - **15 个工具** — 完全适合小模型上下文窗口
257
+ - **16 个工具** — 完全适合小模型上下文窗口
255
258
  - **领域专注** — 存储管理员只看到需要的工具
256
259
  - **最小权限** — 可以配置只有存储只读权限的 vCenter 服务账号
257
260
  - **可组合** — 可与 vmware-monitor 或 vmware-aiops 同时运行
@@ -270,8 +273,8 @@ vmware-storage-mcp
270
273
 
271
274
  | 功能 | 说明 |
272
275
  |------|------|
273
- | 只读为主 | 15 个工具中 11 个只读 |
274
- | 默认只预览(MCP) | 4 个写工具接受 `confirm`(默认 `false`):不带它的调用不做任何变更,只返回 `blast_radius`(主机、适配器;移除目标时还有该目标背后的路径、设备和数据存储)。某个数据存储会失去全部路径、或影响范围有任何部分读不到时,`confirm=true` 会被拒绝。`dry_run` 为已弃用的别名 |
276
+ | 只读为主 | 16 个工具中 12 个只读 |
277
+ | 默认只预览(MCP) | 4 个写工具接受 `confirm`(默认 `false`):不带它的调用不做任何变更,只返回 `blast_radius`(主机、适配器;移除目标时还有该目标背后的路径、设备和数据存储)。某个数据存储会失去全部路径、或影响范围有任何部分读不到时,`confirm=true` 会被拒绝。旧的 `dry_run` 别名已在 2.4.3 移除,仍传它会被当作未知参数拒绝 |
275
278
  | 输入验证 | iSCSI 操作前验证 IP 地址和端口 |
276
279
  | 审计日志 | 所有操作记录到 `~/.vmware-storage/audit.log`(JSON Lines) |
277
280
  | 双重确认 | CLI iSCSI 写操作需两次确认 |
@@ -1,13 +1,15 @@
1
1
  <!-- mcp-name: io.github.vmware-skills/vmware-storage -->
2
2
  # VMware Storage
3
3
 
4
+ For a read-only check of this skill with your model and agent host, use the shared [model-fit self-test](https://github.com/vmware-skills/marketplace/blob/main/MODEL_FIT_SELF_TEST.md) in the marketplace root.
5
+
4
6
  > **Author**: Wei Zhou, VMware by Broadcom — wei-wz.zhou@broadcom.com
5
7
  > This is a community-driven project by a VMware engineer, not an official VMware product.
6
8
  > For official VMware developer tools see [developer.broadcom.com](https://developer.broadcom.com).
7
9
 
8
10
  [English](README.md) | [中文](README-CN.md)
9
11
 
10
- VMware vSphere storage management: datastores, iSCSI, vSAN, and read-only Fibre Channel / multipath diagnostics — 15 MCP tools, domain-focused and lightweight.
12
+ VMware vSphere storage management: datastores, iSCSI, vSAN, and read-only Fibre Channel / multipath diagnostics — 16 MCP tools, domain-focused and lightweight.
11
13
 
12
14
  > Split from vmware-aiops for lighter context and local model compatibility.
13
15
 
@@ -88,11 +90,11 @@ chmod 600 ~/.vmware-storage/.env
88
90
  vmware-storage doctor
89
91
  ```
90
92
 
91
- ## MCP Tools (15)
93
+ ## MCP Tools (16)
92
94
 
93
95
  | Category | Tools | Type |
94
96
  |----------|-------|------|
95
- | Datastore | `list_all_datastores`, `browse_datastore`, `scan_datastore_images`, `list_cached_images` | Read |
97
+ | Datastore | `list_all_datastores`, `get_datastore_vm_usage`, `browse_datastore`, `scan_datastore_images`, `list_cached_images` | Read |
96
98
  | iSCSI | `enable_iscsi`, `get_iscsi_status`, `add_iscsi_target`, `remove_iscsi_target`, `rescan_storage` | Read/Write |
97
99
  | vSAN | `get_vsan_health`, `get_vsan_capacity`, `get_vsan_efficiency` | Read |
98
100
  | FC / multipath | `list_fc_adapters`, `list_storage_device_paths` | Read |
@@ -212,7 +214,7 @@ vmware-storage-mcp
212
214
 
213
215
  `vmware-aiops` has 61 MCP tools — too heavy for local LLMs (7B-14B). By splitting storage into its own skill:
214
216
 
215
- - **15 tools** — fits comfortably in small model context windows
217
+ - **16 tools** — fits comfortably in small model context windows
216
218
  - **Domain-focused** — storage admins get only what they need
217
219
  - **Least privilege** — can run under a vCenter service account with storage read-only permissions
218
220
  - **Composable** — use alongside vmware-monitor or vmware-aiops as needed
@@ -239,8 +241,8 @@ vmware-storage-mcp
239
241
 
240
242
  | Feature | Description |
241
243
  |---------|-------------|
242
- | Read-heavy | 11/15 tools are read-only |
243
- | Preview by default (MCP) | The 4 write tools take `confirm` (default `false`): a call without it changes nothing and returns `blast_radius` (host, adapters, and for target removal the paths, devices and datastores behind the target). `confirm=true` is refused when a datastore would lose every path or part of the blast radius could not be read. `dry_run` is a deprecated alias |
244
+ | Read-heavy | 12/16 tools are read-only |
245
+ | Preview by default (MCP) | The 4 write tools take `confirm` (default `false`): a call without it changes nothing and returns `blast_radius` (host, adapters, and for target removal the paths, devices and datastores behind the target). `confirm=true` is refused when a datastore would lose every path or part of the blast radius could not be read. the old `dry_run` alias was removed in 2.4.3 and is refused as an unknown argument |
244
246
  | Input validation | IP addresses and ports validated before iSCSI operations |
245
247
  | Audit logging | All operations logged to `~/.vmware-storage/audit.log` |
246
248
  | No VM operations | Cannot create, delete, or modify VMs |
@@ -1,3 +1,9 @@
1
+ ## v2.5.0 — per-VM datastore usage and clearer storage refusals (2026-11-01)
2
+
3
+ * New read-only `get_datastore_vm_usage(datastore_name, top_n=10)` reports per-VM committed, uncommitted and unshared space on one datastore, plus attributed/unattributed used space and VMs whose usage could not be measured. It uses one PropertyCollector pass, sorts largest committed usage first, and does not turn unknown into zero. The MCP surface now has 16 tools. Verified against a lab vCenter before this release branch.
4
+ * An unknown cluster or invalid storage path now returns the specific corrective hint instead of a generic connection hint. On a cluster without vSAN, `get_vsan_efficiency` reports both feature flags as unknown and says vSAN is disabled.
5
+ * Removed the deprecated MCP `dry_run` alias from the destructive tools. Use `confirm=False` for a no-write preview and `confirm=True` after reviewing its blast radius; clients still passing `dry_run` must update.
6
+
1
7
  ## v2.4.2 — a login refused for lack of a vSphere role says so (2026-10-19)
2
8
 
3
9
  * **A login vCenter refuses with `NoPermission` gets the role error, not the raw fault.** Found on a vCenter 8.0.3
@@ -8,8 +14,6 @@
8
14
  neutral text naming the (sanitized) privilege. A wrong password (`InvalidLogin`) is unchanged. Same text as
9
15
  vmware-monitor.
10
16
 
11
- ## Unreleased — follow-ups to the 2.4.1 review
12
-
13
17
  * **`list_cached_images(target=...)`** returns one target's cached images; without it, nothing changes.
14
18
  * The cache write takes a lock across processes, so a CLI and an MCP scan at the same time keep both sides' rows; a
15
19
  lock held for more than 10 s is reported in `cache_note` with the lock file's path.
@@ -1512,4 +1516,4 @@ Domain-focused VMware storage skill, split from vmware-aiops for lighter context
1512
1516
 
1513
1517
  ### Datastore Management / 数据存储管理
1514
1518
 
1515
- - `list_all_datastores` — List all datastores with capacity, usage
1519
+ - `list_all_datastores` — List all datastores with capacity, usage
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "vmware-storage",
3
3
  "name": "VMware Storage",
4
- "description": "VMware vSphere storage with 15 MCP tools: datastores, iSCSI, vSAN, FC multipath.",
4
+ "description": "VMware vSphere storage with 16 MCP tools: datastores, iSCSI, vSAN, FC multipath.",
5
5
  "skills": [
6
6
  "./skills"
7
7
  ],
@@ -11,7 +11,7 @@
11
11
  "command": "uvx",
12
12
  "args": [
13
13
  "--from",
14
- "vmware-storage==2.4.2",
14
+ "vmware-storage==2.5.0",
15
15
  "vmware-storage-mcp"
16
16
  ]
17
17
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@zw008/vmware-storage",
3
- "version": "2.4.2",
4
- "description": "VMware vSphere storage with 15 MCP tools: datastores, iSCSI, vSAN, FC multipath.",
3
+ "version": "2.5.0",
4
+ "description": "VMware vSphere storage with 16 MCP tools: datastores, iSCSI, vSAN, FC multipath.",
5
5
  "private": true,
6
6
  "license": "MIT",
7
7
  "homepage": "https://github.com/vmware-skills/VMware-Storage",
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "vmware-storage"
7
- version = "2.4.2"
7
+ version = "2.5.0"
8
8
  description = "VMware vSphere storage management: datastores, iSCSI, vSAN. Domain-focused MCP skill."
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -2,17 +2,17 @@
2
2
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
3
  "name": "io.github.vmware-skills/vmware-storage",
4
4
  "title": "VMware Storage",
5
- "description": "VMware vSphere storage with 15 MCP tools: datastores, iSCSI, vSAN, FC multipath.",
5
+ "description": "VMware vSphere storage with 16 MCP tools: datastores, iSCSI, vSAN, FC multipath.",
6
6
  "repository": {
7
7
  "url": "https://github.com/vmware-skills/VMware-Storage",
8
8
  "source": "github"
9
9
  },
10
- "version": "2.4.2",
10
+ "version": "2.5.0",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "pypi",
14
14
  "identifier": "vmware-storage",
15
- "version": "2.4.2",
15
+ "version": "2.5.0",
16
16
  "runtimeHint": "uvx",
17
17
  "packageArguments": [
18
18
  {
@@ -19,9 +19,11 @@ compatibility: >
19
19
 
20
20
  # VMware Storage
21
21
 
22
+ > Self-test: [guide](https://github.com/vmware-skills/marketplace/blob/main/MODEL_FIT_SELF_TEST.md).
23
+
22
24
  > **Disclaimer**: This is a community-maintained open-source project and is **not affiliated with, endorsed by, or sponsored by VMware, Inc. or Broadcom Inc.** "VMware" and "vSphere" are trademarks of Broadcom. Source code is publicly auditable at [github.com/vmware-skills/VMware-Storage](https://github.com/vmware-skills/VMware-Storage) under the MIT license.
23
25
 
24
- VMware vSphere storage management — 15 MCP tools for datastores, iSCSI, vSAN, Fibre Channel / multipath diagnostics, and an access self-check.
26
+ VMware vSphere storage management — 16 MCP tools for datastores, iSCSI, vSAN, Fibre Channel / multipath diagnostics, and an access self-check.
25
27
 
26
28
  > Split from vmware-aiops for lighter context and local model compatibility.
27
29
  > **Companion skills**: [vmware-aiops](https://github.com/vmware-skills/VMware-AIops) (VM lifecycle), [vmware-monitor](https://github.com/vmware-skills/VMware-Monitor) (read-only monitoring), [vmware-vks](https://github.com/vmware-skills/VMware-VKS) (Tanzu Kubernetes), [vmware-nsx](https://github.com/vmware-skills/VMware-NSX) (NSX networking), [vmware-nsx-security](https://github.com/vmware-skills/VMware-NSX-Security) (DFW/firewall), [vmware-aria](https://github.com/vmware-skills/VMware-Aria) (metrics/alerts/capacity), [vmware-avi](https://github.com/vmware-skills/VMware-AVI) (AVI/ALB/AKO), [vmware-harden](https://github.com/vmware-skills/VMware-Harden) (compliance baselines).
@@ -40,7 +42,7 @@ VMware vSphere storage management — 15 MCP tools for datastores, iSCSI, vSAN,
40
42
  ## Quick Install
41
43
 
42
44
  ```bash
43
- uv tool install vmware-storage==2.4.2
45
+ uv tool install vmware-storage==2.5.0
44
46
  vmware-storage init # guided setup: writes config + .env (chmod 600, password grep-safe), then verifies
45
47
  vmware-storage doctor
46
48
  ```
@@ -147,15 +149,16 @@ vmware-storage iscsi status esxi-lab --target lab-esxi
147
149
  | Cloud models (Claude, GPT-4o) | Either | MCP gives structured JSON I/O |
148
150
  | Automated pipelines | **MCP** | Type-safe parameters, structured output |
149
151
 
150
- ## MCP Tools (15 — 11 read, 4 write)
152
+ ## MCP Tools (16 — 12 read, 4 write)
151
153
 
152
- All MCP tools accept an optional `target` parameter to select which vCenter/ESXi to connect to. The 4 write tools take `confirm` (default false): without it they change nothing and return `blast_radius` — the host and adapters touched, and for `remove_iscsi_target` the paths, devices and datastores behind the target. Show it to the user; pass `confirm: true` only after they decide. `dry_run` is a deprecated alias.
154
+ All MCP tools accept an optional `target` parameter to select which vCenter/ESXi to connect to. The 4 write tools take `confirm` (default false): without it they change nothing and return `blast_radius` — the host and adapters touched, and for `remove_iscsi_target` the paths, devices and datastores behind the target. Show it to the user; pass `confirm: true` only after they decide. the old `dry_run` alias was removed in 2.4.3 and is refused as an unknown argument.
153
155
 
154
156
  The four Datastore read tools return the family list envelope — `{items, returned, limit, total, truncated, hint}` — rather than a bare array. Read the rows from `items`; `truncated` says whether the listing is complete, so it never has to be guessed from the row count. All four enumerate their collection in full, so `total` is the real count and `truncated` is always `false`.
155
157
 
156
158
  | Category | Tool | Type | Description |
157
159
  |----------|------|:----:|-------------|
158
160
  | Datastore | `list_all_datastores` | Read | List datastores with capacity, usage %, VM count |
161
+ | | `get_datastore_vm_usage` | Read | Which VMs take the space on one datastore (committed GB, largest first) and how much no VM accounts for |
159
162
  | | `browse_datastore` | Read | Browse files with optional path and glob pattern |
160
163
  | | `scan_datastore_images` | Read | Find OVA/ISO/OVF/VMDK in a datastore |
161
164
  | | `list_cached_images` | Read | Query local image registry with type/datastore/target filters |
@@ -171,7 +174,7 @@ The four Datastore read tools return the family list envelope — `{items, retur
171
174
  | | `list_storage_device_paths` | Read | Per-device path state across a cluster/host/datastore; visibility and path-count differences |
172
175
  | Access | `get_access_info` | Read | Account in use and, per tool, whether its privileges are granted on the root folder |
173
176
 
174
- **Read/write split**: 11 tools are read-only, 4 modify state. Write tools require explicit parameters (host name, IP address), preview unless `confirm: true`, refuse when a datastore would lose every path or the host's storage view cannot be read, and are audit-logged. `remove_iscsi_target` is classified `risk:high` (destructive — LUNs can become inaccessible) and goes through the policy confirmation gate.
177
+ **Read/write split**: 12 tools are read-only, 4 modify state. Write tools require explicit parameters (host name, IP address), preview unless `confirm: true`, refuse when a datastore would lose every path or the host's storage view cannot be read, and are audit-logged. `remove_iscsi_target` is classified `risk:high` (destructive — LUNs can become inaccessible) and goes through the policy confirmation gate.
175
178
 
176
179
  Running with local or small models? See [`references/agent-guardrails.md`](references/agent-guardrails.md).
177
180
 
@@ -257,7 +260,7 @@ Corporate TLS proxies inject certificates that uv's bundled CA store doesn't tru
257
260
  ## Safety
258
261
 
259
262
  - **No VM operations**: This skill cannot power on/off, create, delete, or modify VMs — that scope belongs to `vmware-aiops`
260
- - **Read-heavy**: 11 of 15 tools are read-only (list, browse, scan, cached images, status, health, capacity, efficiency, FC adapters, device paths, access info)
263
+ - **Read-heavy**: 12 of 16 tools are read-only (list, per-VM usage, browse, scan, cached images, status, health, capacity, efficiency, FC adapters, device paths, access info)
261
264
  - **Audit logging**: All operations (including reads) are logged to `~/.vmware/audit.db` (SQLite WAL, via vmware-policy) with timestamp, user, target, operation, parameters, and result
262
265
  - **Double confirmation**: CLI write commands (iSCSI enable, add/remove target) require two separate "Are you sure?" prompts before executing
263
266
  - **Dry-run mode**: All write commands support `--dry-run` to preview API calls without executing
@@ -271,7 +274,7 @@ Corporate TLS proxies inject certificates that uv's bundled CA store doesn't tru
271
274
  ## Setup
272
275
 
273
276
  ```bash
274
- uv tool install vmware-storage==2.4.2
277
+ uv tool install vmware-storage==2.5.0
275
278
  mkdir -p ~/.vmware-storage
276
279
  cp config.example.yaml ~/.vmware-storage/config.yaml
277
280
  # Edit config.yaml with your vCenter/ESXi targets
@@ -14,7 +14,7 @@ vmware-monitor and vmware-aria against a production vSphere estate with Llama
14
14
  cross-skill rules are identical across this family; the parts below marked
15
15
  vmware-storage are specific to this skill.
16
16
 
17
- vmware-storage exposes 15 MCP tools, 4 of which change state. The write
17
+ vmware-storage exposes 16 MCP tools, 4 of which change state. The write
18
18
  surface is small but sharp: removing an iSCSI send target can make LUNs — and
19
19
  every VM living on them — inaccessible.
20
20
 
@@ -1,6 +1,6 @@
1
1
  # VMware Storage Capabilities
2
2
 
3
- All 15 MCP tools exposed by `vmware-storage-mcp`, organized by category.
3
+ All 16 MCP tools exposed by `vmware-storage-mcp`, organized by category.
4
4
 
5
5
  ## Automation Level Reference
6
6
 
@@ -8,7 +8,7 @@ Each operation is classified by autonomy level per the Enterprise Harness Engine
8
8
 
9
9
  | Level | Meaning | Agent autonomy | Examples in this skill |
10
10
  |:-:|---|---|---|
11
- | **L1** | Read-only, raw data | Always auto-run | `list_all_datastores`, `browse_datastore`, `scan_datastore_images`, `list_cached_images`, `get_iscsi_status`, vSAN status queries |
11
+ | **L1** | Read-only, raw data | Always auto-run | `list_all_datastores`, `get_datastore_vm_usage`, `browse_datastore`, `scan_datastore_images`, `list_cached_images`, `get_iscsi_status`, vSAN status queries |
12
12
  | **L2** | Read + analysis / recommendation | Always auto-run | datastore capacity analysis, image registry queries, iSCSI target health correlation |
13
13
  | **L3** | Single write — user must approve | Only after explicit confirmation; high-risk ops require double-confirm + `--dry-run` (see Confirm column) | `enable_iscsi`, `add_iscsi_target`, `remove_iscsi_target`, vSAN cluster ops |
14
14
  | **L4** | Multi-step plan / apply workflow | Plan generation auto; apply gated by user approval | *(roadmap — multi-host iSCSI rollout, vSAN expansion plans)* |
@@ -24,6 +24,7 @@ Each operation is classified by autonomy level per the Enterprise Harness Engine
24
24
  | Tool | Description | Parameters | Risk | Confirm |
25
25
  |------|-------------|------------|:----:|:-------:|
26
26
  | `list_all_datastores` | List datastores with capacity, usage %, VM count | `target` (string, optional), `limit` (int, optional; omit for all) | Low | No |
27
+ | `get_datastore_vm_usage` | Per-VM space on one datastore: committed / uncommitted / unshared GB, largest first, plus `unattributed_gb` (used space no VM accounts for) and `unmeasured_vms` | `datastore_name` (string, **required**), `top_n` (int, optional, default 10), `target` (string, optional) | Low | No |
27
28
  | `browse_datastore` | Browse files with optional path and glob pattern | `datastore_name` (string, **required**), `path` (string, optional), `pattern` (string, optional), `target` (string, optional) | Low | No |
28
29
  | `scan_datastore_images` | Find OVA/ISO/OVF/VMDK deployable images in a datastore | `datastore_name` (string, **required**), `target` (string, optional) | Low | No |
29
30
  | `list_cached_images` | Query local image registry with type/datastore/target filters | `image_type` (string, optional), `datastore` (string, optional), `target` (string, optional) | Low | No |
@@ -108,7 +109,7 @@ Source: each host's `config.storageDevice` (HBAs, SCSI LUNs, `multipathInfo`) an
108
109
 
109
110
  ## Audit Coverage
110
111
 
111
- All 15 tools are wrapped with `@vmware_tool` from vmware-policy, which provides:
112
+ All 16 tools are wrapped with `@vmware_tool` from vmware-policy, which provides:
112
113
 
113
114
  - **Pre-execution**: Policy rule check against `~/.vmware/rules.yaml` (deny rules, maintenance windows)
114
115
  - **Post-execution**: Audit log entry written to `~/.vmware/audit.db` (SQLite WAL mode)
@@ -118,7 +119,7 @@ All 15 tools are wrapped with `@vmware_tool` from vmware-policy, which provides:
118
119
 
119
120
  | Type | Count | Tools |
120
121
  |------|:-----:|-------|
121
- | Read | 10 | `list_all_datastores`, `browse_datastore`, `scan_datastore_images`, `list_cached_images`, `get_iscsi_status`, `get_vsan_health`, `get_vsan_capacity`, `get_vsan_efficiency`, `list_fc_adapters`, `list_storage_device_paths` |
122
+ | Read | 11 | `list_all_datastores`, `get_datastore_vm_usage`, `browse_datastore`, `scan_datastore_images`, `list_cached_images`, `get_iscsi_status`, `get_vsan_health`, `get_vsan_capacity`, `get_vsan_efficiency`, `list_fc_adapters`, `list_storage_device_paths` |
122
123
  | Write | 4 | `enable_iscsi`, `add_iscsi_target`, `remove_iscsi_target`, `rescan_storage` |
123
124
 
124
125
  > Write tools require explicit parameters (host name, IP address) and support `--dry-run` in CLI mode. Over MCP they preview by default: without `confirm: true` they return `blast_radius` and change nothing. All write operations are audit-logged with timestamp, user, target, operation, parameters, and result.
@@ -13,19 +13,19 @@ Complete setup and security guide for `vmware-storage`.
13
13
  ### Via uv (recommended)
14
14
 
15
15
  ```bash
16
- uv tool install vmware-storage==2.4.2
16
+ uv tool install vmware-storage==2.5.0
17
17
  ```
18
18
 
19
19
  ### Via pip
20
20
 
21
21
  ```bash
22
- pip install vmware-storage==2.4.2
22
+ pip install vmware-storage==2.5.0
23
23
  ```
24
24
 
25
25
  ### From source
26
26
 
27
27
  ```bash
28
- git clone --branch v2.4.2 https://github.com/vmware-skills/VMware-Storage.git
28
+ git clone --branch v2.5.0 https://github.com/vmware-skills/VMware-Storage.git
29
29
  cd VMware-Storage
30
30
  pip install -e .
31
31
  ```
@@ -121,10 +121,17 @@ def test_an_audited_write_lands_in_the_sandbox_and_not_in_the_real_db() -> None:
121
121
  real_db = REAL_HOME / ".vmware" / "audit.db"
122
122
  if not real_db.exists():
123
123
  pytest.skip("no production audit database on this machine to check against")
124
- with sqlite3.connect(f"file:{real_db}?mode=ro", uri=True) as con:
125
- leaked = con.execute(
126
- "SELECT count(*) FROM audit_log WHERE tool = ?", (marker,)
127
- ).fetchone()[0]
124
+ try:
125
+ with sqlite3.connect(f"file:{real_db}?mode=ro", uri=True) as con:
126
+ leaked = con.execute(
127
+ "SELECT count(*) FROM audit_log WHERE tool = ?", (marker,)
128
+ ).fetchone()[0]
129
+ except sqlite3.OperationalError as exc:
130
+ # A restricted runner may see the file but have no permission to open it.
131
+ # The sandbox write assertion above still ran; this external check did not.
132
+ if "unable to open database file" in str(exc):
133
+ pytest.skip("production audit database is outside this runner's readable roots")
134
+ raise
128
135
  assert leaked == 0, (
129
136
  f"{leaked} row(s) from this test reached the operator's audit database "
130
137
  f"at {real_db}"
@@ -88,7 +88,9 @@ def test_a_datastore_over_the_threshold_is_still_styled_red() -> None:
88
88
  assert cool.style != "red"
89
89
 
90
90
  # ...and the style survives to the terminal, not just the object.
91
- console = Console(force_terminal=True, width=40)
91
+ # The runner may set TERM=dumb or NO_COLOR=1. Test Rich's colour rendering
92
+ # explicitly, independent of the surrounding terminal policy.
93
+ console = Console(force_terminal=True, color_system="standard", no_color=False, width=40)
92
94
  with console.capture() as cap:
93
95
  console.print(hot)
94
96
  assert "\x1b[31m" in cap.get()
@@ -101,11 +101,11 @@ def test_iscsi_status_risk_level_is_low():
101
101
 
102
102
 
103
103
  @pytest.mark.parametrize("tool_name", _WRITE_TOOLS)
104
- def test_write_tools_take_confirm_and_keep_dry_run_as_alias(tool_name):
105
- """HLD §7: ``confirm`` defaults to False; ``dry_run`` is a deprecated alias.
104
+ def test_write_tools_take_confirm_and_no_dry_run(tool_name):
105
+ """HLD §7: ``confirm`` defaults to False; the ``dry_run`` alias is gone (2.4.3).
106
106
 
107
107
  Until the confirmation gate, ``dry_run`` defaulted to False, so a bare call
108
- acted. The alias now defaults to None so the schema shows it was not passed.
108
+ acted. It was a deprecated alias for one minor cycle and is now removed.
109
109
  """
110
110
  import inspect
111
111
 
@@ -114,7 +114,7 @@ def test_write_tools_take_confirm_and_keep_dry_run_as_alias(tool_name):
114
114
  fn = getattr(server, tool_name)
115
115
  sig = inspect.signature(inspect.unwrap(fn))
116
116
  assert sig.parameters["confirm"].default is False
117
- assert sig.parameters["dry_run"].default is None
117
+ assert "dry_run" not in sig.parameters
118
118
 
119
119
 
120
120
  @pytest.mark.parametrize(
@@ -126,8 +126,8 @@ def test_write_tools_take_confirm_and_keep_dry_run_as_alias(tool_name):
126
126
  ("rescan_storage", {"host_name": "esxi-01"}),
127
127
  ],
128
128
  )
129
- def test_dry_run_previews_without_writing(tool_name, kwargs, monkeypatch):
130
- """dry_run=True must return a preview and never reach an executor.
129
+ def test_a_bare_call_previews_without_writing(tool_name, kwargs, monkeypatch):
130
+ """A call without confirm must return a preview and never reach an executor.
131
131
 
132
132
  It used to return a canned string without connecting. The preview now
133
133
  measures the blast radius (HLD §7), which needs the connection, so the
@@ -138,7 +138,7 @@ def test_dry_run_previews_without_writing(tool_name, kwargs, monkeypatch):
138
138
  from vmware_storage.mcp_server import server
139
139
 
140
140
  def explode(*a, **k): # pragma: no cover - failure path
141
- raise AssertionError("a dry_run preview must not reach an executor")
141
+ raise AssertionError("a preview must not reach an executor")
142
142
 
143
143
  radius = {"already_enabled": False, "already_configured": False,
144
144
  "blockers": [], "unmeasured": []}
@@ -152,9 +152,9 @@ def test_dry_run_previews_without_writing(tool_name, kwargs, monkeypatch):
152
152
  "_ops_remove_iscsi_target", "_ops_rescan_storage"):
153
153
  monkeypatch.setattr(server, executor, explode)
154
154
  fn = inspect.unwrap(getattr(server, tool_name))
155
- result = fn(dry_run=True, **kwargs)
155
+ result = fn(**kwargs)
156
156
  assert result["action"] == "preview"
157
- assert "dry_run is deprecated" in result["deprecated"]
157
+ assert "deprecated" not in result
158
158
 
159
159
 
160
160
  # ── Fix 3: audit failure must not flip a successful write ──────────────────
@@ -232,12 +232,12 @@ def _list_tools():
232
232
  return asyncio.run(mcp.list_tools())
233
233
 
234
234
 
235
- def test_mcp_exposes_15_tools_11_read_4_write():
235
+ def test_mcp_exposes_16_tools_12_read_4_write():
236
236
  tools = _list_tools()
237
- assert len(tools) == 15, f"Expected 15 MCP tools, got {len(tools)}: {[t.name for t in tools]}"
237
+ assert len(tools) == 16, f"Expected 16 MCP tools, got {len(tools)}: {[t.name for t in tools]}"
238
238
  read = [t.name for t in tools if t.annotations and t.annotations.readOnlyHint]
239
239
  write = [t.name for t in tools if not (t.annotations and t.annotations.readOnlyHint)]
240
- assert len(read) == 11, f"Expected 11 read tools, got {len(read)}: {read}"
240
+ assert len(read) == 12, f"Expected 12 read tools, got {len(read)}: {read}"
241
241
  assert len(write) == 4, f"Expected 4 write tools, got {len(write)}: {write}"
242
242
  assert sorted(write) == sorted(_WRITE_TOOLS)
243
243
 
@@ -178,6 +178,23 @@ def test_efficiency_absent_fields_do_not_crash(patched, monkeypatch):
178
178
  assert result["compression_enabled"] is None
179
179
 
180
180
 
181
+ def test_vsan_off_reports_both_flags_as_not_applicable(patched, monkeypatch):
182
+ # 2026-10-24, 8.0.3 lab, vSAN off: the SDK answered dedupEnabled=False with
183
+ # compressionEnabled absent, so the tool said dedup "off" and compression
184
+ # "unknown" about a feature that does not apply.
185
+ config = SimpleNamespace(
186
+ enabled=False,
187
+ dataEfficiencyConfig=SimpleNamespace(dedupEnabled=False),
188
+ )
189
+ monkeypatch.setattr(ops, "_config_system", lambda si: _config_system_returning(config))
190
+
191
+ result = ops.get_vsan_efficiency(object(), "prod-cluster")
192
+
193
+ assert result["vsan_enabled"] is False
194
+ assert result["dedup_enabled"] is None and result["compression_enabled"] is None
195
+ assert result["message"] == "vSAN is not enabled on cluster 'prod-cluster'"
196
+
197
+
181
198
  def test_config_system_teaches_when_stub_absent(monkeypatch):
182
199
  # A session with no _stub gets a teaching VSANError, not an AttributeError.
183
200
  # VSANError is imported from its home now that vsan_efficiency no longer