Metadata-Version: 2.4
Name: power-hmc-mcp
Version: 0.7.1
Summary: MCP server for IBM Power HMC partition and capacity operations
License: MIT
License-File: LICENSE
Requires-Python: >=3.11
Requires-Dist: httpx>=0.27
Requires-Dist: mcp[cli]<2,>=1.27
Requires-Dist: pydantic-settings>=2
Requires-Dist: pydantic>=2
Requires-Dist: python-dotenv>=1.0
Provides-Extra: dev
Requires-Dist: hypothesis>=6; extra == 'dev'
Requires-Dist: mypy>=1.13; extra == 'dev'
Requires-Dist: pre-commit>=4; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest-cov>=5; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Requires-Dist: ruff<1,>=0.15; extra == 'dev'
Provides-Extra: langgraph
Requires-Dist: langgraph<1,>=0.2; extra == 'langgraph'
Provides-Extra: otel
Requires-Dist: opentelemetry-api>=1.27; extra == 'otel'
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.27; extra == 'otel'
Requires-Dist: opentelemetry-sdk>=1.27; extra == 'otel'
Description-Content-Type: text/markdown

# power-hmc-mcp-server

**The missing MCP server for IBM Power.** Lets AI agents (Claude Desktop, LangGraph, watsonx Orchestrate, anything that speaks [Model Context Protocol](https://modelcontextprotocol.io)) safely look at — and eventually drive — IBM Power servers through their Hardware Management Console.

> **Status:** Phases A, D, E, and **F** (agent ergonomics) are complete on `main`. Read tools work end-to-end; write tools remain Phase A scaffold until Phase C. Open work: Phase B2 firmware captures, Phase C real mutations. Roadmap: [`TODO.md`](TODO.md).
>
> **IBM doc alignment:** schema-aligned with the IBM Power9/10/11 HMC REST documentation for the core resources used by the MCP surface (`ManagedSystem`, `LogicalPartition`, `LogicalPartitionProfile`, PCM `ProcessedMetrics`). Per-builder citations live in [`docs/hmc-compatibility.md`](docs/hmc-compatibility.md#url-routes--ibm-doc-alignment). **Runtime behavioural compatibility** (firmware-specific state strings, header echo behaviour, write job operation names, composite quick endpoint shape, retry tuning) is validated only for the firmware versions listed in that compatibility doc — uncaptured firmware behaviour stays `[ASSUMED]` / `[PLACEHOLDER]` until rows land.

## Why this exists

If you've tried to plug an LLM agent into IBM Power infrastructure, you've probably noticed there is no off-the-shelf MCP server for the Hardware Management Console. That's the gap this fills.

- **For developers:** A typed, tested, observable bridge between MCP and HMC REST. You don't have to learn IBM's XML/Atom feeds or session-token dance. Call `list_lpars(...)` and get JSON.
- **For operators:** A read-only-by-default control plane with structured logging, correlation IDs that span the HMC boundary, secret redaction, and an audit event on every write attempt.
- **For security teams:** Writes are off by default. When enabled, every attempt — *including denials* — emits a `write_audit` log event with a stable schema. The dry-run / authorized split is enforced by the type system, not by convention.

## What you get

- **8 read tools** that work today against a real HMC: list/get managed systems, list/get LPARs, LPAR status, partition profiles, capacity samples, health check.
- **3 write tools** registered behind a flag: `start_lpar`, `stop_lpar`, `create_lpar_from_profile` — Phase A scaffold; Phase C wires real HMC mutation payloads.
- **Two transports**: `stdio` (default; for Claude Desktop / local LangGraph) and `streamable-http` (for shared / containerized deployments behind a gateway).
- **Observability built in**: structured JSON logs, `X-Correlation-ID` propagation to the HMC by default, `write_audit` events, secret redaction.
- **Strong defaults**: TLS verification on, allowlist available, write tools off, retries conservative on writes.

## Quick start

### 1. Install

```bash
git clone https://github.com/TylrDn/power-hmc-mcp-server.git
cd power-hmc-mcp-server
python3.12 -m venv .venv
source .venv/bin/activate          # Windows: .venv\Scripts\activate
pip install -e .
```

### 2. Point it at your HMC

```bash
cp .env.example .env
# edit .env: HMC_HOST, HMC_USERNAME, HMC_PASSWORD (and HMC_VERIFY_TLS=false for self-signed labs)
```

### 3. Run

```bash
python -m power_hmc_mcp.mcp_server
```

That's it — the server is now waiting on stdio. To verify against a real MCP client without writing one yourself:

```bash
python integrations/langgraph/example_list_lpars_stdio.py <managed-system-uuid>
```

The script spawns the server, lists tools, and calls `list_lpars` for one managed system. Get the UUID from `list_managed_systems` first if you don't have one handy.

### 4. Connect Claude Desktop (optional)

Drop the snippet from [`examples/claude_desktop_config.json`](examples/claude_desktop_config.json) into Claude Desktop's settings (Settings → Developer → Edit Config), replace the `CHANGE_ME-…` values, restart Claude. You'll see the `power-hmc` tools available in the conversation.

## Mental model

You don't need to know much about IBM Power to use this — but here's the minimum for context. (Full definitions in the [glossary](docs/glossary.md).)

- **HMC** = the appliance that controls one or more IBM Power servers via REST. This server is a thin, opinionated bridge to that REST API.
- **Managed system** = a Power server. The HMC sees it by UUID.
- **LPAR** = a virtual machine on a managed system (PowerVM hypervisor's unit of compute).
- **MCP** = the open protocol your agent speaks. Tools have typed JSON contracts; agents call `list_lpars` like a function.

What flows through:

```text
your agent (LangGraph, Claude, …)
        │  speaks MCP
        ▼
power-hmc-mcp-server (this repo)         ← the layered bridge
        │  speaks HMC REST + correlation header
        ▼
HMC (HTTPS, port 12443 by default)
        │
        ▼
Power servers / LPARs
```

The agent never sees raw HMC XML; this server normalizes everything to JSON. Logs on both sides of the HMC boundary share a `correlation_id` so a request is one search away.

## What you can do today

### Read (always available)

| Tool | Plain-English purpose | Typical agent prompt |
|------|-----------------------|----------------------|
| `list_managed_systems` | List every Power server this HMC controls. | "Show me every Power server my HMC manages." |
| `get_managed_system` | Full detail for one Power server (name, state, MTMS, processor pool). | "Tell me about this managed system." |
| `list_lpars` | List every LPAR (VM) on one Power server. | "List the virtual machines on this managed system." |
| `get_lpar` | Full LPAR config — processors, memory, partition type, RMC state. | "Give me the full detail for this LPAR." |
| `get_lpar_status` | Run state in raw and canonical form for one LPAR. | "Is this LPAR running? What state is it in?" |
| `list_partition_profiles` | Saved configuration profiles for one LPAR. | "What saved configurations exist for this LPAR?" |
| `get_capacity` | PCM utilization samples for one Power server (`mode=summary` or `metadata`). | "How busy is this server?" |
| `health_check` | Server up, HMC reachable, session alive — no inventory leak. | "Is the HMC reachable right now?" |

`managed_system_id` and `lpar_id` are HMC REST UUIDs (you get them from `list_*` calls), not partition numbers or hostnames.

**JSON contracts vs. firmware behaviour.** The JSON shape each read tool returns is stable and enforced by [`tests/test_contracts.py`](tests/test_contracts.py) — keys, types, and route URLs are schema-aligned with the IBM Power9/10/11 HMC REST documentation. **Firmware-dependent behaviours** (LPAR state strings, correlation header echo, composite quick endpoint shape, write OP names) are documented separately in [`docs/hmc-compatibility.md`](docs/hmc-compatibility.md). Agents should branch on the normalized fields surfaced in the tool contracts (e.g. `canonical_state` from `get_lpar_status`) rather than on raw firmware strings; the canonical fields are exactly what stays stable across firmware levels.

### Write (off by default)

`start_lpar`, `stop_lpar`, `create_lpar_from_profile` register only when `HMC_ENABLE_WRITE_TOOLS=true`. They take an `execute=true|false` flag (default `false`); see [Write safety in plain English](#write-safety-in-plain-english) below.

For full inputs/outputs and sample JSON payloads see the [agent guide](docs/agent-guide.md).

## Agent-safe output behavior

Phase F adds deterministic response shapes so orchestrators can branch without guessing:

- **List tools** (`list_managed_systems`, `list_lpars`, `list_partition_profiles`) return a **paginated envelope** — not a bare array. Use `result["items"]` for entries and `result["summary"]` for counts. Pass optional `limit` / `offset` to paginate; when truncated, `summary.next_offset` tells you the next page. `summary.truncated` is always explicit.
- **Capacity** — use `get_capacity(..., mode="summary")` for a small payload; `mode="metadata"` is already bounded by the HMC query (one sample window per call).
- **Write denials** — every write envelope includes `guardrail_outcome` (`denied`, `preview`, or `authorized_placeholder`) and a deterministic `suggested_next_step`. Denied responses also include `denial_reason_code` (`writes_disabled` or `execute_required`) so agents can recover without parsing free-form text.

**Migration (Phase F — list tool contract change).** If your agent still expects a top-level JSON array from list tools, update accessors:

| Before (pre–Phase F) | After (Phase F) |
|----------------------|-----------------|
| `result[0]["name"]` | `result["items"][0]["name"]` |
| `len(result)` | `result["summary"]["total_count"]` |
| _(n/a)_ | Continue pages with `offset=result["summary"]["next_offset"]` when `truncated` is true |

`items` order matches the HMC Atom feed order for that call (stable for offset pagination within one snapshot; inventory may change between calls).

Details: [agent guide: list envelopes and pagination](docs/agent-guide.md#list-envelopes-and-pagination).

## Write safety in plain English

The short version: **the server cannot accidentally mutate your HMC.** Here's the chain of locks, in order, in plain language:

1. **Off by default.** The write tools aren't even registered with MCP unless you set `HMC_ENABLE_WRITE_TOOLS=true`. Agents simply cannot call what isn't there.
2. **Dry-run by default.** Even after enabling, every write tool defaults to `execute=false`. The agent sees a JSON envelope describing what *would* happen. Nothing leaves the server.
3. **Type-system gate.** When the agent passes `execute=true`, the server's internal write methods refuse to run unless they receive a special "permission token" (`WriteAuthorization`). That token can only be created by one specific helper — the one that just ran the dry-run / denial logic. There is no other way to obtain one. (Both `mypy` and a runtime `isinstance` check enforce this.)
4. **Audited every time.** Every write attempt — denied, dry-run, or authorized — emits a `write_audit` log event with a stable schema (`tool_name`, `operation`, `outcome`, `reason`, `execute_requested`, `correlation_id`). Denials are not silent.
5. **Boot-time check.** If a contributor adds a new write tool but forgets to wire it through the guardrail, the server refuses to start. The mistake is impossible to deploy.
6. **Phase A is still placeholder.** Even on the authorized path, the current code returns `executed=false, placeholder=true` instead of POSTing to the HMC. Real mutation lands in Phase C, after the IBM payload schemas are validated. Until then, you literally cannot break anything.

For the engineering details, see [architecture: write safety model](docs/architecture.md#write-safety-model). For the audit schema your SIEM should pin on, see [operations: write_audit schema](docs/operations.md#write_audit-schema).

## Configuration (essentials)

Full reference: [operations: configuration reference](docs/operations.md#configuration-reference). Quick essentials:

| Variable | What it does |
|----------|--------------|
| `HMC_HOST` / `HMC_PORT` / `HMC_USERNAME` / `HMC_PASSWORD` | HMC connection (required). |
| `HMC_VERIFY_TLS` (default `true`) | TLS verification. Use `HMC_CA_BUNDLE=/path/to/ca.pem` for corporate PKI; `false` only for self-signed labs. |
| `HMC_ALLOWED_MANAGED_SYSTEMS` | Optional comma-separated UUID allowlist. Reduces blast radius. |
| `HMC_ENABLE_WRITE_TOOLS` (default `false`) | Register write tools with MCP. Off ⇒ truly no write surface. |
| `HMC_REQUIRE_EXECUTE_FLAG` (default `true`) | Require `execute=true` to leave dry-run. Keep on. |
| `HMC_LOG_JSON` (default `false`) | Single-line JSON logs (preferred for shippers). |
| `HMC_MCP_TRANSPORT` (default `stdio`) | `stdio` or `streamable-http`. See [transports](docs/transports.md). |
| `HMC_DEPLOYMENT_MODE` (default `development`) | `development` / `controlled` / `production`. Documents intent today. |
| `HMC_API_VERSION_HINT` (default unset) | Optional firmware/API version hint (e.g. `V11R1`). **Logging hint only** — surfaced on `session_logon_end` / `session_logoff_end` and the startup log line. Does NOT flip firmware-gated behaviour. See [operations](docs/operations.md#observability). |

## Connecting an agent

| Agent / host | How |
|--------------|-----|
| **Claude Desktop** | Use [`examples/claude_desktop_config.json`](examples/claude_desktop_config.json). Settings → Developer → Edit Config → paste & edit → restart. |
| **LangGraph** | The MCP server runs as a stdio subprocess; see [`integrations/langgraph/`](integrations/langgraph/) for the working example. |
| **Custom MCP client (Python)** | Stdio: `mcp.client.stdio.stdio_client(...)`. HTTP: `mcp.client.streamable_http.streamablehttp_client(...)`. See [transports](docs/transports.md) for full snippets. |
| **Org MCP gateway / multi-tenant** | Run the server with `HMC_MCP_TRANSPORT=streamable-http`, bind to loopback, terminate TLS / mTLS / SSO at the gateway. See [transports: streamable HTTP](docs/transports.md#streamable-http). |

## Documentation map

- [Glossary](docs/glossary.md) — every term defined once. Start here if any acronym in this README was unfamiliar.
- [Architecture](docs/architecture.md) — module map, request walkthrough, write boundary, design rationale, "how to add a tool" recipes.
- [Operations](docs/operations.md) — full config reference, log event types, `write_audit` schema, exception map, deployment modes, runbook entries.
- [Transports](docs/transports.md) — stdio vs streamable HTTP, with full client snippets.
- [Agent caller guide](docs/agent-guide.md) — what your agent will actually see (sample JSON inputs/outputs and error shapes).
- [Roadmap (`TODO.md`)](TODO.md) — phased plan with `[PLACEHOLDER]` / `[RISK]` / `[ASSUMPTION]` markers.
- [Release engineering](docs/release-engineering.md) — Codecov, PyPI OIDC, milestones, CI workflows (maintainers).
- [Changelog (`CHANGELOG.md`)](CHANGELOG.md) — notable changes per release (`0.1.0a0` … `0.1.0`).
- [Milestone evaluation](docs/milestone-evaluation.md) — team gate between major phases (current: Phase A → B).
- [Contributing (`CONTRIBUTING.md`)](CONTRIBUTING.md) — dev setup, the new-tool recipe, the things not to work around.

## Security and design constraints

This server touches live infrastructure. The defaults reflect that:

- **Read-only out of the box.** `HMC_ENABLE_WRITE_TOOLS=false`. No mutation surface until you opt in.
- **Least privilege.** The HMC user behind `HMC_USERNAME` should have only what the exposed tools need.
- **No credentials in logs.** `RedactingFilter` masks passwords, session tokens, bearer tokens, and HMC `<Password>` XML.
- **Resource IDs are validated.** UUIDs are checked before they go into HMC URL paths (defense against path traversal).
- **TLS verified by default.** Disable only for self-signed labs; prefer `HMC_CA_BUNDLE` for corporate-PKI HMCs.
- **Allowlist available.** Set `HMC_ALLOWED_MANAGED_SYSTEMS` for non-prod pilots.
- **Approval for destructive operations.** Write tools today are placeholder-backed; future destructive surfaces (delete, etc.) will require explicit approval flows, not just `execute=true`.
- **Structural write boundary.** The dry-run / authorized split is enforced by the type system and module boundaries — see [architecture: write safety model](docs/architecture.md#write-safety-model).

## Status & roadmap

**Shipped on `main`:** Phase A foundation ([`v0.1.0`](https://github.com/TylrDn/power-hmc-mcp-server/releases/tag/v0.1.0)), Phase D transport/deployment (`v0.4.0b1`), Phase E observability export (OTEL), Phase F agent ergonomics (tool metadata, list envelopes, denial hints).

**In progress / open:** Phase B2 firmware captures; Phase C write-path execution; Phase G–I per [`TODO.md`](TODO.md).

See [`TODO.md`](TODO.md) for the full phased roadmap (A through I) with `[RISK]`, `[PLACEHOLDER]`, and `[ASSUMPTION]` markers throughout.

## License

MIT. See [`LICENSE`](LICENSE).
