Kubernetes · Model Context Protocol · Local-first

Two faces. One cluster. No exposed keys.

An MCP server that gives AI assistants a safe, controlled window into your Kubernetes clusters. It runs locally, holds your KUBECONFIG close to its chest, and lets the model operate through carefully scoped tools: no token, certificate, or API server URL ever leaves your machine.

Quick start View the source
$ { }  install janus-mcp-server

0
Secrets in the kind registry
3
Redaction layers per response
5
Security invariants, CI-verified
1
Pinned kubeconfig context

How it works
What the model sees

Declarative tool definitions and sanitized results, never raw cluster state, never a text dump.

  • Tools carry ToolAnnotations: readOnlyHint, openWorldHint: false, so the client knows what is safe to call unattended.
  • Clients that support MCP resources can pin cluster://summary into context: a cached health overview for free, no tool call spent.
  • Where elicitation is supported, write approvals arrive as native cards showing live state; elsewhere the tool returns status=pending with an approval id.
Where the policy lives

Operator-authoritative config, checked at startup and again at call time.

  • The kubeconfig context is pinned by exact match. The server refuses to start on any other one, and warns if those credentials can read Secrets.
  • ScopeGuard (deny wins), token-bucket rate limits per tool, and an ApprovalGate that binds a SHA-256 of the exact arguments to the approval id and burns it on use.
  • Strict config parsing rejects unknown keys, so a typo in a security-relevant setting fails at startup rather than silently opening a door.
  • Every call lands in an append-only audit log at ~/.local/state/janus-mcp/audit.jsonl.
The absence is the control

Secrets are not filtered out of the kind registry. They were never in it.

  • A single module imports the Kubernetes client library; raw client errors embed the API server URL, so each one is mapped to a typed, generic message before it can travel.
  • The shipped RBAC manifests grant least privilege, and list no Secret verbs anywhere.
  • EKS, GKE and AKS need nothing janus-specific: auth is whatever your kubeconfig says, including exec: credential plugins.
  • Node provider IDs, external IPs, cloud labels and IAM role annotations are masked by the redaction engine by default.
Every tool call runs the same pipeline
validate ScopeGuard RateLimiter kube call structural redaction pattern + entropy scrub envelope byte-cap audit

Rendering failures fail closed: the model gets a generic error, never a partially-redacted payload.


The surface

Tools, not text dumps.

Read-only diagnostics come free. Write tools only register when an operator lists them in the config, and a global read-only switch removes them from the listing entirely.

get_pods get_events get_logs list_namespaces describe_resource get_cluster_summary cluster://summary rollout_restart scale_deployment
ToolWhat it returnsMode
get_podsPod inventory and status for an allowed namespaceread
get_eventsRecent warning and normal events, sanitizedread
get_logsBounded log tail, scrubbed for secrets and high-entropy stringsread
list_namespacesNamespaces the operator has put in scoperead
describe_resourceOne resource by kind and name, without env-var values or credential annotationsread
get_cluster_summaryOne-screen health overview, cachedread
cluster://summaryThe same overview as a pinnable MCP resourceresource
rollout_restartRestarts a deployment after human approvalwrite
scale_deploymentScales within max_replicas after human approvalwrite

Every tool declares its annotations up front, output is bounded by a byte cap, and each call is rate-limited per tool. Parameter reference lives in the operator runbook.


Quick start
01 Install

Three ways from PyPI, all giving you the janus-mcp CLI. Contributors can clone and uv sync instead.

uv uv tool install janus-mcp-server
pipx pipx install janus-mcp-server
pip pip install janus-mcp-server

No install at all: uvx janus-mcp-server serve runs it one-shot.

02 Configure

Two settings are required: the pinned context and the namespaces it may touch. The server refuses to start on any other context.

context: limited-sa@prod-cluster
scope:
  allowed_namespaces: ["prod", "staging"]
  denied_namespaces: ["kube-system"]
  allow_cluster_scoped: false
read_only: false
write_tools:
  enabled: ["rollout_restart", "scale_deployment"]
  max_replicas: 20
03 Register with your client
{{ snippetPath }}
{{ snippetCode }}

{{ snippetNote }}

04 Ask it something
Why are pods crashing in the prod namespace? Summarize the health of my cluster. Show me recent warning events for payments-api.