{
  "markdown": "<!-- mcp-name: io.github.rex/rancher-mcp -->\n\n<p align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/rex/mcp-rancher/main/docs/assets/header.png\" alt=\"MCP Rancher — the capability-aware control plane for AI-assisted Rancher operations\" />\n</p>\n\n<p align=\"center\">\n  <a href=\"https://github.com/rex/mcp-rancher/actions/workflows/validate.yml\"><img src=\"https://img.shields.io/github/actions/workflow/status/rex/mcp-rancher/validate.yml?branch=main&label=CI\" alt=\"CI\" /></a>\n  <a href=\"https://modelcontextprotocol.io\"><img src=\"https://img.shields.io/badge/MCP-server-2b6cd0\" alt=\"MCP server\" /></a>\n  <a href=\"docs/tool-manifest.json\"><img src=\"https://img.shields.io/badge/tools-206-2b6cd0\" alt=\"206 tools\" /></a>\n  <a href=\"https://www.python.org/\"><img src=\"https://img.shields.io/badge/python-3.12%2B-3776ab?logo=python&logoColor=white\" alt=\"Python 3.12+\" /></a>\n  <a href=\"https://github.com/microsoft/pyright\"><img src=\"https://img.shields.io/badge/types-pyright%20strict-blue\" alt=\"Pyright strict\" /></a>\n  <a href=\"LICENSE\"><img src=\"https://img.shields.io/badge/license-MIT-lightgrey\" alt=\"MIT license\" /></a>\n</p>\n\n<p align=\"center\">\n  Operate Rancher-managed Kubernetes through any MCP client —\n  discovery, generic resource access, and curated operator workflows,\n  wrapped in an audit-logged, rate-limited, confirmation-guarded safety model.\n</p>\n\n<p align=\"center\">\n  <a href=\"#quick-start\">Quick start</a> ·\n  <a href=\"#the-tool-surface\">Tool surface</a> ·\n  <a href=\"#architecture\">Architecture</a> ·\n  <a href=\"#safety-model\">Safety model</a> ·\n  <a href=\"#compatibility\">Compatibility</a> ·\n  <a href=\"#development\">Development</a>\n</p>\n\n---\n\n## Why this exists\n\nRancher is how real fleets run Kubernetes — and it speaks **two APIs** (the legacy\nNorman `/v3` plane and the modern Steve `/v1` plane), varies by version, and wraps\nevery cluster behind its own proxy. Pointing a generic Kubernetes MCP server at it\nmisses everything Rancher-specific; pointing an agent at raw `kubectl` gives up\nauditability, guardrails, and the management-plane view entirely.\n\n**MCP Rancher** is built for that reality:\n\n- **Capability-aware, not version-naive.** It detects what each connected Rancher\n  actually supports instead of assuming. One binary spans **2.6.5 → 2.9.3** with\n  the same tool surface.\n- **Multi-instance first.** Lab, staging, prod — configure them all; mark prod\n  `read_only: true` and every mutation is refused at the config layer, before any\n  guard even has to fire.\n- **Nothing is out of reach.** Curated tools cover the common 95%; the generic\n  engine reaches *every* resource either API plane exposes — even types nobody\n  wrote a tool for yet.\n\n## The tool surface\n\n**206 tools: 178 read-only · 28 writes · 5 destructive** — counted from the\nregistry itself, not by hand. [`docs/tool-manifest.json`](docs/tool-manifest.json)\nis **generated from the live FastMCP registry** (`make tool-manifest`) and a CI\ngate fails the build if it ever drifts from the code. Per-tool descriptions,\nsafety annotations, and parameters all live there; the narrative registry with\nslice tracking is [`docs/tool-catalog.md`](docs/tool-catalog.md).\n\n| Layer | What it does | Examples |\n|---|---|---|\n| **Discovery & schema** | Explore what any instance can do | `rancher_server_version`, `rancher_norman_schema_list`, `rancher_capability_domain_list` |\n| **Generic engine** | CRUD + actions + links + watch on *any* resource, both planes | `rancher_steve_resource_list`, `rancher_norman_resource_action_invoke`, `rancher_steve_resource_watch` |\n| **Curated reads** | Typed, shaped responses across ~25 domains | `rancher_pods_list`, `rancher_deployments_list`, `rancher_longhorn_volumes_list`, `rancher_policy_reports_list` |\n| **Curated writes** | Guarded mutations | `rancher_deployment_scale`, `rancher_deployment_restart`, `rancher_cron_job_suspend`, `rancher_node_cordon`, `rancher_secret_create` |\n| **Operator rollups** | One-call triage | `rancher_cluster_health_check`, `rancher_find_failing_pods`, `rancher_find_stalled_rollouts`, `rancher_project_health_summary` |\n\nDomains covered: clusters & nodes · projects & namespaces · workloads · pods &\nservices · storage · networking · config & secrets *(values masked)* ·\ncertificates *(keys masked)* · RBAC · auth & identity · apps & catalogs ·\nlogging pipeline · Prometheus monitoring · policy reports · CIS compliance ·\nbackup operator · etcd backups · Longhorn · Fleet · provisioning · settings &\nfeatures · alerts & notifiers.\n\nAll 206 stay exposed by default — every tool schema is deferred behind\nClaude Code's own search, so a small default would only help other hosts at\nthe good host's expense. A constrained host (a small local model, a tight\ncontext budget) can opt into a smaller surface via `RANCHER_TOOLSETS`; see\n[Toolset profiles](#toolset-profiles) under Configuration.\n\n## Quick start\n\n### Requirements\n\n- Python 3.12+ and [uv](https://docs.astral.sh/uv/)\n- A Rancher API token ([creating one](https://ranchermanager.docs.rancher.com/reference-guides/user-settings/api-keys))\n\n### Install & run\n\n```bash\n# From source\ngit clone https://github.com/rex/mcp-rancher.git\ncd mcp-rancher\nmake setup                 # deps, .env scaffold, pre-commit hooks\ncp .env.example .env       # set RANCHER_URL + RANCHER_TOKEN\nmake dev                   # run the MCP server (stdio)\n```\n\nOnce published to PyPI, it's one line: `uvx rancher-mcp`.\n\n### Claude Code\n\n```bash\nclaude mcp add rancher \\\n  -e RANCHER_URL=https://rancher.example.com \\\n  -e RANCHER_TOKEN=token-xxxxx:yyyyyyyyy \\\n  -- uv run --directory /path/to/mcp-rancher rancher-mcp\n```\n\n### Claude Desktop\n\n```json\n{\n  \"mcpServers\": {\n    \"rancher\": {\n      \"command\": \"uv\",\n      \"args\": [\"run\", \"--directory\", \"/path/to/mcp-rancher\", \"rancher-mcp\"],\n      \"env\": {\n        \"RANCHER_URL\": \"https://rancher.example.com\",\n        \"RANCHER_TOKEN\": \"token-xxxxx:yyyyyyyyy\"\n      }\n    }\n  }\n}\n```\n\n### Multiple instances\n\n```env\nRANCHER_INSTANCES_JSON='{\n  \"production\": {\"url\": \"https://rancher.prod.example.com\",    \"token\": \"token-a:xxx\", \"verify_ssl\": true, \"read_only\": true},\n  \"lab\":        {\"url\": \"https://rancher.lab.example.com\",     \"token\": \"token-b:yyy\", \"verify_ssl\": false, \"read_only\": false}\n}'\nRANCHER_DEFAULT_INSTANCE=production\n```\n\nEvery tool takes an optional `instance` argument. Instances flagged\n`read_only: true` refuse **all** mutations at the settings layer.\n\n## Architecture\n\n```mermaid\nflowchart LR\n    A[MCP client<br/>Claude Code · Claude Desktop · any] -- stdio --> S\n\n    subgraph S[rancher-mcp]\n        direction TB\n        L1[Discovery & schema<br/>planes · schemas · capabilities]\n        L2[Generic engine<br/>any resource · both planes<br/>CRUD · actions · links · watch]\n        L3[Curated tools<br/>typed models · shaped output<br/>next-step hints]\n        G[Safety layer<br/>read-only guard · confirmation phrases<br/>audit log · rate limit · masking]\n        L1 --> L2 --> L3\n        L3 --> G\n        L2 --> G\n    end\n\n    G -- Norman /v3 --> R1[(Rancher<br/>instance A)]\n    G -- Steve /v1 + k8s proxy --> R1\n    G -- Norman + Steve --> R2[(Rancher<br/>instance B)]\n```\n\nThree layers, deliberately separate: **discovery** tells you what an instance can\ndo, the **generic engine** can touch anything it exposes, and **curated tools**\nmake the common paths typed, shaped, and self-describing (every response carries\n`suggested_next_steps`). Most curated tools are **generated from YAML descriptors**\n(`catalog/curated_tools/`) with a drift gate — the editorial decisions live in\ndescriptors, not boilerplate.\n\n## Safety model\n\nBuilt for the day an agent is pointed at the cluster that pays your salary:\n\n| Guard | Behavior |\n|---|---|\n| **Read-only instances** | `read_only: true` refuses every mutation for that instance, before tool logic runs |\n| **Destructive confirmation** | Deletes require an explicit typed phrase (e.g. `\"delete steve namespace foo\"`) — no phrase, no delete |\n| **Tool annotations** | Every tool declares `readOnlyHint` / `destructiveHint` / `idempotentHint`, so clients can gate UX on them |\n| **Audit log** | Every mutation emits a structured `event=\"audit\"` record — tool, operation, plane, instance, resource, outcome. Argument *names* only; values never logged |\n| **Rate limiting** | Token-bucket on writes (default 60/min) — a runaway loop can't machine-gun your API |\n| **Secret & key masking** | Secret values and certificate private keys are structurally absent from curated responses (reveal is an explicit generic-tool opt-in) |\n| **Structured errors** | Guard rejections return typed `error_code` envelopes agents can branch on — never raw strings |\n\n## Compatibility\n\n| | |\n|---|---|\n| **Primary target** | Rancher **2.9.3** (production-validated) |\n| **Compatibility floor** | Rancher **2.6.5** (kept green via capability detection) |\n| **API planes** | Norman `/v3` + Steve `/v1` (+ per-cluster Kubernetes proxy) |\n| **Transport** | stdio |\n\nCapability detection bridges version differences at runtime — no\nversion-pinned builds, no \"works on my Rancher.\" Both targets are exercised by\nthe same test suite, and read paths have been validated live against both a\n2.6.5 lab and a 2.9.3 production fleet\n([validation report](docs/live-validation-2026-05-06.md)).\n\n## Configuration\n\n| Variable | Default | Purpose |\n|---|---|---|\n| `RANCHER_URL` | — | Rancher server URL (single-instance mode) |\n| `RANCHER_TOKEN` | — | API token (`token-xxxxx:yyyyyyyyy`) |\n| `RANCHER_VERIFY_SSL` | `true` | TLS verification |\n| `RANCHER_INSTANCES_JSON` | — | Multi-instance config (see above) |\n| `RANCHER_DEFAULT_INSTANCE` | first defined | Instance used when a tool call names none |\n| `RANCHER_MCP_SERVER_NAME` | `rancher-mcp` | Server identity announced to clients |\n| `RANCHER_MCP_SERVER_DESCRIPTION` | built-in | Server description announced to clients |\n| `RANCHER_MCP_WRITE_RATE_LIMIT_PER_MIN` | `60` | Write rate limit (`0` disables) |\n| `RANCHER_TOOLSETS` | `all` | Comma-separated toolset profile(s) exposed at startup — see below |\n| `RANCHER_TOOLS` | — | Comma-separated tool names force-included on top of the selected profile(s) |\n| `RANCHER_EXCLUDE_TOOLS` | — | Comma-separated tool names removed, applied last — always wins over `RANCHER_TOOLS` |\n\n### Toolset profiles\n\nThe default is `all`: every tool stays exposed. Claude Code, the primary\nhost, defers every tool schema behind its own search, so a small default\nwould only help other hosts at the cost of making the good host worse — this\nis a deliberate choice, not an oversight.\n\n`RANCHER_TOOLSETS` opts a constrained host (a small local model, or a context\nbudget) into a smaller surface. Values are either a family name — one per\n`src/rancher_mcp/tools/` module (`storage`, `workloads`, `pods_services`,\n`rbac`, …; see `rancher_mcp.toolsets.FAMILY_REGISTRARS` for the full list) —\nor the cross-family `core` profile: a ~32-tool triage/orientation set\n(`rancher_find_*`, the health/summary rollups, core list/get pairs, and the\ngeneric `rancher_{steve,norman}_resource_{list,get}` escape hatches). `core`\ncuts the `tools/list` payload from 206 tools / ~401 KB to 32 tools / ~78 KB\n(~80% smaller). Profiles compose: `RANCHER_TOOLSETS=core,storage` gives the\ntriage set plus everything storage-related.\n\n```env\nRANCHER_TOOLSETS=core                        # small triage surface\nRANCHER_TOOLSETS=core,storage,workloads      # triage + two full families\nRANCHER_TOOLS=rancher_secret_get             # add one extra tool on top\nRANCHER_EXCLUDE_TOOLS=rancher_secret_create  # remove one, wins over the above\n```\n\nAn unknown profile name fails loudly at startup rather than silently\nproducing an empty or shrunken surface. Calling a real tool that exists but\nisn't in the active profile returns a structured `TOOLSET_NOT_ENABLED` error\nnaming the tool, the toolset that would enable it, and the env var to set —\nnever a bare \"unknown tool\", which would make a disabled tool indistinguishable\nfrom a typo.\n\n## Project status\n\nShipping and stable for **read, triage, and guarded write** operations. Honest\nledger of what's beyond that:\n\n- **Destructive workflows** (node drain, etcd/backup restore, cert rotation,\n  cluster upgrade/delete) are **roadmap** — deliberately staged after real-world\n  read-path mileage. The generic engine + confirmation guard already covers\n  these cases for operators who need them today.\n- The Alertmanager routes/silences surface needs an in-cluster API integration\n  and is deferred.\n- The full per-version compatibility matrix (Track G) is in progress; the\n  [first live validation run](docs/live-validation-2026-05-06.md) covers the\n  read matrix on both targets.\n\nWork is tracked to the tool level: [`docs/tool-catalog.md`](docs/tool-catalog.md)\n(every tool has a row, every gap a slice ID) and [`ROADMAP.md`](ROADMAP.md).\n\n## Development\n\n```bash\nmake help               # every target, documented\nmake validate           # codegen drift + manifest drift + architecture + lint + typecheck + tests\nmake tool-manifest      # regenerate docs/tool-manifest.json from the registry\nmake lab-up             # local Rancher 2.6.5 lab (kind + helm), fully scripted\nmake integration-current # isolated Rancher 2.14.3 end-to-end test run\nmake live-read-matrix   # read-only validation probes against configured instances\nmake mock-rancher       # fixture-backed mock Rancher for provider-config testing\n```\n\n- **Local lab** — a self-contained Rancher 2.6.5 on kind with a simulated\n  downstream cluster; repo-local kubeconfigs, never touches your machine state.\n- **Current integration lab** — Rancher 2.14.3 on separate Kind clusters and\n  port `9443`; run it serially with `make integration-current` to avoid\n  overlapping Docker resource demand with the legacy lab.\n- **Contract fixtures** — sanitized captures from live Rancher committed under\n  `tests/fixtures/`; `respx` pins the HTTP boundary in tests.\n- **Codegen** — curated tools are emitted from `catalog/curated_tools/*.yml`\n  descriptors; `make check-codegen` fails on drift.\n- **Gates** — ruff, pyright strict, 624 tests with coverage floor, architecture\n  line-limits, module-shape checks, secret scanning. All fail closed, all wired\n  into pre-commit.\n\nStack: Python 3.12 · [FastMCP](https://github.com/modelcontextprotocol/python-sdk) ·\n[httpx](https://www.python-httpx.org/) · [Pydantic v2](https://docs.pydantic.dev/) ·\n[structlog](https://www.structlog.org/) · [uv](https://docs.astral.sh/uv/)\n\n## Security\n\nSee [SECURITY.md](SECURITY.md) for the threat model, token guidance, and how to\nreport vulnerabilities.\n\n## License\n\n[MIT](LICENSE)\n",
  "bytes": 14606,
  "sha": "c785c0f0ed1591d352cea6707e5301e6386ddb0ac3624778fed9b15766650153",
  "repo_slug": "rex/mcp-rancher",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_rex_rancher_mcp_4a91e59e/readme"
}