node804-hpe-msa 0.1.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 (51) hide show
  1. node804_hpe_msa-0.1.0/.gitignore +21 -0
  2. node804_hpe_msa-0.1.0/.pre-commit-config.yaml +19 -0
  3. node804_hpe_msa-0.1.0/AGENT_PLAN.md +289 -0
  4. node804_hpe_msa-0.1.0/CHANGELOG.md +90 -0
  5. node804_hpe_msa-0.1.0/LICENSE +21 -0
  6. node804_hpe_msa-0.1.0/PKG-INFO +461 -0
  7. node804_hpe_msa-0.1.0/README.md +423 -0
  8. node804_hpe_msa-0.1.0/pyproject.toml +123 -0
  9. node804_hpe_msa-0.1.0/skills/README.md +68 -0
  10. node804_hpe_msa-0.1.0/skills/hpe-msa-engineer/SKILL.md +97 -0
  11. node804_hpe_msa-0.1.0/skills/hpe-msa-engineer/references/events-alerts.md +43 -0
  12. node804_hpe_msa-0.1.0/skills/hpe-msa-engineer/references/health-model.md +54 -0
  13. node804_hpe_msa-0.1.0/skills/hpe-msa-engineer/references/hosts-mapping.md +60 -0
  14. node804_hpe_msa-0.1.0/skills/hpe-msa-engineer/references/playbooks/capacity-review.md +36 -0
  15. node804_hpe_msa-0.1.0/skills/hpe-msa-engineer/references/playbooks/disk-failure-triage.md +42 -0
  16. node804_hpe_msa-0.1.0/skills/hpe-msa-engineer/references/playbooks/event-triage.md +37 -0
  17. node804_hpe_msa-0.1.0/skills/hpe-msa-engineer/references/playbooks/firmware-inventory.md +30 -0
  18. node804_hpe_msa-0.1.0/skills/hpe-msa-engineer/references/playbooks/health-check.md +38 -0
  19. node804_hpe_msa-0.1.0/skills/hpe-msa-engineer/references/playbooks/mapping-audit.md +39 -0
  20. node804_hpe_msa-0.1.0/skills/hpe-msa-engineer/references/playbooks/performance-review.md +41 -0
  21. node804_hpe_msa-0.1.0/skills/hpe-msa-engineer/references/statistics.md +55 -0
  22. node804_hpe_msa-0.1.0/skills/hpe-msa-engineer/references/storage-model.md +64 -0
  23. node804_hpe_msa-0.1.0/skills/hpe-msa-engineer/references/tool-map.md +86 -0
  24. node804_hpe_msa-0.1.0/skills/hpe-msa-engineer/scripts/README.md +5 -0
  25. node804_hpe_msa-0.1.0/src/hpe_msa_mcp/__init__.py +14 -0
  26. node804_hpe_msa-0.1.0/src/hpe_msa_mcp/__main__.py +20 -0
  27. node804_hpe_msa-0.1.0/src/hpe_msa_mcp/_redact.py +75 -0
  28. node804_hpe_msa-0.1.0/src/hpe_msa_mcp/client.py +395 -0
  29. node804_hpe_msa-0.1.0/src/hpe_msa_mcp/config/__init__.py +0 -0
  30. node804_hpe_msa-0.1.0/src/hpe_msa_mcp/config/arrays.py +591 -0
  31. node804_hpe_msa-0.1.0/src/hpe_msa_mcp/py.typed +0 -0
  32. node804_hpe_msa-0.1.0/src/hpe_msa_mcp/responses/__init__.py +0 -0
  33. node804_hpe_msa-0.1.0/src/hpe_msa_mcp/responses/shapers.py +595 -0
  34. node804_hpe_msa-0.1.0/src/hpe_msa_mcp/server.py +123 -0
  35. node804_hpe_msa-0.1.0/src/hpe_msa_mcp/setup.py +781 -0
  36. node804_hpe_msa-0.1.0/src/hpe_msa_mcp/tools/__init__.py +0 -0
  37. node804_hpe_msa-0.1.0/src/hpe_msa_mcp/tools/_common.py +191 -0
  38. node804_hpe_msa-0.1.0/src/hpe_msa_mcp/tools/aggregations.py +152 -0
  39. node804_hpe_msa-0.1.0/src/hpe_msa_mcp/tools/events.py +106 -0
  40. node804_hpe_msa-0.1.0/src/hpe_msa_mcp/tools/hardware.py +160 -0
  41. node804_hpe_msa-0.1.0/src/hpe_msa_mcp/tools/hosts.py +138 -0
  42. node804_hpe_msa-0.1.0/src/hpe_msa_mcp/tools/statistics.py +88 -0
  43. node804_hpe_msa-0.1.0/src/hpe_msa_mcp/tools/status.py +76 -0
  44. node804_hpe_msa-0.1.0/src/hpe_msa_mcp/tools/storage.py +118 -0
  45. node804_hpe_msa-0.1.0/src/hpe_msa_mcp/tools/system.py +190 -0
  46. node804_hpe_msa-0.1.0/src/hpe_msa_mcp/tools/utility.py +143 -0
  47. node804_hpe_msa-0.1.0/tests/__init__.py +0 -0
  48. node804_hpe_msa-0.1.0/tests/test_client_auth.py +190 -0
  49. node804_hpe_msa-0.1.0/tests/test_config.py +315 -0
  50. node804_hpe_msa-0.1.0/tests/test_setup.py +258 -0
  51. node804_hpe_msa-0.1.0/tests/test_shapers.py +165 -0
@@ -0,0 +1,21 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .pytest_cache/
5
+ .mypy_cache/
6
+ .ruff_cache/
7
+ .venv/
8
+ dist/
9
+ build/
10
+ *.log
11
+ .coverage
12
+ htmlcov/
13
+ .env
14
+ arrays.json
15
+ audit*.log
16
+
17
+ # Local Claude Code settings (per-machine permissions, not shared)
18
+ .claude/settings.local.json
19
+
20
+ # Generated duplication/analysis reports (jscpd, etc.)
21
+ report/
@@ -0,0 +1,19 @@
1
+ # Pre-commit hooks for local development.
2
+ #
3
+ # pip install -e ".[dev]" # installs pre-commit + ruff
4
+ # pre-commit install # activates the git hook
5
+ #
6
+ # The ruff rev MUST match the pinned ruff in pyproject.toml [dev] so local,
7
+ # pre-commit, and CI all format identically. Bump both together.
8
+ repos:
9
+ - repo: https://github.com/astral-sh/ruff-pre-commit
10
+ rev: v0.15.13
11
+ hooks:
12
+ # Lint (mirrors CI's `ruff check .`); --fix so fixable issues are
13
+ # corrected in place before the commit.
14
+ - id: ruff
15
+ args: [--fix]
16
+ # Format check (mirrors CI's `ruff format --check .`). The hook
17
+ # reformats in place; a reformatted file blocks the commit so the
18
+ # change gets re-staged and reviewed.
19
+ - id: ruff-format
@@ -0,0 +1,289 @@
1
+ # HPE MSA MCP — Agent Build Plan
2
+
3
+ > Working plan for building `node804-hpe-msa`, a token-efficient, read-only
4
+ > MCP server for HPE MSA (Modular Smart Array) storage. This document is the
5
+ > source of truth an implementing agent follows. Keep it updated as decisions
6
+ > change.
7
+
8
+ ## 1. Goal & scope
9
+
10
+ Build an MCP server that exposes **read-only** visibility into HPE MSA storage
11
+ arrays through the array's REST management API, matching the conventions of the
12
+ existing suite (`panos-mcp-py`, `falcon-mcp`, etc.).
13
+
14
+ Confirmed decisions (from the requester):
15
+
16
+ | Decision | Choice |
17
+ |----------|--------|
18
+ | Tool scope | **Read-only** — only `show`-style tools; no provisioning/writes exposed. Matches the suite's default-deny posture. |
19
+ | Target hardware | **Gen6 (1060/2060/2062)** and **Gen5 (1050/2050/2052)** — REST API v3/v4, JSON-capable. |
20
+ | Build approach | Full build in one pass, matching `panos-mcp-py` structure. |
21
+
22
+ Explicitly **out of scope** (for now): any write/provisioning verb (create/delete/
23
+ map/set/restart), firmware operations, multi-array orchestration beyond a simple
24
+ target registry. Leave a gated `MSA_MODE` seam so writes *could* be added later
25
+ without re-architecting, but register nothing above `Mode.READ`.
26
+
27
+ ## 2. Reference architecture (mirror `panos-mcp-py`)
28
+
29
+ The suite shares `node804-mcp-toolkit`, which provides everything cross-cutting.
30
+ Reuse it; do not reinvent:
31
+
32
+ - `Mode`, `ModeGate` — RBAC. Default-deny: unset `MSA_MODE` ⇒ `READ`. Tools are
33
+ registered via `gate.tool(mcp, required=Mode.READ, ...)`; denied tools are
34
+ never registered, so the client can't see them.
35
+ - `resolve_tls_config(env, prefix="MSA")` / `describe_tls` — verify-by-default TLS,
36
+ `MSA_TLS_VERIFY` / `MSA_TLS_CA` opt-outs.
37
+ - `open_sink(env_var="MSA_AUDIT_LOG")` — JSONL audit sink (no-op when unset).
38
+ - `VerboseFlag`, `FieldsList`, `Pagination`, `Pattern` — standard lean-response params.
39
+ - `whitelist`, `strip_keys`, `paginate`, `filter_by_pattern` — pure shaping helpers.
40
+
41
+ **The one real divergence from panos:** panos wraps `pan-os-python`; MSA has no
42
+ SDK. We talk to the REST API directly over `httpx`, and MSA auth is a
43
+ **session key** (username+password → hashed login → `sessionKey` header), not a
44
+ long-lived API key. So `client.py` owns login, session-key caching, and
45
+ re-login-on-expiry instead of delegating to an SDK.
46
+
47
+ ## 3. Package layout
48
+
49
+ ```
50
+ hpe-msa-mcp/
51
+ pyproject.toml # node804-hpe-msa, console script node804-hpe-msa
52
+ README.md # env table + tool catalog
53
+ LICENSE # MIT
54
+ .gitignore
55
+ AGENT_PLAN.md # this file
56
+ src/hpe_msa_mcp/
57
+ __init__.py # __version__ = "0.1.0"; module docstring w/ 3 principles
58
+ __main__.py # python -m hpe_msa_mcp → server.main()
59
+ _redact.py # strip password / sessionKey from errors & logs
60
+ client.py # MSAClient: login, session cache, request(), error surface
61
+ config/
62
+ __init__.py
63
+ arrays.py # per-array registry (name/host/username in JSON, password in keyring)
64
+ responses/
65
+ __init__.py
66
+ shapers.py # per-object lean whitelists + object-key extraction
67
+ tools/
68
+ __init__.py
69
+ _common.py # ToolContext, fetch() helper, shape wiring
70
+ status.py # server_status (local), check_connectivity
71
+ system.py # system, versions, controllers, enclosures, frus, license, ntp, network
72
+ hardware.py # disks, sensor-status, fans, power-supplies, expander-status, certificate
73
+ storage.py # pools, disk-groups, volumes, snapshots
74
+ hosts.py # host-groups, initiators, hosts, maps, ports
75
+ events.py # events, alerts
76
+ statistics.py # controller/volume/disk statistics
77
+ aggregations.py # get_health_summary (rollup), count helpers
78
+ utility.py # run_show_command (generic, show-verb-only), list_arrays, add_array
79
+ py.typed
80
+ tests/
81
+ __init__.py
82
+ test_shapers.py
83
+ test_config.py
84
+ test_client_auth.py # respx-mocked login + session reuse + re-login
85
+ server.py is src/hpe_msa_mcp/server.py (FastMCP wiring)
86
+ ```
87
+
88
+ ## 4. HPE MSA REST API notes (Gen5/Gen6)
89
+
90
+ Base URL: `https://<host>/api`. All requests set header `datatype: json`.
91
+
92
+ ### Authentication
93
+ 1. Compute `hash = SHA-256("<username>_<password>")` (hex). Gen5/Gen6 support
94
+ SHA-256; older firmware only MD5 — implement SHA-256 first, **fall back to
95
+ MD5** if login returns an auth error, so mixed/older arrays still work.
96
+ 2. `GET /api/login/<hash>` with `datatype: json`.
97
+ 3. Response: `{"status":[{"response-type":"Success","response":"<sessionKey>", "return-code":1, ...}]}`.
98
+ On failure `response-type` is `Error` and `return-code` is negative.
99
+ 4. Subsequent calls: `GET /api/<command>/<args...>` with headers
100
+ `sessionKey: <key>` and `datatype: json`.
101
+ 5. Session expiry (idle ~30 min, or `return-code` == `-10005` / HTTP 401) ⇒
102
+ re-login once and retry the request. Logout is `GET /api/exit` (best-effort
103
+ on shutdown; not required).
104
+
105
+ ### Response envelope
106
+ Every response is a JSON object containing:
107
+ - one or more **object arrays** keyed by object type (see key table below), and
108
+ - a trailing `"status"` array describing the command result.
109
+
110
+ Surface an error dict when the trailing status `response-type == "Error"`.
111
+
112
+ ### Object-key map (key varies by command — auto-detect as fallback)
113
+ | Command | JSON key |
114
+ |---------|----------|
115
+ | `show/system` | `system` |
116
+ | `show/controllers` | `controllers` |
117
+ | `show/disks` | `drives` |
118
+ | `show/disk-groups` | `disk-groups` |
119
+ | `show/pools` | `pools` |
120
+ | `show/volumes` | `volumes` |
121
+ | `show/enclosures` | `enclosures` |
122
+ | `show/ports` | `port` |
123
+ | `show/host-groups` | `host-group` |
124
+ | `show/initiators` | `initiator` |
125
+ | `show/maps` | `volume-view` |
126
+ | `show/sensor-status` | `sensors` |
127
+ | `show/fans` | `fan` |
128
+ | `show/power-supplies` | `power-supplies` |
129
+ | `show/events` | `events` |
130
+ | `show/alerts` | `alert` (Gen6) |
131
+ | `show/versions` | `versions` |
132
+ | `show/license` | `license` |
133
+
134
+ Because keys are inconsistent across firmware, `_extract_objects(payload,
135
+ preferred_key)` should try `preferred_key`, then fall back to "first key whose
136
+ value is a list and key != 'status'". Never assume a key exists.
137
+
138
+ **Always** check `status`/`timed_out`-style signals: a partial or errored
139
+ response is not an empty inventory. Do not report "nothing configured" on an
140
+ auth/transport error — surface the error string instead.
141
+
142
+ ## 5. Config & credentials (`config/arrays.py`)
143
+
144
+ Mirror `panos config/firewalls.py`, adapted for username+password:
145
+
146
+ - File: `$MSA_ARRAYS_CONFIG` or `~/.config/hpe-msa-mcp/arrays.json`.
147
+ - Stored per entry (JSON, **never the password**): `{name, host, username}`.
148
+ - Password lives in the OS keychain: `keyring` service `"hpe-msa-mcp"`,
149
+ account = entry `name`. Auto-migrate any legacy plaintext `password` field to
150
+ keychain on load, then rewrite the file `0o600` without it.
151
+ - Single-array env fallback when no JSON file: `MSA_HOST`, `MSA_USERNAME`,
152
+ `MSA_PASSWORD`.
153
+ - `resolve_array(name=None)`: named entry → sole entry → env fallback → None.
154
+ Multi-array mode (>1 entry) requires every tool to take an explicit
155
+ `array: str` (no silent default).
156
+ - Host normalization: strip `https?://` and trailing slashes.
157
+
158
+ ## 6. Client (`client.py`)
159
+
160
+ `MSAClient` per resolved array, cached for process lifetime (`_CLIENTS`):
161
+ - Fields: `name, host, username, password, tls, timeout, _session_key`.
162
+ - One `httpx.Client(verify=<from TlsConfig>, timeout=<MSA_API_TIMEOUT|30s>)`.
163
+ - verify: `False` when TLS off; `str(ca_path)` when a CA bundle is configured;
164
+ else `True`.
165
+ - `login()` → SHA-256 then MD5 fallback; store `_session_key`.
166
+ - `request(segments: list[str])` → ensure key, GET, parse JSON, detect expiry,
167
+ re-login once, retry. Return parsed dict **or** a `ResolveError`-style error.
168
+ - `ResolveError` dataclass mirrors panos: tools surface `.message`, never raise
169
+ a stack trace at the model.
170
+ - `probe_client()` for `server_status`: resolves config only, **no** network call.
171
+ - All error text passes through `_redact` (strip password + sessionKey).
172
+
173
+ Per-request timeout must be bounded (default 30–60s) so a stuck array fails fast
174
+ instead of hanging the whole MCP process — same rationale as the panos timeout cap.
175
+
176
+ ## 7. Tools (all `Mode.READ`)
177
+
178
+ Every list tool takes `verbose / fields / pattern / pagination` and returns a
179
+ lean whitelist by default. Grouping:
180
+
181
+ - **status.py** — `server_status` (local: mode, tls, audit, configured arrays;
182
+ no network), `check_connectivity` (login + `show/system` round-trip).
183
+ - **system.py** — `get_system`, `get_versions`, `get_controllers`,
184
+ `get_enclosures`, `get_frus`, `get_license`, `get_ntp_status`, `get_network`.
185
+ - **hardware.py** — `get_disks`, `get_sensor_status`, `get_fans`,
186
+ `get_power_supplies`, `get_expander_status`, `get_certificate`.
187
+ - **storage.py** — `get_pools`, `get_disk_groups`, `get_volumes`, `get_snapshots`.
188
+ - **hosts.py** — `get_host_groups`, `get_initiators`, `get_hosts`, `get_maps`,
189
+ `get_ports`.
190
+ - **events.py** — `get_events` (args: severity + count, e.g. `last 100`),
191
+ `get_alerts`.
192
+ - **statistics.py** — `get_controller_statistics`, `get_volume_statistics`,
193
+ `get_disk_statistics`.
194
+ - **aggregations.py** — `get_health_summary`: one call that rolls up overall
195
+ system health + degraded/failed disks + active alerts + controller redundancy,
196
+ so the model can answer "is this array healthy?" cheaply.
197
+ - **utility.py** — `run_show_command(command, args)`: generic escape hatch,
198
+ **hard-restricted to the `show` verb** (reject anything else) so it can't be
199
+ used to mutate; `list_arrays`; `add_array` (writes config + keychain — this is
200
+ a *local config* write, not an array write, so it's acceptable under read-only,
201
+ but gate it behind `Mode.READ` and document clearly).
202
+
203
+ ## 8. Server wiring (`server.py`)
204
+
205
+ - `open_sink(env_var="MSA_AUDIT_LOG")`, `resolve_tls_config(prefix="MSA")`,
206
+ `ModeGate.from_env(env_var="MSA_MODE", default=Mode.READ, audit_sink=...)`.
207
+ - `SERVER_INSTRUCTIONS`: tight operating guidance — MSA has two controllers
208
+ (A/B); health lives in `health`/`health-numeric` fields; prefer
209
+ `get_health_summary` before dumping full inventories; check the response
210
+ `status` before trusting an empty list; read-only server.
211
+ - `ctx = ToolContext(mcp, gate, tls, audit_sink)`, then `register_*_tools(ctx)`
212
+ for every module.
213
+ - `main()`: `load_array_config()`, print version + TLS + RBAC + audit lines to
214
+ stderr, `mcp.run()`.
215
+
216
+ ## 9. Environment variables
217
+
218
+ | Var | Purpose | Default |
219
+ |-----|---------|---------|
220
+ | `MSA_MODE` | RBAC mode. Read-only surface only registers `read`. | `read` |
221
+ | `MSA_AUDIT_LOG` | Path to JSONL audit log. Unset = disabled. | unset |
222
+ | `MSA_TLS_VERIFY` | `true`/`false`. MSA controller certs are often self-signed. | `true` |
223
+ | `MSA_TLS_CA` | Path to a custom CA PEM bundle. Implies verify on. | unset |
224
+ | `MSA_ARRAYS_CONFIG` | Override path for `arrays.json`. | `~/.config/hpe-msa-mcp/arrays.json` |
225
+ | `MSA_HOST` | Single-array fallback hostname. | unset |
226
+ | `MSA_USERNAME` | Single-array fallback username. | unset |
227
+ | `MSA_PASSWORD` | Single-array fallback password. | unset |
228
+ | `MSA_API_TIMEOUT` | Per-request timeout (seconds). | `30` |
229
+
230
+ ## 10. Security & correctness invariants
231
+
232
+ - **Read-only**: no tool above `Mode.READ` is registered. `run_show_command`
233
+ rejects any verb other than `show`.
234
+ - **Secret hygiene**: password never written to JSON once keychain is available;
235
+ every error/log line passes through `_redact` (password, `sessionKey`).
236
+ - **TLS**: verify on by default; disabling emits the toolkit's stderr warning.
237
+ - **No confident-empty**: on transport/auth/status errors, return the error, not
238
+ an empty inventory.
239
+ - **Bounded requests**: per-request timeout cap so one stuck controller can't
240
+ freeze the process.
241
+
242
+ ## 11. Build order (task checklist)
243
+
244
+ 1. Scaffold: `pyproject.toml`, `__init__.py`, `__main__.py`, `LICENSE`, `.gitignore`, `py.typed`.
245
+ 2. `_redact.py` + `config/arrays.py` (+ unit test `test_config.py`).
246
+ 3. `client.py` (+ `test_client_auth.py` with `respx`: login, session reuse, re-login).
247
+ 4. `responses/shapers.py` + `tools/_common.py` (+ `test_shapers.py`).
248
+ 5. Tool modules (status → system → hardware → storage → hosts → events →
249
+ statistics → aggregations → utility).
250
+ 6. `server.py` wiring + `README.md`.
251
+ 7. Verify: `python -c "import hpe_msa_mcp.server"`, `ruff check`, `pytest`.
252
+
253
+ ## 12. Open items / to confirm before/while building
254
+
255
+ - [ ] Live array host + credentials to run the `integration`-marked smoke test
256
+ (`check_connectivity` against a real MSA). Build proceeds without it;
257
+ creds get configured via keyring/env later.
258
+ - [ ] Confirm exact JSON object keys against the target firmware (table in §4 is
259
+ the documented set; auto-detect covers drift, but verify on first live run).
260
+ - [x] Decide whether `add_array` belongs in the read-only surface or should be a
261
+ one-off CLI/setup step instead of an exposed tool.
262
+ **Decision (built):** exposed as a `Mode.READ` tool. It writes only *local
263
+ MCP config* (the array registry + keychain), never the storage array, so it
264
+ stays within the read-only posture toward the hardware. Documented as such
265
+ in the tool docstring and README.
266
+
267
+ ## 13. Build status (as built)
268
+
269
+ Implemented in one pass per the plan. Verified: `python -c "import
270
+ hpe_msa_mcp.server"` OK (34 tools registered), `ruff check` clean, `ruff format`
271
+ clean, `mypy --strict src` clean, `pytest` 30 passed.
272
+
273
+ **Config model (extended post-plan):** one entry per *array* (not per
274
+ controller); an entry may carry a `hosts` failover list (VIP or both controller
275
+ IPs) that the client walks in order. Shared `defaults` (username in
276
+ `arrays.json`, password in a keychain default account) let every array inherit a
277
+ common monitoring credential, with per-array override. Resolution is
278
+ per-array → default → env. New tool `set_default_credentials`; `add_array` takes
279
+ `host`/`hosts` and optional username/password (inherits defaults when omitted).
280
+
281
+ Tool surface (all `Mode.READ`): `server_status`, `check_connectivity`;
282
+ `get_system`, `get_versions`, `get_controllers`, `get_enclosures`, `get_frus`,
283
+ `get_license`, `get_ntp_status`, `get_network`; `get_disks`, `get_sensor_status`,
284
+ `get_fans`, `get_power_supplies`, `get_expander_status`, `get_certificate`;
285
+ `get_pools`, `get_disk_groups`, `get_volumes`, `get_snapshots`;
286
+ `get_host_groups`, `get_hosts`, `get_initiators`, `get_maps`, `get_ports`;
287
+ `get_events`, `get_alerts`; `get_controller_statistics`, `get_volume_statistics`,
288
+ `get_disk_statistics`; `get_health_summary`; `run_show_command`, `list_arrays`,
289
+ `set_default_credentials`, `add_array`.
@@ -0,0 +1,90 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/);
6
+ versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0] — 2026-11-03
11
+
12
+ Initial release. A token-efficient, read-only MCP server for HPE MSA (Modular
13
+ Smart Array) storage — Gen5 (1050/2050/2052) and Gen6 (1060/2060/2062) — over the
14
+ array's REST management API.
15
+
16
+ ### Added — tool surface
17
+
18
+ 33 read-only tools, all `Mode.READ`. List tools return default-lean field
19
+ whitelists (`verbose=true` to opt out) with `fields` / `pattern` / `pagination`.
20
+
21
+ - **Diagnostics** — `server_status` (local; no network), `check_connectivity`.
22
+ - **System** — `get_system`, `get_versions`, `get_controllers`, `get_enclosures`,
23
+ `get_frus`, `get_license`, `get_ntp_status`, `get_network`.
24
+ - **Hardware** — `get_disks`, `get_sensor_status`, `get_fans`,
25
+ `get_power_supplies`, `get_expander_status`, `get_certificate`.
26
+ - **Storage** — `get_pools`, `get_disk_groups`, `get_volumes`, `get_snapshots`.
27
+ - **Hosts / SAN** — `get_host_groups`, `get_hosts`, `get_initiators`, `get_maps`,
28
+ `get_ports`.
29
+ - **Events** — `get_events`, `get_alerts`.
30
+ - **Statistics** — `get_controller_statistics`, `get_volume_statistics`,
31
+ `get_disk_statistics`.
32
+ - **Aggregation** — `get_health_summary`, a one-call rollup of system health,
33
+ controller redundancy, degraded/failed disks, pool capacity, and active alerts.
34
+ - **Utility** — `run_show_command` (hard-restricted to the `show` verb) and
35
+ `list_arrays`. Registering arrays and setting shared credentials are
36
+ intentionally **not** MCP tools (config/keychain mutation from a prompt is a
37
+ credential-exfil risk); those live only in the setup wizard CLI.
38
+
39
+ ### Added — configuration & security
40
+
41
+ - Multi-array registry (`arrays.json`): one entry per array, with an optional
42
+ `hosts` failover list (VIP or both controller IPs) walked in order.
43
+ - OS-keychain credential storage (service `hpe-msa-mcp`); plaintext passwords in
44
+ the file are auto-migrated to the keychain and stripped on load.
45
+ - Shared default credentials (one monitoring user+password reused by arrays with
46
+ none of their own); resolution is per-array → default → env.
47
+ - Session-key authentication (SHA-256 with MD5 fallback) with transparent
48
+ re-login on expiry; login hashes, session keys, and passwords redacted from
49
+ every error and log line.
50
+ - Default-deny RBAC (`MSA_MODE`, read-only surface), verify-by-default TLS
51
+ (`MSA_TLS_VERIFY` / `MSA_TLS_CA`), and an optional JSONL audit log
52
+ (`MSA_AUDIT_LOG`).
53
+
54
+ ### Added — `node804-hpe-msa-setup` install wizard
55
+
56
+ A bundled wizard that configures the server end to end, so a new install no
57
+ longer means hand-editing JSON. Run `node804-hpe-msa-setup` (or
58
+ `python -m hpe_msa_mcp.setup` if the console script isn't on `PATH`).
59
+
60
+ - Stores the controller password in the OS keychain (read with `getpass`,
61
+ confirmed by retype, never written to disk) and writes only non-secret fields
62
+ to `arrays.json`. Guards against terminals where `getpass` mis-reads (warns on
63
+ the insecure fallback and on a suspiciously short value); `--password-stdin`
64
+ offers a terminal-agnostic alternative.
65
+ - Supports a comma-separated failover host list and optional shared defaults.
66
+ - Wires a Claude Desktop server entry (host and password resolve from
67
+ `arrays.json` + keychain, so neither lands in the client config), backing up the
68
+ existing config first.
69
+ - Installs the `hpe-msa-engineer` skill into `~/.claude/skills` and writes an
70
+ upload zip for Claude Desktop / claude.ai.
71
+ - Modify-aware, with `array list|add|remove` and `skill` subcommands; a
72
+ non-interactive mode (`MSA_SETUP_PASSWORD` + `--yes`) for scripted installs.
73
+
74
+ ### Added — `hpe-msa-engineer` skill pack
75
+
76
+ Procedural knowledge for driving the server well, bundled inside the wheel so the
77
+ wizard can install it offline. A router `SKILL.md` plus on-demand references
78
+ (health model, storage model, host mapping, events/alerts, statistics, tool map)
79
+ and seven playbooks (health check, capacity review, disk failure triage, mapping
80
+ audit, performance review, event triage, firmware inventory).
81
+
82
+ ### Added — packaging & CI
83
+
84
+ - Console scripts `node804-hpe-msa` and `node804-hpe-msa-setup`.
85
+ - GitHub Actions: CI (ruff, ruff format, mypy `--strict`, pytest on Python 3.13
86
+ and 3.14) and PyPI publishing via Trusted Publishing (OIDC) on `v*` tags.
87
+ - Pre-commit hooks (ruff) pinned to the same ruff version as CI.
88
+
89
+ [Unreleased]: https://github.com/Node804/node804-hpe-msa/compare/v0.1.0...HEAD
90
+ [0.1.0]: https://github.com/Node804/node804-hpe-msa/releases/tag/v0.1.0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Michael Pope
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.