vmware-storage 1.2.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.
@@ -0,0 +1,13 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.pyo
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .eggs/
8
+ .env
9
+ *.log
10
+ .DS_Store
11
+ .venv/
12
+ .mcpregistry_*
13
+ mcp-publisher
@@ -0,0 +1,13 @@
1
+ FROM python:3.12-slim
2
+
3
+ WORKDIR /app
4
+
5
+ RUN pip install --no-cache-dir uv
6
+
7
+ COPY pyproject.toml .
8
+ COPY vmware_storage/ vmware_storage/
9
+ COPY mcp_server/ mcp_server/
10
+
11
+ RUN uv pip install --system .
12
+
13
+ CMD ["python", "-m", "mcp_server"]
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Wei Zhou
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 DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,148 @@
1
+ Metadata-Version: 2.4
2
+ Name: vmware-storage
3
+ Version: 1.2.0
4
+ Summary: VMware vSphere storage management: datastores, iSCSI, vSAN. Domain-focused MCP skill.
5
+ Author: Wei Zhou / 周崴
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.10
9
+ Requires-Dist: mcp[cli]<2.0,>=1.0
10
+ Requires-Dist: pyaml<27.0,>=24.0
11
+ Requires-Dist: python-dotenv<2.0,>=1.0
12
+ Requires-Dist: pyvmomi<10.0,>=8.0.3.0
13
+ Requires-Dist: rich<15.0,>=13.0
14
+ Requires-Dist: typer<1.0,>=0.12
15
+ Provides-Extra: dev
16
+ Requires-Dist: pytest-cov<8.0,>=5.0; extra == 'dev'
17
+ Requires-Dist: pytest<10.0,>=8.0; extra == 'dev'
18
+ Requires-Dist: ruff<1.0,>=0.5; extra == 'dev'
19
+ Description-Content-Type: text/markdown
20
+
21
+ <!-- mcp-name: io.github.zw008/vmware-storage -->
22
+ # VMware Storage
23
+
24
+ [English](README.md) | [中文](README-CN.md)
25
+
26
+ Domain-focused VMware vSphere storage management: datastores, iSCSI, vSAN.
27
+
28
+ > **Part of the VMware MCP Skills family:**
29
+ >
30
+ > | Skill | Scope | Tools |
31
+ > |-------|-------|:-----:|
32
+ > | **vmware-monitor** (read-only) | Inventory, health, alarms, events | 8 |
33
+ > | **vmware-aiops** (full ops) | VM lifecycle, deployment, guest ops, plans | 33 |
34
+ > | **vmware-storage** (this) | Datastores, iSCSI, vSAN | 11 |
35
+
36
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
37
+
38
+ ## Quick Install
39
+
40
+ ```bash
41
+ # Via PyPI
42
+ uv tool install vmware-storage
43
+
44
+ # Or pip
45
+ pip install vmware-storage
46
+ ```
47
+
48
+ ## Configuration
49
+
50
+ ```bash
51
+ mkdir -p ~/.vmware-storage
52
+ cp config.example.yaml ~/.vmware-storage/config.yaml
53
+ # Edit with your vCenter/ESXi credentials
54
+
55
+ echo "VMWARE_MY_VCENTER_PASSWORD=your_password" > ~/.vmware-storage/.env
56
+ chmod 600 ~/.vmware-storage/.env
57
+
58
+ # Verify
59
+ vmware-storage doctor
60
+ ```
61
+
62
+ ## MCP Tools (11)
63
+
64
+ | Category | Tools | Type |
65
+ |----------|-------|------|
66
+ | Datastore | `list_all_datastores`, `browse_datastore`, `scan_datastore_images`, `list_cached_images` | Read |
67
+ | iSCSI | `storage_iscsi_enable`, `storage_iscsi_status`, `storage_iscsi_add_target`, `storage_iscsi_remove_target`, `storage_rescan` | Read/Write |
68
+ | vSAN | `vsan_health`, `vsan_capacity` | Read |
69
+
70
+ ## CLI
71
+
72
+ ```bash
73
+ # Datastore
74
+ vmware-storage datastore list
75
+ vmware-storage datastore browse datastore01
76
+ vmware-storage datastore scan-images datastore01
77
+
78
+ # iSCSI
79
+ vmware-storage iscsi status esxi-01
80
+ vmware-storage iscsi enable esxi-01
81
+ vmware-storage iscsi add-target esxi-01 192.168.1.100
82
+ vmware-storage iscsi remove-target esxi-01 192.168.1.100
83
+ vmware-storage iscsi rescan esxi-01
84
+
85
+ # vSAN
86
+ vmware-storage vsan health Cluster-Prod
87
+ vmware-storage vsan capacity Cluster-Prod
88
+
89
+ # Diagnostics
90
+ vmware-storage doctor
91
+ ```
92
+
93
+ ## MCP Server
94
+
95
+ ```bash
96
+ # Run directly
97
+ python -m mcp_server
98
+
99
+ # Or via Docker
100
+ docker compose up -d
101
+ ```
102
+
103
+ ### Agent Configuration
104
+
105
+ Add to your AI agent's MCP config:
106
+
107
+ ```json
108
+ {
109
+ "mcpServers": {
110
+ "vmware-storage": {
111
+ "command": "vmware-storage-mcp",
112
+ "env": {
113
+ "VMWARE_STORAGE_CONFIG": "~/.vmware-storage/config.yaml"
114
+ }
115
+ }
116
+ }
117
+ }
118
+ ```
119
+
120
+ ## Why a Separate Skill?
121
+
122
+ `vmware-aiops` has 33 MCP tools — too heavy for local LLMs (7B-14B). By splitting storage into its own skill:
123
+
124
+ - **11 tools** — fits comfortably in small model context windows
125
+ - **Domain-focused** — storage admins get only what they need
126
+ - **Composable** — use alongside vmware-monitor or vmware-aiops as needed
127
+
128
+ ## Version Compatibility
129
+
130
+ | vSphere | Support | Notes |
131
+ |---------|---------|-------|
132
+ | 8.0 | Full | vSAN SDK built into pyVmomi 8.0.3+ |
133
+ | 7.0 | Full | All storage APIs work |
134
+ | 6.7 | Compatible | iSCSI + datastore features work; vSAN limited |
135
+
136
+ ## Safety
137
+
138
+ | Feature | Description |
139
+ |---------|-------------|
140
+ | Read-heavy | 6/11 tools are read-only |
141
+ | Input validation | IP addresses and ports validated before iSCSI operations |
142
+ | Audit logging | All operations logged to `~/.vmware-storage/audit.log` |
143
+ | No VM operations | Cannot create, delete, or modify VMs |
144
+ | Credential safety | Passwords only from environment variables, never config files |
145
+
146
+ ## License
147
+
148
+ [MIT](LICENSE)
@@ -0,0 +1,128 @@
1
+ <!-- mcp-name: io.github.zw008/vmware-storage -->
2
+ # VMware Storage
3
+
4
+ [English](README.md) | [中文](README-CN.md)
5
+
6
+ Domain-focused VMware vSphere storage management: datastores, iSCSI, vSAN.
7
+
8
+ > **Part of the VMware MCP Skills family:**
9
+ >
10
+ > | Skill | Scope | Tools |
11
+ > |-------|-------|:-----:|
12
+ > | **vmware-monitor** (read-only) | Inventory, health, alarms, events | 8 |
13
+ > | **vmware-aiops** (full ops) | VM lifecycle, deployment, guest ops, plans | 33 |
14
+ > | **vmware-storage** (this) | Datastores, iSCSI, vSAN | 11 |
15
+
16
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
17
+
18
+ ## Quick Install
19
+
20
+ ```bash
21
+ # Via PyPI
22
+ uv tool install vmware-storage
23
+
24
+ # Or pip
25
+ pip install vmware-storage
26
+ ```
27
+
28
+ ## Configuration
29
+
30
+ ```bash
31
+ mkdir -p ~/.vmware-storage
32
+ cp config.example.yaml ~/.vmware-storage/config.yaml
33
+ # Edit with your vCenter/ESXi credentials
34
+
35
+ echo "VMWARE_MY_VCENTER_PASSWORD=your_password" > ~/.vmware-storage/.env
36
+ chmod 600 ~/.vmware-storage/.env
37
+
38
+ # Verify
39
+ vmware-storage doctor
40
+ ```
41
+
42
+ ## MCP Tools (11)
43
+
44
+ | Category | Tools | Type |
45
+ |----------|-------|------|
46
+ | Datastore | `list_all_datastores`, `browse_datastore`, `scan_datastore_images`, `list_cached_images` | Read |
47
+ | iSCSI | `storage_iscsi_enable`, `storage_iscsi_status`, `storage_iscsi_add_target`, `storage_iscsi_remove_target`, `storage_rescan` | Read/Write |
48
+ | vSAN | `vsan_health`, `vsan_capacity` | Read |
49
+
50
+ ## CLI
51
+
52
+ ```bash
53
+ # Datastore
54
+ vmware-storage datastore list
55
+ vmware-storage datastore browse datastore01
56
+ vmware-storage datastore scan-images datastore01
57
+
58
+ # iSCSI
59
+ vmware-storage iscsi status esxi-01
60
+ vmware-storage iscsi enable esxi-01
61
+ vmware-storage iscsi add-target esxi-01 192.168.1.100
62
+ vmware-storage iscsi remove-target esxi-01 192.168.1.100
63
+ vmware-storage iscsi rescan esxi-01
64
+
65
+ # vSAN
66
+ vmware-storage vsan health Cluster-Prod
67
+ vmware-storage vsan capacity Cluster-Prod
68
+
69
+ # Diagnostics
70
+ vmware-storage doctor
71
+ ```
72
+
73
+ ## MCP Server
74
+
75
+ ```bash
76
+ # Run directly
77
+ python -m mcp_server
78
+
79
+ # Or via Docker
80
+ docker compose up -d
81
+ ```
82
+
83
+ ### Agent Configuration
84
+
85
+ Add to your AI agent's MCP config:
86
+
87
+ ```json
88
+ {
89
+ "mcpServers": {
90
+ "vmware-storage": {
91
+ "command": "vmware-storage-mcp",
92
+ "env": {
93
+ "VMWARE_STORAGE_CONFIG": "~/.vmware-storage/config.yaml"
94
+ }
95
+ }
96
+ }
97
+ }
98
+ ```
99
+
100
+ ## Why a Separate Skill?
101
+
102
+ `vmware-aiops` has 33 MCP tools — too heavy for local LLMs (7B-14B). By splitting storage into its own skill:
103
+
104
+ - **11 tools** — fits comfortably in small model context windows
105
+ - **Domain-focused** — storage admins get only what they need
106
+ - **Composable** — use alongside vmware-monitor or vmware-aiops as needed
107
+
108
+ ## Version Compatibility
109
+
110
+ | vSphere | Support | Notes |
111
+ |---------|---------|-------|
112
+ | 8.0 | Full | vSAN SDK built into pyVmomi 8.0.3+ |
113
+ | 7.0 | Full | All storage APIs work |
114
+ | 6.7 | Compatible | iSCSI + datastore features work; vSAN limited |
115
+
116
+ ## Safety
117
+
118
+ | Feature | Description |
119
+ |---------|-------------|
120
+ | Read-heavy | 6/11 tools are read-only |
121
+ | Input validation | IP addresses and ports validated before iSCSI operations |
122
+ | Audit logging | All operations logged to `~/.vmware-storage/audit.log` |
123
+ | No VM operations | Cannot create, delete, or modify VMs |
124
+ | Credential safety | Passwords only from environment variables, never config files |
125
+
126
+ ## License
127
+
128
+ [MIT](LICENSE)
@@ -0,0 +1,39 @@
1
+ # Release Notes
2
+
3
+ ## v1.2.0 (2026-03-22)
4
+
5
+ ### Initial Release / 首次发布
6
+
7
+ Domain-focused VMware storage skill, split from vmware-aiops for lighter context and better local model compatibility.
8
+
9
+ 从 vmware-aiops 中按领域拆分出的存储管理 skill,更轻量,对本地模型更友好。
10
+
11
+ ### Datastore Management / 数据存储管理
12
+
13
+ - `list_all_datastores` — List all datastores with capacity, usage %, accessibility
14
+ - `browse_datastore` — Browse files in any datastore directory
15
+ - `scan_datastore_images` — Find OVA, ISO, OVF, VMDK across datastores
16
+ - `list_cached_images` — Query local image registry with filters
17
+
18
+ ### iSCSI Configuration / iSCSI 配置
19
+
20
+ - `storage_iscsi_enable` — Enable software iSCSI adapter on ESXi hosts
21
+ - `storage_iscsi_status` — Show adapter status and configured targets
22
+ - `storage_iscsi_add_target` — Add iSCSI send target with auto-rescan
23
+ - `storage_iscsi_remove_target` — Remove target with auto-rescan
24
+ - `storage_rescan` — Force rescan all HBAs and VMFS volumes
25
+
26
+ ### vSAN Monitoring / vSAN 监控
27
+
28
+ - `vsan_health` — Cluster health summary with disk group details
29
+ - `vsan_capacity` — Total/used/free capacity with usage percentage
30
+
31
+ ### Infrastructure / 基础设施
32
+
33
+ - CLI (`vmware-storage`) with typer — datastore/iscsi/vsan subcommands
34
+ - MCP server (11 tools) via stdio transport
35
+ - Docker one-command launch
36
+ - `vmware-storage doctor` — 6-check environment diagnostics
37
+ - Audit logging (JSON Lines)
38
+
39
+ **PyPI**: `uv tool install vmware-storage==1.2.0`
@@ -0,0 +1,10 @@
1
+ targets:
2
+ - name: my-vcenter
3
+ host: vcenter.example.com
4
+ username: administrator@vsphere.local
5
+ type: vcenter
6
+ port: 443
7
+ verify_ssl: false
8
+
9
+ notify:
10
+ webhook_url: ""
@@ -0,0 +1,8 @@
1
+ services:
2
+ vmware-storage-mcp:
3
+ build: .
4
+ volumes:
5
+ - ~/.vmware-storage:/root/.vmware-storage:ro
6
+ environment:
7
+ - VMWARE_STORAGE_CONFIG=/root/.vmware-storage/config.yaml
8
+ stdin_open: true
File without changes
@@ -0,0 +1,5 @@
1
+ """Entry point for: python -m mcp_server"""
2
+
3
+ from mcp_server.server import main
4
+
5
+ main()
@@ -0,0 +1,286 @@
1
+ """MCP server wrapping VMware Storage operations.
2
+
3
+ This module exposes VMware vSphere datastore browsing, iSCSI configuration,
4
+ and vSAN health/capacity tools via the Model Context Protocol (MCP) using
5
+ stdio transport.
6
+
7
+ Tool categories
8
+ ---------------
9
+ * **Read-only**: list_all_datastores, browse_datastore, scan_datastore_images,
10
+ list_cached_images, storage_iscsi_status, vsan_health, vsan_capacity
11
+ * **Write**: storage_iscsi_enable, storage_iscsi_add_target,
12
+ storage_iscsi_remove_target, storage_rescan
13
+
14
+ Security considerations
15
+ -----------------------
16
+ * Credentials are loaded from environment variables / .env file.
17
+ * Transport: Uses stdio transport (local only); no network listener.
18
+ * iSCSI operations modify host storage configuration; confirmation recommended.
19
+
20
+ Source: https://github.com/zw008/VMware-Storage
21
+ License: MIT
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import logging
27
+ import os
28
+ from pathlib import Path
29
+ from typing import Any
30
+
31
+ from mcp.server.fastmcp import FastMCP
32
+
33
+ from vmware_storage.config import load_config
34
+ from vmware_storage.connection import ConnectionManager
35
+ from vmware_storage.ops import datastore_browser
36
+ from vmware_storage.ops.inventory import list_datastores
37
+ from vmware_storage.ops.iscsi_config import (
38
+ add_iscsi_target,
39
+ enable_software_iscsi,
40
+ get_iscsi_status,
41
+ remove_iscsi_target,
42
+ rescan_storage,
43
+ )
44
+ from vmware_storage.ops.vsan import get_vsan_capacity, get_vsan_health
45
+ from vmware_storage.notify.audit import AuditLogger
46
+
47
+ logging.basicConfig(level=logging.INFO)
48
+ logger = logging.getLogger("vmware-storage.mcp")
49
+
50
+ mcp = FastMCP("VMware Storage")
51
+
52
+ _audit = AuditLogger()
53
+
54
+ # ---------------------------------------------------------------------------
55
+ # Connection management (lazy-init singleton)
56
+ # ---------------------------------------------------------------------------
57
+
58
+ _conn_mgr: ConnectionManager | None = None
59
+
60
+
61
+ def _get_conn_mgr() -> ConnectionManager:
62
+ global _conn_mgr
63
+ if _conn_mgr is None:
64
+ config_path = os.environ.get("VMWARE_STORAGE_CONFIG")
65
+ config = load_config(Path(config_path) if config_path else None)
66
+ _conn_mgr = ConnectionManager(config)
67
+ return _conn_mgr
68
+
69
+
70
+ def _get_connection(target: str | None = None):
71
+ return _get_conn_mgr().connect(target)
72
+
73
+
74
+ # ---------------------------------------------------------------------------
75
+ # Datastore tools
76
+ # ---------------------------------------------------------------------------
77
+
78
+
79
+ @mcp.tool()
80
+ def list_all_datastores(target: str | None = None) -> list[dict]:
81
+ """List all datastores with capacity, usage percentage, and accessibility.
82
+
83
+ Args:
84
+ target: Optional vCenter/ESXi target name from config.
85
+ """
86
+ si = _get_connection(target)
87
+ return list_datastores(si)
88
+
89
+
90
+ @mcp.tool()
91
+ def browse_datastore(
92
+ ds_name: str,
93
+ path: str = "",
94
+ pattern: str = "*",
95
+ target: str | None = None,
96
+ ) -> list[dict]:
97
+ """Browse files in a datastore directory.
98
+
99
+ Args:
100
+ ds_name: Datastore name.
101
+ path: Subdirectory path (empty for root).
102
+ pattern: Glob pattern to filter files (e.g. "*.ova", "*.iso").
103
+ target: Optional vCenter/ESXi target name from config.
104
+ """
105
+ si = _get_connection(target)
106
+ return datastore_browser.browse_datastore(si, ds_name, path=path, pattern=pattern)
107
+
108
+
109
+ @mcp.tool()
110
+ def scan_datastore_images(
111
+ ds_name: str,
112
+ path: str = "",
113
+ target: str | None = None,
114
+ ) -> list[dict]:
115
+ """Scan a datastore for deployable images (OVA, ISO, OVF, VMDK).
116
+
117
+ Args:
118
+ ds_name: Datastore name.
119
+ path: Subdirectory path (empty for root).
120
+ target: Optional vCenter/ESXi target name from config.
121
+ """
122
+ si = _get_connection(target)
123
+ return datastore_browser.scan_images(si, ds_name, path=path)
124
+
125
+
126
+ @mcp.tool()
127
+ def list_cached_images(
128
+ image_type: str | None = None,
129
+ datastore: str | None = None,
130
+ ) -> list[dict]:
131
+ """List images from the local cache registry.
132
+
133
+ Args:
134
+ image_type: Filter by extension (e.g. "ova", "iso").
135
+ datastore: Filter by datastore name.
136
+ """
137
+ return datastore_browser.list_images(image_type=image_type, datastore=datastore)
138
+
139
+
140
+ # ---------------------------------------------------------------------------
141
+ # iSCSI tools
142
+ # ---------------------------------------------------------------------------
143
+
144
+
145
+ @mcp.tool()
146
+ def storage_iscsi_enable(
147
+ host_name: str,
148
+ target: str | None = None,
149
+ ) -> str:
150
+ """Enable the software iSCSI adapter on an ESXi host.
151
+
152
+ Args:
153
+ host_name: ESXi host name.
154
+ target: Optional vCenter/ESXi target name from config.
155
+ """
156
+ si = _get_connection(target)
157
+ result = enable_software_iscsi(si, host_name)
158
+ _audit.log(target=target or "default", operation="iscsi_enable",
159
+ resource=host_name, parameters={"host_name": host_name}, result=result)
160
+ return result
161
+
162
+
163
+ @mcp.tool()
164
+ def storage_iscsi_status(
165
+ host_name: str,
166
+ target: str | None = None,
167
+ ) -> dict:
168
+ """Get iSCSI adapter status and configured send targets.
169
+
170
+ Args:
171
+ host_name: ESXi host name.
172
+ target: Optional vCenter/ESXi target name from config.
173
+ """
174
+ si = _get_connection(target)
175
+ return get_iscsi_status(si, host_name)
176
+
177
+
178
+ @mcp.tool()
179
+ def storage_iscsi_add_target(
180
+ host_name: str,
181
+ address: str,
182
+ port: int = 3260,
183
+ target: str | None = None,
184
+ ) -> str:
185
+ """Add an iSCSI send target to an ESXi host and rescan storage.
186
+
187
+ Args:
188
+ host_name: ESXi host name.
189
+ address: iSCSI target IP address.
190
+ port: iSCSI target port (default 3260).
191
+ target: Optional vCenter/ESXi target name from config.
192
+ """
193
+ si = _get_connection(target)
194
+ result = add_iscsi_target(si, host_name, address, port)
195
+ _audit.log(target=target or "default", operation="iscsi_add_target",
196
+ resource=host_name,
197
+ parameters={"host_name": host_name, "address": address, "port": port},
198
+ result=result)
199
+ return result
200
+
201
+
202
+ @mcp.tool()
203
+ def storage_iscsi_remove_target(
204
+ host_name: str,
205
+ address: str,
206
+ port: int = 3260,
207
+ target: str | None = None,
208
+ ) -> str:
209
+ """Remove an iSCSI send target from an ESXi host and rescan storage.
210
+
211
+ Args:
212
+ host_name: ESXi host name.
213
+ address: iSCSI target IP address.
214
+ port: iSCSI target port (default 3260).
215
+ target: Optional vCenter/ESXi target name from config.
216
+ """
217
+ si = _get_connection(target)
218
+ result = remove_iscsi_target(si, host_name, address, port)
219
+ _audit.log(target=target or "default", operation="iscsi_remove_target",
220
+ resource=host_name,
221
+ parameters={"host_name": host_name, "address": address, "port": port},
222
+ result=result)
223
+ return result
224
+
225
+
226
+ @mcp.tool()
227
+ def storage_rescan(
228
+ host_name: str,
229
+ target: str | None = None,
230
+ ) -> str:
231
+ """Rescan all HBAs and VMFS volumes on an ESXi host.
232
+
233
+ Args:
234
+ host_name: ESXi host name.
235
+ target: Optional vCenter/ESXi target name from config.
236
+ """
237
+ si = _get_connection(target)
238
+ result = rescan_storage(si, host_name)
239
+ _audit.log(target=target or "default", operation="storage_rescan",
240
+ resource=host_name, parameters={"host_name": host_name}, result=result)
241
+ return result
242
+
243
+
244
+ # ---------------------------------------------------------------------------
245
+ # vSAN tools
246
+ # ---------------------------------------------------------------------------
247
+
248
+
249
+ @mcp.tool()
250
+ def vsan_health(
251
+ cluster_name: str,
252
+ target: str | None = None,
253
+ ) -> dict:
254
+ """Get vSAN cluster health summary and disk groups.
255
+
256
+ Args:
257
+ cluster_name: Name of the vSAN-enabled cluster.
258
+ target: Optional vCenter/ESXi target name from config.
259
+ """
260
+ si = _get_connection(target)
261
+ return get_vsan_health(si, cluster_name)
262
+
263
+
264
+ @mcp.tool()
265
+ def vsan_capacity(
266
+ cluster_name: str,
267
+ target: str | None = None,
268
+ ) -> dict:
269
+ """Get vSAN capacity overview (total/used/free) for a cluster.
270
+
271
+ Args:
272
+ cluster_name: Name of the vSAN-enabled cluster.
273
+ target: Optional vCenter/ESXi target name from config.
274
+ """
275
+ si = _get_connection(target)
276
+ return get_vsan_capacity(si, cluster_name)
277
+
278
+
279
+ # ---------------------------------------------------------------------------
280
+ # Entry point
281
+ # ---------------------------------------------------------------------------
282
+
283
+
284
+ def main():
285
+ """Run the MCP server."""
286
+ mcp.run(transport="stdio")