{"id":"054626a4-ac31-452f-b380-eef19bde620f","slug":"michaelrice-vcenter-mcp","kind":"mcp","name":"vcenter-mcp","summary":"MCP server for VMware vCenter / ESXi VM lifecycle management","owner":"michaelrice","repoUrl":"https://github.com/michaelrice/vcenter-mcp","license":"Apache-2.0","licenseTier":"permissive","trust":"community","stars":0,"match":null,"fewStars":true,"format":"mcp","createdAt":"2026-05-05T02:50:51.000Z","addedAt":"2026-10-21T06:03:56.789Z","logoUrl":"https://avatars.githubusercontent.com/u/441148?u=f615ad21688d53fb183d218753822eab822c0fed&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["cloud-infra","memory-knowledge"],"tags":["esxi","pyvmomi","vcenter","vmware"],"runtime":"python","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-23T17:22:08.243Z","sample":false,"url":"https://conduits.dev/tools/michaelrice-vcenter-mcp","language":"Python","transports":["stdio"],"install":{"pypi":"vcenter-mcp"},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# vcenter-mcp\n\nA [Model Context Protocol](https://modelcontextprotocol.io) server that exposes VMware vCenter / ESXi VM lifecycle tools to [Claude Code](https://claude.com/claude-code) and other MCP clients. Built on [pyVmomi](https://github.com/vmware/pyvmomi).\n\n## What it does\n\n- List VMs on a vCenter datacenter (grouped by host) or on a standalone ESXi host\n- Create a VM (network-boot first; thin or thick provisioning; nested-virt option for ESXi targets)\n- Power VMs on and off\n- Delete VMs (powers off first if running, then destroys from disk)\n\nLookups accept either a display name or a moref ID (e.g. `vm-42`) — the moref path skips the inventory scan and is faster on large environments.\n\n## Prerequisites\n\n- Python 3.10 or newer\n- A vCenter Server or standalone ESXi host you can reach over the network\n- A vSphere account with the privileges needed for whatever you plan to do (read-only is enough for `list_vms`; create / delete need the corresponding VM and resource-pool privileges)\n\n## Install\n\nInstall into a project-local virtualenv. Using a venv keeps `vcenter-mcp` and its dependencies (notably `pyVmomi`) isolated from your system Python.\n\nFrom a clone of this repository:\n\n```bash\npython3 -m venv .venv\n.venv/bin/pip install --upgrade pip\n.venv/bin/pip install -e .\n```\n\nFor development (also installs pytest):\n\n```bash\n.venv/bin/pip install -e \".[dev]\"\n```\n\n> Throughout this README, commands use `.venv/bin/...`. You can instead `source .venv/bin/activate` once per shell and drop the prefix — same result.\n\n## Configure a target\n\nRun the interactive setup using the venv's Python:\n\n```bash\n.venv/bin/python -m vcenter_mcp setup\n```\n\nYou'll be prompted for:\n\n1. A target name (e.g. `lab-vcenter`) — used to refer to this target later\n2. Host or IP of the vCenter / ESXi\n3. Username and password\n4. Target type: `vcenter` or `esxi`\n5. (vCenter only) Datacenter and cluster names\n6. Datastore name\n7. One or more network profiles, each a name plus one or more portgroup names\n\nThe setup writes a config to `~/.config/vcenter-mcp/config.json` (mode `0600`). Re-run it any time to add another target or update an existing one.\n\n### Config file shape\n\n```json\n{\n  \"default_target\": \"lab-vcenter\",\n  \"targets\": {\n    \"lab-vcenter\": {\n      \"host\": \"vcenter.lab.example.com\",\n      \"user\": \"admin@vsphere.local\",\n      \"password\": \"...\",\n      \"type\": \"vcenter\",\n      \"datacenter\": \"Lab DC\",\n      \"cluster\": \"Lab Cluster\",\n      \"datastore\": \"datastore1\",\n      \"networks\": {\n        \"standard\": [\"VM Network\"],\n        \"secure-boot\": [\"pg-secure-1\", \"pg-secure-2\"]\n      },\n      \"default_network\": \"standard\"\n    }\n  },\n  \"templates\": {\n    \"esxi\":   { \"cpu\": 4, \"ram_mb\": 16384, \"disk_gb\": 100, \"disk_provisioning\": \"thin\", \"guest_id\": \"vmkernel7Guest\", \"vhv\": true },\n    \"ubuntu\": { \"cpu\": 2, \"ram_mb\": 4096,  \"disk_gb\": 40,  \"disk_provisioning\": \"thin\", \"guest_id\": \"ubuntu64Guest\",  \"vhv\": false },\n    \"rhel\":   { \"cpu\": 2, \"ram_mb\": 4096,  \"disk_gb\": 40,  \"disk_provisioning\": \"thin\", \"guest_id\": \"rhel9_64Guest\",  \"vhv\": false }\n  }\n}\n```\n\nA network profile is a list of portgroups; the first entry becomes the boot NIC. To add your own VM types, add entries to `templates` — `vm_type` strings passed to `create_vm` are matched against this dict.\n\n## Register with Claude Code\n\nRegister the MCP server using the venv's Python by absolute path. Claude Code launches the server in a fresh shell that does **not** inherit your activated venv, so the absolute path is required — pointing at a bare `python` here will fail to import `vcenter_mcp`.\n\n```bash\nVCENTER_MCP_DIR=\"$(pwd)\"   # run this from the repo root, after install\nclaude mcp add --scope user vcenter -- \"$VCENTER_MCP_DIR/.venv/bin/python\" -m vcenter_mcp\n```\n\nOr just inline the absolute path you want:\n\n```bash\nclaude mcp add --scope user vcenter -- /absolute/path/to/vcenter-mcp/.venv/bin/python -m vcenter_mcp\n```\n\nRead tools (`list_vms`) are safe to allow without prompting. Add it to `permissions.allow` in `~/.claude/settings.json`:\n\n```json\n{\n  \"permissions\": {\n    \"allow\": [\n      \"mcp__vcenter__list_vms\"\n    ]\n  }\n}\n```\n\nThe destructive tools (`create_vm`, `power_on_vm`, `power_off_vm`, `delete_vm`) are intentionally not in the default allow-list — Claude will prompt you per call.\n\n## Tools\n\n### `list_vms`\n\nList VMs on a target using a single PropertyCollector RPC (no per-VM round trips).\n\n| Parameter | Type | Description |\n|---|---|---|\n| `target` | string (optional) | Target name from config. Defaults to `default_target`. |\n| `datacenter` | string (optional) | vCenter only — datacenter to list. Defaults to the target's configured datacenter. |\n\nEach VM in the returned list includes:\n\n| Field | Description |\n|---|---|\n| `name` | Display name |\n| `moref` | Managed object reference ID (e.g. `vm-42`) |\n| `uuid` | BIOS UUID |\n| `power_state` | `poweredOn`, `poweredOff`, or `suspended` |\n| `guest_id` | Guest OS identifier (e.g. `ubuntu64Guest`) |\n| `guest_full_name` | Full guest OS name |\n| `cpu_count` | Number of vCPUs |\n| `memory_mb` | Memory in MB |\n| `storage_used_bytes` | Committed storage in bytes |\n| `primary_ip` | Primary guest IP (requires VMware Tools) |\n| `nics` | List of NICs — each with `mac`, `network`, and `guest_ips` (requires VMware Tools) |\n| `disks` | List of virtual disks — each with `capacity_bytes` and `file` (datastore-relative path) |\n\nOn error, returns a single dict with an `error` key.\n\n---\n\n### `create_vm`\n\nCreate a VM that network-boots first.\n\n| Parameter | Type | Description |\n|---|---|---|\n| `name` | string | Display name for the new VM |\n| `vm_type` | string | Template name from config (e.g. `esxi`, `ubuntu`, `rhel`) |\n| `target` | string (optional) | Target name from config. Defaults to `default_target`. |\n| `network_profile` | string (optional) | Named network profile from target config (e.g. `standard`, `secure-boot`). Defaults to `default_network`. |\n| `cpu` | int (optional) | Override template CPU count |\n| `ram_mb` | int (optional) | Override template memory in MB |\n| `disk_gb` | int (optional) | Override template disk size in GB |\n| `disk_provisioning` | string (optional) | `thin` (default) or `thick` |\n\n---\n\n### `power_on_vm`\n\nPower on a VM by display name or moref ID (e.g. `vm-42`).\n\n| Parameter | Type | Description |\n|---|---|---|\n| `name_or_id` | string | Display name or moref ID |\n| `target` | string (optional) | Target name from config. Defaults to `default_target`. |\n\n---\n\n### `power_off_vm`\n\nHard power off a VM by display name or moref ID (e.g. `vm-42`).\n\n| Parameter | Type | Description |\n|---|---|---|\n| `name_or_id` | string | Display name or moref ID |\n| `target` | string (optional) | Target name from config. Defaults to `default_target`. |\n\n---\n\n### `delete_vm`\n\nPermanently delete a VM (powers off first if running, then destroys from disk).\n\n| Parameter | Type | Description |\n|---|---|---|\n| `name_or_id` | string | Display name or moref ID |\n| `target` | string (optional) | Target name from config. Defaults to `default_target`. |\n\n## Connection management\n\n`vcenter-mcp` maintains a shared `ServiceInstance` per `user@host` combination for the lifetime of the MCP server process rather than opening and closing a new connection on every tool call.\n\n**Session lifecycle:**\n- **Created** on the first tool call for a given target\n- **Reused** on all subsequent calls with the same credentials\n- **Reconnected** automatically if a liveness ping (`currentSession`) detects the session has expired\n- **Disconnected** explicitly when a session is replaced (ping failure) or invalidated (`NotAuthenticated` / `SecurityError` fault)\n\n**Session sharing rules:**\n\n| Scenario | Result |\n|---|---|\n| Same user, same vCenter | Single shared session |\n| Different user, same vCenter | Separate session per user |\n| Same user, different vCenter | Separate session per host |\n\nConcurrent tool calls are safe — a `threading.Lock` guards all cache reads and writes.\n\n## Notes on TLS\n\n`vcenter-mcp` connects with an unverified SSL context, which is the same default that `govc` and most pyVmomi sample code use because lab vCenters very commonly have self-signed certs. If your target uses a properly-signed certificate and you'd prefer real verification, swap `_ssl_context()` in `src/vcenter_mcp/client.py`.\n\n## Development\n\n```bash\npython3 -m venv .venv\n.venv/bin/pip install -e \".[dev]\"\n.venv/bin/pytest\n```\n\nTests run on Python 3.10, 3.11, and 3.12 in CI (see `.github/workflows/test.yml`).\n\n## License\n\n[Apache-2.0](LICENSE)\n"}