Skip to main content
Glama
hanumanthvendra

finops-copilot

README.md
# An AI that right-sizes your cluster — and a platform that won't let it break prod

> **The problem:** "let an AI touch our infrastructure" is a great way to get a
> confident model to scale prod to one replica at 3am. The interesting
> engineering in agentic ops isn't the model — it's the **guardrails**. **The
> fix:** give the AI a *typed, bounded* interface to your cluster over **MCP**,
> and put the safety logic **server-side**, where the model can't route around
> it. The AI decides *what* to change; the platform decides what it's *allowed*
> to do — and refuses the rest, even when asked directly.

A **Model Context Protocol (MCP) server** exposes five FinOps tools over a live
Kubernetes cluster. **Claude** (or any MCP client — Claude Desktop, etc.) uses
them to find over-provisioned workloads and propose right-sizing. Every
*mutating* call is checked against a policy **before** anything happens and
defaults to a **dry-run**; unsafe changes — a protected `prod` namespace, a
resource floor, an over-aggressive cut — are **refused by the server** and
written to an **audit log**.

Runs entirely on a laptop: a **kind** cluster seeded with deliberately wasteful
workloads, a ~120-line MCP server, and a copilot driver that runs **offline by
default** (a deterministic mock, so it works with no API key and in CI) or with
**real Claude** when `ANTHROPIC_API_KEY` is set.

---

## What it does

```
   Claude / MCP client
          │  calls typed tools
          ▼
   ┌─────────────── MCP server (finops-copilot) ───────────────┐
   │  list_namespaces  list_workloads  estimate_cost           │
   │  recommend_rightsizing        get_audit_log               │
   │                                                           │
   │  apply_rightsizing(…, dry_run=True)                       │
   │        │                                                  │
   │        ▼   ┌─────────── policy.py (guardrails) ────────┐  │
   │     every  │ protected namespaces? resource floors?    │  │
   │     apply ─┤ max single-step cut?  → REFUSE + audit    │  │
   │            └───────────────────────────────────────────┘  │
   └────────────────────────┬──────────────────────────────────┘
                            │ kubectl (read specs / patch)
                            ▼
                  kind cluster: staging (waste) · prod (protected)
```

| Piece | Why it matters | How it works here |
|---|---|---|
| **MCP, not raw kubectl** | The AI gets *typed, auditable* actions, not an open shell | Five tools with schemas; the server is the only thing that touches the cluster |
| **Guardrails server-side** | Safety the model can't prompt its way around | `apply_rightsizing` runs every request through `policy.py` first; a refusal is a refusal |
| **Dry-run by default** | The safe thing is the default thing | `apply_rightsizing(dry_run=True)` shows the diff + saving; real applies are opt-in |
| **Everything audited** | "What did the AI try to do?" has an answer | Every apply — allowed, refused, or dry-run — is appended to an audit log the client can read |

---

## Run it

```bash
make up            # kind cluster seeded with waste + Python env (MCP deps)
make demo          # the copilot finds & cuts waste — OFFLINE, no API key needed
make demo-claude   # real Claude drives the same MCP tools (needs ANTHROPIC_API_KEY)
make test          # unit-test the guardrail policy (no cluster needed)
make down          # delete the cluster
```

`make demo` runs a **deterministic mock** copilot over the **real MCP server** —
same tools, same guardrails as the Claude path — so it works with no key and in
CI. `make demo-claude` swaps the mock for Claude driving the tools agentically
(official Anthropic SDK + its MCP tool-runner).

### What the demo proves (real output)

Against a `staging` namespace seeded with four workloads (three grossly
over-provisioned, one healthy) and a protected `prod`:

1. **Finds the waste.** `staging` costs **~$138/mo** at current requests; the
   tool flags the three workloads sitting at 1–4% CPU utilisation and proposes
   CPU + replica cuts — **~$102/mo (73%)** in projected savings.
2. **Dry-runs first.** Every change is a dry-run showing the exact diff and its
   monthly saving. Nothing is applied without an explicit non-dry-run call.
3. **The platform refuses prod.** Asked to trim `prod` too, the server
   **refuses** — `namespace 'prod' is protected`. The AI asked; the guardrail
   said no. No change, not even a dry-run.
4. **The platform refuses an over-aggressive cut.** Asked to slash a CPU request
   to `10m`, the server refuses — below the floor, and a >90% single-step cut.
5. **Every action is logged.** The audit trail shows all five attempts —
   three dry-runs, two refusals — with reasons.

> The AI found the waste and proposed the cuts. The platform decided what it was
> allowed to do. **That division of labour is the whole point.**

---

## Use it from Claude Desktop (real MCP)

This is a real MCP server — point any MCP client at it. For Claude Desktop, add
to `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "finops": {
      "command": "/path/to/mcp-finops-copilot/.venv/bin/python",
      "args": ["-m", "finops.server"],
      "env": { "FINOPS_CTX": "kind-mcp-finops" }
    }
  }
}
```

Then ask Claude: *"What's my staging namespace costing, and what can I safely
right-size?"*

---

## How it maps to production

| Demo | Production |
|---|---|
| kind, `pause` workloads | EKS / AKS / GKE with your real Deployments |
| utilisation from a seeded annotation | Prometheus / metrics-server (the `kube.py` read is the only thing that changes) |
| `policy.py` floors + protected namespaces | your org's guardrails — RBAC scope, PodDisruptionBudgets, change windows |
| in-memory audit log | your audit sink (stdout → Loki, an events table, etc.) |
| illustrative cost rates | your provider's real per-vCPU / per-GB pricing |

## Layout

```
finops/policy.py     the guardrail layer — pure, unit-tested (make test)
finops/cost.py       cost model + right-sizing heuristic
finops/kube.py       kubectl read/patch helpers
finops/server.py     the MCP server: 5 tools, dry-run-by-default, audited
copilot/mock_agent.py   deterministic offline copilot (CI-safe)
copilot/llm_agent.py    real Claude via the Anthropic SDK's MCP tool-runner
copilot/driver.py       picks mock vs Claude by ANTHROPIC_API_KEY
k8s/                 staging (seeded waste) + prod (protected)
```

---

MIT-licensed. A small, laptop-runnable, NDA-safe demo that proves one sharp
idea: **agentic ops is a guardrails problem. Give the AI real actions, but make
the platform the thing that says no.**

Maintenance

ActivityMaintained
ResponsivenessNo issues