vmware-debug 2.2.0__tar.gz → 2.3.1__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_debug-2.2.0 → vmware_debug-2.3.1}/.claude-plugin/plugin.json +1 -1
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/.mcp.json +1 -1
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/PKG-INFO +73 -3
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/README-CN.md +67 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/README.md +69 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/RELEASE_NOTES.md +27 -1
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/openclaw.plugin.json +1 -1
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/package.json +1 -1
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/pyproject.toml +4 -3
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/server.json +2 -2
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/skills/vmware-debug/SKILL.md +3 -1
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/skills/vmware-debug/references/setup-guide.md +38 -2
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/regression/test_audit_isolation.py +7 -2
- vmware_debug-2.3.1/tests/eval/regression/test_cli_policy_denial.py +23 -0
- vmware_debug-2.3.1/tests/test_policy_roles.py +32 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/uv.lock +16 -14
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/vmware_debug/__init__.py +1 -1
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/vmware_debug/cli.py +17 -1
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/vmware_debug/mcp_server/server.py +3 -0
- vmware_debug-2.3.1/vmware_debug/policy_roles.py +27 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/.clawhubignore +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/.gitignore +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/SECURITY.md +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/docs/plans/2026-08-28-investigation-layer-design.md +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/skills/vmware-debug/references/agent-guardrails.md +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/skills/vmware-debug/references/capabilities.md +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/skills/vmware-debug/references/cli-reference.md +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/skills/vmware-debug/references/event-envelope.md +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/skills/vmware-debug/references/routing.md +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/__init__.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/conftest.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/__init__.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/capability/__init__.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/capability/_family.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/capability/_scores.json +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/capability/_scoring.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/capability/_skill.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/capability/conftest.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/capability/test_entity_reachability.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/capability/test_error_actionability.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/capability/test_tool_description_quality.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/capability/test_tool_manifest_budget.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/regression/__init__.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/regression/_vsphere_event_vocabulary.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/regression/test_capability_grader.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/regression/test_case_id_non_ascii.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/regression/test_case_plan_max_steps.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/regression/test_classification_against_vsphere_vocabulary.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/regression/test_debug_regressions.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/regression/test_environment_resolver_isolation.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/regression/test_every_tool_parameter_is_documented.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/regression/test_evidence_payload_contract.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/regression/test_host_lifecycle_classification.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/regression/test_investigation_metrics.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/regression/test_real_alert_titles_classify.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/regression/test_result_envelope.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/regression/test_skill_name_aliases.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/regression/test_spike_resolution.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/regression/test_tool_annotations.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/test_case_conclusion.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/test_case_evidence.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/test_case_grading.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/test_case_grading_per_hypothesis.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/test_case_hypotheses.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/test_case_ids.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/test_case_ledger_concurrency.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/test_case_plan.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/test_case_readiness.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/test_case_store.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/test_case_timeline_close.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/test_case_tools.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/test_knowledge.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/test_mcp_audit.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/test_safe_error_passthrough.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/test_timeline.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/vmware_debug/envelope.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/vmware_debug/mcp/__init__.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/vmware_debug/mcp/tools.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/vmware_debug/mcp_server/__init__.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/vmware_debug/ops/__init__.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/__init__.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/api.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/conclusion.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/evidence.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/grading.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/hypotheses.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/ids.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/knowledge.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/model.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/payloads.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/plan.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/readiness.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/sources.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/store.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/timeline.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/vmware_debug/ops/skill_names.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/vmware_debug/ops/timeline.py +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/vmware_debug/rules/evidence_sources.yaml +0 -0
- {vmware_debug-2.2.0 → vmware_debug-2.3.1}/vmware_debug/rules/grading_rules.yaml +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://anthropic.com/claude-code/plugin.schema.json",
|
|
3
3
|
"name": "vmware-debug",
|
|
4
4
|
"displayName": "VMware Debug",
|
|
5
|
-
"version": "2.
|
|
5
|
+
"version": "2.3.1",
|
|
6
6
|
"description": "VMware incident timeline, root-cause routing, local case ledger; no vSphere access. 14 MCP tools.",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Wei Zhou",
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: vmware-debug
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.1
|
|
4
4
|
Summary: VMware diagnostic brain — offline incident triage, log/event correlation, and root-cause routing across the VMware skill family
|
|
5
5
|
Project-URL: Homepage, https://github.com/vmware-skills/VMware-Debug
|
|
6
6
|
Project-URL: Repository, https://github.com/vmware-skills/VMware-Debug
|
|
7
7
|
Project-URL: Issues, https://github.com/vmware-skills/VMware-Debug/issues
|
|
8
8
|
Project-URL: Changelog, https://github.com/vmware-skills/VMware-Debug/blob/main/RELEASE_NOTES.md
|
|
9
|
-
Author-email: Wei Zhou <
|
|
9
|
+
Author-email: Wei Zhou <zhouwei008@gmail.com>
|
|
10
10
|
License-Expression: MIT
|
|
11
11
|
Keywords: ai-ops,debug,diagnostics,mcp,rca,troubleshooting,vmware,vsphere
|
|
12
12
|
Classifier: Development Status :: 4 - Beta
|
|
@@ -14,7 +14,8 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
15
15
|
Classifier: Topic :: System :: Monitoring
|
|
16
16
|
Requires-Python: >=3.10
|
|
17
|
-
Requires-Dist:
|
|
17
|
+
Requires-Dist: certifi>=2026.10.19
|
|
18
|
+
Requires-Dist: mcp[cli]<2.0,>=1.30
|
|
18
19
|
Requires-Dist: pyyaml<7.0,>=6.0
|
|
19
20
|
Requires-Dist: rich<15.0,>=13.0
|
|
20
21
|
Requires-Dist: typer<1.0,>=0.12
|
|
@@ -25,6 +26,8 @@ Description-Content-Type: text/markdown
|
|
|
25
26
|
|
|
26
27
|
# VMware Debug
|
|
27
28
|
|
|
29
|
+
For a read-only check of this skill with your model and agent host, use the shared [model-fit self-test](https://github.com/vmware-skills/marketplace/blob/main/MODEL_FIT_SELF_TEST.md) in the marketplace root.
|
|
30
|
+
|
|
28
31
|
> **Disclaimer**: Community-maintained open-source project, **not affiliated with,
|
|
29
32
|
> endorsed by, or sponsored by VMware, Inc. or Broadcom Inc.** "VMware" and
|
|
30
33
|
> "vSphere" are trademarks of Broadcom. Source is publicly auditable under the MIT
|
|
@@ -105,6 +108,8 @@ event actually is.
|
|
|
105
108
|
|
|
106
109
|
## Security
|
|
107
110
|
|
|
111
|
+
CLI policy refusals print the reason and rule, exit with code 1, and retain one `denied` audit row.
|
|
112
|
+
|
|
108
113
|
Offline and credential-free by construction: it connects to no vCenter/NSX/Aria, has no destructive surface on
|
|
109
114
|
VMware, and holds no secret to leak. The only writes are the local case ledger: evidence, gaps, hypotheses and
|
|
110
115
|
grade history are append-only; `timeline.md` is regenerated from the evidence, and `case.json` holds the current
|
|
@@ -145,6 +150,71 @@ pip wheel . -w dist # → dist/*.whl (or: uv build, for just this packa
|
|
|
145
150
|
pip install --no-index --find-links dist vmware-debug
|
|
146
151
|
```
|
|
147
152
|
|
|
153
|
+
<!-- usage: remote-and-exposure -->
|
|
154
|
+
## Remote access (HTTPS) and limiting tools
|
|
155
|
+
|
|
156
|
+
By default `vmware-debug mcp` speaks stdio to one local client and offers every tool. Two options change that
|
|
157
|
+
(vmware-policy ≥ 1.21.0).
|
|
158
|
+
|
|
159
|
+
**Serve over HTTPS** so clients on other machines can connect. Every caller needs a bearer token:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
vmware-audit token create alice # prints the token once; give it to alice
|
|
163
|
+
vmware-audit token list # names only
|
|
164
|
+
vmware-audit token revoke alice # refused from its next request, no restart
|
|
165
|
+
vmware-debug mcp --transport https --host 0.0.0.0 --port 8443 \
|
|
166
|
+
--cert /etc/vmware-mcp/server.pem --key /etc/vmware-mcp/server-key.pem \
|
|
167
|
+
--public-name mcp.example.com
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Connect a client (Claude Code shown; any MCP client that supports streamable HTTP and custom headers works):
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
claude mcp add --transport http vmware-debug https://mcp.example.com:8443/mcp \
|
|
174
|
+
--header "Authorization: Bearer $VMWARE_MCP_TOKEN"
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
```json
|
|
178
|
+
{
|
|
179
|
+
"mcpServers": {
|
|
180
|
+
"vmware-debug": {
|
|
181
|
+
"type": "http",
|
|
182
|
+
"url": "https://mcp.example.com:8443/mcp",
|
|
183
|
+
"headers": { "Authorization": "Bearer ${VMWARE_MCP_TOKEN}" }
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
- The server will not start without a token, refuses Host/Origin values outside `--public-name`, and checks
|
|
190
|
+
before it binds that unauthenticated requests get 401. Use a certificate your clients trust for those names.
|
|
191
|
+
- Behind a TLS-terminating reverse proxy on the same machine use `--transport http --host 127.0.0.1
|
|
192
|
+
--public-name mcp.example.com` and pass the `Authorization` header through.
|
|
193
|
+
- Behind a load balancer or gateway on another machine, keep `--transport https` (a self-signed certificate is
|
|
194
|
+
fine), list in `--public-name` the host name the balancer sends as `Host`, and have it forward `Authorization`.
|
|
195
|
+
Its health probe can use `GET /healthz`, which needs no token and returns only `{"status": "ok"}`
|
|
196
|
+
(vmware-policy ≥ 1.23.0).
|
|
197
|
+
- Send `traceparent` or `X-Request-ID` with each request to have it stored on the audit row;
|
|
198
|
+
`vmware-audit log --correlation-id ID` then finds the calls behind one platform request.
|
|
199
|
+
- The audit log records each caller as `token:<name>`. Calls naming different targets run in parallel; calls that
|
|
200
|
+
name no target run one at a time, so name the target when several people share a server.
|
|
201
|
+
- **Everyone who can reach the endpoint acts with this server's account.** Tokens identify callers; they grant
|
|
202
|
+
nothing on the platform. Run one endpoint per privilege level.
|
|
203
|
+
- **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".
|
|
204
|
+
|
|
205
|
+
**Offer only read tools, plus the writes you choose:**
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
vmware-debug mcp --expose read --allow-write close_case
|
|
209
|
+
# or: VMWARE_DEBUG_MCP_EXPOSE=read VMWARE_DEBUG_MCP_ALLOW_WRITE=close_case vmware-debug mcp
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Tools not offered do not appear in the client's tool list. A bad value or an unknown write tool stops the server
|
|
213
|
+
(exit 2). This limits what clients are offered; it is not authorization, so the account itself should be
|
|
214
|
+
least-privilege.
|
|
215
|
+
|
|
216
|
+
Details: `skills/vmware-debug/references/setup-guide.md` ("Serving MCP over HTTPS", "Limiting which tools are offered").
|
|
217
|
+
|
|
148
218
|
## License
|
|
149
219
|
|
|
150
220
|
MIT.
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
# VMware Debug(中文)
|
|
4
4
|
|
|
5
|
+
要用自己的模型和 Agent 平台进行只读自测,请看 marketplace 根目录的[共享模型适配自测任务](https://github.com/vmware-skills/marketplace/blob/main/MODEL_FIT_SELF_TEST.md)。
|
|
6
|
+
|
|
5
7
|
> **声明**:本项目为社区维护的开源项目,**与 VMware, Inc. 或 Broadcom Inc. 无任何隶属、
|
|
6
8
|
> 背书或赞助关系。** "VMware"、"vSphere" 为 Broadcom 商标。源码以 MIT 许可证公开可审计。
|
|
7
9
|
|
|
@@ -68,6 +70,8 @@ debug;debug 因此与其它包零运行时依赖。**请把每条事件的 `ev
|
|
|
68
70
|
|
|
69
71
|
## 安全
|
|
70
72
|
|
|
73
|
+
CLI 被策略拒绝时会显示原因和规则,以退出码 1 结束,并保留一条 `denied` 审计记录。
|
|
74
|
+
|
|
71
75
|
结构上离线、无凭据:不连任何 vCenter/NSX/Aria,对 VMware 没有可破坏面,也没有秘密可泄露。
|
|
72
76
|
唯一的写入是本地案卷:证据、缺口、假设和等级历史只增不改;`timeline.md` 由证据重新生成,
|
|
73
77
|
`case.json` 保存当前等级和状态。多个写入者同样成立:每次写案卷都要先拿该案卷的锁文件(`.ledger.lock`,
|
|
@@ -102,6 +106,69 @@ pip wheel . -w dist # → dist/*.whl (或:uv build,只构建本
|
|
|
102
106
|
pip install --no-index --find-links dist vmware-debug
|
|
103
107
|
```
|
|
104
108
|
|
|
109
|
+
<!-- usage: remote-and-exposure -->
|
|
110
|
+
## 远程访问(HTTPS)与限制工具
|
|
111
|
+
|
|
112
|
+
默认情况下 `vmware-debug mcp` 通过 stdio 服务一个本地客户端,并提供全部工具。以下两个选项可以改变这一点
|
|
113
|
+
(需 vmware-policy ≥ 1.21.0)。
|
|
114
|
+
|
|
115
|
+
**通过 HTTPS 提供服务**,让其他机器上的客户端连接。每个调用者都需要一个 bearer token:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
vmware-audit token create alice # 只显示一次,交给 alice
|
|
119
|
+
vmware-audit token list # 只列名字
|
|
120
|
+
vmware-audit token revoke alice # 下一个请求起即拒绝,无需重启
|
|
121
|
+
vmware-debug mcp --transport https --host 0.0.0.0 --port 8443 \
|
|
122
|
+
--cert /etc/vmware-mcp/server.pem --key /etc/vmware-mcp/server-key.pem \
|
|
123
|
+
--public-name mcp.example.com
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
连接客户端(以 Claude Code 为例;任何支持 streamable HTTP 和自定义请求头的 MCP 客户端都可以):
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
claude mcp add --transport http vmware-debug https://mcp.example.com:8443/mcp \
|
|
130
|
+
--header "Authorization: Bearer $VMWARE_MCP_TOKEN"
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"mcpServers": {
|
|
136
|
+
"vmware-debug": {
|
|
137
|
+
"type": "http",
|
|
138
|
+
"url": "https://mcp.example.com:8443/mcp",
|
|
139
|
+
"headers": { "Authorization": "Bearer ${VMWARE_MCP_TOKEN}" }
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
- 没有 token 时服务不会启动;`--public-name` 之外的 Host/Origin 一律拒绝;绑定端口前会自检,确认未认证请求得到 401。
|
|
146
|
+
证书要用客户端信任、且覆盖这些名字的证书。
|
|
147
|
+
- 在同一台机器上由反向代理终结 TLS 时,用 `--transport http --host 127.0.0.1 --public-name mcp.example.com`,
|
|
148
|
+
并让代理透传 `Authorization` 头。
|
|
149
|
+
- 负载均衡或网关在另一台机器上时,保持 `--transport https`(自签证书也可以),在 `--public-name` 里写上负载均衡
|
|
150
|
+
转发时带的 `Host` 名,并让它透传 `Authorization`。健康检查可以用 `GET /healthz`:不需要 token,只返回
|
|
151
|
+
`{"status": "ok"}`(需 vmware-policy ≥ 1.23.0)。
|
|
152
|
+
- 每个请求带上 `traceparent` 或 `X-Request-ID`,它会记进审计记录;用 `vmware-audit log --correlation-id ID`
|
|
153
|
+
就能查到平台上某一次请求对应的调用。
|
|
154
|
+
- 审计日志把每个调用者记为 `token:<名字>`。指定不同 target 的调用并行执行;不指定 target 的调用逐个执行,
|
|
155
|
+
所以多人共用一个 server 时请写明 target。
|
|
156
|
+
- **能连到这个端点的人,都以该 server 配置的账号操作。** token 只用来识别调用者,不在平台上授予任何权限。
|
|
157
|
+
每个权限级别单独起一个端点。
|
|
158
|
+
- **一个端点给多人或多种角色用:** 先运行一次 `vmware-audit roles enable`,再给每个 token 指定角色(`vmware-audit token create 名字 --role reader`,或在 `roles.yaml` 里定义可用的工具和 target;需 vmware-policy ≥ 1.24.0)。角色只能限定这个 token 在本端点上能调用什么,不能超出账号本身的权限。详见 vmware-policy README 的「网络调用者的角色」一节。
|
|
159
|
+
|
|
160
|
+
**只提供读工具,加上你选定的写工具:**
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
vmware-debug mcp --expose read --allow-write close_case
|
|
164
|
+
# 或:VMWARE_DEBUG_MCP_EXPOSE=read VMWARE_DEBUG_MCP_ALLOW_WRITE=close_case vmware-debug mcp
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
未提供的工具不会出现在客户端的工具列表里。取值错误或写工具名不存在时服务直接退出(exit 2)。
|
|
168
|
+
这只限制提供给客户端的工具,不是授权;账号本身仍应遵循最小权限。
|
|
169
|
+
|
|
170
|
+
详见 `skills/vmware-debug/references/setup-guide.md`("Serving MCP over HTTPS"、"Limiting which tools are offered")。
|
|
171
|
+
|
|
105
172
|
## 许可证
|
|
106
173
|
|
|
107
174
|
MIT。
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
# VMware Debug
|
|
4
4
|
|
|
5
|
+
For a read-only check of this skill with your model and agent host, use the shared [model-fit self-test](https://github.com/vmware-skills/marketplace/blob/main/MODEL_FIT_SELF_TEST.md) in the marketplace root.
|
|
6
|
+
|
|
5
7
|
> **Disclaimer**: Community-maintained open-source project, **not affiliated with,
|
|
6
8
|
> endorsed by, or sponsored by VMware, Inc. or Broadcom Inc.** "VMware" and
|
|
7
9
|
> "vSphere" are trademarks of Broadcom. Source is publicly auditable under the MIT
|
|
@@ -82,6 +84,8 @@ event actually is.
|
|
|
82
84
|
|
|
83
85
|
## Security
|
|
84
86
|
|
|
87
|
+
CLI policy refusals print the reason and rule, exit with code 1, and retain one `denied` audit row.
|
|
88
|
+
|
|
85
89
|
Offline and credential-free by construction: it connects to no vCenter/NSX/Aria, has no destructive surface on
|
|
86
90
|
VMware, and holds no secret to leak. The only writes are the local case ledger: evidence, gaps, hypotheses and
|
|
87
91
|
grade history are append-only; `timeline.md` is regenerated from the evidence, and `case.json` holds the current
|
|
@@ -122,6 +126,71 @@ pip wheel . -w dist # → dist/*.whl (or: uv build, for just this packa
|
|
|
122
126
|
pip install --no-index --find-links dist vmware-debug
|
|
123
127
|
```
|
|
124
128
|
|
|
129
|
+
<!-- usage: remote-and-exposure -->
|
|
130
|
+
## Remote access (HTTPS) and limiting tools
|
|
131
|
+
|
|
132
|
+
By default `vmware-debug mcp` speaks stdio to one local client and offers every tool. Two options change that
|
|
133
|
+
(vmware-policy ≥ 1.21.0).
|
|
134
|
+
|
|
135
|
+
**Serve over HTTPS** so clients on other machines can connect. Every caller needs a bearer token:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
vmware-audit token create alice # prints the token once; give it to alice
|
|
139
|
+
vmware-audit token list # names only
|
|
140
|
+
vmware-audit token revoke alice # refused from its next request, no restart
|
|
141
|
+
vmware-debug mcp --transport https --host 0.0.0.0 --port 8443 \
|
|
142
|
+
--cert /etc/vmware-mcp/server.pem --key /etc/vmware-mcp/server-key.pem \
|
|
143
|
+
--public-name mcp.example.com
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Connect a client (Claude Code shown; any MCP client that supports streamable HTTP and custom headers works):
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
claude mcp add --transport http vmware-debug https://mcp.example.com:8443/mcp \
|
|
150
|
+
--header "Authorization: Bearer $VMWARE_MCP_TOKEN"
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
```json
|
|
154
|
+
{
|
|
155
|
+
"mcpServers": {
|
|
156
|
+
"vmware-debug": {
|
|
157
|
+
"type": "http",
|
|
158
|
+
"url": "https://mcp.example.com:8443/mcp",
|
|
159
|
+
"headers": { "Authorization": "Bearer ${VMWARE_MCP_TOKEN}" }
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
- The server will not start without a token, refuses Host/Origin values outside `--public-name`, and checks
|
|
166
|
+
before it binds that unauthenticated requests get 401. Use a certificate your clients trust for those names.
|
|
167
|
+
- Behind a TLS-terminating reverse proxy on the same machine use `--transport http --host 127.0.0.1
|
|
168
|
+
--public-name mcp.example.com` and pass the `Authorization` header through.
|
|
169
|
+
- Behind a load balancer or gateway on another machine, keep `--transport https` (a self-signed certificate is
|
|
170
|
+
fine), list in `--public-name` the host name the balancer sends as `Host`, and have it forward `Authorization`.
|
|
171
|
+
Its health probe can use `GET /healthz`, which needs no token and returns only `{"status": "ok"}`
|
|
172
|
+
(vmware-policy ≥ 1.23.0).
|
|
173
|
+
- Send `traceparent` or `X-Request-ID` with each request to have it stored on the audit row;
|
|
174
|
+
`vmware-audit log --correlation-id ID` then finds the calls behind one platform request.
|
|
175
|
+
- The audit log records each caller as `token:<name>`. Calls naming different targets run in parallel; calls that
|
|
176
|
+
name no target run one at a time, so name the target when several people share a server.
|
|
177
|
+
- **Everyone who can reach the endpoint acts with this server's account.** Tokens identify callers; they grant
|
|
178
|
+
nothing on the platform. Run one endpoint per privilege level.
|
|
179
|
+
- **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".
|
|
180
|
+
|
|
181
|
+
**Offer only read tools, plus the writes you choose:**
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
vmware-debug mcp --expose read --allow-write close_case
|
|
185
|
+
# or: VMWARE_DEBUG_MCP_EXPOSE=read VMWARE_DEBUG_MCP_ALLOW_WRITE=close_case vmware-debug mcp
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Tools not offered do not appear in the client's tool list. A bad value or an unknown write tool stops the server
|
|
189
|
+
(exit 2). This limits what clients are offered; it is not authorization, so the account itself should be
|
|
190
|
+
least-privilege.
|
|
191
|
+
|
|
192
|
+
Details: `skills/vmware-debug/references/setup-guide.md` ("Serving MCP over HTTPS", "Limiting which tools are offered").
|
|
193
|
+
|
|
125
194
|
## License
|
|
126
195
|
|
|
127
196
|
MIT.
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
## v2.3.1 — readable Policy refusals and dependency security (2026-11-08)
|
|
2
|
+
|
|
3
|
+
* Update public author/project contact metadata to `zhouwei008@gmail.com`.
|
|
4
|
+
|
|
5
|
+
### dependency security and sandbox test stability
|
|
6
|
+
* Require certifi 2026.10.19 or later for MCP's HTTP/TLS dependencies (GHSA-6q48-6c7w-9gw2); the lock resolves 2026.10.23.
|
|
7
|
+
* The audit-isolation regression still proves its write reached only the sandbox DB; it skips the production-DB comparison when the host sandbox denies even read-only access to that DB.
|
|
8
|
+
* CLI policy refusals now print their reason and rule without a traceback. The audited wrapper still records exactly one `denied` row before the CLI exits with code 1.
|
|
9
|
+
|
|
10
|
+
## v2.3.0 — roles for network callers (HLD §18)
|
|
11
|
+
|
|
12
|
+
* Declares the role metadata vmware-policy ≥ 1.24.0 needs to apply **roles** to this server over HTTPS
|
|
13
|
+
(HLD §18): that no tool takes a target. With an older vmware-policy nothing changes. Once an operator runs
|
|
14
|
+
`vmware-audit roles enable`, a token's role decides which tools and targets it may use here; a refused call is
|
|
15
|
+
audited as `denied`. See vmware-policy's README, "Roles for network callers".
|
|
16
|
+
* README and setup guide: one endpoint for several people or roles.
|
|
17
|
+
|
|
1
18
|
## v2.2.0 — serve MCP over HTTPS
|
|
2
19
|
|
|
3
20
|
`vmware-debug mcp` can now serve the network as well as stdio (HLD §16, via vmware-policy 1.21.0):
|
|
@@ -8,6 +25,15 @@ vmware-debug mcp --transport https --host 0.0.0.0 --port 8443 \
|
|
|
8
25
|
--cert server.pem --key server-key.pem --public-name mcp.example.com
|
|
9
26
|
```
|
|
10
27
|
|
|
28
|
+
Connect a client and manage callers (Claude Code shown; any MCP client with streamable HTTP and custom headers works):
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
claude mcp add --transport http vmware-debug https://mcp.example.com:8443/mcp \
|
|
32
|
+
--header "Authorization: Bearer $VMWARE_MCP_TOKEN"
|
|
33
|
+
vmware-audit token list # names only
|
|
34
|
+
vmware-audit token revoke alice # refused from its next request, no restart
|
|
35
|
+
```
|
|
36
|
+
|
|
11
37
|
* Every request needs `Authorization: Bearer <token>`; the server will not start without a token, refuses Host or
|
|
12
38
|
Origin values outside `--public-name`, and checks itself (unauthenticated requests must get 401) before it
|
|
13
39
|
binds. `--transport http` binds loopback only, for a TLS-terminating reverse proxy.
|
|
@@ -881,4 +907,4 @@ executes fixes (advisor/executor split, mirroring vmware-harden → vmware-pilot
|
|
|
881
907
|
### Notes
|
|
882
908
|
- Read-only by construction; remediation is routed, never executed.
|
|
883
909
|
- `parse_timestamp` rejects implausible/garbage timestamps loudly rather than
|
|
884
|
-
silently landing at the epoch.
|
|
910
|
+
silently landing at the epoch.
|
|
@@ -4,12 +4,12 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "vmware-debug"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.3.1"
|
|
8
8
|
description = "VMware diagnostic brain — offline incident triage, log/event correlation, and root-cause routing across the VMware skill family"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
11
11
|
requires-python = ">=3.10"
|
|
12
|
-
authors = [{ name = "Wei Zhou", email = "
|
|
12
|
+
authors = [{ name = "Wei Zhou", email = "zhouwei008@gmail.com" }]
|
|
13
13
|
keywords = ["vmware", "vsphere", "debug", "troubleshooting", "diagnostics", "rca", "mcp", "ai-ops"]
|
|
14
14
|
classifiers = [
|
|
15
15
|
"Development Status :: 4 - Beta",
|
|
@@ -20,7 +20,8 @@ classifiers = [
|
|
|
20
20
|
dependencies = [
|
|
21
21
|
"typer>=0.12,<1.0",
|
|
22
22
|
"rich>=13.0,<15.0",
|
|
23
|
-
"mcp[cli]>=1.
|
|
23
|
+
"mcp[cli]>=1.30,<2.0",
|
|
24
|
+
"certifi>=2026.10.19", # GHSA-6q48-6c7w-9gw2 via MCP/httpx
|
|
24
25
|
"vmware-policy>=1.21.0,<2.0",
|
|
25
26
|
# Imported directly by ops/cases/grading.py for the rules file. It has
|
|
26
27
|
# always arrived transitively via vmware-policy; a package should
|
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"url": "https://github.com/vmware-skills/VMware-Debug",
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
|
-
"version": "2.
|
|
10
|
+
"version": "2.3.1",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "pypi",
|
|
14
14
|
"identifier": "vmware-debug",
|
|
15
|
-
"version": "2.
|
|
15
|
+
"version": "2.3.1",
|
|
16
16
|
"runtimeHint": "uvx",
|
|
17
17
|
"packageArguments": [
|
|
18
18
|
{
|
|
@@ -25,6 +25,8 @@ metadata: {"openclaw":{"requires":{"anyBins":["vmware-debug","uvx"]},"optional":
|
|
|
25
25
|
|
|
26
26
|
# VMware Debug
|
|
27
27
|
|
|
28
|
+
> Self-test: [guide](https://github.com/vmware-skills/marketplace/blob/main/MODEL_FIT_SELF_TEST.md).
|
|
29
|
+
|
|
28
30
|
> **Disclaimer**: Community-maintained open-source project, **not affiliated with,
|
|
29
31
|
> endorsed by, or sponsored by VMware, Inc. or Broadcom Inc.** "VMware" and "vSphere"
|
|
30
32
|
> are trademarks of Broadcom. Source is publicly auditable under the MIT license.
|
|
@@ -51,7 +53,7 @@ write tools touch only the local case ledger.
|
|
|
51
53
|
## Quick Install
|
|
52
54
|
|
|
53
55
|
```bash
|
|
54
|
-
uv tool install vmware-debug==2.
|
|
56
|
+
uv tool install vmware-debug==2.3.1
|
|
55
57
|
vmware-debug categories # see what it can diagnose
|
|
56
58
|
```
|
|
57
59
|
|
|
@@ -6,7 +6,7 @@ is a pure, offline correlation engine. There is no `config.yaml` and no `.env`.
|
|
|
6
6
|
## Install
|
|
7
7
|
|
|
8
8
|
```bash
|
|
9
|
-
uv tool install vmware-debug==2.
|
|
9
|
+
uv tool install vmware-debug==2.3.1
|
|
10
10
|
vmware-debug categories # verify it runs
|
|
11
11
|
```
|
|
12
12
|
|
|
@@ -15,7 +15,7 @@ vmware-debug categories # verify it runs
|
|
|
15
15
|
```json
|
|
16
16
|
{
|
|
17
17
|
"command": "uvx",
|
|
18
|
-
"args": ["--from", "vmware-debug==2.
|
|
18
|
+
"args": ["--from", "vmware-debug==2.3.1", "vmware-debug-mcp"]
|
|
19
19
|
}
|
|
20
20
|
```
|
|
21
21
|
|
|
@@ -51,6 +51,42 @@ vmware-debug mcp --expose read --allow-write close_case
|
|
|
51
51
|
- This limits what clients are offered. It is **not** authorization. This server's write tools change case records on this machine;
|
|
52
52
|
deny rules in `~/.vmware/rules.yaml` still apply to every exposed tool.
|
|
53
53
|
|
|
54
|
+
## Serving MCP over HTTPS
|
|
55
|
+
|
|
56
|
+
By default the MCP server speaks stdio to one local client. To let clients on other machines reach it, serve it over
|
|
57
|
+
HTTPS. Every caller then needs a bearer token.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
vmware-audit token create alice # prints the token once; give it to alice
|
|
61
|
+
vmware-debug mcp --transport https --host 0.0.0.0 --port 8443 \
|
|
62
|
+
--cert /etc/vmware-mcp/server.pem --key /etc/vmware-mcp/server-key.pem \
|
|
63
|
+
--public-name mcp.example.com
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
- Clients connect to `https://mcp.example.com:8443/mcp` and send `Authorization: Bearer <token>`.
|
|
67
|
+
- `--public-name` lists every host name clients use (comma-separated); requests for any other Host or Origin are
|
|
68
|
+
refused. Use a certificate your clients trust for those names.
|
|
69
|
+
- Behind a reverse proxy that terminates TLS (Nginx, HAProxy) on the same machine, use
|
|
70
|
+
`--transport http --host 127.0.0.1 --public-name mcp.example.com` (the names the proxy forwards in `Host`), and
|
|
71
|
+
have the proxy pass the `Authorization` header through.
|
|
72
|
+
- Behind a load balancer or gateway on another machine, keep `--transport https` (a self-signed certificate is
|
|
73
|
+
fine: the balancer is the client that has to trust it), list in `--public-name` the host name the balancer sends
|
|
74
|
+
as `Host`, and have it forward `Authorization`. Point its health probe at `GET /healthz`: it needs no token, returns
|
|
75
|
+
only `{"status": "ok"}` and accepts any `Host` (vmware-policy ≥ 1.23.0). Every other path still needs the token.
|
|
76
|
+
- Correlation: each audit row stores the caller's `traceparent` trace-id, else its `X-Request-ID` /
|
|
77
|
+
`X-Correlation-ID`. Have the agent platform send one, and `vmware-audit log --correlation-id ID` lists the calls
|
|
78
|
+
behind one platform request.
|
|
79
|
+
- `vmware-audit token list` / `token revoke NAME` manage callers; a revoked token is refused from its next request.
|
|
80
|
+
Replace a leaked token under a new name.
|
|
81
|
+
- The server will not start without at least one token, and checks before it binds that unauthenticated requests
|
|
82
|
+
are refused.
|
|
83
|
+
- Each audit row records the caller as `token:<name>`. Calls naming different targets run in parallel; calls that
|
|
84
|
+
name no target run one at a time, so name the target when several people use one server.
|
|
85
|
+
- **Everyone who can reach the endpoint acts with this server's account.** Tokens identify callers; they grant
|
|
86
|
+
nothing on the platform. Run one endpoint per privilege level (for example a read-only account behind
|
|
87
|
+
`--expose read`, and a separate endpoint for operators).
|
|
88
|
+
- **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".
|
|
89
|
+
|
|
54
90
|
## Security
|
|
55
91
|
|
|
56
92
|
> **Disclaimer**: Community-maintained open-source project, **not affiliated with,
|
|
@@ -62,6 +62,11 @@ def test_an_audited_write_lands_in_the_sandbox_and_not_in_the_real_db() -> None:
|
|
|
62
62
|
real_db = REAL_HOME / ".vmware" / "audit.db"
|
|
63
63
|
if not real_db.exists():
|
|
64
64
|
pytest.skip("no production audit database on this machine to check against")
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
try:
|
|
66
|
+
with sqlite3.connect(f"file:{real_db}?mode=ro", uri=True) as con:
|
|
67
|
+
leaked = con.execute("SELECT count(*) FROM audit_log WHERE tool = ?", (marker,)).fetchone()[0]
|
|
68
|
+
except sqlite3.OperationalError as exc:
|
|
69
|
+
if "unable to open database file" not in str(exc):
|
|
70
|
+
raise
|
|
71
|
+
pytest.skip("production audit DB is inaccessible from this sandbox")
|
|
67
72
|
assert leaked == 0, f"{leaked} row(s) from this test reached {real_db}"
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""CLI policy denial must remain a denial in audit and be readable by an operator."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from typer.testing import CliRunner
|
|
5
|
+
|
|
6
|
+
from vmware_debug.cli import app
|
|
7
|
+
from vmware_policy import cli_guard
|
|
8
|
+
from vmware_policy.policy import PolicyDenied, PolicyResult
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.mark.parametrize("args", [["categories"], ["triage"]])
|
|
12
|
+
def test_denied_cli_command_prints_reason_and_records_one_denial(monkeypatch, args):
|
|
13
|
+
rows = []
|
|
14
|
+
def deny(*args, **kwargs):
|
|
15
|
+
raise PolicyDenied(PolicyResult(allowed=False, rule="ops-review", reason="offline checks denied"))
|
|
16
|
+
monkeypatch.setattr(cli_guard, "guard", deny)
|
|
17
|
+
monkeypatch.setattr(cli_guard, "audit_call", lambda *a, **kw: rows.append(kw))
|
|
18
|
+
result = CliRunner().invoke(app, args, input="[]")
|
|
19
|
+
assert result.exit_code == 1
|
|
20
|
+
assert "Denied by policy" in result.output
|
|
21
|
+
assert "offline checks denied" in result.output and "ops-review" in result.output
|
|
22
|
+
assert "Traceback" not in result.output
|
|
23
|
+
assert len(rows) == 1 and rows[0]["status"] == "denied"
|
|
@@ -0,0 +1,32 @@
|
|
|
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_debug import policy_roles
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _registry() -> set[str]:
|
|
13
|
+
from vmware_debug.mcp_server.server import build_server
|
|
14
|
+
|
|
15
|
+
return {t.name for t in asyncio.run(build_server().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_debug.mcp_server.server # noqa: F401 (registers the metadata on import)
|
|
28
|
+
|
|
29
|
+
meta = roles.metadata("debug")
|
|
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 False
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
version = 1
|
|
2
|
-
revision =
|
|
2
|
+
revision = 5
|
|
3
3
|
requires-python = ">=3.10"
|
|
4
4
|
resolution-markers = [
|
|
5
5
|
"python_full_version >= '3.14' and sys_platform == 'win32'",
|
|
6
6
|
"python_full_version >= '3.14' and sys_platform != 'win32'",
|
|
7
7
|
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'",
|
|
8
|
-
"python_full_version < '3.11' and sys_platform == 'win32'",
|
|
9
8
|
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'win32'",
|
|
9
|
+
"python_full_version < '3.11' and sys_platform == 'win32'",
|
|
10
10
|
"python_full_version < '3.11' and sys_platform != 'win32'",
|
|
11
11
|
]
|
|
12
12
|
|
|
@@ -53,11 +53,11 @@ wheels = [
|
|
|
53
53
|
|
|
54
54
|
[[package]]
|
|
55
55
|
name = "certifi"
|
|
56
|
-
version = "2026.
|
|
56
|
+
version = "2026.10.23"
|
|
57
57
|
source = { registry = "https://pypi.org/simple" }
|
|
58
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
58
|
+
sdist = { url = "https://files.pythonhosted.org/packages/fd/11/b524cbd9bbdaca58608e4cf4053fd463417549e1959fa8d3aa18319f9555/certifi-2026.10.23.tar.gz", hash = "sha256:a9895e6e2758b371e0545e321a3a8fd1fcfd9829ada8824a936dfb16aaaf36cd", size = 136225, upload-time = "2026-10-23T11:36:16.478Z" }
|
|
59
59
|
wheels = [
|
|
60
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
60
|
+
{ url = "https://files.pythonhosted.org/packages/32/d1/a3652fe2c19134d0d15922177821d863cbd7e842d53a21bbe7e8e3a1b5ac/certifi-2026.10.23-py3-none-any.whl", hash = "sha256:e9b54de76e530bb6cf0ccd036787103acb58fd42ba930ea8bf948ddbaa2f61d1", size = 135082, upload-time = "2026-10-23T11:36:14.763Z" },
|
|
61
61
|
]
|
|
62
62
|
|
|
63
63
|
[[package]]
|
|
@@ -356,7 +356,7 @@ name = "exceptiongroup"
|
|
|
356
356
|
version = "1.3.1"
|
|
357
357
|
source = { registry = "https://pypi.org/simple" }
|
|
358
358
|
dependencies = [
|
|
359
|
-
{ name = "typing-extensions"
|
|
359
|
+
{ name = "typing-extensions" },
|
|
360
360
|
]
|
|
361
361
|
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
|
|
362
362
|
wheels = [
|
|
@@ -469,7 +469,7 @@ wheels = [
|
|
|
469
469
|
|
|
470
470
|
[[package]]
|
|
471
471
|
name = "mcp"
|
|
472
|
-
version = "1.
|
|
472
|
+
version = "1.30.0"
|
|
473
473
|
source = { registry = "https://pypi.org/simple" }
|
|
474
474
|
dependencies = [
|
|
475
475
|
{ name = "anyio" },
|
|
@@ -487,9 +487,9 @@ dependencies = [
|
|
|
487
487
|
{ name = "typing-inspection" },
|
|
488
488
|
{ name = "uvicorn", marker = "sys_platform != 'emscripten'" },
|
|
489
489
|
]
|
|
490
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
490
|
+
sdist = { url = "https://files.pythonhosted.org/packages/ba/93/0142dc84a666daf8ad51a34268f34c12fd6fda4f3810c4be2504eecc8212/mcp-1.30.0.tar.gz", hash = "sha256:445414625fce5c295faa505bb11bacece661ab6f4028d57c935db57820b7a3e4", size = 680511, upload-time = "2026-09-07T14:34:15.845Z" }
|
|
491
491
|
wheels = [
|
|
492
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
492
|
+
{ url = "https://files.pythonhosted.org/packages/f5/f4/e58bc33317c92a0203664daaf00bf6f41166cc0149e5d6870a03f7cd004a/mcp-1.30.0-py3-none-any.whl", hash = "sha256:666edb5009503e1047c9d60346a756f94b261f05cc2625f23d41c728ffc484d0", size = 234581, upload-time = "2026-09-07T14:34:14.266Z" },
|
|
493
493
|
]
|
|
494
494
|
|
|
495
495
|
[package.optional-dependencies]
|
|
@@ -1293,9 +1293,10 @@ wheels = [
|
|
|
1293
1293
|
|
|
1294
1294
|
[[package]]
|
|
1295
1295
|
name = "vmware-debug"
|
|
1296
|
-
version = "2.
|
|
1296
|
+
version = "2.3.1"
|
|
1297
1297
|
source = { editable = "." }
|
|
1298
1298
|
dependencies = [
|
|
1299
|
+
{ name = "certifi" },
|
|
1299
1300
|
{ name = "mcp", extra = ["cli"] },
|
|
1300
1301
|
{ name = "pyyaml" },
|
|
1301
1302
|
{ name = "rich" },
|
|
@@ -1312,7 +1313,8 @@ dev = [
|
|
|
1312
1313
|
|
|
1313
1314
|
[package.metadata]
|
|
1314
1315
|
requires-dist = [
|
|
1315
|
-
{ name = "
|
|
1316
|
+
{ name = "certifi", specifier = ">=2026.10.19" },
|
|
1317
|
+
{ name = "mcp", extras = ["cli"], specifier = ">=1.30,<2.0" },
|
|
1316
1318
|
{ name = "pyyaml", specifier = ">=6.0,<7.0" },
|
|
1317
1319
|
{ name = "rich", specifier = ">=13.0,<15.0" },
|
|
1318
1320
|
{ name = "typer", specifier = ">=0.12,<1.0" },
|
|
@@ -1328,14 +1330,14 @@ dev = [
|
|
|
1328
1330
|
|
|
1329
1331
|
[[package]]
|
|
1330
1332
|
name = "vmware-policy"
|
|
1331
|
-
version = "1.
|
|
1333
|
+
version = "1.24.2"
|
|
1332
1334
|
source = { registry = "https://pypi.org/simple" }
|
|
1333
1335
|
dependencies = [
|
|
1334
1336
|
{ name = "pyyaml" },
|
|
1335
1337
|
{ name = "rich" },
|
|
1336
1338
|
{ name = "typer" },
|
|
1337
1339
|
]
|
|
1338
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
1340
|
+
sdist = { url = "https://files.pythonhosted.org/packages/15/8d/b9867ea1c0112caa481841c290c3fe718c0dbd738757db7877836f46267a/vmware_policy-1.24.2.tar.gz", hash = "sha256:6c816174fcd0b490bcd1f9ebaaa18b80fcb26b0eccb614e938938bb2e8452a40", size = 369315, upload-time = "2026-10-18T03:43:52.753Z" }
|
|
1339
1341
|
wheels = [
|
|
1340
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1342
|
+
{ url = "https://files.pythonhosted.org/packages/20/01/bc5f984a420d22514d8c3a627f332891f2008213d455c2f682f311e355f1/vmware_policy-1.24.2-py3-none-any.whl", hash = "sha256:2734ab49a76474fd74beb221a2d6535caab1f91a477668484c145dc2802ae072", size = 134565, upload-time = "2026-10-18T03:43:50.856Z" },
|
|
1341
1343
|
]
|
|
@@ -11,6 +11,7 @@ from __future__ import annotations
|
|
|
11
11
|
|
|
12
12
|
import json
|
|
13
13
|
import sys
|
|
14
|
+
from functools import wraps
|
|
14
15
|
from pathlib import Path
|
|
15
16
|
from typing import Optional
|
|
16
17
|
|
|
@@ -20,7 +21,7 @@ from rich.table import Table
|
|
|
20
21
|
|
|
21
22
|
from vmware_debug import __version__
|
|
22
23
|
from vmware_debug.mcp.tools import build_incident_timeline, list_symptom_categories
|
|
23
|
-
from vmware_policy import audited, cli_local
|
|
24
|
+
from vmware_policy import PolicyDenied, audited, cli_local, sanitize
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
def _harden_console_encoding() -> None:
|
|
@@ -53,6 +54,19 @@ app = typer.Typer(
|
|
|
53
54
|
console = Console()
|
|
54
55
|
|
|
55
56
|
|
|
57
|
+
def handle_cli_errors(func):
|
|
58
|
+
"""Render policy refusals after the audited wrapper has recorded the denial."""
|
|
59
|
+
@wraps(func)
|
|
60
|
+
def wrapper(*args, **kwargs):
|
|
61
|
+
try:
|
|
62
|
+
return func(*args, **kwargs)
|
|
63
|
+
except PolicyDenied as exc:
|
|
64
|
+
rule = f" (rule: {sanitize(exc.result.rule, 100)})" if exc.result.rule else ""
|
|
65
|
+
typer.echo(f"Denied by policy: {sanitize(exc.result.reason, 500)}{rule}", err=True)
|
|
66
|
+
raise typer.Exit(1) from None
|
|
67
|
+
return wrapper
|
|
68
|
+
|
|
69
|
+
|
|
56
70
|
@app.command()
|
|
57
71
|
@cli_local("prints the installed version")
|
|
58
72
|
def version() -> None:
|
|
@@ -61,6 +75,7 @@ def version() -> None:
|
|
|
61
75
|
|
|
62
76
|
|
|
63
77
|
@app.command()
|
|
78
|
+
@handle_cli_errors
|
|
64
79
|
@audited(tool="list_symptom_categories")
|
|
65
80
|
def categories() -> None:
|
|
66
81
|
"""List the symptom categories debug recognises and what to check for each."""
|
|
@@ -74,6 +89,7 @@ def categories() -> None:
|
|
|
74
89
|
|
|
75
90
|
|
|
76
91
|
@app.command()
|
|
92
|
+
@handle_cli_errors
|
|
77
93
|
@audited(tool="build_incident_timeline")
|
|
78
94
|
def triage(
|
|
79
95
|
events_file: Optional[Path] = typer.Option(
|
|
@@ -0,0 +1,27 @@
|
|
|
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 the MCP server (roles apply only to a network
|
|
5
|
+
MCP server). With a vmware-policy that has no roles module (< 1.24) nothing is
|
|
6
|
+
declared.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from vmware_policy import skill_name
|
|
12
|
+
|
|
13
|
+
#: Tools that give their holder more than a role. Host-path tools are added by
|
|
14
|
+
#: policy while a file root is set.
|
|
15
|
+
ESCAPES = frozenset()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
from vmware_policy.roles import declare_role_metadata
|
|
20
|
+
except ImportError: # vmware-policy without roles: nothing to declare
|
|
21
|
+
declare_role_metadata = None
|
|
22
|
+
|
|
23
|
+
if declare_role_metadata is not None:
|
|
24
|
+
declare_role_metadata(
|
|
25
|
+
skill_name(__name__),
|
|
26
|
+
escapes=ESCAPES,
|
|
27
|
+
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vmware_debug-2.2.0 → vmware_debug-2.3.1}/docs/plans/2026-08-28-investigation-layer-design.md
RENAMED
|
File without changes
|
{vmware_debug-2.2.0 → vmware_debug-2.3.1}/skills/vmware-debug/references/agent-guardrails.md
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
|
{vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/capability/test_tool_description_quality.py
RENAMED
|
File without changes
|
{vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/capability/test_tool_manifest_budget.py
RENAMED
|
File without changes
|
|
File without changes
|
{vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/regression/_vsphere_event_vocabulary.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
|
{vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/regression/test_evidence_payload_contract.py
RENAMED
|
File without changes
|
|
File without changes
|
{vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/regression/test_investigation_metrics.py
RENAMED
|
File without changes
|
{vmware_debug-2.2.0 → vmware_debug-2.3.1}/tests/eval/regression/test_real_alert_titles_classify.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
|