vmware-avi 2.3.0__tar.gz → 2.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/.claude-plugin/plugin.json +1 -1
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/.mcp.json +1 -1
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/PKG-INFO +8 -1
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/README-CN.md +6 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/README.md +7 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/RELEASE_NOTES.md +8 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/openclaw.plugin.json +1 -1
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/package.json +1 -1
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/pyproject.toml +1 -1
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/server.json +2 -2
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/skills/vmware-avi/SKILL.md +2 -2
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/skills/vmware-avi/references/capabilities.md +1 -1
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/skills/vmware-avi/references/setup-guide.md +12 -4
- vmware_avi-2.4.0/tests/test_policy_roles.py +69 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/uv.lock +4 -4
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/__init__.py +1 -1
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/policy_environment.py +4 -0
- vmware_avi-2.4.0/vmware_avi/policy_roles.py +69 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/.clawhubignore +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/.env.example +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/.gitattributes +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/.gitignore +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/Dockerfile +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/SECURITY.md +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/config.example.yaml +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/evals/evals.json +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/examples/mcp-configs/claude-code.json +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/examples/mcp-configs/cursor.json +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/skills/vmware-avi/references/agent-guardrails.md +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/skills/vmware-avi/references/cli-reference.md +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/smithery.yaml +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/__init__.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/conftest.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/__init__.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/capability/__init__.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/capability/_family.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/capability/_scores.json +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/capability/_scoring.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/capability/_skill.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/capability/conftest.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/capability/test_entity_reachability.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/capability/test_error_actionability.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/capability/test_tool_description_quality.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/capability/test_tool_manifest_budget.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/__init__.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_ako_diff_predicts_upgrade.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_avi_specific.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_capability_grader.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_cli_writes_guarded.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_config_path_resolution_is_shared.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_env_secret_obfuscation.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_every_tool_parameter_is_documented.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_external_text_sanitized.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_fix_pack.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_helm_output_display_is_redacted.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_helm_values_redaction.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_init_and_onboarding.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_instructions_name_targets.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_list_scaling.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_release_blockers.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_text_io_declares_encoding.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_write_command_teaching.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_ako_pod.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_ako_sync.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_ako_write_gate.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_analytics.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_audit.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_config.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_error_logs.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_lb_write_gate.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_mcp_error_paths.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_mcp_tools.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_no_destructive_ops.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_pool_mgmt.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_safe_error_passthrough.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_se_mgmt.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_session_pool.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_vs_mgmt.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/_errors.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/_safety.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/cli.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/config.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/connection.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/doctor.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/init_wizard.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/k8s_connection.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/mcp_server/__init__.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/mcp_server/__main__.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/mcp_server/server.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/notify/__init__.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/notify/audit.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/ops/__init__.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/ops/ako_config.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/ops/ako_gate.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/ops/ako_ingress.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/ops/ako_multi_cluster.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/ops/ako_pod.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/ops/ako_sync.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/ops/analytics.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/ops/lb_gate.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/ops/pool_mgmt.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/ops/se_mgmt.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/ops/ssl_mgmt.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/ops/vs_mgmt.py +0 -0
- {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/ops/write_gate.py +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://anthropic.com/claude-code/plugin.schema.json",
|
|
3
3
|
"name": "vmware-avi",
|
|
4
4
|
"displayName": "VMware AVI",
|
|
5
|
-
"version": "2.
|
|
5
|
+
"version": "2.4.0",
|
|
6
6
|
"description": "VMware AVI (NSX ALB) load balancer plus AKO Kubernetes ops — 28 MCP tools.",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Wei Zhou",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: vmware-avi
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.0
|
|
4
4
|
Summary: AVI (NSX Advanced Load Balancer) management and AKO Kubernetes operations tool
|
|
5
5
|
Project-URL: Homepage, https://github.com/vmware-skills/VMware-AVI
|
|
6
6
|
Project-URL: Repository, https://github.com/vmware-skills/VMware-AVI
|
|
@@ -613,11 +613,18 @@ claude mcp add --transport http vmware-avi https://mcp.example.com:8443/mcp \
|
|
|
613
613
|
before it binds that unauthenticated requests get 401. Use a certificate your clients trust for those names.
|
|
614
614
|
- Behind a TLS-terminating reverse proxy on the same machine use `--transport http --host 127.0.0.1
|
|
615
615
|
--public-name mcp.example.com` and pass the `Authorization` header through.
|
|
616
|
+
- Behind a load balancer or gateway on another machine, keep `--transport https` (a self-signed certificate is
|
|
617
|
+
fine), list in `--public-name` the host name the balancer sends as `Host`, and have it forward `Authorization`.
|
|
618
|
+
Its health probe can use `GET /healthz`, which needs no token and returns only `{"status": "ok"}`
|
|
619
|
+
(vmware-policy ≥ 1.23.0).
|
|
620
|
+
- Send `traceparent` or `X-Request-ID` with each request to have it stored on the audit row;
|
|
621
|
+
`vmware-audit log --correlation-id ID` then finds the calls behind one platform request.
|
|
616
622
|
- The audit log records each caller as `token:<name>`. Calls run side by side: each holds its own session to a
|
|
617
623
|
Controller (or Kubernetes context), up to `VMWARE_MCP_POOL_SIZE` (default 2) sessions per target. Each Controller
|
|
618
624
|
session is a login on the Controller; a call waits at most 300 s for a free one.
|
|
619
625
|
- **Everyone who can reach the endpoint acts with this server's account.** Tokens identify callers; they grant
|
|
620
626
|
nothing on the platform. Run one endpoint per privilege level.
|
|
627
|
+
- **Several people or roles on one endpoint:** run `vmware-audit roles enable` once, then give each token a role (`vmware-audit token create NAME --role reader`, or a role in `roles.yaml` naming tools and targets; vmware-policy ≥ 1.24.0). A role limits what the token may call on this endpoint; it cannot grant more than the account has. See vmware-policy's README, "Roles for network callers".
|
|
621
628
|
|
|
622
629
|
**Offer only read tools, plus the writes you choose:**
|
|
623
630
|
|
|
@@ -571,11 +571,17 @@ claude mcp add --transport http vmware-avi https://mcp.example.com:8443/mcp \
|
|
|
571
571
|
证书要用客户端信任、且覆盖这些名字的证书。
|
|
572
572
|
- 在同一台机器上由反向代理终结 TLS 时,用 `--transport http --host 127.0.0.1 --public-name mcp.example.com`,
|
|
573
573
|
并让代理透传 `Authorization` 头。
|
|
574
|
+
- 负载均衡或网关在另一台机器上时,保持 `--transport https`(自签证书也可以),在 `--public-name` 里写上负载均衡
|
|
575
|
+
转发时带的 `Host` 名,并让它透传 `Authorization`。健康检查可以用 `GET /healthz`:不需要 token,只返回
|
|
576
|
+
`{"status": "ok"}`(需 vmware-policy ≥ 1.23.0)。
|
|
577
|
+
- 每个请求带上 `traceparent` 或 `X-Request-ID`,它会记进审计记录;用 `vmware-audit log --correlation-id ID`
|
|
578
|
+
就能查到平台上某一次请求对应的调用。
|
|
574
579
|
- 审计日志把每个调用者记为 `token:<名字>`。调用并行执行:每个调用独占一个到 Controller(或 Kubernetes context)的
|
|
575
580
|
会话,每个 target 最多 `VMWARE_MCP_POOL_SIZE`(默认 2)个会话。每个 Controller 会话都是在 Controller 上的一次登录;
|
|
576
581
|
调用最多等待 300 秒拿到空闲会话。
|
|
577
582
|
- **能连到这个端点的人,都以该 server 配置的账号操作。** token 只用来识别调用者,不在平台上授予任何权限。
|
|
578
583
|
每个权限级别单独起一个端点。
|
|
584
|
+
- **一个端点给多人或多种角色用:** 先运行一次 `vmware-audit roles enable`,再给每个 token 指定角色(`vmware-audit token create 名字 --role reader`,或在 `roles.yaml` 里定义可用的工具和 target;需 vmware-policy ≥ 1.24.0)。角色只能限定这个 token 在本端点上能调用什么,不能超出账号本身的权限。详见 vmware-policy README 的「网络调用者的角色」一节。
|
|
579
585
|
|
|
580
586
|
**只提供读工具,加上你选定的写工具:**
|
|
581
587
|
|
|
@@ -591,11 +591,18 @@ claude mcp add --transport http vmware-avi https://mcp.example.com:8443/mcp \
|
|
|
591
591
|
before it binds that unauthenticated requests get 401. Use a certificate your clients trust for those names.
|
|
592
592
|
- Behind a TLS-terminating reverse proxy on the same machine use `--transport http --host 127.0.0.1
|
|
593
593
|
--public-name mcp.example.com` and pass the `Authorization` header through.
|
|
594
|
+
- Behind a load balancer or gateway on another machine, keep `--transport https` (a self-signed certificate is
|
|
595
|
+
fine), list in `--public-name` the host name the balancer sends as `Host`, and have it forward `Authorization`.
|
|
596
|
+
Its health probe can use `GET /healthz`, which needs no token and returns only `{"status": "ok"}`
|
|
597
|
+
(vmware-policy ≥ 1.23.0).
|
|
598
|
+
- Send `traceparent` or `X-Request-ID` with each request to have it stored on the audit row;
|
|
599
|
+
`vmware-audit log --correlation-id ID` then finds the calls behind one platform request.
|
|
594
600
|
- The audit log records each caller as `token:<name>`. Calls run side by side: each holds its own session to a
|
|
595
601
|
Controller (or Kubernetes context), up to `VMWARE_MCP_POOL_SIZE` (default 2) sessions per target. Each Controller
|
|
596
602
|
session is a login on the Controller; a call waits at most 300 s for a free one.
|
|
597
603
|
- **Everyone who can reach the endpoint acts with this server's account.** Tokens identify callers; they grant
|
|
598
604
|
nothing on the platform. Run one endpoint per privilege level.
|
|
605
|
+
- **Several people or roles on one endpoint:** run `vmware-audit roles enable` once, then give each token a role (`vmware-audit token create NAME --role reader`, or a role in `roles.yaml` naming tools and targets; vmware-policy ≥ 1.24.0). A role limits what the token may call on this endpoint; it cannot grant more than the account has. See vmware-policy's README, "Roles for network callers".
|
|
599
606
|
|
|
600
607
|
**Offer only read tools, plus the writes you choose:**
|
|
601
608
|
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
## v2.4.0 — roles for network callers (HLD §18)
|
|
2
|
+
|
|
3
|
+
* Declares the role metadata vmware-policy ≥ 1.24.0 needs to apply **roles** to this server over HTTPS
|
|
4
|
+
(HLD §18): how a missing controller resolves, that tools without a `controller` argument act on the default controller, that the AKO tools act on a Kubernetes context (so a controller-limited role cannot use them), and that `diff_ako_config` and `upgrade_ako_config` escape a role (their Helm values can hold the controller password). With an older vmware-policy nothing changes. Once an operator runs
|
|
5
|
+
`vmware-audit roles enable`, a token's role decides which tools and targets it may use here; a refused call is
|
|
6
|
+
audited as `denied`. See vmware-policy's README, "Roles for network callers".
|
|
7
|
+
* README and setup guide: one endpoint for several people or roles.
|
|
8
|
+
|
|
1
9
|
## v2.3.0 — calls run side by side
|
|
2
10
|
|
|
3
11
|
**Calls no longer wait for each other** (HLD §16 D-13(2), via vmware-policy 1.22.0). When one server serves several
|
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"url": "https://github.com/vmware-skills/VMware-AVI",
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
|
-
"version": "2.
|
|
10
|
+
"version": "2.4.0",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "pypi",
|
|
14
14
|
"identifier": "vmware-avi",
|
|
15
|
-
"version": "2.
|
|
15
|
+
"version": "2.4.0",
|
|
16
16
|
"runtimeHint": "uvx",
|
|
17
17
|
"packageArguments": [
|
|
18
18
|
{
|
|
@@ -53,7 +53,7 @@ AVI (NSX Advanced Load Balancer) application delivery and AKO Kubernetes operati
|
|
|
53
53
|
## Quick Install
|
|
54
54
|
|
|
55
55
|
```bash
|
|
56
|
-
uv tool install vmware-avi==2.
|
|
56
|
+
uv tool install vmware-avi==2.4.0
|
|
57
57
|
vmware-avi doctor # checks Controller connectivity + kubeconfig + avisdk
|
|
58
58
|
```
|
|
59
59
|
|
|
@@ -235,7 +235,7 @@ Force resync triggers AKO to re-reconcile all K8s objects. If the drift persists
|
|
|
235
235
|
## Setup
|
|
236
236
|
|
|
237
237
|
```bash
|
|
238
|
-
uv tool install vmware-avi==2.
|
|
238
|
+
uv tool install vmware-avi==2.4.0
|
|
239
239
|
mkdir -p ~/.vmware-avi
|
|
240
240
|
vmware-avi init # generates config.yaml and .env templates
|
|
241
241
|
chmod 600 ~/.vmware-avi/.env
|
|
@@ -37,7 +37,7 @@ All 28 MCP tools exposed by `vmware-avi mcp` (v1.5.15+; legacy entry point: `vmw
|
|
|
37
37
|
| `vmware-avi mcp` (CLI subcommand, stdio) | ✅ Full | ✅ v1.5.15+ default — no PyPI re-resolve, works behind corporate TLS proxies. |
|
|
38
38
|
| `vmware-avi-mcp` (legacy console script, stdio) | ✅ Full | Kept for backward compatibility with pre-1.5.15 configs. |
|
|
39
39
|
| `python -m vmware_avi.mcp_server` (stdio, via `__main__.py`) | ✅ Full | Docker image `CMD` only — not for end-user CLI install, and no longer used by `smithery.yaml` (which now calls the `vmware-avi mcp` entry point). Added v1.5.22. |
|
|
40
|
-
| `uvx --from vmware-avi==2.
|
|
40
|
+
| `uvx --from vmware-avi==2.4.0 vmware-avi-mcp` | ⚠ Fallback | Re-resolves PyPI on each launch; fails behind corporate TLS proxies (踩坑 #25). Use `UV_NATIVE_TLS=true` workaround. |
|
|
41
41
|
|
|
42
42
|
## Automation Level Reference
|
|
43
43
|
|
|
@@ -21,7 +21,7 @@ Complete installation, configuration, and AI platform integration guide for the
|
|
|
21
21
|
### Standard Install (recommended)
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
uv tool install vmware-avi==2.
|
|
24
|
+
uv tool install vmware-avi==2.4.0
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
This installs the CLI (`vmware-avi`, with `vmware-avi mcp` subcommand for the MCP server in v1.5.15+), the legacy `vmware-avi-mcp` entry point (for backward compatibility), and all Python dependencies in an isolated environment.
|
|
@@ -29,7 +29,7 @@ This installs the CLI (`vmware-avi`, with `vmware-avi mcp` subcommand for the MC
|
|
|
29
29
|
### Development Install
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
|
-
git clone --branch v2.
|
|
32
|
+
git clone --branch v2.4.0 https://github.com/vmware-skills/VMware-AVI.git
|
|
33
33
|
cd VMware-AVI
|
|
34
34
|
uv pip install -e ".[dev]"
|
|
35
35
|
```
|
|
@@ -43,7 +43,7 @@ For platforms that prefer containerized MCP servers (e.g., Smithery registry, Ku
|
|
|
43
43
|
Build and run the MCP server in a container. The image uses `python:3.12-slim` with `uv` for dependency installation and runs `python -m vmware_avi.mcp_server` on stdio (no port exposed — MCP uses stdin/stdout).
|
|
44
44
|
|
|
45
45
|
```bash
|
|
46
|
-
git clone --branch v2.
|
|
46
|
+
git clone --branch v2.4.0 https://github.com/vmware-skills/VMware-AVI.git
|
|
47
47
|
cd VMware-AVI
|
|
48
48
|
|
|
49
49
|
# Build
|
|
@@ -72,7 +72,7 @@ Users can install via the Smithery UI or CLI without managing Python environment
|
|
|
72
72
|
|
|
73
73
|
| Deployment | Best For |
|
|
74
74
|
|------------|----------|
|
|
75
|
-
| `uv tool install vmware-avi==2.
|
|
75
|
+
| `uv tool install vmware-avi==2.4.0` + `vmware-avi mcp` | Local developer workstation, single-user CLI + MCP |
|
|
76
76
|
| Docker image | Self-hosted agents, CI runners, isolated environments, multi-user servers |
|
|
77
77
|
| Smithery | Zero-install agent integration, registry-managed discovery, hosted-MCP workflows |
|
|
78
78
|
|
|
@@ -257,6 +257,13 @@ vmware-avi mcp --transport https --host 0.0.0.0 --port 8443 \
|
|
|
257
257
|
- Behind a reverse proxy that terminates TLS (Nginx, HAProxy) on the same machine, use
|
|
258
258
|
`--transport http --host 127.0.0.1 --public-name mcp.example.com` (the names the proxy forwards in `Host`), and
|
|
259
259
|
have the proxy pass the `Authorization` header through.
|
|
260
|
+
- Behind a load balancer or gateway on another machine, keep `--transport https` (a self-signed certificate is
|
|
261
|
+
fine: the balancer is the client that has to trust it), list in `--public-name` the host name the balancer sends
|
|
262
|
+
as `Host`, and have it forward `Authorization`. Point its health probe at `GET /healthz`: it needs no token, returns
|
|
263
|
+
only `{"status": "ok"}` and accepts any `Host` (vmware-policy ≥ 1.23.0). Every other path still needs the token.
|
|
264
|
+
- Correlation: each audit row stores the caller's `traceparent` trace-id, else its `X-Request-ID` /
|
|
265
|
+
`X-Correlation-ID`. Have the agent platform send one, and `vmware-audit log --correlation-id ID` lists the calls
|
|
266
|
+
behind one platform request.
|
|
260
267
|
- `vmware-audit token list` / `token revoke NAME` manage callers; a revoked token is refused from its next request.
|
|
261
268
|
Replace a leaked token under a new name.
|
|
262
269
|
- The server will not start without at least one token, and checks before it binds that unauthenticated requests
|
|
@@ -267,6 +274,7 @@ vmware-avi mcp --transport https --host 0.0.0.0 --port 8443 \
|
|
|
267
274
|
- **Everyone who can reach the endpoint acts with this server's account.** Tokens identify callers; they grant
|
|
268
275
|
nothing on the platform. Run one endpoint per privilege level (for example a read-only account behind
|
|
269
276
|
`--expose read`, and a separate endpoint for operators).
|
|
277
|
+
- **Several people or roles on one endpoint:** run `vmware-audit roles enable` once, then give each token a role (`vmware-audit token create NAME --role reader`, or a role in `roles.yaml` naming tools and targets; vmware-policy ≥ 1.24.0). A role limits what the token may call on this endpoint; it cannot grant more than the account has. See vmware-policy's README, "Roles for network callers".
|
|
270
278
|
|
|
271
279
|
## Security
|
|
272
280
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""HLD §18: the role metadata this skill declares matches its real tools."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from vmware_avi import policy_roles
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _registry() -> set[str]:
|
|
13
|
+
from vmware_avi.mcp_server.server import mcp
|
|
14
|
+
|
|
15
|
+
return {t.name for t in asyncio.run(mcp.list_tools())}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@pytest.mark.unit
|
|
19
|
+
def test_every_declared_name_is_a_real_tool():
|
|
20
|
+
named = set(policy_roles.ESCAPES) | set(getattr(policy_roles, "AKO_TOOLS", ()))
|
|
21
|
+
assert named - _registry() == set(), sorted(named - _registry())
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@pytest.mark.unit
|
|
25
|
+
def test_metadata_is_declared_when_policy_has_roles():
|
|
26
|
+
roles = pytest.importorskip("vmware_policy.roles")
|
|
27
|
+
import vmware_avi.mcp_server.server # noqa: F401 (registers the metadata on import)
|
|
28
|
+
|
|
29
|
+
meta = roles.metadata("avi")
|
|
30
|
+
assert meta is not None and meta.version == 1
|
|
31
|
+
assert meta.escapes == policy_roles.ESCAPES
|
|
32
|
+
assert (meta.resolver is not None) is True
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
#: Reviewed: these act on the default Controller (``untagged_scope="one"``).
|
|
36
|
+
_DEFAULT_CONTROLLER_TOOLS = frozenset(
|
|
37
|
+
{
|
|
38
|
+
"check_ssl_cert_expiry",
|
|
39
|
+
"disable_lb_pool_member",
|
|
40
|
+
"enable_lb_pool_member",
|
|
41
|
+
"get_service_engine_health",
|
|
42
|
+
"get_virtual_service_analytics",
|
|
43
|
+
"get_virtual_service_error_logs",
|
|
44
|
+
"get_virtual_service_status",
|
|
45
|
+
"list_lb_pool_members",
|
|
46
|
+
"list_lb_pools",
|
|
47
|
+
"list_service_engines",
|
|
48
|
+
"list_ssl_certs",
|
|
49
|
+
"set_virtual_service_enabled",
|
|
50
|
+
}
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@pytest.mark.unit
|
|
55
|
+
def test_every_tool_without_a_controller_argument_is_classified():
|
|
56
|
+
"""Review L1: a new Kubernetes tool must not inherit 'acts on the default Controller'."""
|
|
57
|
+
from vmware_avi.mcp_server.server import mcp
|
|
58
|
+
|
|
59
|
+
untargeted = {
|
|
60
|
+
t.name
|
|
61
|
+
for t in asyncio.run(mcp.list_tools())
|
|
62
|
+
if "controller" not in (t.inputSchema.get("properties") or {})
|
|
63
|
+
}
|
|
64
|
+
unclassified = untargeted - policy_roles.AKO_TOOLS - _DEFAULT_CONTROLLER_TOOLS
|
|
65
|
+
assert not unclassified, (
|
|
66
|
+
f"add to policy_roles.AKO_TOOLS (acts on Kubernetes) or to the reviewed "
|
|
67
|
+
f"default-Controller list here: {sorted(unclassified)}"
|
|
68
|
+
)
|
|
69
|
+
assert not (_DEFAULT_CONTROLLER_TOOLS - untargeted), "reviewed list names a stale tool"
|
|
@@ -1423,7 +1423,7 @@ wheels = [
|
|
|
1423
1423
|
|
|
1424
1424
|
[[package]]
|
|
1425
1425
|
name = "vmware-avi"
|
|
1426
|
-
version = "2.
|
|
1426
|
+
version = "2.4.0"
|
|
1427
1427
|
source = { editable = "." }
|
|
1428
1428
|
dependencies = [
|
|
1429
1429
|
{ name = "avisdk" },
|
|
@@ -1466,16 +1466,16 @@ dev = [
|
|
|
1466
1466
|
|
|
1467
1467
|
[[package]]
|
|
1468
1468
|
name = "vmware-policy"
|
|
1469
|
-
version = "1.
|
|
1469
|
+
version = "1.24.0"
|
|
1470
1470
|
source = { registry = "https://pypi.org/simple" }
|
|
1471
1471
|
dependencies = [
|
|
1472
1472
|
{ name = "pyyaml" },
|
|
1473
1473
|
{ name = "rich" },
|
|
1474
1474
|
{ name = "typer" },
|
|
1475
1475
|
]
|
|
1476
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
1476
|
+
sdist = { url = "https://files.pythonhosted.org/packages/4a/62/3a3cb83af77e3dd3c8fc92cb3b7968bfda04a2f71bf0d6f9b72ee3198441/vmware_policy-1.24.0.tar.gz", hash = "sha256:fba12732ccbd4371232c15d9fd014f9f5d697497210aedff2515d1d205ed3577", size = 361302, upload-time = "2026-10-16T07:18:28.317Z" }
|
|
1477
1477
|
wheels = [
|
|
1478
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1478
|
+
{ url = "https://files.pythonhosted.org/packages/3d/f2/fe3d9d384712023916e2cacddc385f14b095509c5e00aea417d1a9550e98/vmware_policy-1.24.0-py3-none-any.whl", hash = "sha256:cf615d0256134f01fb84c39b083649281a66d2c064c2eaa28786a0fa95ec6e58", size = 131460, upload-time = "2026-10-16T07:18:26.907Z" },
|
|
1479
1479
|
]
|
|
1480
1480
|
|
|
1481
1481
|
[[package]]
|
|
@@ -54,3 +54,7 @@ def _environment_for(target: str | None) -> str:
|
|
|
54
54
|
# to ALLOW. Keyed, a resolver only ever answers for its own skill, so
|
|
55
55
|
# registering at import time is safe again.
|
|
56
56
|
set_environment_resolver(_environment_for, skill=skill_name(__name__))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# Role metadata (HLD §18) is declared beside the environment resolver, on every surface.
|
|
60
|
+
import vmware_avi.policy_roles # noqa: E402,F401
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""What vmware-policy needs to apply roles to this skill (HLD §18 D-21/D-22).
|
|
2
|
+
|
|
3
|
+
Policy cannot see which tools escape a role or how a missing target resolves; this
|
|
4
|
+
module declares them. Imported by ``policy_environment``, so the MCP server and
|
|
5
|
+
the CLI both register it. With a vmware-policy that has no roles module (< 1.24)
|
|
6
|
+
nothing is declared.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from vmware_policy import skill_name
|
|
12
|
+
|
|
13
|
+
from vmware_avi.policy_environment import _cached_config
|
|
14
|
+
|
|
15
|
+
#: AKO tools act on a Kubernetes context, not on an AVI controller target, so a
|
|
16
|
+
#: controller-limited role cannot use them (scope "all"). Every other tool without a
|
|
17
|
+
#: ``controller`` argument acts on the default controller (``untagged_scope="one"``).
|
|
18
|
+
AKO_TOOLS = frozenset(
|
|
19
|
+
{
|
|
20
|
+
"check_ako_ingress",
|
|
21
|
+
"diagnose_ako_ingress",
|
|
22
|
+
"diff_ako_config",
|
|
23
|
+
"diff_ako_sync",
|
|
24
|
+
"force_ako_sync",
|
|
25
|
+
"get_ako_amko_status",
|
|
26
|
+
"get_ako_config",
|
|
27
|
+
"get_ako_logs",
|
|
28
|
+
"get_ako_status",
|
|
29
|
+
"get_ako_sync_status",
|
|
30
|
+
"get_ako_version",
|
|
31
|
+
"list_ako_clusters",
|
|
32
|
+
"list_ako_ingresses",
|
|
33
|
+
"restart_ako",
|
|
34
|
+
"upgrade_ako_config",
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
#: Tools that give their holder more than a role. The AKO config tools return Helm
|
|
39
|
+
#: values that can hold the controller password. ``get_ako_config`` is not one: it
|
|
40
|
+
#: redacts credential values and withholds output it cannot redact. Host-path tools
|
|
41
|
+
#: are added by policy while a file root is set.
|
|
42
|
+
ESCAPES = frozenset(
|
|
43
|
+
{
|
|
44
|
+
"diff_ako_config",
|
|
45
|
+
"upgrade_ako_config",
|
|
46
|
+
}
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _target_name(target: str | None) -> str:
|
|
51
|
+
"""The target the connection layer would use for ``target`` (or the default)."""
|
|
52
|
+
config = _cached_config()
|
|
53
|
+
return (config.get_controller(target) if target else config.active_controller).name
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
from vmware_policy.roles import declare_role_metadata
|
|
58
|
+
except ImportError: # vmware-policy without roles: nothing to declare
|
|
59
|
+
declare_role_metadata = None
|
|
60
|
+
|
|
61
|
+
if declare_role_metadata is not None:
|
|
62
|
+
declare_role_metadata(
|
|
63
|
+
skill_name(__name__),
|
|
64
|
+
escapes=ESCAPES,
|
|
65
|
+
resolver=_target_name,
|
|
66
|
+
target_param="controller",
|
|
67
|
+
untagged_scope="one",
|
|
68
|
+
target_scope={name: "all" for name in AKO_TOOLS},
|
|
69
|
+
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/capability/test_tool_description_quality.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_ako_diff_predicts_upgrade.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_config_path_resolution_is_shared.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_helm_output_display_is_redacted.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_instructions_name_targets.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_text_io_declares_encoding.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|