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.
Files changed (105) hide show
  1. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/.claude-plugin/plugin.json +1 -1
  2. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/.mcp.json +1 -1
  3. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/PKG-INFO +8 -1
  4. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/README-CN.md +6 -0
  5. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/README.md +7 -0
  6. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/RELEASE_NOTES.md +8 -0
  7. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/openclaw.plugin.json +1 -1
  8. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/package.json +1 -1
  9. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/pyproject.toml +1 -1
  10. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/server.json +2 -2
  11. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/skills/vmware-avi/SKILL.md +2 -2
  12. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/skills/vmware-avi/references/capabilities.md +1 -1
  13. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/skills/vmware-avi/references/setup-guide.md +12 -4
  14. vmware_avi-2.4.0/tests/test_policy_roles.py +69 -0
  15. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/uv.lock +4 -4
  16. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/__init__.py +1 -1
  17. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/policy_environment.py +4 -0
  18. vmware_avi-2.4.0/vmware_avi/policy_roles.py +69 -0
  19. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/.clawhubignore +0 -0
  20. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/.env.example +0 -0
  21. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/.gitattributes +0 -0
  22. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/.gitignore +0 -0
  23. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/Dockerfile +0 -0
  24. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/SECURITY.md +0 -0
  25. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/config.example.yaml +0 -0
  26. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/evals/evals.json +0 -0
  27. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/examples/mcp-configs/claude-code.json +0 -0
  28. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/examples/mcp-configs/cursor.json +0 -0
  29. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/skills/vmware-avi/references/agent-guardrails.md +0 -0
  30. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/skills/vmware-avi/references/cli-reference.md +0 -0
  31. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/smithery.yaml +0 -0
  32. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/__init__.py +0 -0
  33. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/conftest.py +0 -0
  34. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/__init__.py +0 -0
  35. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/capability/__init__.py +0 -0
  36. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/capability/_family.py +0 -0
  37. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/capability/_scores.json +0 -0
  38. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/capability/_scoring.py +0 -0
  39. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/capability/_skill.py +0 -0
  40. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/capability/conftest.py +0 -0
  41. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/capability/test_entity_reachability.py +0 -0
  42. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/capability/test_error_actionability.py +0 -0
  43. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/capability/test_tool_description_quality.py +0 -0
  44. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/capability/test_tool_manifest_budget.py +0 -0
  45. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/__init__.py +0 -0
  46. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_ako_diff_predicts_upgrade.py +0 -0
  47. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_avi_specific.py +0 -0
  48. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_capability_grader.py +0 -0
  49. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_cli_writes_guarded.py +0 -0
  50. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_config_path_resolution_is_shared.py +0 -0
  51. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_env_secret_obfuscation.py +0 -0
  52. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_every_tool_parameter_is_documented.py +0 -0
  53. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_external_text_sanitized.py +0 -0
  54. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_fix_pack.py +0 -0
  55. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_helm_output_display_is_redacted.py +0 -0
  56. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_helm_values_redaction.py +0 -0
  57. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_init_and_onboarding.py +0 -0
  58. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_instructions_name_targets.py +0 -0
  59. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_list_scaling.py +0 -0
  60. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_release_blockers.py +0 -0
  61. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_text_io_declares_encoding.py +0 -0
  62. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/eval/regression/test_write_command_teaching.py +0 -0
  63. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_ako_pod.py +0 -0
  64. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_ako_sync.py +0 -0
  65. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_ako_write_gate.py +0 -0
  66. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_analytics.py +0 -0
  67. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_audit.py +0 -0
  68. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_config.py +0 -0
  69. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_error_logs.py +0 -0
  70. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_lb_write_gate.py +0 -0
  71. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_mcp_error_paths.py +0 -0
  72. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_mcp_tools.py +0 -0
  73. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_no_destructive_ops.py +0 -0
  74. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_pool_mgmt.py +0 -0
  75. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_safe_error_passthrough.py +0 -0
  76. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_se_mgmt.py +0 -0
  77. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_session_pool.py +0 -0
  78. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/tests/test_vs_mgmt.py +0 -0
  79. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/_errors.py +0 -0
  80. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/_safety.py +0 -0
  81. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/cli.py +0 -0
  82. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/config.py +0 -0
  83. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/connection.py +0 -0
  84. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/doctor.py +0 -0
  85. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/init_wizard.py +0 -0
  86. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/k8s_connection.py +0 -0
  87. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/mcp_server/__init__.py +0 -0
  88. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/mcp_server/__main__.py +0 -0
  89. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/mcp_server/server.py +0 -0
  90. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/notify/__init__.py +0 -0
  91. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/notify/audit.py +0 -0
  92. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/ops/__init__.py +0 -0
  93. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/ops/ako_config.py +0 -0
  94. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/ops/ako_gate.py +0 -0
  95. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/ops/ako_ingress.py +0 -0
  96. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/ops/ako_multi_cluster.py +0 -0
  97. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/ops/ako_pod.py +0 -0
  98. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/ops/ako_sync.py +0 -0
  99. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/ops/analytics.py +0 -0
  100. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/ops/lb_gate.py +0 -0
  101. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/ops/pool_mgmt.py +0 -0
  102. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/ops/se_mgmt.py +0 -0
  103. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/ops/ssl_mgmt.py +0 -0
  104. {vmware_avi-2.3.0 → vmware_avi-2.4.0}/vmware_avi/ops/vs_mgmt.py +0 -0
  105. {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.3.0",
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",
@@ -5,7 +5,7 @@
5
5
  "command": "uvx",
6
6
  "args": [
7
7
  "--from",
8
- "vmware-avi==2.3.0",
8
+ "vmware-avi==2.4.0",
9
9
  "vmware-avi-mcp"
10
10
  ]
11
11
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: vmware-avi
3
- Version: 2.3.0
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
@@ -11,7 +11,7 @@
11
11
  "command": "uvx",
12
12
  "args": [
13
13
  "--from",
14
- "vmware-avi==2.3.0",
14
+ "vmware-avi==2.4.0",
15
15
  "vmware-avi-mcp"
16
16
  ]
17
17
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zw008/vmware-avi",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "description": "VMware AVI (NSX ALB) load balancer plus AKO Kubernetes ops — 28 MCP tools.",
5
5
  "private": true,
6
6
  "license": "MIT",
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "vmware-avi"
7
- version = "2.3.0"
7
+ version = "2.4.0"
8
8
  description = "AVI (NSX Advanced Load Balancer) management and AKO Kubernetes operations tool"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -7,12 +7,12 @@
7
7
  "url": "https://github.com/vmware-skills/VMware-AVI",
8
8
  "source": "github"
9
9
  },
10
- "version": "2.3.0",
10
+ "version": "2.4.0",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "pypi",
14
14
  "identifier": "vmware-avi",
15
- "version": "2.3.0",
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.3.0
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.3.0
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.3.0 vmware-avi-mcp` | ⚠ Fallback | Re-resolves PyPI on each launch; fails behind corporate TLS proxies (踩坑 #25). Use `UV_NATIVE_TLS=true` workaround. |
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.3.0
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.3.0 https://github.com/vmware-skills/VMware-AVI.git
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.3.0 https://github.com/vmware-skills/VMware-AVI.git
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.3.0` + `vmware-avi mcp` | Local developer workstation, single-user CLI + MCP |
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.3.0"
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.22.0"
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/d7/52/405a9d5e88d79649f4d7bddade8248f38ab6a508007a7500a3cdcf81897a/vmware_policy-1.22.0.tar.gz", hash = "sha256:e1a96b44ddf3c4d00fb8bdcaae4e1a059b03f343a84df9de23f865122d3dc280", size = 319890, upload-time = "2026-10-15T14:39:45.757Z" }
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/73/b5/2536cc8750c3ecb0a57c573def664989642ccae1a4f4a8b4283e34877721/vmware_policy-1.22.0-py3-none-any.whl", hash = "sha256:ca8afc5731e926b5eb1b93e26e14763b477bd45c33d3808138fb72903e2af664", size = 108585, upload-time = "2026-10-15T14:39:43.888Z" },
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]]
@@ -1,3 +1,3 @@
1
1
  """VMware AVI - AVI (NSX ALB) management and AKO Kubernetes operations."""
2
2
 
3
- __version__ = "2.3.0"
3
+ __version__ = "2.4.0"
@@ -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