
Kubernetes Troubleshoot
io.github.NanaGyamfiPrempeh30由 GitHub 帳號 NanaGyamfiPrempeh30 發布v1.0.0更新於 Oct 24, 2026
Read-only Kubernetes diagnostics: pods, logs, events, workloads, services, PVCs and nodes.
概覽
唯讀的 Kubernetes 診斷 MCP 伺服器,讓助理在允許的命名空間中檢視 Pod、日誌、事件、工作負載、服務、PVC 與節點。
- 可以做什麼
- 檢視 Pod:狀態、狀況、容器狀態、日誌與事件,依命名空間和標籤選擇器列出 Pod,以及命名空間層級事件與命名空間清單。檢視節點:就緒狀態、容量、可分配資源、污點與角色,可單一查詢或列出全部。檢視工作負載:Deployment、StatefulSet 與 DaemonSet 的狀態與清單。檢視網路與儲存:Service 詳細資料、就緒與未就緒端點、PVC 綁定與擴容狀態。檢視自動擴縮:HPA 副本範圍、目前指標與狀況。
- 適用情境
- 適合在聊天用戶端中排查叢集問題:Pod 為何反覆崩潰或未就緒、最近的日誌或事件說明了什麼、哪些節點異常,以及 Service、PVC 或 HPA 是否如預期運作。它以結構化回答取代一連串手動 kubectl 查詢。
- 執行需求
- 以 stdio 在本機執行,可用 Python 3.11+ 與 uv 從原始碼啟動,或使用已發佈的 Docker 映像。需要 Kubernetes 叢集(1.24+)以及路徑由 KUBECONFIG 指定的 kubeconfig 檔案,ALLOWED_NAMESPACES 也是必填。初始化需要一個有權限建立 ServiceAccount、Role、RoleBinding、ClusterRole 與 ClusterRoleBinding 的 kubectl 環境。選用:API_TIMEOUT_SECONDS、LOG_LEVEL、MAX_LOG_LINES。
安裝
在 SourceWeft 中
- 開啟 儀表板中的 Kubernetes Troubleshoot,將其新增到工作區。
- 為需要使用其工具的對話啟用該服務。
Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。
其他 MCP 客戶端
參照 儲存庫 中的啟動說明。
README
k8s-troubleshoot-mcp
A read-only MCP (Model Context Protocol)
server that lets an AI assistant diagnose a Kubernetes cluster. Ask why a pod is
crash-looping instead of running six kubectl commands and correlating the
output by hand.
Read-only is a structural property, not a promise. There are no write tools, and the RBAC manifests grant no write verbs. See Security model.
What It Does
The server exposes 16 diagnostic tools over stdio. Connect it to Claude Desktop, VS Code, or Kiro, and you can ask things like:
- "Why is the
checkoutpod instagingnot ready?" - "Show me the last 50 lines of logs from the
apicontainer" - "Which nodes are not Ready, and what are their taints?"
- "Is the
webHPA scaling, and what does its current metric say?" - "What events fired in
productionin the last few minutes?"
The assistant calls the tools, the server queries the Kubernetes API with a scoped ServiceAccount token, and every response comes back as a structured dict — including errors, which are never raised as exceptions into the MCP layer.
Architecture
Configuration is validated once at startup. If anything is wrong — KUBECONFIG
unset, the file unreadable or malformed, ALLOWED_NAMESPACES missing or
containing a wildcard — the process writes one line to stderr and exits 1. It
never starts in a partially-valid state.
Available Tools
Arguments marked ? are optional.
Every namespaced tool validates its namespace argument before making any
API call, so a disallowed namespace produces a structured error and no network
request.
Deliberately absent
No get_secrets, get_configmap, exec_into_pod, port_forward, or any
create/update/patch/delete tool. These are excluded from all versions
unless a new threat-model review is conducted and documented — they are not
backlog items. The reasoning for each is in
SECURITY.md.
Security model
Full detail is in SECURITY.md. The summary:
The boundary is Kubernetes RBAC
Everything this server does in application code is defense-in-depth. The enforcement boundary is the ServiceAccount's RBAC bindings. If the bindings grant more than intended, the application-layer allowlist is all that stands in the way, and it is not a boundary you should rely on.
Provisioning is split by scope so that the cluster-scoped grant is minimal:
Applying the cluster-scoped pair makes no namespace readable. A namespace
becomes readable only when a Role and a RoleBinding exist in it. A namespace
listed in ALLOWED_NAMESPACES but never bound stays unreadable — RBAC wins.
pods/log is granted in its own rule block, never folded into the pods rule,
because Kubernetes subresources do not inherit from their parent.
Defense-in-depth layers
Prompt injection is mitigated, not eliminated
Pod logs and event messages are written by workloads in the cluster. A container can print anything, including text shaped like instructions to the model reading it. Escaping keeps injected text inside its JSON string; it cannot stop a model from acting on instructions it reads as data. Treat tool output as untrusted input to whatever consumes it. This residual risk is accepted and documented.
Prerequisites
- A Kubernetes cluster and a
kubectlcontext with enough permission to create a ServiceAccount, Role, RoleBinding, ClusterRole and ClusterRoleBinding — you need this once, to provision. The server itself never uses your admin credential. - Kubernetes 1.24+ —
scripts/generate-kubeconfig.shmints a token via the TokenRequest API, not a legacy auto-mounted Secret. - Python 3.11+
- uv —
curl -LsSf https://astral.sh/uv/install.sh | sh(or use Docker, which needs neither Python nor uv on the host)
Setup
Provision RBAC and mint a kubeconfig
The first argument is where to write the kubeconfig; the rest are the namespaces
the server may read. Pass the same set you intend to put in
ALLOWED_NAMESPACES — RBAC is the enforcement boundary, and a namespace bound
here but absent from the allowlist (or the reverse) is a mismatch between real
permission and configured capability.
The script applies the cluster-scoped manifests together, then applies
role.yaml with an explicit -n <namespace> and renders a RoleBinding per
namespace. On success it prints the kubeconfig path to stdout and nothing else;
all diagnostics go to stderr. It also asserts after provisioning that
kubectl auth can-i get secrets returns no, and aborts if it does not.
Do not run
kubectl apply -f kubernetes/. It does not fail — it reports success while creatingrole.yamlin the current namespace and skippingrolebinding.yaml.templateentirely, becausekubectl apply -f <dir>only reads.yaml/.yml/.json. The result is a server that looks provisioned and can read nothing. Verified against a v1.35 API server with--dry-run=server: 5 resources applied, not 6.
The generated kubeconfig is written with umask 077 and chmod 600. Keep it
out of the repository — the script warns if the output path is inside a
repository and not covered by .gitignore.
Run it
The server speaks JSON-RPC on stdin/stdout, so it will appear to hang — that is correct. It is waiting for a client.
Connecting to Claude Desktop
Add to claude_desktop_config.json
(%APPDATA%\Claude\claude_desktop_config.json on Windows,
~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
Restart Claude Desktop fully (quit from the system tray, reopen), then check
Settings → Developer → k8s-troubleshoot shows running.
On Windows, if the server shows as disconnected, use a batch file wrapper — Claude Desktop has working-directory issues with direct interpreter invocation:
Running with Docker
-i is required — JSON-RPC travels on stdin/stdout. :ro is not decoration:
this server performs no writes of any kind, so a writable mount would grant
privilege it has no use for.
The image runs as non-root, UID 10001, and contains no credentials.
Two things that will bite you:
- The kubeconfig must be readable by UID 10001. A file created mode
600and owned by your host user is not, and bind mounts preserve host ownership. Either grant group/other read, or run with--user "$(id -u)". - A cluster on the host's loopback (minikube, kind) needs
--network hoston Linux. On Docker Desktop that is not enough — see Local testing with minikube.
To use the container from Claude Desktop, set "command": "docker" and put the
whole run -i --rm … invocation in "args".
Environment Variables
All logging goes to stderr. stdout carries the JSON-RPC stream exclusively —
a single stray print() would corrupt the protocol, so there are none in src/.
Reading get_pod_logs output
The content field is escaped log text, not log text. One json.loads of
the MCP response is not enough to get printable output.
serialize_log_content escapes the log before the response envelope is
JSON-encoded, so the escaping survives transport. Decoding the response undoes
only the transport layer:
To recover the original text, decode the field a second time:
This is intentional, not a bug. The escaping is the structural prompt-injection
mitigation (REQ-020, REQ-027) — if a single decode restored real control
characters and raw <, the mitigation would be gone before the content reached
a model. Decode the second time only where the output is going somewhere that
will not interpret it, such as a terminal or a log file.
In MCP Inspector's raw view you will see \\n (two backslashes). That is
correct: the transport layer escaping the backslash of an already-escaped \n.
Do not compute a line count from content. Splitting it on a newline
returns 1 for a log of any length. Use lines_returned, which is why it exists.
Local testing with minikube
Stale kubeconfig after a minikube restart
minikube exposes the API server through a proxy address whose port is assigned
per session. That address — visible via kubectl cluster-info — can change
across minikube stop / minikube start cycles, and across WSL2 restarts.
A kubeconfig minted by scripts/generate-kubeconfig.sh during a previous
session pins the old host:port. The server will start normally and then fail
every tool call with a connection_error naming an endpoint nothing is
listening on:
To recover:
-
Confirm the current endpoint:
-
Mint a fresh kubeconfig against it:
-
Fully restart the MCP server process. The server reads
KUBECONFIGonce at startup (REQ-002) and never re-reads it, so overwriting the file underneath a running server changes nothing. There is also no fallback to~/.kube/config(REQ-003) — a workingkubectlon the same machine will not rescue a stale kubeconfig.
The same failure looks identical whether the cause is a stale port, a revoked
token, or a genuinely unreachable cluster. kubectl cluster-info distinguishes
them: if it succeeds while the server reports connection_error, the
kubeconfig is stale.
Reaching minikube from a container on Docker Desktop
--network host joins the Docker VM's network namespace, not the WSL
distribution's, so the published minikube port is not on that loopback and the
container gets ConnectionRefused. host.docker.internal is reachable, but
rewriting the kubeconfig's server to it fails TLS verification —
host.docker.internal is not among the minikube API server certificate's SANs.
Point the URL at host.docker.internal and add tls-server-name to the
cluster entry, which is the kubeconfig field that exists for exactly this:
Project Structure
Troubleshooting
KUBECONFIG environment variable is not set — the server refuses to start
without an explicit path and will not fall back to ~/.kube/config. This is
deliberate (REQ-003). Run scripts/generate-kubeconfig.sh if you have not yet.
is not a valid kubeconfig — the file exists and is readable but is
malformed. The message names the path, line and column, never the file's
contents; a kubeconfig holds a bearer token.
connection_error on every tool while kubectl works — a stale kubeconfig.
See Local testing with minikube.
namespace_not_allowed — the namespace is not in ALLOWED_NAMESPACES, or
it is kube-system/kube-public, which are stripped at startup even if listed.
kubernetes_api_error with http_status: 403 while the namespace is
allowed — the allowlist and the RBAC bindings have diverged.
ALLOWED_NAMESPACES grants nothing; a namespace is only readable once a Role
and RoleBinding exist in it. Re-run generate-kubeconfig.sh with the full
namespace set.
Errors come back as one of three codes: namespace_not_allowed (rejected before
any API call), kubernetes_api_error (carries http_status and reason from
the API server), and connection_error.
Permission denied reading the kubeconfig in Docker — the container runs as UID 10001 and bind mounts preserve host ownership. See Running with Docker.
Log content looks like one long line with \n in it — that is the escaping
working. See Reading get_pod_logs output.
MCP Inspector shows Logging ✗ — expected, not a defect. FastMCP does not
register a set_logging_level handler, so get_capabilities() omits the
capability. That MCP feature sends log records to the client; this server logs
to stderr, which is unrelated. Inspector shows Resources and Prompts as
supported for the mirror-image reason — FastMCP registers those handlers
unconditionally even though none are defined.
How this was built
DEBUG_LOG.md records every error encountered during development — root cause and resolution for each, including several found only by running against a real cluster after the entire test suite was green.
It is worth reading if you are evaluating whether to trust this server with a cluster credential, because a recurring theme runs through it: the test mocks agreed with each other rather than with the cluster. Five separate defects were found that way, each with a passing test asserting the opposite. The verification steps taken in response are summarized in SECURITY.md.
Development
The property tests (tests/property/) enumerate tools from a shared registry,
NAMESPACED_TOOLS in tests/property/strategies.py. Any new tool must be added
there in the same change — a tool missing from the registry causes P4/P6/P7 to
silently stop covering it while still reporting green.
Install the TruffleHog pre-commit hook before your first commit (pre-commit is
not a project dependency — it is a developer tool installed alongside):
Roadmap
- 16 read-only diagnostic tools
- 18 correctness properties (P1-P18), Hypothesis-driven
- RBAC manifests + scoped kubeconfig generation
- Live-cluster validation
- Docker packaging (multi-stage, non-root, no baked credentials)
- TruffleHog secret scanning (pre-commit + GitHub Actions)
- Docker Hub + MCP Registry listing prepared (docs/PUBLISHING.md)
- Migrate
get_endpointstodiscovery.k8s.io/v1EndpointSlice - Publish to Smithery
-
get_ingress_statusandget_networkpolicytools - HTTP transport for network-based deployment
Credits
License
MIT
來源:README.md,提交 730a427
工具
0版本歷史
1- v1.0.0最新Sep 16, 2026
評分
已滿足適用檢查項的 50%
該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。
- 近 30 天內可連通25 分
請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。
- 已宣告認證方式10 分
請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。
- 已列出工具15 分
請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。
- 描述必需15 分
- 圖示10 分
- 文件15 分
- 授權條款10 分

