Skip to main content
Glama
mcp-vmwhere
by Bright0505
README.md
# mcp-vmwhere

一個**唯讀**的 MCP server,讓 Claude 能查詢 VMware vCenter 環境。

- 只讀不控 — 不提供任何開關機、修改、刪除等操作。
- 透過 vSphere Automation **REST API**(`/api/vcenter/*`)呼叫 vCenter。
- 僅發出 `GET` 請求查詢資訊(唯一例外是 session 登入/登出)。
- 遵循 **MCP 2026-07-28** 規範(MCP Python SDK 2.x)。

## MCP 2.x 規範對應

| 規範要點 | 本專案實作 |
|---|---|
| Stateless protocol core(2026-07-28) | 使用 SDK 2.x 的 `MCPServer`,協定版本 `2026-07-28` |
| Tool annotations | 所有 tool 標註 `readOnlyHint=true`、`destructiveHint=false`、`idempotentHint=true` |
| Structured output | 每個 tool 都有 `outputSchema`,回傳 `structuredContent`(見 `models.py`) |
| Tool title / server title | server 與 tool 皆提供人類可讀的 `title` |
| Cacheable list results | `tools/list`、`server/discover` 帶 cache hint(TTL 1 小時、`public`) |
| Lifespan 資源管理 | vCenter session 由 server lifespan 建立,結束時自動登出 |

## 需求

- Python 3.10+
- vCenter Server 7.0 / 8.0(使用 `/api/vcenter/*` REST 路徑)
- 一組具備唯讀(Read-only)權限的 vCenter 帳號即可

## 安裝

```bash
pip install -e .
```

相依套件:`mcp>=2.2.0`、`httpx2`(MCP SDK 2.x 隨附的 HTTP client)、`pydantic`。

## 設定

透過環境變數提供連線資訊(可參考 `.env.example`):

| 變數 | 必填 | 說明 |
|---|---|---|
| `VCENTER_HOST` | 是 | vCenter FQDN 或 IP,例 `vcenter.example.com` |
| `VCENTER_USER` | 是 | 帳號,例 `administrator@vsphere.local` |
| `VCENTER_PASSWORD` | 是 | 密碼 |
| `VCENTER_INSECURE` | 否 | 設為 `true` 時關閉 TLS 憑證驗證(內部自簽憑證環境用),預設 `false` |

## 啟動

```bash
python -m mcp_vmwhere
```

以 stdio transport 執行,通常由 MCP 用戶端(Claude Code / Claude Desktop)自動啟動。

## 接入 Claude Code

```bash
claude mcp add vmwhere \
  -e VCENTER_HOST=vcenter.example.com \
  -e VCENTER_USER=administrator@vsphere.local \
  -e VCENTER_PASSWORD=yourpassword \
  -- python -m mcp_vmwhere
```

## 接入 Claude Desktop

Claude Desktop 由 app 直接啟動 server 行程,**不會**繼承終端機的 `PATH` 與虛擬環境 —
`command` 請填 Python 直譯器的**絕對路徑**(有用虛擬環境就填該環境下的 python),
否則會出現找不到指令或 `No module named mcp_vmwhere`。

取得絕對路徑:

```bash
# 先啟用安裝本專案的環境,再執行
python -c "import sys; print(sys.executable)"
```

### 設定步驟

1. 開啟 Claude 選單(macOS 在系統最上方的選單列)→ **Settings…** → 左側 **Developer** →
   按 **Edit Config**,會開啟設定檔:
   - macOS:`~/Library/Application Support/Claude/claude_desktop_config.json`
   - Windows:`%APPDATA%\Claude\claude_desktop_config.json`

2. 在 `mcpServers` 底下加入 `vmwhere`(把路徑與帳密換成自己的):

   macOS / Linux:

   ```json
   {
     "mcpServers": {
       "vmwhere": {
         "command": "/Users/yourname/git/mcp-vmware/.venv/bin/python",
         "args": ["-m", "mcp_vmwhere"],
         "env": {
           "VCENTER_HOST": "vcenter.example.com",
           "VCENTER_USER": "readonly@vsphere.local",
           "VCENTER_PASSWORD": "yourpassword",
           "VCENTER_INSECURE": "false"
         }
       }
     }
   }
   ```

   Windows(路徑的反斜線要寫成 `\\`):

   ```json
   {
     "mcpServers": {
       "vmwhere": {
         "command": "C:\\Users\\yourname\\git\\mcp-vmware\\.venv\\Scripts\\python.exe",
         "args": ["-m", "mcp_vmwhere"],
         "env": {
           "VCENTER_HOST": "vcenter.example.com",
           "VCENTER_USER": "readonly@vsphere.local",
           "VCENTER_PASSWORD": "yourpassword",
           "VCENTER_INSECURE": "false"
         }
       }
     }
   }
   ```

3. **完全結束**(Quit,不是關視窗)Claude Desktop 後重新開啟。

4. 在對話輸入框左下角點 **Add files, connectors, and more** → **Connectors** →
   **Manage connectors**,選 `vmwhere` 就會看到本 server 的 9 個唯讀工具。

### 試著問問看

- 「現在有幾台 VM 是開機狀態?」
- 「列出所有 ESXi 主機和它們的連線狀態」
- 「哪個 datastore 的剩餘空間最少?」

### 疑難排解

看不到 `vmwhere` 或工具呼叫失敗時,先看 log:

```bash
# macOS
tail -n 50 -f ~/Library/Logs/Claude/mcp*.log
```

```powershell
# Windows
type "%APPDATA%\Claude\logs\mcp-server-vmwhere.log"
```

再用同一組路徑與環境變數手動啟動一次,確認 server 本身沒問題:

```bash
VCENTER_HOST=vcenter.example.com \
VCENTER_USER=readonly@vsphere.local \
VCENTER_PASSWORD=yourpassword \
/Users/yourname/git/mcp-vmware/.venv/bin/python -m mcp_vmwhere
```

正常情況下不會有任何輸出(server 在等 stdin),按 `Ctrl-C` 結束即可。

常見原因:

| 症狀 | 原因 |
|---|---|
| 完全看不到這個 connector | `claude_desktop_config.json` JSON 格式錯誤(多一個逗號就整份不載入) |
| log 出現 `No such file` / `No module named mcp_vmwhere` | `command` 用了相對路徑,或該直譯器沒裝本專案 |
| log 出現「缺少必要環境變數: …」 | `env` 少填 `VCENTER_HOST` / `VCENTER_USER` / `VCENTER_PASSWORD` |
| TLS 憑證錯誤 | 自簽憑證環境請設 `"VCENTER_INSECURE": "true"` |

## 用 MCP Inspector 測試

```bash
npx @modelcontextprotocol/inspector python -m mcp_vmwhere
```

## 提供的 Tool(全部唯讀)

| Tool | 說明 | 回傳型別 |
|---|---|---|
| `list_vms` | 列出虛擬機摘要(可依電源狀態、名稱過濾) | `VMSummary[]` |
| `get_vm` | 取得單一 VM 詳情(硬體、電源、客體 OS) | `VMDetail` |
| `list_hosts` | 列出 ESXi 主機(可依連線狀態過濾) | `HostSummary[]` |
| `list_clusters` | 列出叢集 | `ClusterSummary[]` |
| `list_datastores` | 列出 Datastore 容量資訊 | `DatastoreSummary[]` |
| `list_networks` | 列出網路 / Port Group | `NetworkSummary[]` |
| `list_datacenters` | 列出資料中心 | `DatacenterSummary[]` |
| `list_resource_pools` | 列出資源池 | `ResourcePoolSummary[]` |
| `list_folders` | 列出資料夾 | `FolderSummary[]` |

回傳模型的欄位名稱與 vSphere REST API 一致,且允許額外欄位 —
不同 vCenter 版本多回的欄位會原樣保留,不會被丟棄或造成驗證失敗。

## 安全說明

- 本 server 不會對 vCenter 做任何異動;`vcenter.py` 中除 session 登入(`POST /api/session`)
  與登出(`DELETE /api/session`)外,僅有 `GET` 請求。
- 所有 tool 皆標註 `readOnlyHint`,用戶端可據此判斷本 server 不會改動環境。
- 建議搭配僅具唯讀權限的 vCenter 服務帳號使用,以從權限層面再加一道保險。
- Claude Desktop / Claude Code 的設定檔會以明文存放 `VCENTER_PASSWORD`,請勿把設定檔提交進版控;
  macOS 可再收緊權限:`chmod 600 ~/Library/Application\ Support/Claude/claude_desktop_config.json`。

## HTTP 模式(Open WebUI 原生 MCP 連線)

除了 stdio,也可用 Streamable HTTP 執行,端點 `/mcp/`:

```bash
PYTHONPATH=src HTTP_PORT=8000 MCP_AUTH_TOKEN=<token> python -m http_server
```

- 驗證:請求需帶 `Authorization: Bearer <MCP_AUTH_TOKEN>`(可逗號分隔多組以便輪替)。
  未設 `MCP_AUTH_TOKEN` 時不驗證並在 log 警告 —— 任何連得到的人都能讀整個 vCenter inventory,只限本機除錯。
  另支援選用的 OAuth access token(`MCP_OAUTH_ISSUER`/`MCP_OAUTH_AUDIENCE`/`MCP_OAUTH_JWKS_URL`,三者皆設定才啟用),
  詳見 `src/mcp_vmwhere/auth.py`。
- `/health` 免驗證,供容器健康檢查使用。
- 缺 `VCENTER_HOST`/`VCENTER_USER`/`VCENTER_PASSWORD` 時程序直接結束(健康檢查會失敗)。
- 無狀態(stateless)、JSON 回應;全程序共用一個 vCenter session。
- 工具名不帶前綴;Open WebUI 的函式名是「連線識別碼+底線+工具名」,例 `vmwhere_list_vms`。
- 所有 `list_*` 工具都回傳物件而非裸陣列:附 `total` 與依狀態/類型分組的計數(`list_datastores` 另附 GiB 換算、
  `used_pct`,並已依使用率由高到低排序)。數量一律由工具算好 —— LLM 自行計算長清單
  容易數錯。`list_vms` 另有 `name_contains`(不分大小寫的部分名稱比對)。

### 容器與部署

```bash
docker build --target production -t mcp-vmwhere .

docker run -d --name mcp-vmwhere -p 8000:8000 \
  -e VCENTER_HOST=vcenter.example.com \
  -e VCENTER_USER=readonly@vsphere.local \
  -e VCENTER_PASSWORD=yourpassword \
  -e MCP_AUTH_TOKEN=<token> \
  mcp-vmwhere

curl http://localhost:8000/health
```

- 容器內預設執行 `python -m http_server`,監聽 `HTTP_PORT`(預設 `8000`)。
- 建議以 `--env-file .env` 或部署平台的 secret 機制注入 `VCENTER_*` 與 `MCP_AUTH_TOKEN`,勿寫進 image。
- 依賴版本以 `constraints.txt` 鎖定;有意升級時在建好的 image 內 `pip freeze` 重新產生。

## 授權

[MIT](LICENSE)