# Rancher MCP Server

[English](README.md) | [中文](README.zh-CN.md)

[![GitHub License](https://img.shields.io/github/license/futuretea/rancher-mcp-server)](https://github.com/futuretea/rancher-mcp-server/blob/main/LICENSE)
[![npm](https://img.shields.io/npm/v/@futuretea/rancher-mcp-server)](https://www.npmjs.com/package/@futuretea/rancher-mcp-server)
[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/futuretea/rancher-mcp-server?sort=semver)](https://github.com/futuretea/rancher-mcp-server/releases/latest)

[Features](#features) | [Getting Started](#getting-started) | [Configuration](#configuration) | [Tools](#tools-and-functionalities) | [Development](#development)

## Features <a id="features"></a>

A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server for Rancher multi-cluster management.

- **Multi-cluster Management**: Access Kubernetes clusters through the Rancher API or configured kubeconfig paths
- **Kubernetes Resources via Steve API**: CRUD operations on any resource type
  - Get/List any resource (Pod, Deployment, Service, ConfigMap, Secret, CRD, etc.)
  - Create resources from JSON manifests
  - Patch resources using JSON Patch (RFC 6902)
  - Delete resources
  - Describe resources with related events (similar to `kubectl describe`)
  - List and filter Kubernetes events by namespace, object name, and object kind
  - Query container logs with filtering (tail lines, time range, timestamps, keyword search)
  - Multi-pod log aggregation via label selector with time-based sorting
  - View rollout history for Deployments
  - Inspect node state and resource usage
  - Inspect pods with parent workload, metrics, and logs
  - Show dependency/dependent trees for any resource (inspired by kube-lineage)
  - **Get all resources** (inspired by [ketall](https://github.com/corneliusweig/ketall)): List all Kubernetes resources including ConfigMaps, Secrets, RBAC, CRDs
  - **Compare resource versions** (kubernetes_diff): Show git-style diffs between two resource versions
  - **Watch resource changes** (kubernetes_watch): Monitor resources and return git-style diffs at regular intervals
  - **Resource capacity overview** (inspired by [kube-capacity](https://github.com/robscott/kube-capacity)): Show cluster resource capacity, requests, limits, and utilization
  - **Resource top ranking** (`kubernetes_top`): Rank pods or nodes by CPU/memory usage, requests, limits, or restart count
  - **Workload health summary** (`kubernetes_workload_health`): Health overview for Deployments, StatefulSets, and DaemonSets with ready/desired ratios and status derivation
  - **Resource summary by group** (`kubernetes_resource_summary`): Aggregate pod resources by namespace or label key with totals for requests/limits
  - **Event pattern analysis** (`kubernetes_event_summary`): Group and rank events by reason, kind, and frequency to identify recurring issues
- **Rancher Resources via Norman API**: List clusters and projects
- **Security Controls**:
  - `read_only`: Disables create, patch, and delete operations
  - `disable_destructive`: Disables delete operations only
  - `show_sensitive_data`: Global administrator control for sensitive data visibility (default: `false`)
    - When disabled (default): All sensitive data is masked with `***`
    - When enabled: Per-tool `showSensitiveData` parameter controls visibility
    - Applies to: Kubernetes Secret `data` and `stringData` fields
    - Affects tools: `kubernetes_get`, `kubernetes_list`, `kubernetes_describe`
  - `enable_container_exec`: Explicit opt-in for pod command execution (default: `false`, also requires `read_only=false`)
  - `enable_container_file_upload` / `enable_container_file_download`: Explicit opt-in for container file transfer tools; upload also requires `read_only=false`
- **Output Formats**: Table, YAML, and JSON
- **Output Filters**: Remove verbose fields like `managedFields` from responses
- **Pagination**: Limit and page parameters for list operations
- **Cross-platform**: Native binaries for Linux, macOS, Windows, and npm package

## Getting Started <a id="getting-started"></a>

### Requirements

Configure at least one cluster access method:

- Rancher access: a Rancher server and API credentials (Token or Access Key/Secret Key)
- Direct Kubernetes access: one or more kubeconfig paths

Rancher access and credentials are optional when kubeconfig paths are configured.

### Claude Code

```shell
claude mcp add rancher -- npx -y @futuretea/rancher-mcp-server@latest \
  --rancher-server-url https://your-rancher-server.com \
  --rancher-token your-token
```

Arguments after `--` are written to your MCP config as-is. The `-y` flag tells `npx` to install without prompting, which is required for non-interactive MCP startup. See the [VS Code / Cursor](#vs-code--cursor) section for the equivalent JSON config.

### VS Code / Cursor <a id="vs-code--cursor"></a>

Add to `.vscode/mcp.json` or `~/.cursor/mcp.json`:

```json
{
  "servers": {
    "rancher": {
      "command": "npx",
      "args": [
        "-y",
        "@futuretea/rancher-mcp-server@latest",
        "--rancher-server-url",
        "https://your-rancher-server.com",
        "--rancher-token",
        "your-token"
      ]
    }
  }
}
```

## Configuration <a id="configuration"></a>

Configuration can be set via CLI flags, environment variables, or a config file.

**Priority (highest to lowest):**
1. Command-line flags
2. Environment variables (prefix: `RANCHER_MCP_`)
3. Configuration file
4. Default values

### CLI Options

```shell
npx @futuretea/rancher-mcp-server@latest --help
```

| Option | Description | Default |
|--------|-------------|---------|
| `--config` | Config file path (YAML) | |
| `--port` | Port for HTTP/SSE mode (0 = stdio mode) | `0` |
| `--sse-base-url` | Public base URL for SSE endpoint | |
| `--log-level` | Log level (0-9) | `5` |
| `--rancher-server-url` | Rancher server URL | |
| `--rancher-token` | Rancher bearer token | |
| `--rancher-access-key` | Rancher access key | |
| `--rancher-secret-key` | Rancher secret key | |
| `--rancher-tls-insecure` | Skip TLS verification | `false` |
| `--kubeconfig-paths` | Kubeconfig files for direct Kubernetes cluster access | |
| `--rancher-request-token-auth` | Use `Authorization: Bearer <token>`, or raw `R_token` only when Authorization is absent, from each HTTP/SSE request instead of static credentials | `false` |
| `--rancher-oauth-token-auth` | Verify a Rancher OAuth Bearer token before `/mcp` calls Rancher | `false` |
| `--rancher-oauth-authorization-server-url` | Rancher OAuth authorization-server root URL and JWT issuer; do not append `/authorize` | |
| `--rancher-oauth-jwks-url` | Rancher OAuth JWKS URL loaded at startup and refreshed automatically | |
| `--rancher-oauth-resource-url` | Root public resource URL used by discovery metadata and the 401 challenge | |
| `--read-only` | Disable write operations | `true` |
| `--disable-destructive` | Disable delete operations | `false` |
| `--show-sensitive-data` | Global admin flag to allow sensitive data visibility | `false` |
| `--enable-container-exec` | Enable pod command execution tool; requires `--read-only=false` | `false` |
| `--enable-container-file-upload` | Enable container file upload tool | `false` |
| `--enable-container-file-download` | Enable container file download tool | `false` |
| `--max-file-size` | Max file size for container file operations | `10Mi` |
| `--list-output` | Reserved compatibility setting; current tool handlers ignore it | `json` |
| `--output-filters` | Fields to remove from output | `metadata.managedFields` |
| `--toolsets` | Toolsets to enable | `kubernetes,rancher` |
| `--enabled-tools` | Specific tools to enable | |
| `--disabled-tools` | Specific tools to disable | |

### Configuration File

Create `config.yaml`:

```yaml
port: 0  # 0 for stdio, or set a port like 8080 for HTTP/SSE

log_level: 5

rancher_server_url: https://your-rancher-server.com

# Authentication: choose exactly one mode.

# Mode 1: Static credentials
# Static bearer token:
rancher_token: your-bearer-token
# Or use Access Key/Secret Key:
# rancher_access_key: your-access-key
# rancher_secret_key: your-secret-key

# Mode 2: Direct per-request token (HTTP/SSE mode only)
# When enabled, the server uses Authorization: Bearer <token> from each HTTP/SSE
# request. If Authorization is absent, it uses a non-empty raw R_token header instead.
# A present malformed or empty Authorization header never falls back to R_token.
# Static credentials above must be empty.
# rancher_request_token_auth: true

# Mode 3: Rancher OAuth token passthrough (Streamable HTTP /mcp only)
# The client obtains its Rancher access token outside this service. The server
# verifies the Bearer JWT before it calls Rancher and then passes that verified
# token to Rancher APIs. Do not combine this with Modes 1 or 2.
# rancher_oauth_token_auth: true
# rancher_oauth_authorization_server_url: https://rancher.example.com/oidc
# The server loads the JWKS at startup and refreshes it automatically.
# rancher_oauth_jwks_url: https://rancher.example.com/oidc/.well-known/jwks.json
# rancher_oauth_resource_url: https://mcp.example.com

# rancher_tls_insecure: false

# Optional direct Kubernetes cluster source. Each context is exposed as
# kubeconfig:<context>; use cluster_list to discover available IDs.
# When multiple files define the same context, the first file wins.
# kubeconfig_paths:
#   - /etc/rancher-mcp/kubeconfig
#   - /etc/rancher-mcp/extra-kubeconfig

read_only: true  # default: true
disable_destructive: false

# High-risk container operations are disabled by default.
# enable_container_exec requires read_only: false.
# enable_container_file_upload also requires read_only: false.
enable_container_exec: false
enable_container_file_upload: false
enable_container_file_download: false

# Sensitive Data Control:
# Global administrator setting that controls whether sensitive data can be shown.
# - false (default): All sensitive data is always masked with '***'
# - true: Allows per-tool showSensitiveData parameter to control visibility
# Applies to Kubernetes Secret data and stringData fields.
show_sensitive_data: false

# Reserved compatibility setting; current tool handlers use each tool's format parameter.
list_output: json

# Remove verbose fields from output
output_filters:
  - metadata.managedFields
  - metadata.annotations.kubectl.kubernetes.io/last-applied-configuration

toolsets:
  - kubernetes
  - rancher

# enabled_tools: []
# disabled_tools: []
```

Start the server with the file explicitly; it is not discovered automatically:

```shell
rancher-mcp-server --config ./config.yaml
```

### Kubeconfig cluster source

`kubeconfig_paths` adds direct Kubernetes contexts without routing requests through
Rancher. Files are loaded in order; if more than one file declares the same context,
the first file wins. Run `cluster_list` to obtain IDs such as
`kubeconfig:production` and pass those IDs to Kubernetes tools.

The result of `cluster_list` includes a `source` column. Rancher rows use
`rancher`; direct-context rows use `kubeconfig` and intentionally leave Rancher-only
fields empty. `project_list` remains Rancher-only and rejects `kubeconfig:` IDs.

Do not combine `kubeconfig_paths` with `rancher_request_token_auth` or
`rancher_oauth_token_auth`: kubeconfig credentials belong to the server process,
while those modes use caller-scoped Rancher tokens. In HTTP/SSE mode (`port > 0`),
this server has no HTTP authentication for kubeconfig-backed requests. Prefer stdio
or restrict network access to trusted callers.

For an MCP client that uses `npx`, pass the same arguments after the package
name: `npx -y @futuretea/rancher-mcp-server@latest --config ./config.yaml`.

### Environment Variables

Use `RANCHER_MCP_` prefix with underscores:

```shell
RANCHER_MCP_PORT=8080
RANCHER_MCP_RANCHER_SERVER_URL=https://rancher.example.com
RANCHER_MCP_RANCHER_TOKEN=your-token
RANCHER_MCP_KUBECONFIG_PATHS=/etc/rancher-mcp/kubeconfig,/etc/rancher-mcp/extra-kubeconfig
RANCHER_MCP_READ_ONLY=true
RANCHER_MCP_SHOW_SENSITIVE_DATA=false  # Global admin control for sensitive data
RANCHER_MCP_ENABLE_CONTAINER_EXEC=false
```

`RANCHER_MCP_KUBECONFIG_PATHS` uses a comma-separated ordered list, matching
`kubeconfig_paths`. When multiple files define the same context, the first path wins.

For Rancher OAuth token passthrough, configure the OAuth settings with the same
prefix:

```shell
RANCHER_MCP_RANCHER_OAUTH_TOKEN_AUTH=true
RANCHER_MCP_RANCHER_OAUTH_AUTHORIZATION_SERVER_URL=https://rancher.example.com/oidc
RANCHER_MCP_RANCHER_OAUTH_JWKS_URL=https://rancher.example.com/oidc/.well-known/jwks.json
RANCHER_MCP_RANCHER_OAUTH_RESOURCE_URL=https://mcp.example.com
```

### HTTP/SSE Mode

Run with a port number for network access:

```shell
rancher-mcp-server --port 8080 \
  --rancher-server-url https://your-rancher-server.com \
  --rancher-token your-token
```

Endpoints:
- `/healthz` - Health check
- `/mcp` - Streamable HTTP endpoint
- `/sse` - Server-Sent Events endpoint
- `/message` - Message endpoint for SSE clients

In Rancher OAuth token passthrough mode, `/mcp` and
`/.well-known/oauth-protected-resource` are the MCP transport routes. `/sse`
and `/message` return `404` in that mode; `/healthz` and `/debug/vars` remain
available.

With a public URL behind a proxy:

```shell
rancher-mcp-server --port 8080 \
  --sse-base-url https://your-domain.com:8080 \
  --rancher-server-url https://your-rancher-server.com \
  --rancher-token your-token
```

### Per-Request Rancher Token Authentication

When running in HTTP/SSE mode behind a gateway that authenticates users, you can use `--rancher-request-token-auth` so the server does not store static Rancher credentials. It uses `Authorization: Bearer <token>` when that field is present; otherwise, it uses a non-empty raw `R_token` header for the Rancher API.

Requirements:
- HTTP/SSE mode only (`--port` must be greater than `0`; incompatible with stdio mode)
- The upstream gateway or proxy must forward either `Authorization` or `R_token` on every request to `/mcp`, `/sse`, and `/message`; a present malformed or empty Authorization field blocks `R_token` fallback
- Cannot be combined with `--rancher-token`, `--rancher-access-key`, or `--rancher-secret-key`

Example:

```shell
rancher-mcp-server --port 8080 \
  --rancher-request-token-auth \
  --rancher-server-url https://your-rancher-server.com
```

#### Gateway Examples

The upstream gateway must forward the selected request-token header. Minimal examples use Authorization:

**nginx:**

```nginx
location ~ ^/(mcp|sse|message)$ {
    proxy_pass http://rancher-mcp-server:8080;
    proxy_set_header Authorization $http_authorization;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
```

This route preserves the original request path and applies to every transport
route required by direct per-request token authentication.

**Traefik ForwardAuth:**

```yaml
http:
  middlewares:
    rancher-mcp-auth:
      forwardAuth:
        address: "https://auth.example.com/verify"
        authResponseHeaders:
          - Authorization
```

Use this only when the authentication service returns a valid
`Authorization: Bearer <Rancher token>` response header. Traefik then copies
that header to the upstream request. If the incoming request already carries
the Rancher Bearer token, do not overwrite it with a static header value.

### Rancher OAuth Token Passthrough Authentication

Rancher OAuth token passthrough is an opt-in alternative to static credentials
and direct per-request tokens. An MCP client obtains a Rancher access token
outside this service, sends it as `Authorization: Bearer <token>` to `/mcp`, and
the server verifies its RS256 signature, issuer, present time claims with a
ten-second leeway, and the `offline_access` and `rancher:mcp` scopes before
constructing Rancher clients. Audience is not enforced and expiration is not required.
Invalid or missing tokens receive `401 Unauthorized` with a
`WWW-Authenticate` resource-metadata challenge and make no Rancher API calls.

Use it only in Streamable HTTP mode:

```shell
rancher-mcp-server --port 8080 \
  --rancher-server-url https://rancher.example.com \
  --rancher-oauth-token-auth \
  --rancher-oauth-authorization-server-url https://rancher.example.com/oidc \
  --rancher-oauth-jwks-url https://rancher.example.com/oidc/.well-known/jwks.json \
  --rancher-oauth-resource-url https://mcp.example.com
```

Requirements and limits:

- `--rancher-oauth-token-auth` and the three OAuth URLs are required. A Rancher
  server URL is also required. OAuth mode cannot be combined with static
  credentials or `--rancher-request-token-auth`, and it is rejected in stdio
  mode.
- The removed audience YAML key and derived environment name are not newly rejected;
  current configuration loading ignores unknown legacy inputs.
- The resource URL must be the root public URL. Path-mounted deployments are
  unsupported. The server does not validate URL format, URL path, or HTTPS;
  deployment owns those settings.
- OAuth mode registers `/mcp` and
  `/.well-known/oauth-protected-resource` as its MCP transport routes. It does
  not provide OAuth SSE or session-principal state, so `/sse` and `/message`
  return `404`; `/healthz` and `/debug/vars` remain available.
- This service never reads, forwards, stores, or logs Rancher browser cookies
  such as `R_SESS`. It does not implement authorization-code exchange, token
  exchange, refresh tokens, or dynamic client registration.
- The server loads the JWKS during startup and fails to start unless it contains
  a usable RS256 signature-verification key. It refreshes the JWKS hourly; an
  unknown key ID can trigger an additional refresh, rate-limited to once every
  five minutes. A failed or unusable refresh keeps the last known-good keyset.
  A successful refresh immediately removes keys no longer published by the
  authorization server.
- This is an explicitly accepted Rancher-token passthrough mode. It provides
  reference-compatible metadata and a challenge, but does not claim generic
  MCP OAuth or RFC 9728 interoperability.

### Rancher Version Support

Which authentication modes work with which Rancher versions:

| Rancher | Static credentials | `--rancher-request-token-auth` | `--rancher-oauth-token-auth` |
|---|---|---|---|
| 2.11 and earlier | Supported | Supported | Not available: Rancher has no OIDC provider |
| 2.12 - 2.13 | Supported | Supported | Not usable: the Rancher API rejects OIDC access tokens (JWT authentication was added in 2.14 by [rancher/rancher#53016](https://github.com/rancher/rancher/pull/53016)) |
| 2.14 - 2.15 | Supported | Supported | Supported, with the prerequisites below |

OAuth passthrough prerequisites on 2.14 and later:

- The `oidc-provider` feature must be enabled. It is on by default only on
  Rancher Prime; community installations enable it manually, and Rancher
  restarts once.
- The `OIDCClient` must allow the scopes this server requires. With the default
  required scope set the client needs `offline_access` and `rancher:mcp`, and
  Rancher issues `rancher:mcp` only when it is listed in
  `OIDCClient.spec.scopes`. A default-configured client issues only `openid`,
  `profile`, and `offline_access`, which the server rejects.
- The `server-url` setting must be configured. Without it, issued tokens carry a
  relative `/oidc` issuer that no external verifier can match.

Rows for 2.11 and earlier and for 2.12 are based on Rancher source analysis. The
2.13.3, 2.14.3, and 2.15.1 behaviors are covered by the integration suite
described under [Test](#test).

## Tools and Functionalities <a id="tools-and-functionalities"></a>

### Sensitive Data Protection

The server uses a two-tier security model for Secret resources:

1. **Global flag** `--show-sensitive-data` (default: `false`): When disabled, all Secret `data` and `stringData` fields are **always masked** with `***`, regardless of per-tool parameters. When enabled, per-tool control is allowed.
2. **Per-tool parameter** `showSensitiveData` (default: `false`): Only takes effect when the global flag is enabled. Controls visibility per call.

**Affected tools:** `kubernetes_get`, `kubernetes_list`, `kubernetes_describe`.

See [Configuration](#configuration) for setup examples.

```yaml
# --show-sensitive-data=false (default): always masked
apiVersion: v1
kind: Secret
data:
  password: "***"

# --show-sensitive-data=true + showSensitiveData: true
apiVersion: v1
kind: Secret
data:
  password: "<base64-encoded-value>"
```

Tools are organized into toolsets. Use `--toolsets` to enable specific sets or `--enabled-tools`/`--disabled-tools` for fine-grained control.

### High-Risk Container Operations

Container exec and file transfer tools are disabled by default and must be explicitly enabled:

| Tool | Gate | Requires `read_only=false` |
|------|------|---------------------------|
| `kubernetes_exec` | `--enable-container-exec` | Yes |
| `kubernetes_upload_file` | `--enable-container-file-upload` | Yes |
| `kubernetes_download_file` | `--enable-container-file-download` | No |

`kubernetes_exec` accepts an argv-style command array (no stdin, no TTY) and returns `exitCode`, `stdout`, and `stderr`. The file transfer tools require `tar` in the container and respect `--max-file-size` (default: 10Mi).

Enable uploads with both `--read-only=false` and
`--enable-container-file-upload`. Enable downloads with
`--enable-container-file-download`.

See the [kubernetes tools section](#kubernetes) for full parameter documentation.

### Toolsets

| Toolset | API | Description |
|---------|-----|-------------|
| kubernetes | Steve | Kubernetes CRUD operations for any resource type |
| rancher | Norman | Cluster and project listing |

### kubernetes

<details>
<summary>kubernetes_capacity</summary>

Show Kubernetes cluster resource capacity, requests, limits, and utilization. Similar to [kube-capacity](https://github.com/robscott/kube-capacity) CLI tool.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `cluster` | string | Yes | Cluster reference: Rancher ID or `kubeconfig:<context>` |
| `pods` | boolean | No | Include individual pod resources in the output (default: false) |
| `containers` | boolean | No | Include individual container resources in the output (implies pods=true) (default: false) |
| `util` | boolean | No | Include actual resource utilization from metrics-server (requires metrics-server) (default: false) |
| `available` | boolean | No | Show raw available capacity instead of percentages (default: false) |
| `podCount` | boolean | No | Include pod counts for each node and the whole cluster (default: false) |
| `showLabels` | boolean | No | Include node labels in the output (default: false) |
| `hideRequests` | boolean | No | Hide request columns from output (default: false) |
| `hideLimits` | boolean | No | Hide limit columns from output (default: false) |
| `namespace` | string | No | Filter by namespace (empty for all namespaces) |
| `labelSelector` | string | No | Filter pods by label selector (e.g., "app=nginx,env=prod") |
| `nodeLabelSelector` | string | No | Filter nodes by label selector (e.g., "node-role.kubernetes.io/worker=true") |
| `namespaceLabelSelector` | string | No | Filter namespaces by label selector (e.g., "env=production") |
| `nodeTaints` | string | No | Filter nodes by taints. Use 'key=value:effect' to include, 'key=value:effect-' to exclude. Multiple taints can be separated by comma |
| `noTaint` | boolean | No | Exclude nodes with any taints (default: false) |
| `sortBy` | string | No | Sort by: cpu.util, mem.util, cpu.request, mem.request, cpu.limit, mem.limit, cpu.util.percentage, mem.util.percentage, cpu.request.percentage, mem.request.percentage, cpu.limit.percentage, mem.limit.percentage, pod.count, name |
| `format` | string | No | Output format: table, json, yaml (default: table) |

**Examples:**

```json
// Basic node overview with utilization
{
  "cluster": "c-abc123",
  "util": true
}

// Include pod and container detail, sorted by CPU utilization
{
  "cluster": "c-abc123",
  "pods": true,
  "containers": true,
  "util": true,
  "sortBy": "cpu.util"
}

// Filter by namespace and show pod counts
{
  "cluster": "c-abc123",
  "namespace": "production",
  "podCount": true
}

// Filter by node and namespace labels
{
  "cluster": "c-abc123",
  "nodeLabelSelector": "node-role.kubernetes.io/worker=true",
  "namespaceLabelSelector": "env=production"
}

// Filter by taints (include or exclude)
{
  "cluster": "c-abc123",
  "nodeTaints": "dedicated=special:NoSchedule",
  "noTaint": false
}
```

</details>

<details>
<summary>kubernetes_top</summary>

Rank pods or nodes by resource requests and limits. With metrics-server,
utilization fields are also available; without it, utilization is omitted and
the result includes a warning. Pod rankings also support restart count.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `cluster` | string | Yes | Cluster reference: Rancher ID or `kubeconfig:<context>` |
| `kind` | string | No | Resource kind to rank: `pod` or `node` (default: `pod`) |
| `namespace` | string | No | Namespace (empty = all namespaces) |
| `labelSelector` | string | No | Label selector for filtering (e.g., "app=nginx,env=prod") |
| `sortBy` | string | No | Sort by field. Pods: `cpu.util`, `mem.util`, `cpu.request`, `mem.request`, `cpu.limit`, `mem.limit`, `restart.count`. Nodes: `cpu.util`, `mem.util`, `cpu.request`, `mem.request`, `cpu.limit`, `mem.limit`, `cpu.util.percentage`, `mem.util.percentage`, `name` |
| `limit` | integer | No | Maximum results to return (default: 50, max: 500) |
| `format` | string | No | Output format: `json`, `table`, `yaml` (default: `table`) |

**Examples:**

```json
// Top pods by CPU utilization
{
  "cluster": "c-abc123",
  "kind": "pod",
  "sortBy": "cpu.util",
  "limit": 20
}

// Top pods by restart count across a namespace
{
  "cluster": "c-abc123",
  "kind": "pod",
  "namespace": "production",
  "sortBy": "restart.count",
  "limit": 10
}

// Top nodes by memory utilization percentage
{
  "cluster": "c-abc123",
  "kind": "node",
  "sortBy": "mem.util.percentage",
  "limit": 10
}
```

</details>

<details>
<summary>kubernetes_workload_health</summary>

Get a health summary for Deployments, StatefulSets, and DaemonSets. Shows ready vs desired replicas, unavailable count, update progress, and derived status.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `cluster` | string | Yes | Cluster reference: Rancher ID or `kubeconfig:<context>` |
| `namespace` | string | No | Namespace (empty = all namespaces) |
| `kind` | string | No | Workload kind: `deployment`, `statefulset`, `daemonset`, or `all` (default: `all`) |
| `labelSelector` | string | No | Label selector for filtering |
| `sortBy` | string | No | Sort by: `unready.count`, `ready.ratio`, `name` |
| `limit` | integer | No | Maximum results (default: 50, max: 500) |
| `format` | string | No | Output format: `json`, `table`, `yaml` (default: `table`) |

**Examples:**

```json
// All workloads sorted by unready count
{
  "cluster": "c-abc123",
  "sortBy": "unready.count"
}

// Deployment health in a namespace
{
  "cluster": "c-abc123",
  "namespace": "production",
  "kind": "deployment",
  "sortBy": "ready.ratio"
}
```

</details>

<details>
<summary>kubernetes_resource_summary</summary>

Aggregate pod/container resources by namespace or label key. Returns total requests, limits, and pod counts per group.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `cluster` | string | Yes | Cluster reference: Rancher ID or `kubeconfig:<context>` |
| `namespace` | string | No | Namespace filter (empty = all namespaces) |
| `labelSelector` | string | No | Label selector for filtering pods |
| `groupBy` | string | No | Group by: `namespace` or `label` (default: `namespace`) |
| `groupByKey` | string | No | Label key to group by (required when `groupBy=label`) |
| `sortBy` | string | No | Sort by: `cpu.request`, `mem.request`, `cpu.limit`, `mem.limit`, `pod.count`, `name` |
| `limit` | integer | No | Maximum results (default: 50, max: 500) |
| `format` | string | No | Output format: `json`, `table`, `yaml` (default: `table`) |

**Examples:**

```json
// Resource summary by namespace
{
  "cluster": "c-abc123",
  "groupBy": "namespace",
  "sortBy": "cpu.request"
}

// Resource summary by app label in production
{
  "cluster": "c-abc123",
  "namespace": "production",
  "groupBy": "label",
  "groupByKey": "app",
  "sortBy": "cpu.request"
}
```

</details>

<details>
<summary>kubernetes_event_summary</summary>

Group and rank Kubernetes events by reason, kind, and frequency. Useful for identifying recurring issues and patterns.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `cluster` | string | Yes | Cluster reference: Rancher ID or `kubeconfig:<context>` |
| `namespace` | string | No | Namespace (empty = all namespaces) |
| `kind` | string | No | Filter by involved object kind (e.g., Pod, Deployment, Node) |
| `type` | string | No | Filter by event type: `Warning` or `Normal` |
| `since` | string | No | Only include events newer than this duration (e.g., "1h30m", "2h") |
| `sortBy` | string | No | Sort by: `count`, `lastSeen`, `name` |
| `limit` | integer | No | Maximum results (default: 50, max: 500) |
| `format` | string | No | Output format: `json`, `table`, `yaml` (default: `table`) |

**Examples:**

```json
// Top warning events in the last hour
{
  "cluster": "c-abc123",
  "type": "Warning",
  "since": "1h",
  "sortBy": "count",
  "limit": 10
}

// Recent events for a specific kind
{
  "cluster": "c-abc123",
  "kind": "Pod",
  "since": "30m",
  "sortBy": "lastSeen"
}
```

</details>

<details>
<summary>kubernetes_dep</summary>

Show all dependencies or dependents of any Kubernetes resource as a tree. Covers OwnerReference chains, Pod→Node/SA/ConfigMap/Secret/PVC, Service→Pod (label selector), Ingress→IngressClass/Service/TLS Secret, PVC↔PV→StorageClass, RBAC bindings, PDB→Pod, and Events.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `cluster` | string | Yes | Cluster reference: Rancher ID or `kubeconfig:<context>` |
| `kind` | string | Yes | Resource kind (e.g., deployment, pod, service, ingress, node, App) |
| `apiVersion` | string | No | API version for CRDs or ambiguous kinds (e.g., catalog.cattle.io/v1) |
| `namespace` | string | No | Namespace (optional for cluster-scoped resources) |
| `name` | string | Yes | Resource name |
| `direction` | string | No | Traversal direction: `dependents` (default) or `dependencies` |
| `depth` | integer | No | Maximum traversal depth, 1-20 (default: 10) |
| `scanNamespace` | string | No | Namespace for auxiliary scans of a cluster-scoped root; a namespaced root must use its own namespace |
| `maxScannedObjects` | integer | No | Fail-fast limit for total scanned objects; `0` disables the limit (default: 0) |
| `format` | string | No | Output format: tree, json (default: tree) |

</details>

<details>
<summary>kubernetes_get</summary>

Get a Kubernetes resource by kind, namespace, and name.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `cluster` | string | Yes | Cluster reference: Rancher ID or `kubeconfig:<context>` |
| `kind` | string | Yes | Resource kind (e.g., pod, deployment, service, App) |
| `apiVersion` | string | No | API version for CRDs or ambiguous kinds (e.g., catalog.cattle.io/v1) |
| `namespace` | string | No | Namespace (optional for cluster-scoped resources) |
| `name` | string | Yes | Resource name |
| `format` | string | No | Output format: json, yaml (default: json) |
| `showSensitiveData` | boolean | No | Show sensitive data values (e.g., Secret data). Default: false. Only takes effect when global `--show-sensitive-data` is enabled. When global setting is disabled, data is always masked with `***` |

</details>

<details>
<summary>kubernetes_list</summary>

List Kubernetes resources by kind.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `cluster` | string | Yes | Cluster reference: Rancher ID or `kubeconfig:<context>` |
| `kind` | string | Yes | Resource kind (e.g., pod, deployment, service, App) |
| `apiVersion` | string | No | API version for CRDs or ambiguous kinds (e.g., catalog.cattle.io/v1) |
| `namespace` | string | No | Namespace (empty = all namespaces) |
| `name` | string | No | Filter by name (partial match) |
| `labelSelector` | string | No | Label selector (e.g., "app=nginx,env=prod") |
| `limit` | integer | No | Items per page (default: 100) |
| `page` | integer | No | Page number, starting from 1 (default: 1) |
| `format` | string | No | Output format: json, table, yaml (default: json) |
| `showSensitiveData` | boolean | No | Show sensitive data values (e.g., Secret data). Default: false. Only takes effect when global `--show-sensitive-data` is enabled. When global setting is disabled, data is always masked with `***` |

CRDs can use their manifest identity directly:

```json
{
  "cluster": "c-abc123",
  "apiVersion": "catalog.cattle.io/v1",
  "kind": "App",
  "namespace": "cattle-system"
}
```

</details>

<details>
<summary>kubernetes_logs</summary>

Get logs from a pod container. Supports multi-pod log aggregation via label selector with time-based sorting.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `cluster` | string | Yes | Cluster reference: Rancher ID or `kubeconfig:<context>` |
| `namespace` | string | Yes | Namespace |
| `name` | string | No | Pod name (required if labelSelector not specified) |
| `labelSelector` | string | No | Label selector for multi-pod log aggregation (e.g., "app=nginx") |
| `container` | string | No | Container name (empty = all containers) |
| `tailLines` | integer | No | Lines from end (default: 100) |
| `sinceSeconds` | integer | No | Logs from last N seconds |
| `timestamps` | boolean | No | Include timestamps (default: false) |
| `previous` | boolean | No | Previous container instance (default: false) |
| `keyword` | string | No | Filter log lines containing this keyword (case-insensitive) |

**Notes:**
- When `labelSelector` is specified, logs from all matching pods are aggregated; they are time-sorted only when `timestamps=true`
- With `timestamps=true`, single-pod output is `[container] timestamp content`
- With `timestamps=true`, multi-pod output is `[pod/container] timestamp content`

</details>

<details>
<summary>kubernetes_inspect_pod</summary>

Get pod diagnostics: details, parent workload, metrics, and logs.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `cluster` | string | Yes | Cluster reference: Rancher ID or `kubeconfig:<context>` |
| `namespace` | string | Yes | Namespace |
| `name` | string | Yes | Pod name |

</details>

<details>
<summary>kubernetes_rollout_history</summary>

View rollout history for Deployments. Shows revision history with change annotations (similar to `kubectl rollout history`).

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `cluster` | string | Yes | Cluster reference: Rancher ID or `kubeconfig:<context>` |
| `namespace` | string | Yes | Namespace |
| `name` | string | Yes | Deployment name |
| `format` | string | No | Output format: json, table (default: table) |

</details>

<details>
<summary>kubernetes_node_analysis</summary>

Inspect one node's state and resource usage. Shows node capacity, allocatable
resources, taints, labels, and scheduled pods.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `cluster` | string | Yes | Cluster reference: Rancher ID or `kubeconfig:<context>` |
| `name` | string | Yes | Node name |
| `format` | string | No | Output format: json, yaml (default: json) |

</details>

<details>
<summary>kubernetes_describe</summary>

Describe a Kubernetes resource with its related events. Similar to `kubectl describe`.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `cluster` | string | Yes | Cluster reference: Rancher ID or `kubeconfig:<context>` |
| `kind` | string | Yes | Resource kind (e.g., pod, deployment, service, node, App) |
| `apiVersion` | string | No | API version for CRDs or ambiguous kinds (e.g., catalog.cattle.io/v1) |
| `namespace` | string | No | Namespace (optional for cluster-scoped resources) |
| `name` | string | Yes | Resource name |
| `format` | string | No | Output format: json, yaml (default: json) |
| `showSensitiveData` | boolean | No | Show sensitive data values (e.g., Secret data). Default: false. Only takes effect when global `--show-sensitive-data` is enabled. When global setting is disabled, data is always masked with `***` |

</details>

<details>
<summary>kubernetes_diff</summary>

Compare two Kubernetes resource versions and show the differences as a git-style diff. Useful for comparing current vs desired state, or before/after changes.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `resource1` | string | Yes | First resource version as JSON string (the 'before' or 'old' version). Use kubernetes_get to retrieve the resource. |
| `resource2` | string | Yes | Second resource version as JSON string (the 'after' or 'new' version). Use kubernetes_get to retrieve the resource. |
| `ignoreStatus` | boolean | No | Ignore changes under the status field when computing diffs (default: false) |
| `ignoreMeta` | boolean | No | Ignore non-essential metadata differences like managedFields, resourceVersion, etc. (default: false) |

**Examples:**

```json
// Compare two versions of the same deployment
// First, get the current resource
{
  "cluster": "c-abc123",
  "kind": "deployment",
  "namespace": "default",
  "name": "nginx"
}
// Then compare with previous version (from rollout history)
{
  "resource1": "<previous-revision-json>",
  "resource2": "<current-revision-json>",
  "ignoreMeta": true
}
```

</details>

<details>
<summary>kubernetes_resource_diff</summary>

Compare two resources of the same kind, including resources in different
clusters or namespaces. Returns a git-style diff.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `kind` | string | Yes | Resource kind (e.g., deployment, daemonset, statefulset) |
| `apiVersion` | string | No | API version for CRDs or ambiguous kinds (e.g., `apps/v1`) |
| `left` | object | Yes | First resource: `cluster` is a cluster reference (Rancher ID or `kubeconfig:<context>`); `name` is required and `namespace` is optional |
| `right` | object | Yes | Second resource: `cluster` is a cluster reference (Rancher ID or `kubeconfig:<context>`); `name` is required and `namespace` is optional |
| `ignoreStatus` | boolean | No | Ignore changes under `status` (default: false) |
| `ignoreMeta` | boolean | No | Ignore non-essential metadata differences (default: true) |

</details>

<details>
<summary>kubernetes_get_all</summary>

Get really all Kubernetes resources in the cluster (inspired by [ketall](https://github.com/corneliusweig/ketall)). Unlike `kubectl get all`, this shows all resource types including ConfigMaps, Secrets, RBAC resources, CRDs, and other resources that are normally hidden.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `cluster` | string | Yes | Cluster reference: Rancher ID or `kubeconfig:<context>` |
| `namespace` | string | No | Filter by namespace (optional, empty for all namespaces) |
| `name` | string | No | Filter by resource name (partial match, client-side) |
| `labelSelector` | string | No | Label selector for filtering (e.g., "app=nginx,env=prod") |
| `excludeEvents` | boolean | No | Exclude events from output (default: true, as events are often noisy) |
| `scope` | string | No | Filter by scope: 'namespaced' for namespaced resources only, 'cluster' for cluster-scoped resources only, or empty for all |
| `since` | string | No | Only show resources created since this duration (e.g., '1h30m', '2d', '1w') |
| `limit` | integer | No | Limit number of resources per API call (0 for no limit, default: 0) |
| `format` | string | No | Output format: json, table, yaml (default: table) |

**Examples:**

```json
// Get all resources in the cluster
{
  "cluster": "c-abc123"
}

// Get all resources in a specific namespace
{
  "cluster": "c-abc123",
  "namespace": "production"
}

// Get only cluster-scoped resources
{
  "cluster": "c-abc123",
  "scope": "cluster"
}

// Get resources created in the last 24 hours
{
  "cluster": "c-abc123",
  "since": "24h"
}

// Get all resources with specific labels
{
  "cluster": "c-abc123",
  "labelSelector": "app=nginx,env=prod"
}
```

</details>

<details>
<summary>kubernetes_watch</summary>

Watch Kubernetes resources and return git-style diffs of changes at regular intervals, similar to the Linux `watch` command.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `cluster` | string | Yes | Cluster reference: Rancher ID or `kubeconfig:<context>` |
| `kind` | string | Yes | Resource kind (e.g., pod, deployment, service, App) |
| `apiVersion` | string | No | API version for CRDs or ambiguous kinds (e.g., catalog.cattle.io/v1) |
| `namespace` | string | No | Namespace (empty = all namespaces or cluster-scoped resources) |
| `labelSelector` | string | No | Label selector (e.g., "app=nginx,env=prod") |
| `fieldSelector` | string | No | Field selector for filtering resources |
| `ignoreStatus` | boolean | No | Ignore changes under the `status` field when computing diffs (similar to `--no-status`) |
| `ignoreMeta` | boolean | No | Ignore non-essential metadata differences (similar to `--no-meta`) |
| `intervalSeconds` | integer | No | Interval in seconds between evaluations (default: 10, min: 1, max: 600) |
| `iterations` | integer | No | Number of times to re-evaluate and diff before returning (default: 6, min: 1, max: 100) |

**Notes:**
- Each iteration compares the current resource state with the previous iteration and only emits diffs when there are changes.
- The tool returns the concatenated diffs for all iterations in a single response.

**Examples:**

```json
// Watch pods in a namespace for changes
{
  "cluster": "c-abc123",
  "kind": "pod",
  "namespace": "production",
  "intervalSeconds": 5,
  "iterations": 3
}

// Watch deployments and ignore status changes
{
  "cluster": "c-abc123",
  "kind": "deployment",
  "namespace": "default",
  "ignoreStatus": true,
  "intervalSeconds": 10,
  "iterations": 6
}

// Watch resources by label selector
{
  "cluster": "c-abc123",
  "kind": "pod",
  "labelSelector": "app=nginx",
  "intervalSeconds": 5,
  "iterations": 12
}
```

</details>

<details>
<summary>kubernetes_events</summary>

List Kubernetes events. Supports filtering by namespace, involved object name, and kind.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `cluster` | string | Yes | Cluster reference: Rancher ID or `kubeconfig:<context>` |
| `namespace` | string | No | Namespace (empty = all namespaces) |
| `name` | string | No | Filter by involved object name |
| `kind` | string | No | Filter by involved object kind (e.g., Pod, Deployment, Node) |
| `limit` | integer | No | Events per page (default: 50) |
| `page` | integer | No | Page number, starting from 1 (default: 1) |
| `format` | string | No | Output format: json, table, yaml (default: table) |

</details>

<details>
<summary>kubernetes_create</summary>

Create a Kubernetes resource. Disabled when `read_only=true`.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `cluster` | string | Yes | Cluster reference: Rancher ID or `kubeconfig:<context>` |
| `resource` | string | Yes | JSON manifest valid for its resource kind; include `spec` only when that kind defines one |

</details>

<details>
<summary>kubernetes_patch</summary>

Patch a resource using JSON Patch (RFC 6902). Disabled when `read_only=true`.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `cluster` | string | Yes | Cluster reference: Rancher ID or `kubeconfig:<context>` |
| `kind` | string | Yes | Resource kind |
| `apiVersion` | string | No | API version for CRDs or ambiguous kinds (e.g., catalog.cattle.io/v1) |
| `namespace` | string | No | Namespace (optional for cluster-scoped) |
| `name` | string | Yes | Resource name |
| `patch` | string | Yes | JSON Patch array, e.g., `[{"op":"replace","path":"/spec/replicas","value":3}]` |

</details>

<details>
<summary>kubernetes_delete</summary>

Delete a Kubernetes resource. Disabled when `read_only=true` or `disable_destructive=true`.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `cluster` | string | Yes | Cluster reference: Rancher ID or `kubeconfig:<context>` |
| `kind` | string | Yes | Resource kind |
| `apiVersion` | string | No | API version for CRDs or ambiguous kinds (e.g., catalog.cattle.io/v1) |
| `namespace` | string | No | Namespace (optional for cluster-scoped) |
| `name` | string | Yes | Resource name |

</details>

<details>
<summary>kubernetes_exec</summary>

Execute a non-interactive command in a pod container. Disabled by default (`--enable-container-exec` required, also requires `--read-only=false`). The command must be an argv-style array; stdin and TTY are not supported. Returns `exitCode`, `stdout`, and `stderr`.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `cluster` | string | Yes | Cluster reference: Rancher ID or `kubeconfig:<context>` |
| `namespace` | string | Yes | Namespace |
| `name` | string | Yes | Pod name |
| `container` | string | No | Container name (defaults to first container) |
| `command` | array | Yes | Command and arguments, e.g. `["printenv", "HOSTNAME"]` |

**Example:**

```json
{
  "cluster": "c-abc123",
  "namespace": "default",
  "name": "nginx-7d8b8f9c4-x7k2q",
  "command": ["cat", "/etc/hostname"]
}
```

</details>

<details>
<summary>kubernetes_upload_file</summary>

Upload a file to a pod container. Disabled by default; requires both
`--read-only=false` and `--enable-container-file-upload`. Accepts
base64-encoded content and writes to the specified path. Requires `tar` in the
container. Files are limited by `--max-file-size` (default: 10Mi).

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `cluster` | string | Yes | Cluster reference: Rancher ID or `kubeconfig:<context>` |
| `namespace` | string | Yes | Namespace |
| `name` | string | Yes | Pod name |
| `container` | string | No | Container name (defaults to first container) |
| `filePath` | string | Yes | Absolute destination path in the container |
| `content` | string | Yes | Base64-encoded file content |

</details>

<details>
<summary>kubernetes_download_file</summary>

Download a file from a pod container. Disabled by default; enable it with
`--enable-container-file-download`. Returns base64-encoded file content with
metadata. Requires `tar` in the container. Files are limited by
`--max-file-size` (default: 10Mi).

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `cluster` | string | Yes | Cluster reference: Rancher ID or `kubeconfig:<context>` |
| `namespace` | string | Yes | Namespace |
| `name` | string | Yes | Pod name |
| `container` | string | No | Container name (defaults to first container) |
| `filePath` | string | Yes | Absolute path of the file to download |

</details>

### rancher

<details>
<summary>cluster_list</summary>

List all available clusters from Rancher and configured kubeconfig contexts.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | No | Filter by cluster name (partial match) |
| `limit` | integer | No | Items per page (default: 100) |
| `page` | integer | No | Page number (default: 1) |
| `format` | string | No | Output format: json, table, yaml (default: json) |

</details>

<details>
<summary>project_list</summary>

List Rancher projects.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `cluster` | string | No | Rancher cluster ID filter; `kubeconfig:` references are rejected |
| `name` | string | No | Filter by project name (partial match) |
| `limit` | integer | No | Items per page (default: 100) |
| `page` | integer | No | Page number (default: 1) |
| `format` | string | No | Output format: json, table, yaml (default: json) |

</details>

## Development <a id="development"></a>

### Prerequisites

- Go 1.26.0+
- Access to a Rancher server (only for Rancher integration testing; kubeconfig-only operation does not require Rancher access)

### Build

```shell
make build
```

### Test

```shell
make test        # unit tests only, no Docker required
```

Integration tests start real Rancher containers with Docker and exercise the
built server against them. They need Docker, several minutes per version, and
enough memory for one Rancher container at a time:

```shell
go test -tags=integration -timeout 45m ./test/integration/...                       # 2.13.3, 2.14.3, 2.15.1
RANCHER_TEST_VERSIONS=2.14.3 go test -tags=integration -timeout 30m ./test/integration/...
```

Set `RANCHER_TEST_KEEP=1` to keep the Rancher containers for inspection. The
same suite runs from the `Integration` GitHub Actions workflow.

### Lint

```shell
make lint        # Run golangci-lint
make format      # Auto-format code
```

### Run Locally

```shell
make build
./rancher-mcp-server \
  --rancher-server-url https://your-rancher-server.com \
  --rancher-token your-token
```

### Debug with MCP Inspector

```shell
npx @modelcontextprotocol/inspector@latest $(pwd)/rancher-mcp-server
```

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, project structure, and pull request guidelines.

## Support

- [GitHub Issues](https://github.com/futuretea/rancher-mcp-server/issues)
- Run `rancher-mcp-server --help` to inspect the supported configuration flags.

## License

[Apache-2.0](LICENSE)
