vcf-mcp
A local MCP server for VCF APIs (different specs available via Swagger REST specification)
Install / Use
claude mcp add giulianoberteo -- npx -y github:giulianoberteo/vcf-mcpIf the server publishes to npm under a different name, use that package instead — check the repo README.
MCP Server
Model Context Protocol server
Quality Score
Category
Development & EngineeringSupported Platforms
Our assessment of vcf-mcp
vcf-mcp scores 80/100 on our quality scale, 3573rd of 4,624 Development & Engineering skills we index.
Its MCP Server is 14 KB long, well organised into 9 sections with 7 code examples: a thorough specification that gives an agent plenty to work with.
It has 3 GitHub stars, so there is little community track record yet; judge it on its content.
Maintenance, license and trust
- The repository was last updated 38 days ago, so vcf-mcp is actively maintained.
- Our last check on 2026-10-19 found the source still online.
- No license is declared. By default that means all rights are reserved: you can read it, but reusing or redistributing it is not clearly permitted. Ask the author before building on it commercially.
- Its trust signals score 75/100, with 3 cautions from licensing, adoption, age or documentation. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.
Safety scan
No issues foundOur scan of the whole file found no instruction hijacking, hidden characters, credential access, data exfiltration or destructive commands. An AI review of the same text found nothing harmful.
AI review by kimi-k2.7-code on 2026-10-18. Automated pattern scan on 2026-10-18. It catches known dangerous patterns, not every risk — read a skill before letting an agent act on it.
vcf-mcp compared with similar skills
All 4 of these similar skills score higher than vcf-mcp; compare them before choosing.
| Skill | Score | Stars | Updated | Format |
|---|---|---|---|---|
| vcf-mcp (this skill)by giulianoberteo | 80 | 3 | 38d ago | MCP Server |
| taste-skillby Leonxlnx | 100 | 111.3k | 2d ago | CLAUDE.md |
| Agent-Reachby Panniantong | 100 | 109.7k | today | CLAUDE.md |
| headroomby headroomlabs-ai | 100 | 79.0k | today | CLAUDE.md |
| ai-job-searchby ai-job-search-dev | 100 | 70.1k | 1d ago | CLAUDE.md |
Frequently asked questions
- How do I install vcf-mcp?
- Run
claude mcp add giulianoberteo -- npx -y github:giulianoberteo/vcf-mcp. The install tabs above show the steps for each supported agent. - Which AI agents does vcf-mcp work with?
- It is written for Claude Code and Claude Desktop, as a MCP Server file. Other agents that read the same format can often use it too.
- Is vcf-mcp safe to use?
- Our scan of the whole file found no instruction hijacking, hidden characters, credential access, data exfiltration or destructive commands. An AI review of the same text found nothing harmful. It declares no license and scores 75/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
- Is vcf-mcp still maintained?
- The repository was last updated 38 days ago, so vcf-mcp is actively maintained.
Skill content
View source on GitHubvcf-mcp
Talk to your VMware Cloud Foundation (VCF) environment from a SaaS AI Agent (like Claude). Ask about resource health, pull active alerts, manage certificates, or drive lifecycle operations — without hand-writing a single API integration.
vcf-mcp is a Python MCP server that
exposes VCF REST APIs to any MCP-compatible client (Claude Desktop,
Claude Code, etc.) by reading their OpenAPI/Swagger specs directly, rather
than shipping a hand-coded wrapper per endpoint:
For now, it supports three VCF API specs:
fleet— VCF Operations Fleet Management API (Swagger 2.0, 106 operations)vcf-ops— VCF Operations API (OpenAPI 3.0, 370 operations)sddc— VCF (SDDC Manager) API (OpenAPI 3.0, 375 operations)
Across the three specs that's 851 operations covering nearly everything you'd otherwise do through the VCF UI — resource and alert management, certificate operations, LCM/environment lifecycle, domain/workload management, auth administration, and more — all reachable through natural-language requests.
All three spec files ship inside specs/ and are parsed and normalized at
startup into one common shape, so the server logic doesn't care which spec
format an operation came from — see config/openapi_utils.py if you're
curious how that normalization works.
How it works
Instead of 851 individual MCP tools, this server exposes 4:
| Tool | Purpose |
|---|---|
| list_specs() | Shows all specs, endpoint counts, and whether credentials are configured |
| search_endpoints(spec, query) | Keyword search over operation_id / path / summary / tags |
| get_endpoint(spec, operation_id) | Full parameter list + resolved request body JSON schema for one operation |
| call_api(spec, operation_id, path_params, query_params, body, extra_headers) | Looks up the operation, substitutes path params into the URL, attaches query params and JSON body, adds the Authorization header, and executes the HTTP call |
The typical flow a model follows: search_endpoints → get_endpoint → call_api.
How a spec file becomes a working API call
There's no generated code and no per-endpoint wrapper anywhere in this repo — everything the server needs to find an endpoint and call it comes from parsing the spec file itself, at startup, in memory. Three steps:
1. Normalize the spec once, at first use. openapi_utils.load_and_normalize_spec
detects whether a spec is Swagger 2.0 (fleet) or OpenAPI 3.0 (vcf-ops,
sddc) by checking for a swagger vs openapi key, then converts either
shape into one common list of operations:
{
"operation_id": "getResources",
"method": "GET",
"path": "/api/resources/{id}",
"summary": "...",
"tags": [...],
"parameters": [{"name": ..., "in": "path"|"query"|"header", "required": ..., "type": ..., "description": ...}],
"request_body_schema": {...} | None, # $refs already resolved into a real JSON schema
}
The two formats disagree about where the request body lives (Swagger 2
mixes it into parameters with in: "body"; OpenAPI 3 has its own
requestBody.content["application/json"].schema) and about $ref resolution
(#/definitions/Foo vs #/components/schemas/Foo) — _resolve_schema
walks either one, inlining refs recursively (cycle-safe, capped at 6 levels
deep) so request_body_schema always comes out as a ready-to-read JSON
schema, regardless of which spec it came from. The result is cached in
memory (server._cache) so a ~370-operation spec is only parsed once per
process, not once per tool call.
Raw Swagger 2.0, straight from specs/fleet-management-api-docs.json —
paths["/lcm/lcops/api/depot-configuration/{depotType}"]["post"]:
{
"operationId": "saveDepotConfigurationUsingPOST",
"summary": "Save Depot Configuration",
"tags": ["Settings Controller"],
"parameters": [
{
"in": "body",
"name": "depotConfigurationDTO",
"required": true,
"schema": { "$ref": "#/definitions/DepotConfigurationDTO" }
},
{ "name": "depotType", "in": "path", "required": true, "type": "string", "description": "depotType" }
],
"responses": { "200": {...}, "201": {...}, "400": {...}, "...": "6 more" }
}
After _normalize_swagger2, by_operation_id["saveDepotConfigurationUsingPOST"]:
{
"operation_id": "saveDepotConfigurationUsingPOST",
"method": "POST",
"path": "/lcm/lcops/api/depot-configuration/{depotType}",
"summary": "Save Depot Configuration",
"tags": ["Settings Controller"],
"parameters": [
{ "name": "depotType", "in": "path", "required": true, "type": "string", "description": "depotType" }
],
"request_body_schema": {
"type": "object",
"properties": {
"depotType": { "type": "string" },
"directoryPath": { "type": "string" },
"isEnabled": { "type": "boolean" },
"offlineDepotUrl": { "type": "string" },
"password": { "type": "string" },
"trustCertificate": { "type": "boolean" },
"userName": { "type": "string" }
},
"title": "DepotConfigurationDTO"
}
}
Three changes, matching the three things the normalizer does:
- The
in: "body"parameter is pulled out ofparametersentirely and becomes its ownrequest_body_schemakey — every other parameter (path,query,formData) stays put. {"$ref": "#/definitions/DepotConfigurationDTO"}is expanded into the real object shape (properties: {depotType, directoryPath, ...}) by_resolve_schemafollowing the ref intospec["definitions"], so nothing downstream ever has to chase a$refitself.responsesis dropped — the normalized shape only carries what's needed to make the call, not what the server might send back.
An OpenAPI 3.0 operation (vcf-ops, sddc) starts from a different raw
shape (body under requestBody, refs under #/components/schemas/...), but
_normalize_openapi3 produces this exact same output structure — which is
why server.py never needs to know or care which spec format it came from.
2. Find the right operation by keyword, not by knowing the API. A model
doesn't need to already know an operation's exact name — search_endpoints
scores every operation by how many times the query string appears across
its operation_id, path, summary, and tags, and returns the best
matches. get_endpoint then hands back that operation's full parameter
list and resolved body schema, so the model knows exactly what call_api
needs before calling it.
3. Build the HTTP request purely from what the spec said. call_api
takes the same operation dict and assembles a real request with nothing
hardcoded per-endpoint:
- the URL is
base_url(from your env config) +server_prefix(the API's own base path, e.g./suite-api, read straight out of the spec'sbasePathorservers[0].url) + the operation'spathtemplate, with any{placeholder}in that template substituted frompath_params— and it fails fast if a required one is missing or left unresolved; query_paramsare attached as-is, andbodyis only sent when the operation actually declares arequest_body_schemaor uses a method that expects one;- the
Authorizationheader is derived from the spec's configured credentials and auth scheme (see below) — never hand-typed per call.
None of this logic branches on which API it's talking to. Adding a fourth
VCF API means dropping its spec file into specs/ and adding one entry to
config.SPECS — search_endpoints, get_endpoint, and call_api pick it
up automatically because they only ever operate on the normalized shape,
never on a spec's original format.
Setup
With uv (recommended — used by claude_desktop_config.json below):
cd vcf-mcp
uv sync
cp .env.example .env # then fill in real values
Or with plain pip:
cd vcf-mcp
python3 -m venv .venv && source .venv/bin/activate # requires Python 3.10+
pip install -r requirements.txt
cp .env.example .env # then fill in real values
Required environment variables (see .env.example):
FLEET_BASE_URL,FLEET_USER,FLEET_PASSWORD— for the Fleet Management APIVCFOPS_BASE_URL,VCFOPS_USER,VCFOPS_PASSWORD— for the VCF Operations APIVCFOPS_AUTH_SOURCE(optional) — auth source display name, for LDAPvcf-opsusersSDDC_BASE_URL,SDDC_USER,SDDC_PASSWORD— for the SDDC Manager APIFLEET_VERIFY_SSL/VCFOPS_VERIFY_SSL/SDDC_VERIFY_SSL(optional, defaultfalse) — set totrueto enforce TLS certificate verification; defaults to skipping it since lab VCF instances typically run self-signed certsAPI_TIMEOUT_SECONDS(optional, default30)
No API token is ever stored in .env — only a username/password pair per
spec. call_api derives the Authorization header from those credentials
at request time, per each API's own auth scheme:
fleet— HTTP Basic (Authorization: Basic base64(user:password)), rebuilt from credentials on every call. See Broadcom KB 409715.vcf-ops— exchanges the username/password for a short-lived OpsToken (6-hour validity) viaPOST /api/auth/token/acquire, then caches that token in memory only (never written to disk). Mirrors_acquire_ops_tokeninprivateAI-demo/mcp/server.py.sddc— exchanges the username/password for a bearer access token (1-hour validity) viaPOST /v1/tokens, cached the same way asvcf-ops.
Cached tokens aren't proactively refreshed on a timer — if a call comes back
401, call_api clears that spec's cached token and retries once with a
freshly acquired one before giving up, so a token going stale mid-process
(sddc's 1-hour window is the one to watch) self-heals on the next call.
Running standalone
python server.py
This starts the server on stdio, ready to be connected to by an MCP client.
Connecting from Claude Desktop
Add to your claude_desktop_config.json:
{
"mcpServers": {
"vcf-mcp": {
"command": "uv",
"args": [
"--directory", "/absolute/path/to/vcf-mcp",
"run", "/absolute/path/to/vcf-mcp/server.py"
],
"env": {
"FLEET_BASE_URL": "https://your-fleet-management-host",
"FLEET_USER": "admin@local",
"FLEET_PASSWORD": "your-fleet-password",
"VCFOPS_BASE_URL": "https://your-vcf-ops-host",
"VCFOPS_USER": "your-vcf-ops-username",
"VCFOPS_PASSWORD": "your-vcf-ops-password",
"SDDC_BASE_URL": "https://your-sddc-manager-host",
"SDDC_USER": "administrator@vsphere.local",
"SDDC_PASSWORD": "your-sddc-password"
}
}
}
}
⚠️ Security note:
claude_desktop_config.jsonstores these credentials in plain text on disk — that's true of theenvblock for any MCP server, not something specific to this one, but worth calling out explicitly since it's real VCF admin credentials sitting in a JSON file. This project is currently a prototype built for a personal lab; it hasn't been hardened for anything beyond that. Don't point it at production credentials as-is. A proper secrets-management approach (OS keychain, a secrets manager, short-lived tokens injected at launch rather than stored, etc.) is something to revisit before using this anywhere more sensitive than a lab.
Example interaction
search_endpoints(spec="vcf-ops", query="resources")→ findsgetResources,updateResource, etc.get_endpoint(spec="vcf-ops", operation_id="getResources")→ shows its query parameters and any body schema.call_api(spec="vcf-ops", operation_id="getResources", query_params={"pageSize": 50})→ builds `GET {VCFOPS_BASE_URL}/suit
Truncated for display — read the full file on GitHub.
Related Skills
taste-skill
111.3kAgent skills that give coding agents taste: art direction, reference images and a browser check instead of generic AI pages
Agent-Reach
109.7kGive your AI agent eyes to see the entire internet. Read & search Twitter, Reddit, YouTube, GitHub, Bilibili, XiaoHongShu — one CLI, zero API fees.
headroom
79.0kCompress tool outputs, logs, files, and RAG chunks before they reach the LLM. 20% fewer tokens for coding agents, 60-95% fewer tokens for JSON, same answers. Library, proxy, MCP server.
ai-job-search
70.1kThe job search that runs on your machine. AI job application framework built on Claude Code: evaluate postings, tailor CVs, write cover letters, prep interviews. Fork it and own it.
Languages
Trust signals
From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.
