内容
# vcf-mcp
通过SaaS AI代理(如Claude)与VMware Cloud Foundation(VCF)环境进行交互。询问
关于资源健康状况、拉取活动警报、管理系统证书或驱动
生命周期操作 — 无需手动编写单个API集成。
`vcf-mcp`是一个Python [MCP](https://modelcontextprotocol.io) 服务器,
通过直接读取OpenAPI/Swagger规范,暴露VCF REST API给任何MCP兼容的客户端
(如Claude Desktop、Claude Code等),而不是为每个端点编写一个特定的包装器。
目前,它支持三个VCF API规范:
- **`fleet`** — VCF Operations Fleet Management API(Swagger 2.0,106个操作)
- **`vcf-ops`** — VCF Operations API(OpenAPI 3.0,370个操作)
- **`sddc`** — VCF(SDDC Manager)API(OpenAPI 3.0,375个操作)
这三个规范涵盖了851个操作,几乎涵盖了您通过VCF UI可以执行的所有操作
— 资源和警报管理、证书操作、LCM/环境生命周期、域/工作负载管理、身份验证
管理等 — 所有这些都可以通过自然语言请求访问。
所有三个规范文件都存放在 `specs/` 目录下,并在启动时解析和标准化为一种通用格式,
因此服务器逻辑不关心操作来自哪个规范 — 请参阅 `config/openapi_utils.py` 以了解其工作原理。
## 工作原理
该服务器暴露了4种工具,而不是851种独立的MCP工具:
| 工具 | 用途 |
|---|---|
| `list_specs()` | 显示所有规范、端点计数和是否配置了凭证 |
| `search_endpoints(spec, query)` | 按操作 ID、路径、摘要或标签进行关键字搜索 |
| `get_endpoint(spec, operation_id)` | 获取一个操作的完整参数列表 + 解析后的请求体 JSON 模式 |
| `call_api(spec, operation_id, path_params, query_params, body, extra_headers)` | 查找操作,替换路径参数到 URL 中,附加查询参数和 JSON 请求体,添加 `Authorization` 头部,并执行 HTTP 调用 |
模型通常的流程:`search_endpoints` → `get_endpoint` → `call_api`.
## 规范文件如何变成可用的 API 调用
没有生成的代码,也没有此仓库中每个端点的特定包装器
— 服务器需要的一切信息都来自于在启动时内存中解析规范文件本身。三个步骤:
**1. 规范标准化**。`openapi_utils.load_and_normalize_spec`
检测规范是 Swagger 2.0(`fleet`)还是 OpenAPI 3.0(`vcf-ops`、`sddc`),
通过检查 `swagger` 或 `openapi` 密钥,然后将任一格式转换为一种通用的操作列表:
```python
{
"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 已经解析为实际的 JSON 模式
}
```
两种格式对请求体位置(Swagger 2 将其混入 `parameters` 中,带有 `in: "body"`;OpenAPI 3 有其自己的 `requestBody.content["application/json"].schema`)和 `$ref` 解析(`#/definitions/Foo` 与 `#/components/schemas/Foo`)有不同的约定
— `_resolve_schema` 遍历任一格式,内联引用递归(安全循环,限制 6 级深度),
因此 `request_body_schema` 总是以可读的 JSON 模式出现,无论它来自哪个规范。
<details>
<summary>具体的示例(来自 <code>fleet</code> 规范)</summary>
原始 Swagger 2.0,直接来自 `specs/fleet-management-api-docs.json` —
`paths["/lcm/lcops/api/depot-configuration/{depotType}"]["post"]`:
```json
{
"operationId": "saveDepotConfigurationUsingPOST",
"summary": "保存仓库配置",
"tags": ["设置控制器"],
"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" }
}
```
经过 `_normalize_swagger2` 后,`by_operation_id["saveDepotConfigurationUsingPOST"]`:
```json
{
"operation_id": "saveDepotConfigurationUsingPOST",
"method": "POST",
"path": "/lcm/lcops/api/depot-configuration/{depotType}",
"summary": "保存仓库配置",
"tags": ["设置控制器"],
"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"
}
}
```
三个变化,与规范标准化所做的三件事相匹配:
1. `in: "body"` 参数从 `parameters` 中完全分离出来,成为其自己的 `request_body_schema` 键
— 其他参数(`path`、`query`、`formData`)保持不变。
2. `{"$ref": "#/definitions/DepotConfigurationDTO"}` 扩展为实际的对象形状(`properties: {depotType, directoryPath, ...}`),
通过 `_resolve_schema` 进入 `spec["definitions"]`,因此下游永远不需要自己追踪 `$ref`。
3. `responses` 被丢弃
— 规范化形状仅包含调用所需的内容,而不是服务器可能发送的内容。
OpenAPI 3.0 操作(`vcf-ops`、`sddc`)从不同的原始形状开始(请求体位于 `requestBody` 下,引用位于 `#/components/schemas/...` 下),
但 `_normalize_openapi3` 生成完全相同的输出结构
— 这就是 `server.py` 永远不需要知道或关心它来自哪个规范格式的原因。
</details>
**2. 按关键词查找正确的操作,而不是通过知道 API**。模型
不需要已经知道操作的确切名称
— `search_endpoints` 根据查询字符串在 `operation_id`、`path`、`summary` 和 `tags` 中出现的次数对每个操作进行评分,
并返回最佳匹配。然后 `get_endpoint` 返回该操作的完整参数列表和已解析的请求体模式,
以便模型知道 `call_api` 在调用它之前需要什么。
**3. 从规范中纯粹地构建 HTTP 请求**。`call_api`
获取相同的操作字典,并组装一个真正的请求,没有为端点硬编码:
- URL 是 `base_url`(来自您的环境配置)+ `server_prefix`(API自己的基准路径,例如 `/suite-api`,直接从规范的 `basePath` 或 `servers[0].url` 读取)+ 操作的 `path` 模板,其中任何 `{placeholder}` 在该模板中用 `path_params` 替换
— 如果缺少必需的参数或未解决,则快速失败;
- `query_params` 直接附加,`body` 仅在操作实际声明 `request_body_schema` 或使用预期需要它的请求方法时发送;
- `Authorization` 头部根据规范配置的凭证和身份验证方案(见下文)派生
— 从不手动输入每个调用。
没有此逻辑分支到它正在与哪个 API 对话。添加第四个 VCF API 意味着将其规范文件放入 `specs/` 并在 `config.SPECS` 中添加一个条目
— `search_endpoints`、`get_endpoint` 和 `call_api` 自动拾取它,因为它们只在规范化形状上运行,而不是在规范的原始格式上。
## 安装
使用 [uv](https://docs.astral.sh/uv/)(推荐 — 由 `claude_desktop_config.json` 使用):
```bash
cd vcf-mcp
uv sync
cp .env.example .env # 然后填写实际值
```
或使用普通的 pip:
```bash
cd vcf-mcp
python3 -m venv .venv && source .venv/bin/activate # 需要 Python 3.10+
pip install -r requirements.txt
cp .env.example .env # 然后填写实际值
```
必需的环境变量(参见 `.env.example`):
- `FLEET_BASE_URL`、`FLEET_USER`、`FLEET_PASSWORD` — 用于 Fleet Management API
- `VCFOPS_BASE_URL`、`VCFOPS_USER`、`VCFOPS_PASSWORD` — 用于 VCF Operations API
- `VCFOPS_AUTH_SOURCE`(可选)— LDAP `vcf-ops` 用户的身份验证源显示名称
- `SDDC_BASE_URL`、`SDDC_USER`、`SDDC_PASSWORD` — 用于 SDDC Manager API
- `FLEET_VERIFY_SSL` / `VCFOPS_VERIFY_SSL` / `SDDC_VERIFY_SSL`(可选,默认为 `false`)— 设置为 `true` 以强制执行 TLS 证书验证;默认为跳过,因为实验室 VCF 实例通常运行自签名证书
- `API_TIMEOUT_SECONDS`(可选,默认为 `30`)
`.env` 中永远不会存储 API令牌
— 每个规范只有用户名/密码对。`call_api` 在每次请求时根据每个 API 的身份验证方案从凭证中派生 `Authorization` 头部:
- **`fleet`** — HTTP 基本身份验证(`Authorization: Basic base64(user:password)`),在每次调用时从凭证重新构建。参见
[Broadcom KB 409715](https://knowledge.broadcom.com/external/article/409715/how-to-authorize-vcf-operations-fleet-ma.html)。
- **`vcf-ops`** — 通过 `POST /api/auth/token/acquire` 交换用户名/密码以获取短期 OpsToken(有效期 6 小时),然后缓存在**内存中**(从不写入磁盘)。镜像
`_acquire_ops_token` 在 `privateAI-demo/mcp/server.py` 中。
- **`sddc`** — 通过 `POST /v1/tokens` 交换用户名/密码以获取 bearer 访问令牌(有效期 1 小时),缓存方式与 `vcf-ops` 相同。
缓存令牌不会在计时器上主动刷新
— 如果调用返回 `401`,`call_api` 清除该规范的缓存令牌并重试一次以获取新的令牌,然后再放弃,因此令牌在进程中过期(`sddc` 的 1 小时窗口是需要关注的)会在下一次调用时自我恢复。
## 独立运行
```bash
python server.py
```
这会在标准输入/输出上启动服务器,准备好与MCP客户端连接。
## 从Claude Desktop 连接
将以下内容添加到您的 `claude_desktop_config.json`:
```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"
}
}
}
}
```
> **⚠️ 安全注意事项:** `claude_desktop_config.json` 以**纯文本形式存储在磁盘上**
> 这些凭证是实际的 VCF 管理员凭证,存储在 JSON 文件中。这是任何 MCP 服务器的 `env` 块的通用情况
> ,并非本例特有,但值得明确指出,因为这是实验室中实际的 VCF 管理员凭证驻留的 JSON 文件。
> 该项目目前是一个为个人实验室构建的原型;它尚未针对任何超过实验室的敏感信息进行强化。
> 不要将其指向生产凭证而不进行适当的保护。一个正确的机密管理方法(OS 密钥库、机密管理器、启动时注入的短期令牌等)是使用比实验室更敏感的环境之前需要重新审视的内容。
## 示例交互
1. `search_endpoints(spec="vcf-ops", query="resources")` →
查找 `getResources`、`updateResource` 等。
2. `get_endpoint(spec="vcf-ops", operation_id="getResources")` →
显示其查询参数和请求体模式。
3. `call_api(spec="vcf-ops", operation_id="getResources", query_params={"pageSize": 50})` →
构建 `GET {VCFOPS_BASE_URL}/suite-api/api/resources?pageSize=50` 并附加身份验证头部,执行后返回 `{status_code, url, method, ok, response}`。
## 截图
**来自 VCF Operations 的实时警报** — "检查 VCF Ops 的警报" 通过 `vcf-ops` 拉取
所有活动警报,并按严重性分组(34 个警报:10 个关键,8 个立即,8 个警告)直接来自 API,无需手动查找。

**用资源名称丰富警报** — 警报仅包含不透明的资源 ID,因此后续操作(“列出警报详细信息”)
模型通过 `vcf-ops` 批处理查找每个 ID 以获取其真实主机名(`vcf-mgmt-esxi01.vlabs.local`、`vcf-edge-tn-01` 等),
将 UUID 列表转换为可读表格,显示健康状态和每个警报的活动时间。

**来自 SDDC Manager 的网络池配置** — “您可以列出 SDDC 中的网络池设置?”解析池,
通过 `sddc` 拉取其网络(vMotion、vSAN),并自行检查数字(使用 +
空闲 IP 对主机计数、VLAN/MTU 正确性)后再回答。

**跨舰队凭证过期审计** — “您可以列出所有凭证及其过期日期?” 跨多个凭证类型(ESXi SSH、NSX Manager API、vCenter SSO、服务帐户)
通过 `fleet` 进行过期检查,然后按紧急程度对 17 个凭证进行排序
— 标记 3 个即将过期而不是有效期设置为永不过期。

**启动和轮询长时间运行的任务** — “您可以运行 SDDC 备份并持续轮询任务直到完成?” 通过 `sddc` 触发备份,
然后在它自己的基础上轮询任务状态,直到所有 8 个子任务(静置、备份数据库/配置、打包和加密、保存到 SFTP、保留清理、解除静置)都报告成功
— 无需手动跟进。

## 已知限制
- **多部分/文件上传端点**:一个 Fleet 端点
(`uploadContentUsingPOST`) 使用 `multipart/form-data`,不支持 `call_api` 中的通用 JSON 正文路径。如果需要它,需要一个专用代码路径。
- **本地 `$ref` 解析**:模式解析内部 `#/...` 引用;
两个规范中没有外部文件引用,因此这里没有实际限制。
- **深度递归模式** 在 6 级 `$ref` 解析时限制为可读性;
更深的引用显示为 `{"$ref_name": "TypeName"}` 占位符而不是完全展开。