vmware-avi 2.0.0__tar.gz → 2.2.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 (102) hide show
  1. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/.claude-plugin/plugin.json +1 -1
  2. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/.mcp.json +1 -1
  3. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/PKG-INFO +3 -3
  4. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/RELEASE_NOTES.md +36 -0
  5. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/openclaw.plugin.json +1 -1
  6. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/package.json +1 -1
  7. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/pyproject.toml +3 -3
  8. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/server.json +2 -2
  9. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/skills/vmware-avi/SKILL.md +2 -2
  10. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/skills/vmware-avi/references/capabilities.md +1 -1
  11. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/skills/vmware-avi/references/setup-guide.md +31 -4
  12. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/capability/_family.py +9 -7
  13. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/uv.lock +6 -6
  14. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/__init__.py +1 -1
  15. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/cli.py +38 -4
  16. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/mcp_server/server.py +18 -2
  17. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/.clawhubignore +0 -0
  18. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/.env.example +0 -0
  19. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/.gitattributes +0 -0
  20. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/.gitignore +0 -0
  21. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/Dockerfile +0 -0
  22. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/README-CN.md +0 -0
  23. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/README.md +0 -0
  24. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/SECURITY.md +0 -0
  25. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/config.example.yaml +0 -0
  26. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/evals/evals.json +0 -0
  27. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/examples/mcp-configs/claude-code.json +0 -0
  28. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/examples/mcp-configs/cursor.json +0 -0
  29. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/skills/vmware-avi/references/agent-guardrails.md +0 -0
  30. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/skills/vmware-avi/references/cli-reference.md +0 -0
  31. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/smithery.yaml +0 -0
  32. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/__init__.py +0 -0
  33. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/conftest.py +0 -0
  34. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/__init__.py +0 -0
  35. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/capability/__init__.py +0 -0
  36. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/capability/_scores.json +0 -0
  37. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/capability/_scoring.py +0 -0
  38. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/capability/_skill.py +0 -0
  39. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/capability/conftest.py +0 -0
  40. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/capability/test_entity_reachability.py +0 -0
  41. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/capability/test_error_actionability.py +0 -0
  42. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/capability/test_tool_description_quality.py +0 -0
  43. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/capability/test_tool_manifest_budget.py +0 -0
  44. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/regression/__init__.py +0 -0
  45. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/regression/test_ako_diff_predicts_upgrade.py +0 -0
  46. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/regression/test_avi_specific.py +0 -0
  47. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/regression/test_capability_grader.py +0 -0
  48. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/regression/test_cli_writes_guarded.py +0 -0
  49. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/regression/test_config_path_resolution_is_shared.py +0 -0
  50. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/regression/test_env_secret_obfuscation.py +0 -0
  51. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/regression/test_every_tool_parameter_is_documented.py +0 -0
  52. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/regression/test_external_text_sanitized.py +0 -0
  53. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/regression/test_fix_pack.py +0 -0
  54. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/regression/test_helm_output_display_is_redacted.py +0 -0
  55. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/regression/test_helm_values_redaction.py +0 -0
  56. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/regression/test_init_and_onboarding.py +0 -0
  57. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/regression/test_instructions_name_targets.py +0 -0
  58. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/regression/test_list_scaling.py +0 -0
  59. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/regression/test_release_blockers.py +0 -0
  60. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/regression/test_text_io_declares_encoding.py +0 -0
  61. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/eval/regression/test_write_command_teaching.py +0 -0
  62. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/test_ako_pod.py +0 -0
  63. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/test_ako_sync.py +0 -0
  64. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/test_ako_write_gate.py +0 -0
  65. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/test_analytics.py +0 -0
  66. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/test_audit.py +0 -0
  67. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/test_config.py +0 -0
  68. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/test_error_logs.py +0 -0
  69. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/test_lb_write_gate.py +0 -0
  70. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/test_mcp_error_paths.py +0 -0
  71. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/test_mcp_tools.py +0 -0
  72. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/test_no_destructive_ops.py +0 -0
  73. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/test_pool_mgmt.py +0 -0
  74. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/test_safe_error_passthrough.py +0 -0
  75. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/test_se_mgmt.py +0 -0
  76. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/tests/test_vs_mgmt.py +0 -0
  77. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/_errors.py +0 -0
  78. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/_safety.py +0 -0
  79. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/config.py +0 -0
  80. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/connection.py +0 -0
  81. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/doctor.py +0 -0
  82. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/init_wizard.py +0 -0
  83. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/k8s_connection.py +0 -0
  84. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/mcp_server/__init__.py +0 -0
  85. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/mcp_server/__main__.py +0 -0
  86. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/notify/__init__.py +0 -0
  87. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/notify/audit.py +0 -0
  88. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/ops/__init__.py +0 -0
  89. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/ops/ako_config.py +0 -0
  90. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/ops/ako_gate.py +0 -0
  91. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/ops/ako_ingress.py +0 -0
  92. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/ops/ako_multi_cluster.py +0 -0
  93. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/ops/ako_pod.py +0 -0
  94. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/ops/ako_sync.py +0 -0
  95. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/ops/analytics.py +0 -0
  96. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/ops/lb_gate.py +0 -0
  97. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/ops/pool_mgmt.py +0 -0
  98. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/ops/se_mgmt.py +0 -0
  99. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/ops/ssl_mgmt.py +0 -0
  100. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/ops/vs_mgmt.py +0 -0
  101. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/ops/write_gate.py +0 -0
  102. {vmware_avi-2.0.0 → vmware_avi-2.2.0}/vmware_avi/policy_environment.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.0.0",
5
+ "version": "2.2.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.0.0",
8
+ "vmware-avi==2.2.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.0.0
3
+ Version: 2.2.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
@@ -12,12 +12,12 @@ Requires-Python: >=3.10
12
12
  Requires-Dist: avisdk<31.0,>=22.1
13
13
  Requires-Dist: httpx<1.0,>=0.27
14
14
  Requires-Dist: kubernetes<33.0,>=28.0
15
- Requires-Dist: mcp[cli]<2.0,>=1.10
15
+ Requires-Dist: mcp[cli]<2.0,>=1.28
16
16
  Requires-Dist: pyaml<27.0,>=24.0
17
17
  Requires-Dist: python-dotenv<2.0,>=1.0
18
18
  Requires-Dist: rich<15.0,>=13.0
19
19
  Requires-Dist: typer<1.0,>=0.12
20
- Requires-Dist: vmware-policy<2.0,>=1.18.0
20
+ Requires-Dist: vmware-policy<2.0,>=1.21.0
21
21
  Description-Content-Type: text/markdown
22
22
 
23
23
  <!-- mcp-name: io.github.vmware-skills/vmware-avi -->
@@ -1,3 +1,39 @@
1
+ ## v2.2.0 — serve MCP over HTTPS
2
+
3
+ `vmware-avi mcp` can now serve the network as well as stdio (HLD §16, via vmware-policy 1.21.0):
4
+
5
+ ```bash
6
+ vmware-audit token create alice # once per caller; shown once
7
+ vmware-avi mcp --transport https --host 0.0.0.0 --port 8443 \
8
+ --cert server.pem --key server-key.pem --public-name mcp.example.com
9
+ ```
10
+
11
+ * Every request needs `Authorization: Bearer <token>`; the server will not start without a token, refuses Host or
12
+ Origin values outside `--public-name`, and checks itself (unauthenticated requests must get 401) before it
13
+ binds. `--transport http` binds loopback only, for a TLS-terminating reverse proxy.
14
+ * Audit rows record the caller as `token:<name>` and the surface as `mcp-https`. Calls naming different targets
15
+ run in parallel; calls naming no target run one at a time.
16
+ * stdio remains the default; the `vmware-avi-mcp` console script and `python -m` are unchanged.
17
+ * Everyone who can reach the endpoint acts with this server's account; run one endpoint per privilege level.
18
+ * Requires `vmware-policy>=1.21.0` and `mcp>=1.28`.
19
+
20
+ ## v2.1.0 — offer only read tools, plus chosen write tools
21
+
22
+ `vmware-avi mcp` can now offer a subset of this server's tools (HLD §17 D-16, via vmware-policy 1.20.0):
23
+
24
+ ```bash
25
+ vmware-avi mcp --expose read --allow-write disable_lb_pool_member
26
+ ```
27
+
28
+ * `--expose read` (or `VMWARE_AVI_MCP_EXPOSE=read`) keeps only tools annotated `readOnlyHint: true`; `--allow-write a,b`
29
+ (or `VMWARE_AVI_MCP_ALLOW_WRITE`) keeps chosen write tools on top. The default is unchanged: every tool.
30
+ * A bad value, or a name that is not a write tool of this server, stops the server before it serves (exit 2).
31
+ Flags and environment variables are not mixed; a blank flag counts as not given. Pre-2.0 names are accepted.
32
+ * This limits what clients are offered; it is not authorization. See "Limiting which tools are offered" in
33
+ `references/setup-guide.md`, which also notes that an agent with a shell can bypass MCP, so the account itself
34
+ must be least-privilege.
35
+ * Requires `vmware-policy>=1.20.0`.
36
+
1
37
  ## v2.0.0 — MCP tools renamed verb-first
2
38
 
3
39
  Every MCP tool in the VMware skill family now follows one naming convention: the verb comes first
@@ -11,7 +11,7 @@
11
11
  "command": "uvx",
12
12
  "args": [
13
13
  "--from",
14
- "vmware-avi==2.0.0",
14
+ "vmware-avi==2.2.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.0.0",
3
+ "version": "2.2.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.0.0"
7
+ version = "2.2.0"
8
8
  description = "AVI (NSX Advanced Load Balancer) management and AKO Kubernetes operations tool"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -18,8 +18,8 @@ dependencies = [
18
18
  "rich>=13.0,<15.0",
19
19
  "httpx>=0.27,<1.0",
20
20
  "python-dotenv>=1.0,<2.0",
21
- "mcp[cli]>=1.10,<2.0",
22
- "vmware-policy>=1.18.0,<2.0",
21
+ "mcp[cli]>=1.28,<2.0",
22
+ "vmware-policy>=1.21.0,<2.0",
23
23
  ]
24
24
 
25
25
 
@@ -7,12 +7,12 @@
7
7
  "url": "https://github.com/vmware-skills/VMware-AVI",
8
8
  "source": "github"
9
9
  },
10
- "version": "2.0.0",
10
+ "version": "2.2.0",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "pypi",
14
14
  "identifier": "vmware-avi",
15
- "version": "2.0.0",
15
+ "version": "2.2.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.0.0
56
+ uv tool install vmware-avi==2.2.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.0.0
238
+ uv tool install vmware-avi==2.2.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.0.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.2.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.0.0
24
+ uv tool install vmware-avi==2.2.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.0.0 https://github.com/vmware-skills/VMware-AVI.git
32
+ git clone --branch v2.2.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.0.0 https://github.com/vmware-skills/VMware-AVI.git
46
+ git clone --branch v2.2.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.0.0` + `vmware-avi mcp` | Local developer workstation, single-user CLI + MCP |
75
+ | `uv tool install vmware-avi==2.2.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
 
@@ -212,6 +212,33 @@ whitespace are handled correctly).
212
212
  > Secrets Manager, or a Kubernetes Secret) into the `*_PASSWORD` environment
213
213
  > variable at process start. The code reads the env var either way.
214
214
 
215
+ ## Limiting which tools are offered
216
+
217
+ By default the MCP server offers every tool. To offer only read tools, start it with `--expose read`, or set
218
+ `VMWARE_AVI_MCP_EXPOSE=read` in the MCP client's `env` block (useful when the client launches `vmware-avi mcp` itself). To offer
219
+ chosen write tools as well, add `--allow-write` with a comma-separated list (or `VMWARE_AVI_MCP_ALLOW_WRITE`):
220
+
221
+ ```bash
222
+ vmware-avi mcp --expose read --allow-write disable_lb_pool_member
223
+ ```
224
+
225
+ - A read tool is one annotated `readOnlyHint: true`; every other tool counts as a write.
226
+ - An unknown setting, or a name that is not a write tool of this server, stops the server before it serves (exit
227
+ code 2), so it never offers more than you asked for. Pre-2.0 tool names are accepted.
228
+ - With `--expose read`, the server prints one line to stderr saying what it exposes.
229
+ - Flags and environment variables are not mixed: if either flag is given, both environment variables are
230
+ ignored, so a leftover `_ALLOW_WRITE` in the shell cannot widen `--expose read`. A blank flag value counts as
231
+ not given.
232
+ - A tool that runs other steps (a plan, workflow or batch tool) carries every write it can execute, including
233
+ writes this setting hides. Expose such a tool only if you would expose those writes too.
234
+ - A launcher that imports the server object can call
235
+ `vmware_policy.exposure.restrict_tools(mcp, skill="avi", expose="read", allow_write=[...])` before running it,
236
+ instead of editing the SDK's tool registry.
237
+ - This limits what clients are offered. It is **not** authorization: whether a call may change anything is decided by
238
+ the account's permissions, and deny rules in `~/.vmware/rules.yaml` still apply to every exposed tool.
239
+ - An agent that can run shell commands can also reach the platform without MCP (SSH, curl, govc, PowerCLI). So
240
+ the service account itself must be least-privilege: exposure and deny rules are guardrails, not the boundary.
241
+
215
242
  ## Security
216
243
 
217
244
  > **Disclaimer**: This is a community-maintained open-source project and is **not affiliated with, endorsed by, or sponsored by VMware, Inc. or Broadcom Inc.** "VMware", "NSX", and "AVI" are trademarks of Broadcom.
@@ -104,9 +104,10 @@ FAMILY_TOOLS: dict[str, frozenset[str]] = {
104
104
  'update_dfw_policy', 'update_dfw_rule',
105
105
  }),
106
106
  'vmware-pilot': frozenset({
107
- 'approve', 'cancel_workflow', 'confirm_draft', 'create_workflow', 'design_workflow',
108
- 'get_skill_catalog', 'get_workflow_status', 'list_workflows', 'plan_workflow',
109
- 'review_workflow', 'rollback', 'run_workflow', 'update_draft',
107
+ 'approve', 'cancel_workflow', 'confirm_draft', 'create_workflow',
108
+ 'create_workflow_from_template', 'design_workflow', 'get_skill_catalog',
109
+ 'get_workflow_status', 'list_workflows', 'review_workflow', 'rollback',
110
+ 'run_workflow', 'update_draft',
110
111
  }),
111
112
  'vmware-storage': frozenset({
112
113
  'add_iscsi_target', 'browse_datastore', 'enable_iscsi', 'get_iscsi_status',
@@ -118,10 +119,11 @@ FAMILY_TOOLS: dict[str, frozenset[str]] = {
118
119
  'vmware-vks': frozenset({
119
120
  'check_vks_compatibility', 'create_namespace', 'create_tkc_cluster',
120
121
  'delete_namespace', 'delete_tkc_cluster', 'get_harbor_info', 'get_namespace',
121
- 'get_supervisor_kubeconfig', 'get_supervisor_status', 'get_tkc_available_versions',
122
- 'get_tkc_cluster', 'get_tkc_kubeconfig', 'list_namespace_storage_usage',
123
- 'list_namespaces', 'list_supervisor_storage_policies', 'list_tkc_clusters',
124
- 'list_vm_classes', 'list_vm_service_groups', 'list_vm_service_network_interfaces',
122
+ 'get_supervisor_status', 'get_tkc_available_versions', 'get_tkc_cluster',
123
+ 'issue_supervisor_kubeconfig', 'issue_tkc_kubeconfig',
124
+ 'list_namespace_storage_usage', 'list_namespaces',
125
+ 'list_supervisor_storage_policies', 'list_tkc_clusters', 'list_vm_classes',
126
+ 'list_vm_service_groups', 'list_vm_service_network_interfaces',
125
127
  'list_vm_service_snapshots', 'scale_tkc_cluster', 'update_namespace',
126
128
  'upgrade_tkc_cluster',
127
129
  }),
@@ -1423,7 +1423,7 @@ wheels = [
1423
1423
 
1424
1424
  [[package]]
1425
1425
  name = "vmware-avi"
1426
- version = "2.0.0"
1426
+ version = "2.2.0"
1427
1427
  source = { editable = "." }
1428
1428
  dependencies = [
1429
1429
  { name = "avisdk" },
@@ -1449,12 +1449,12 @@ requires-dist = [
1449
1449
  { name = "avisdk", specifier = ">=22.1,<31.0" },
1450
1450
  { name = "httpx", specifier = ">=0.27,<1.0" },
1451
1451
  { name = "kubernetes", specifier = ">=28.0,<33.0" },
1452
- { name = "mcp", extras = ["cli"], specifier = ">=1.10,<2.0" },
1452
+ { name = "mcp", extras = ["cli"], specifier = ">=1.28,<2.0" },
1453
1453
  { name = "pyaml", specifier = ">=24.0,<27.0" },
1454
1454
  { name = "python-dotenv", specifier = ">=1.0,<2.0" },
1455
1455
  { name = "rich", specifier = ">=13.0,<15.0" },
1456
1456
  { name = "typer", specifier = ">=0.12,<1.0" },
1457
- { name = "vmware-policy", specifier = ">=1.18.0,<2.0" },
1457
+ { name = "vmware-policy", specifier = ">=1.21.0,<2.0" },
1458
1458
  ]
1459
1459
 
1460
1460
  [package.metadata.requires-dev]
@@ -1466,16 +1466,16 @@ dev = [
1466
1466
 
1467
1467
  [[package]]
1468
1468
  name = "vmware-policy"
1469
- version = "1.18.2"
1469
+ version = "1.21.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/5d/ce/a4f31330f983f411051ab0f42dcbd3c7174670541fa5cdb7faaf22991b24/vmware_policy-1.18.2.tar.gz", hash = "sha256:8e52629bf4a0d355a6841f1d9d5fb4ac1e69bbc7ae22d3843dd7cf3426b30e4c", size = 208412, upload-time = "2026-10-14T06:08:54.124Z" }
1476
+ sdist = { url = "https://files.pythonhosted.org/packages/87/5d/4221ea4a344ec33d6b54903682ce8ae2013bb0a2d6137c94ac769690ea8b/vmware_policy-1.21.0.tar.gz", hash = "sha256:f42b794ea69e52e1fa13aff8b5bc07631d82683b1c12872f2f2ec744e3853549", size = 303862, upload-time = "2026-10-15T10:13:33.719Z" }
1477
1477
  wheels = [
1478
- { url = "https://files.pythonhosted.org/packages/6b/cc/0ad37c57ee902819efc1c193b363df9a7c762794e7dff3318577c17743de/vmware_policy-1.18.2-py3-none-any.whl", hash = "sha256:6e7414f6cc7d783f486db858fd99dd7123ddefc0e823a8b319756e17ece03455", size = 80791, upload-time = "2026-10-14T06:08:52.267Z" },
1478
+ { url = "https://files.pythonhosted.org/packages/b3/b4/feb7015ae5f7870e8058ad713050f7fe3ddefa57aa7bdd4a83f2e3b9abb4/vmware_policy-1.21.0-py3-none-any.whl", hash = "sha256:a10c26816be09f9a2823cc58bc4eacb9b5eff8b6df49dfc5b504b3d76cb3b7ec", size = 99818, upload-time = "2026-10-15T10:13:31.67Z" },
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.0.0"
3
+ __version__ = "2.2.0"
@@ -5,6 +5,7 @@ Typer-based CLI with subcommands for VS, Pool, SSL, SE, Analytics, and AKO opera
5
5
 
6
6
  from __future__ import annotations
7
7
 
8
+ from typing import Optional
8
9
  import sys
9
10
 
10
11
  import typer
@@ -110,13 +111,43 @@ def doctor() -> None:
110
111
 
111
112
  @app.command("mcp")
112
113
  @cli_local("starts the MCP server; its tools audit themselves")
113
- def mcp_cmd() -> None:
114
- """Start the MCP server (stdio transport).
114
+ def mcp_cmd(
115
+ expose: Optional[str] = typer.Option(
116
+ None,
117
+ "--expose",
118
+ help="Which tools to offer: all (default) or read. Limits what clients see; "
119
+ "not authorization: the account's permissions still decide.",
120
+ ),
121
+ allow_write: Optional[str] = typer.Option(
122
+ None,
123
+ "--allow-write",
124
+ help="With --expose read: comma-separated write tools to offer as well.",
125
+ ),
126
+ transport: Optional[str] = typer.Option(
127
+ None,
128
+ "--transport",
129
+ help="stdio (default), https, or http (loopback only, behind a TLS reverse proxy). "
130
+ "https/http need a token: 'vmware-audit token create NAME'.",
131
+ ),
132
+ host: Optional[str] = typer.Option(None, "--host", help="Bind address (default 127.0.0.1)."),
133
+ port: Optional[int] = typer.Option(None, "--port", help="Port (default 8443)."),
134
+ cert: Optional[str] = typer.Option(None, "--cert", help="TLS certificate (PEM) for https."),
135
+ key: Optional[str] = typer.Option(None, "--key", help="TLS private key (PEM) for https."),
136
+ public_name: Optional[str] = typer.Option(
137
+ None,
138
+ "--public-name",
139
+ help="Host names clients use, comma-separated; other Host/Origin values are refused.",
140
+ ),
141
+ token_file: Optional[str] = typer.Option(
142
+ None, "--token-file", help="Token hash file (default $OPS_HOME/mcp-tokens)."
143
+ ),
144
+ ) -> None:
145
+ """Start the MCP server (stdio by default; --transport https serves the network).
115
146
 
116
147
  Single-command entry point for MCP clients:
117
148
  vmware-avi mcp
118
149
 
119
- Equivalent to the legacy `vmware-avi-mcp` console script.
150
+ Equivalent to the legacy `vmware-avi-mcp` console script for stdio (the console script takes no flags).
120
151
  """
121
152
  import sys
122
153
 
@@ -134,7 +165,10 @@ def mcp_cmd() -> None:
134
165
 
135
166
  from vmware_avi.mcp_server.server import main as _mcp_main
136
167
 
137
- _mcp_main()
168
+ _mcp_main(
169
+ expose=expose, allow_write=allow_write, transport=transport, host=host, port=port,
170
+ cert=cert, key=key, public_name=public_name, token_file=token_file,
171
+ )
138
172
 
139
173
 
140
174
  @app.command()
@@ -1146,10 +1146,26 @@ from vmware_avi.policy_environment import _cached_config, _environment_for # no
1146
1146
  # ═══════════════════════════════════════════════════════════════════════════════
1147
1147
 
1148
1148
 
1149
- def main() -> None:
1149
+ def main(
1150
+ expose: Optional[str] = None,
1151
+ allow_write: Optional[str] = None,
1152
+ transport: Optional[str] = None,
1153
+ host: Optional[str] = None,
1154
+ port: Optional[int] = None,
1155
+ cert: Optional[str] = None,
1156
+ key: Optional[str] = None,
1157
+ public_name: Optional[str] = None,
1158
+ token_file: Optional[str] = None,
1159
+ ) -> None:
1150
1160
  """Entry point for vmware-avi-mcp."""
1151
1161
  logging.basicConfig(level=logging.INFO)
1152
- mcp.run(transport="stdio")
1162
+ from vmware_policy.serve import run_server
1163
+ run_server(
1164
+ mcp,
1165
+ skill="avi",
1166
+ expose=expose, allow_write=allow_write, transport=transport, host=host, port=port,
1167
+ cert=cert, key=key, public_name=public_name, token_file=token_file,
1168
+ )
1153
1169
 
1154
1170
 
1155
1171
  if __name__ == "__main__":
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