MCP Servers

A collection of Model Context Protocol servers, templates, tools and more.

VMware vCenter 8.0 MCP server with dry-run safety

Created 5/4/2026
Updated about 4 hours ago
Repository documentation and setup instructions

VMware vCenter MCP by iOblako

VMware vCenter 8.0 MCP Server

CI License: MIT Node >= 22 MCP compatible vCenter 8.0

A Model Context Protocol server that lets Cursor (or any MCP-compatible client) manage a VMware vCenter 8.0 deployment end-to-end: read inventory, query statistics, take snapshots, power VMs on and off, vMotion across hosts, mount ISOs, configure DRS/HA, remediate clusters with vLCM, and more.

The server fronts three vSphere API surfaces behind a single session:

  • vSphere Automation REST API (/api/...) - primary, modern JSON.
  • VI/JSON API (/sdk/vim25/{release}/..., vCenter 8.0 U1+) - full vim25 parity for ops not exposed by the Automation API (snapshots, advanced VM config, performance counters, alarms, RBAC).
  • vSphere Web Services SOAP via @vates/node-vsphere-soap - lazy fallback; exposed through a single soap_runCommand escape hatch.

Safety first. Every destructive tool (*_delete, *_powerOff, snapshot_remove, host_reboot, ...) requires an explicit confirm: true argument. Otherwise, the tool returns a structured dry-run describing the exact request that would have been sent, but never touches vCenter. A global VCENTER_READ_ONLY=true kill switch refuses every destructive tool regardless of confirm.


Quick start

git clone https://github.com/TheEvalon/vmware-vcenter-mcp.git
cd vmware-vcenter-mcp
npm install
copy .env.example .env
# edit .env with your vCenter details
npm run build
npm start

For development you can run the server with tsx:

npm run dev

Run the unit test suite:

npm test

Run the comprehensive read-only integration suite against a real vCenter (recommended pre-publish gate). It spawns the MCP server twice over stdio, discovers your lab inventory, exercises every read-only tool, dry-runs every destructive tool, and verifies the VCENTER_READ_ONLY=true kill switch blocks every write path:

npm run test:integration:readonly

The legacy single-test smoke pass is still available:

$env:VCENTER_INTEGRATION = "true"; npm run test:integration

Integrating with Cursor

This MCP server uses the standard stdio transport and registers with Cursor the same way any MCP server does: a single entry in your mcp.json. Five steps end-to-end:

1. Prerequisites

  • Node.js >= 22 on PATH (node --version).
  • This repo cloned and built once -- see Quick start. The important output is dist/index.js.
  • Cursor 0.42 or newer. Older Cursor builds support MCP but use a different config schema.
  • vCenter credentials handy (VCENTER_HOST, VCENTER_USER, VCENTER_PASS).

2. Get the absolute path to dist/index.js

Cursor launches the MCP server itself, so it needs an absolute path -- a relative path will silently fail to start. From inside your clone:

# Windows (PowerShell)
(Resolve-Path .\dist\index.js).Path
# macOS / Linux
realpath dist/index.js

Copy the printed path; you'll paste it into args below.

3. Edit your Cursor MCP config

Cursor reads MCP servers from one of two locations. Either works -- the project-scoped one is recommended if you want the MCP available only inside specific Cursor workspaces.

| Scope | File | |---------------|--------------------------------------------------| | Global | ~/.cursor/mcp.json (macOS/Linux) | | Global | %USERPROFILE%\.cursor\mcp.json (Windows) | | Project-only | <your-project>/.cursor/mcp.json |

Open the file (create it if missing) and add the vmware entry:

{
  "mcpServers": {
    "vmware": {
      "command": "node",
      "args": ["<absolute-path-from-step-2>/dist/index.js"],
      "env": {
        "VCENTER_HOST": "vcsa.lab.local",
        "VCENTER_USER": "administrator@vsphere.local",
        "VCENTER_PASS": "********",
        "VCENTER_INSECURE": "false",
        "VCENTER_READ_ONLY": "true",
        "VCENTER_LOG_LEVEL": "info"
      }
    }
  }
}

Notes on the snippet:

  • VCENTER_READ_ONLY=true is the recommended starting point. Every destructive tool is blocked globally until you explicitly flip it to false. See Safety / dry-run.
  • VCENTER_INSECURE should stay false in production. Set it to true only for homelabs with self-signed certs.
  • If you already have other MCP servers in mcp.json, just add the "vmware" key alongside them inside the existing "mcpServers" object -- don't replace the file.
  • On Windows, write the path with forward slashes or double-escaped backslashes in JSON: "D:/Repos/vmware-vcenter-mcp/dist/index.js" or "D:\\Repos\\vmware-vcenter-mcp\\dist\\index.js".

4. Reload Cursor and verify the server is up

  1. Quit Cursor fully (don't just close the window).
  2. Reopen Cursor.
  3. Open Settings -> MCP (or Cmd/Ctrl+Shift+J -> "MCP"). You should see a vmware entry with a green dot. The tools list should show 100+ entries (vm_list, snapshot_create, host_enterMaintenance, etc.) once the server has finished registering.
  4. If the dot is red, click it to view the stderr log -- the most common cause is a wrong absolute path or unreachable VCENTER_HOST. See Troubleshooting below.

5. Try it from a Cursor chat

Open a new chat in any project and ask, for example:

"List the VMs in my vCenter and show their power state."

"Take a snapshot of vm-101 named pre-upgrade. Use dry-run first, then ask me before applying."

"Show DRS recommendations for cluster cl-prod-01."

Cursor will pick the right tool from the catalog automatically. Because VCENTER_READ_ONLY=true is set, any destructive ask will be answered with a structured dry-run preview rather than a real change -- exactly what you want for the first session.

Development variant (run from source via tsx)

If you're hacking on this MCP and want changes to take effect without a rebuild, swap the command/args for:

"command": "npx",
"args": ["tsx", "<absolute-path-to-clone>/src/index.ts"]

Cursor restarts the MCP on every config save, so a Cursor reload picks up the new source.


Environment variables

| Variable | Required | Default | Description | |---------------------------|----------|---------|-------------| | VCENTER_HOST | yes | - | Hostname or IP of the vCenter Server (no scheme). | | VCENTER_PORT | no | 443 | HTTPS port. | | VCENTER_USER | yes | - | SSO username (e.g. administrator@vsphere.local). | | VCENTER_PASS | yes | - | SSO password. | | VCENTER_INSECURE | no | false | Skip TLS verification (homelab / self-signed certs). | | VCENTER_LOG_LEVEL | no | info | trace, debug, info, warn, error. Logs go to stderr only. | | VCENTER_TASK_TIMEOUT_MS | no | 600000| Max wait for any long-running task (clones, vMotion, remediation). | | VCENTER_TASK_POLL_MS | no | 1500 | Task polling interval. | | VCENTER_READ_ONLY | no | false | Block all destructive tools regardless of confirm. |

The server reads .env automatically via dotenv.


Safety / dry-run

// First call: no confirm flag, returns a structured dry-run preview.
{
  "tool": "snapshot_create",
  "arguments": { "vmId": "vm-101", "name": "pre-upgrade" }
}
// -> {
//   "content": [{"type":"text","text":"DRY RUN: Would create snapshot \"pre-upgrade\" on vm-101 ..."}],
//   "structuredContent": {
//     "dryRun": true,
//     "tool": "snapshot_create",
//     "summary": "...",
//     "request": {"method":"POST","path":"/sdk/vim25/{release}/.../CreateSnapshotEx_Task","body":{...}},
//     "hint": "Re-run with confirm:true to execute."
//   }
// }

// Second call: confirm:true actually creates the snapshot.
{
  "tool": "snapshot_create",
  "arguments": { "vmId": "vm-101", "name": "pre-upgrade", "confirm": true }
}

Pre-publish testing

npm run test:integration:readonly is wired into prepublishOnly, so a broken build cannot be published to npm. The suite covers, against a live vCenter:

  • Tool registration - every name in the README catalog is registered exactly once and exposes both inputSchema and outputSchema.
  • Read-only happy paths - every *_list, *_get, vcenter_about, vcenter_health, task_*, event_list, alarm_list, stats_*, iso_listFromDatastore, customization_*, role_list, permission_list, identityProvider_list, lifecycle_listClusterImage, lifecycle_checkCompliance, drs_recommendations, dvswitch_list, dvportgroup_list, template_list, contentLibrary* is invoked through the MCP stdio transport and the parsed envelope is structurally validated.
  • Safety: dry-run - every destructive tool is called WITHOUT confirm and asserted to return a structured { dryRun: true, ... } preview instead of touching vCenter.
  • Safety: kill switch - every destructive tool is called WITH confirm:true against a VCENTER_READ_ONLY=true server fixture and asserted to return isError:true with the read-only refusal message.
  • Protocol stream - the server's stdout is parsed line-by-line to guarantee every chunk is a JSON-RPC envelope (no stray console.log).

The suite is inventory-agnostic: tools that need an entity that may not exist in every lab (vm_consoleTicket, customization_get, vLCM-managed clusters, content libraries) skip-with-warn instead of failing.

Read-only mode

Set VCENTER_READ_ONLY=true to disable every destructive tool globally:

snapshot_create blocked: Server is running in read-only mode (VCENTER_READ_ONLY=true).
Set VCENTER_READ_ONLY=false to enable writes.

Read-only tools (*_list, *_get, vcenter_health, task_get, etc.) are unaffected.


Tool catalog

Names in bold are destructive (require confirm: true).

vCenter

  • vcenter_about - product version / build / api version.
  • vcenter_health - aggregate appliance health components.

VM lifecycle

  • vm_list, vm_get, vm_powerState
  • vm_create, vm_clone, vm_delete
  • vm_powerOn, vm_powerOff, vm_reset, vm_suspend
  • vm_shutdown, vm_reboot (guest-OS-aware)
  • vm_reconfigure (CPU / memory / name)
  • vm_migrate (vMotion), vm_relocate (Storage vMotion)
  • vm_consoleTicket (mints a one-shot VMRC ticket)
  • vm_attachNetwork

Snapshots

  • snapshot_list
  • snapshot_create, snapshot_revert, snapshot_remove, snapshot_removeAll

Hosts (ESXi)

  • host_list, host_get
  • host_enterMaintenance, host_exitMaintenance
  • host_reboot, host_shutdown
  • host_disconnect, host_reconnect
  • host_addToCluster

Clusters / DRS / HA

  • cluster_list, cluster_get
  • cluster_create, cluster_delete
  • cluster_setDrs, cluster_setHa
  • drs_recommendations, drs_apply

Datacenters / folders

  • datacenter_list, datacenter_create, datacenter_delete
  • folder_list, folder_create, folder_delete

Datastores

  • datastore_list, datastore_get
  • datastore_browse - lists one folder; supports a matchPattern glob array.
  • datastore_searchRecursive - finds files recursively by glob (e.g. ["*.vmdk"], ["*.iso", "*.img"], ["myvm-*.vmx"]). Auto-resolves the datastore root when path is omitted. Optional fileTypes restricts to vSphere file-type queries (VmDiskFileQuery, IsoImageFileQuery, FloppyImageFileQuery, FolderFileQuery, VmConfigFileQuery, VmTemplateFileQuery, VmLogFileQuery, VmNvramFileQuery, VmSnapshotFileQuery) for faster scans on large datastores. caseInsensitive defaults to true.
  • datastore_deleteFile, datastore_moveFile

Networks

  • network_list, dvswitch_list, dvportgroup_list
  • portgroup_create, portgroup_delete

Resource pools

  • resourcepool_list, resourcepool_create, resourcepool_delete, resourcepool_reconfigure

Templates / content library

  • template_list, template_deploy
  • contentLibrary_list, contentLibraryItem_list, contentLibraryItem_deploy
  • contentLibrary_publish

Tags

  • category_list, tag_list, tag_create
  • tag_attach, tag_detach

Alarms / events

  • alarm_list, alarm_acknowledge
  • event_list

Performance / stats

  • stats_listCounters, stats_query, stats_summary

ISO / media

  • iso_listFromDatastore, iso_mount, iso_unmount

Customization specs

  • customization_list, customization_get, customization_apply

Identity / RBAC

  • role_list, permission_list, permission_assign, identityProvider_list

vSphere Lifecycle Manager (vLCM)

  • lifecycle_listClusterImage, lifecycle_checkCompliance, lifecycle_remediate

Tasks

  • task_list, task_get

SOAP escape hatch

  • soap_runCommand - invokes any vim25 SOAP method when REST and VI/JSON do not expose the operation. Lazy-loads @vates/node-vsphere-soap on first use.

Architecture

src/
  index.ts                   stdio entry, McpServer + StdioServerTransport
  config.ts                  Zod-validated env loader
  client/
    session-manager.ts       POST /api/session, cached vmware-api-session-id
    http-client.ts           shared HTTP layer (auto re-auth on 401)
    rest-client.ts           Automation REST helpers (/api/...)
    vimjson-client.ts        VI/JSON helpers (/sdk/vim25/{release}/...)
    soap-client.ts           lazy @vates/node-vsphere-soap wrapper
    task-tracker.ts          poll vim25 Task to terminal state
    errors.ts                vCenter error mapping
    http-agent.ts            undici Agent with self-signed cert handling
  tools/
    _register.ts             registers every tool module
    _safety.ts               withConfirm() / safeReadOnly() helpers
    vm/, snapshot/, host/, ...   one folder per domain
  schemas/                   shared Zod schemas (MoRef, PowerState, ...)
  utils/                     logger (stderr-only; stdout reserved for JSON-RPC)
  types/                     ambient module declarations
tests/
  unit/                      vitest + undici MockAgent
  integration/               gated by VCENTER_INTEGRATION=true

Why three API surfaces?

| Concern | Surface used | Why | |---------------------------------|--------------|-----| | VM CRUD, hosts, clusters, networks, datastores, resource pools | Automation REST | Modern, JSON, fully documented. | | Snapshots, advanced VM config, alarms, events, performance counters, RBAC | VI/JSON | The Automation API does not expose these. | | Anything else (legacy or 8.0 GA without U1) | SOAP via soap_runCommand | Maintained library, full vim25 parity. |

All three share the same vmware-api-session-id token established once by POST /api/session, with transparent re-auth on 401 responses.


Troubleshooting

  • Failed to reach vCenter at https://... - check connectivity, hostname resolution, and that the vCenter Server is reachable on VCENTER_PORT (default 443). For homelab or self-signed certs, set VCENTER_INSECURE=true.
  • Login failed (401) - check VCENTER_USER and VCENTER_PASS. The user must include the SSO domain (administrator@vsphere.local).
  • Tool blocked: Server is running in read-only mode - clear or set VCENTER_READ_ONLY=false if you intend to make changes.
  • Could not auto-detect VI/JSON release - vCenter is older than 8.0 U1 (or the user lacks permission for the ServiceInstance). The client falls back to the literal release segment, which only works on U1+. Use the soap_runCommand escape hatch for older deployments.
  • Task ... did not complete within 600000ms - bump VCENTER_TASK_TIMEOUT_MS for clones/migrations of large VMs.
  • MCP client logs say "tool result is not valid JSON" - confirm that no custom logger is writing to stdout. The bundled logger only writes to stderr; if you add console.log() calls anywhere they will corrupt the JSON-RPC stream.

About

Built and maintained by iOblako (info@iOblako.com). oblako means cloud in Russian — fitting for an MCP that talks to a virtualized cloud. Pull requests, bug reports, and field notes from real lab and production deployments are very welcome.

Other projects by iOblako

Support / contact

  • Bug reports and feature requests: GitHub Issues.
  • Security disclosures (private): see SECURITY.md, or email info@iOblako.com.
  • Other inquiries: info@iOblako.com.

License

MIT © 2026 Gregory / iOblako

Quick Setup
Installation guide for this server

Install Package (if required)

npx @modelcontextprotocol/server-vmware-vcenter-mcp

Cursor configuration (mcp.json)

{ "mcpServers": { "theevalon-vmware-vcenter-mcp": { "command": "npx", "args": [ "theevalon-vmware-vcenter-mcp" ] } } }