MCP Server (podscape-mcp)
podscape-mcp is a standalone binary that exposes your Kubernetes cluster as Model Context Protocol (MCP) tools. It lets AI assistants — Claude, Claude Code, Cursor, and any other MCP-compatible client — query and manage your cluster in plain language without requiring kubectl knowledge.
The MCP server connects directly to the Kubernetes API using your kubeconfig. It is an independent binary that runs alongside (or without) the Podscape desktop app.
Prerequisites
- A kubeconfig file with at least one context configured (default:
~/.kube/config) - Go 1.22+ if building from source, or one of the pre-built binaries from GitHub Releases
- The Podscape desktop app is not required —
podscape-mcptalks to the cluster directly
Installation
Option A — Pre-built binary (recommended)
Pre-built binaries for all platforms are published on every GitHub Release.
macOS (Apple Silicon):
sudo curl -L https://github.com/codingprotocols/podscape/releases/latest/download/podscape-mcp-darwin-arm64 \
-o /usr/local/bin/podscape-mcp && sudo chmod +x /usr/local/bin/podscape-mcp
# Clear Gatekeeper quarantine (unsigned binary)
xattr -dr com.apple.quarantine /usr/local/bin/podscape-mcp
macOS (Intel):
sudo curl -L https://github.com/codingprotocols/podscape/releases/latest/download/podscape-mcp-darwin-amd64 \
-o /usr/local/bin/podscape-mcp && sudo chmod +x /usr/local/bin/podscape-mcp
xattr -dr com.apple.quarantine /usr/local/bin/podscape-mcp
Linux (amd64):
sudo curl -L https://github.com/codingprotocols/podscape/releases/latest/download/podscape-mcp-linux-amd64 \
-o /usr/local/bin/podscape-mcp && sudo chmod +x /usr/local/bin/podscape-mcp
Windows (amd64): Download podscape-mcp-windows-amd64.exe from the release page and place it somewhere on your PATH.
Option B — Build from source
cd go-core
go build ./cmd/podscape-mcp/
The binary is produced at go-core/podscape-mcp.
Configuring your AI assistant
The MCP server communicates over stdio and is started automatically by your MCP client — you do not run it manually.
Claude Code (CLI)
# Installed binary
claude mcp add --transport stdio podscape -- /usr/local/bin/podscape-mcp
# During development — point at the locally built binary
claude mcp add --transport stdio podscape -- $(pwd)/go-core/podscape-mcp
After adding, start a new Claude Code session — MCP servers connect on startup:
claude
Verify the server is connected inside the new session:
/mcp
Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"podscape": {
"command": "/usr/local/bin/podscape-mcp",
"args": []
}
}
}
To use a non-default kubeconfig:
{
"mcpServers": {
"podscape": {
"command": "/usr/local/bin/podscape-mcp",
"args": ["--kubeconfig", "/path/to/your/kubeconfig"]
}
}
}
Restart Claude Desktop after editing the config file.
Cursor
Edit .cursor/mcp.json in your project (or the global Cursor MCP config):
{
"mcpServers": {
"podscape": {
"command": "podscape-mcp",
"args": []
}
}
}
Other MCP clients
Any MCP client that supports stdio transport works. The server name is podscape and the command is the path to the podscape-mcp binary with an optional --kubeconfig argument.
CLI flags
| Flag | Default | Description |
|---|---|---|
--kubeconfig | (see below) | Path to kubeconfig file |
Kubeconfig resolution order: --kubeconfig flag → $KUBECONFIG environment variable → ~/.kube/config.
Safety gates
Three destructive tools require an explicit confirm=true parameter to execute. Calling them without confirm=true returns a preview of what would be affected — no cluster state is changed.
| Tool | Preview shows |
|---|---|
delete_resource | Kind, name, and namespace that would be deleted |
drain_node | Exact pod list that would be evicted vs skipped (capped at 50) |
helm_uninstall | Release name and namespace that would be removed |
This two-step pattern prevents accidental deletions when an AI assistant constructs a tool call from an ambiguous instruction.
Available Tools
podscape-mcp exposes 37 tools across four categories.
Read-only tools (15)
These tools only read from the cluster and never modify state.
list_resources
List any Kubernetes resource type. Supports all built-in types and any CRD by plural name.
| Parameter | Type | Required | Description |
|---|---|---|---|
resource | string | yes | Resource type: pods, deployments, services, nodes, configmaps, secrets, custom CRD plural names (e.g. virtualservices, ingressroutes), etc. |
namespace | string | no | Namespace filter; omit for all namespaces |
label_selector | string | no | Label selector, e.g. app=nginx or env=prod,tier=frontend |
field_selector | string | no | Field selector, e.g. status.phase=Running or spec.nodeName=node-1 |
limit | number | no | Max results to return (default 100; set to 0 for unlimited) |
get_resource
Get a single Kubernetes resource by name. Supports built-in types and CRDs.
| Parameter | Type | Required | Description |
|---|---|---|---|
resource | string | yes | Resource type |
name | string | yes | Resource name |
namespace | string | no | Namespace; omit for cluster-scoped resources |
get_resource_yaml
Get the full YAML manifest of a resource. Returns the same data as get_resource but formatted as YAML, equivalent to kubectl get -o yaml.
| Parameter | Type | Required | Description |
|---|---|---|---|
resource | string | yes | Resource type |
name | string | yes | Resource name |
namespace | string | no | Namespace; omit for cluster-scoped resources |
get_pod_logs
Fetch container logs. Output is capped at 512 KB total; a truncation notice is appended if the limit is reached.
| Parameter | Type | Required | Description |
|---|---|---|---|
pod | string | yes | Pod name |
namespace | string | yes | Namespace |
container | string | no | Container name (defaults to first container) |
tail | number | no | Number of lines (default 100); ignored when since_minutes is set |
since_minutes | number | no | Return logs from the last N minutes (overrides tail) |
previous | boolean | no | Fetch logs from the previously terminated container instance (useful for crash-looping pods) |
init_container | boolean | no | When true, fetches logs from init containers instead of main containers |
list_events
List Kubernetes events sorted by most recent first.
| Parameter | Type | Required | Description |
|---|---|---|---|
namespace | string | no | Namespace filter; omit for all namespaces |
type | string | no | Warning or Normal; omit for all types |
object_name | string | no | Filter events for a specific object name |
limit | number | no | Max events to return (default 100) |
list_contexts
List all available Kubernetes contexts from the kubeconfig, and report the currently active one.
No parameters required.
Example output:
{
"contexts": ["prod-cluster", "staging-cluster", "local-kind"],
"current": "prod-cluster"
}
get_current_context
Return the name of the active Kubernetes context. No parameters required.
list_namespaces
List all namespaces in the cluster. No parameters required.
helm_list
List Helm releases in the cluster.
| Parameter | Type | Required | Description |
|---|---|---|---|
namespace | string | no | Namespace filter; omit for all namespaces |
helm_status
Get the full status output of a Helm release.
| Parameter | Type | Required | Description |
|---|---|---|---|
release | string | yes | Release name |
namespace | string | yes | Namespace |
helm_values
Get the values of a Helm release.
| Parameter | Type | Required | Description |
|---|---|---|---|
release | string | yes | Release name |
namespace | string | yes | Namespace |
all | boolean | no | Include computed/default values (equivalent to helm get values --all) |
helm_history
List all revisions of a Helm release.
| Parameter | Type | Required | Description |
|---|---|---|---|
release | string | yes | Release name |
namespace | string | yes | Namespace |
Returns an array of revisions, each with revision, status, chart, appVersion, updated, and description.
security_scan
Run a security posture scan on all pods in a namespace. Checks for:
- Missing
SecurityContext - Privileged containers
- Containers running as root (container-level and pod-level)
- Missing resource limits
- Host network / PID / IPC namespace usage
allowPrivilegeEscalationnot explicitly set tofalsereadOnlyRootFilesystemnot set totrue- Dangerous capabilities present:
NET_ADMIN,SYS_ADMIN,SYS_PTRACE,ALL
| Parameter | Type | Required | Description |
|---|---|---|---|
namespace | string | yes | Namespace to scan |
Each finding includes: pod, container, issue, and severity (WARN, HIGH, or CRITICAL).
detect_providers
Detect installed ingress controllers and service mesh providers in the cluster (Istio, Traefik v2/v3, NGINX Inc, NGINX Community). Uses the Kubernetes discovery API and IngressClass controller fields.
No parameters required.
list_crds
List all CustomResourceDefinitions installed in the cluster.
| Parameter | Type | Required | Description |
|---|---|---|---|
group | string | no | Filter by API group, e.g. karpenter.sh or traefik.io |
get_metrics
Get CPU and memory usage for pods or nodes. Requires metrics-server to be installed in the cluster.
| Parameter | Type | Required | Description |
|---|---|---|---|
kind | string | yes | pods or nodes |
namespace | string | no | Namespace filter for pods; omit for all namespaces |
Diagnostic tools (5)
These tools bundle multiple API calls into a single response to reduce round-trips during common diagnostic workflows.
pod_summary
Get a combined view of a pod’s status, container states, recent events, and last N log lines — everything needed to diagnose a failing pod in one call. Init container logs are fetched concurrently alongside main container logs and included in container_logs under init:<name> keys.
| Parameter | Type | Required | Description |
|---|---|---|---|
pod | string | yes | Pod name |
namespace | string | yes | Namespace |
tail | number | no | Log lines per container (default 50) |
previous | boolean | no | Fetch logs from the previously terminated container instance |
Returns: phase, conditions, containers (state/reason/message per container), events, and container_logs (map of container name → log output; init containers keyed as init:<name>).
cluster_health
Get a one-call cluster health overview: node ready/total counts, pod counts by phase (Running, Pending, Failed, etc.), and Warning events from the last hour.
No parameters required.
Warning events are capped at 50 entries; a warning_events_truncated: true field is added when the cap is reached.
list_failing_pods
List all pods that are not in Running or Succeeded state, plus any Running pods with containers in a Waiting or not-ready state. Includes phase, reason, and per-container failure details.
| Parameter | Type | Required | Description |
|---|---|---|---|
namespace | string | no | Namespace filter; omit for all namespaces |
get_resource_events
Get Kubernetes events for a specific named resource — equivalent to the Events section of kubectl describe.
| Parameter | Type | Required | Description |
|---|---|---|---|
kind | string | yes | Resource kind, e.g. Pod, Deployment, Node, Service |
name | string | yes | Resource name |
namespace | string | no | Namespace; omit for cluster-scoped resources |
describe_resource
Get a resource and its events in one call — equivalent to kubectl describe without the table formatting. Avoids requiring two separate tool calls when diagnosing a resource.
| Parameter | Type | Required | Description |
|---|---|---|---|
resource | string | yes | Resource type (plural), e.g. pods, deployments, services |
name | string | yes | Resource name |
namespace | string | no | Namespace; omit for cluster-scoped resources |
Returns: { "resource": { ... }, "events": [ ... ] }
Mutating tools (11)
These tools modify cluster state. Destructive tools require confirm=true — see Safety gates.
scale_resource
Scale a deployment or statefulset to a desired replica count.
| Parameter | Type | Required | Description |
|---|---|---|---|
kind | string | yes | deployment or statefulset |
name | string | yes | Resource name |
namespace | string | yes | Namespace |
replicas | number | yes | Desired replica count (must be >= 0) |
delete_resource
Delete a Kubernetes resource. Requires confirm=true to execute — call without it first to see a preview.
| Parameter | Type | Required | Description |
|---|---|---|---|
kind | string | yes | Resource kind, e.g. pod, deployment, service |
name | string | yes | Resource name |
namespace | string | yes | Namespace |
confirm | boolean | no | Must be true to delete. Omit or set false to preview what will be deleted. |
rollout_restart
Trigger a rolling restart of a deployment, daemonset, or statefulset by patching the pod template annotation with the current timestamp.
| Parameter | Type | Required | Description |
|---|---|---|---|
kind | string | yes | deployment, daemonset, or statefulset |
name | string | yes | Resource name |
namespace | string | yes | Namespace |
rollout_undo
Roll back a deployment to a previous revision.
| Parameter | Type | Required | Description |
|---|---|---|---|
kind | string | yes | deployment (currently the only supported kind) |
name | string | yes | Resource name |
namespace | string | yes | Namespace |
revision | number | no | Target revision number; omit or set to 0 for the previous revision |
apply_yaml
Apply a Kubernetes YAML manifest using server-side apply. Equivalent to kubectl apply --server-side.
| Parameter | Type | Required | Description |
|---|---|---|---|
yaml | string | yes | The full YAML manifest content |
cordon_node
Cordon or uncordon a node to prevent or allow new pod scheduling.
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | yes | Node name |
unschedulable | boolean | yes | true = cordon (prevent new pods), false = uncordon |
drain_node
Evict all pods from a node to prepare it for maintenance. Requires confirm=true to execute — call without it first to see which pods would be evicted.
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | yes | Node name |
force | boolean | no | Delete pods not managed by a controller (default false) |
ignore_daemonsets | boolean | no | Skip DaemonSet-managed pods (default true) |
delete_emptydir_data | boolean | no | Allow eviction of pods with emptyDir volumes (default false) |
confirm | boolean | no | Must be true to drain. Omit or set false to preview which pods would be evicted. |
Preview response (without confirm=true):
{
"node": "node-1",
"would_evict": 12,
"would_skip": 4,
"pods_to_evict": ["production/api-pod-abc", "production/worker-pod-xyz"],
"message": "Set confirm=true to drain node node-1 (will evict 12 pods, skip 4)."
}
The pods_to_evict list is capped at 50 entries; a "truncated": true field is added when there are more.
trigger_cronjob
Manually trigger a CronJob by creating a Job from its template — equivalent to kubectl create job --from=cronjob/<name>.
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | yes | CronJob name |
namespace | string | yes | Namespace |
Returns the name of the created Job.
exec_command
Execute a one-shot command inside a running pod container and return combined stdout + stderr. Suitable for non-interactive commands (ls, env, cat /path, ps aux).
| Parameter | Type | Required | Description |
|---|---|---|---|
pod | string | yes | Pod name |
namespace | string | yes | Namespace |
container | string | no | Container name (defaults to first container) |
command | array | yes | Command and arguments, e.g. ["ls", "-la", "/tmp"] |
Non-zero container exit codes are returned as part of the result text (not as a tool error), so the AI can see the output even when the command fails.
switch_context
Switch the active Kubernetes context for all subsequent tool calls. All tools called after a successful switch_context will operate against the new cluster.
| Parameter | Type | Required | Description |
|---|---|---|---|
context | string | yes | Kubernetes context name (must exist in kubeconfig) |
Returns "Switched to context <name>" on success. Returns an error if the context is not found in the kubeconfig — the active context is not changed on error.
helm_rollback
Roll back a Helm release to a previous revision.
| Parameter | Type | Required | Description |
|---|---|---|---|
release | string | yes | Release name |
namespace | string | yes | Namespace |
revision | number | no | Target revision; omit or set to 0 for the previous revision |
Helm lifecycle tools (6)
helm_upgrade
Upgrade an existing Helm release, or install it if not present (--install).
| Parameter | Type | Required | Description |
|---|---|---|---|
release | string | yes | Release name |
namespace | string | yes | Namespace |
chart | string | yes | Chart reference: local path, repo/chart, or OCI reference |
values | string | no | Optional YAML string of values to merge over chart defaults |
Returns: { "release": "...", "status": "deployed", "revision": 3 }
helm_uninstall
Uninstall a Helm release and remove all associated Kubernetes resources. Requires confirm=true to execute — call without it first to see a preview.
| Parameter | Type | Required | Description |
|---|---|---|---|
release | string | yes | Release name |
namespace | string | yes | Namespace |
confirm | boolean | no | Must be true to uninstall. Omit or set false to preview what will be removed. |
helm_history
List all revisions of a Helm release (same as helm history).
| Parameter | Type | Required | Description |
|---|---|---|---|
release | string | yes | Release name |
namespace | string | yes | Namespace |
Authentication
podscape-mcp authenticates with the Kubernetes API using the credentials already present in your kubeconfig — exactly as kubectl does. This includes certificate-based auth, token-based auth, OIDC, exec plugins (e.g. aws eks get-token), and any other provider supported by client-go.
The MCP server does not connect to the Podscape desktop app or its sidecar. There is no separate token or shared secret to configure.
The permissions available to the MCP server are the same as those of the user or service account referenced by the active kubeconfig context. If your kubeconfig user only has read access to certain namespaces, the MCP tools will reflect that — they will return API errors for operations outside that scope.
Example workflows
Diagnose a failing pod in production
Ask your AI assistant:
“One of my pods in the production namespace is crash-looping. Can you find it and tell me what’s wrong?”
The assistant will likely call:
list_failing_podswithnamespace=productionto find the podpod_summarywith the pod name and namespace to get status, events, and logs in one shot
Check cluster health before a deployment
Ask your AI assistant:
“Give me a quick health check of the cluster before I push the release.”
The assistant will call:
cluster_healthto get node/pod counts and recent Warning events- Optionally
list_failing_podsfor a full list of unhealthy pods
Drain a node for maintenance
Ask your AI assistant:
“I need to take node-3 offline for maintenance. Drain it.”
The assistant will:
- Call
drain_nodewithname=node-3(noconfirm) — receives the pod preview - Present the preview to you for confirmation
- Call
drain_nodeagain withconfirm=trueto execute
Debug a failing init container
Ask your AI assistant:
“The api-server pod keeps getting stuck in Init state. What’s happening?”
The assistant will likely call:
describe_resourcewithresource=pods, name=api-server— gets the pod spec and events in one callget_pod_logswithinit_container=trueto see what the init container printed before failing
Switch clusters mid-session
Ask your AI assistant:
“Switch to the staging cluster and check if the same issue exists there.”
The assistant calls:
list_contextsto see available contextsswitch_contextwith the staging context name- Any subsequent tool calls now operate against staging
Roll back a broken Helm release
Ask your AI assistant:
“The last deploy of the checkout-service Helm release broke something. Roll it back to the previous revision.”
The assistant will likely call:
helm_statuswithrelease=checkout-serviceto confirm the current statehelm_historyto see available revisionshelm_rollbackwithrelease=checkout-serviceandrevision=0helm_statusagain to confirm the rollback succeeded
Troubleshooting
“error building kubeconfig from …”
The kubeconfig file does not exist at the expected path, or the path passed via --kubeconfig is wrong. Check that the file exists and is readable. If you use a non-standard path, set $KUBECONFIG or pass --kubeconfig explicitly.
“connection refused” or API server unreachable
The cluster referenced by the active context is not reachable. Verify with kubectl cluster-info. Common causes: VPN not connected, cluster is down, or the kubeconfig context points to a stale server address.
Tool calls return permission errors
The kubeconfig user does not have RBAC permission for the requested operation. Review the cluster role bindings for your user, or switch to a context with sufficient privileges.
“metrics unavailable (is metrics-server installed?)”
The get_metrics tool requires the Kubernetes Metrics Server to be running in the cluster. Install it with:
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
MCP server not appearing in the AI assistant
- Confirm the binary path in the MCP client config is correct and the binary is executable (
chmod +x). - On macOS, if Gatekeeper blocks the binary, run
xattr -dr com.apple.quarantine /usr/local/bin/podscape-mcp. - For Claude Desktop, restart the app after editing the config file.
- For Claude Code, start a new session after running
claude mcp add— MCP servers connect on session startup, not mid-session. - Run the binary manually to confirm it starts without errors:
/usr/local/bin/podscape-mcp --help
Tools operate on the wrong cluster after a context switch
Use the switch_context tool to change the active context mid-session — the server will reinitialise its client against the new cluster immediately. Alternatively, restart the MCP client session after running kubectl config use-context in your terminal.