关于 Kubernetes Troubleshoot
Kubernetes Troubleshoot 是面向 Kubernetes 运维与开发人员的只读 MCP 服务,通过结构化工具查询集群状态,帮助定位工作负载问题。它以 RBAC 和命名空间限制读取范围,不提供任何写入工具。
主要功能
- 01查询 Pod 状态、日志与事件
- 02查看节点状态及资源信息
- 03检查 Deployment、StatefulSet 与 DaemonSet
- 04查看服务、端点、PVC 和 HPA
- 05按命名空间限制读取范围
适用场景
- 01排查 Pod 未就绪或反复重启
- 02查看节点未就绪原因与污点
- 03检查 HPA 指标和扩缩容状态
MCP 相关
通过 stdio 暴露 16 个 Kubernetes 诊断工具,供 MCP 客户端调用。服务使用受限的 ServiceAccount 凭据读取集群信息,并以结构化结果返回。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
docker run -i --rm -e KUBECONFIG -e ALLOWED_NAMESPACES -e LOG_LEVEL -e API_TIMEOUT_SECONDS -e MAX_LOG_LINES docker.io/yawgyamfiprem32/k8s-troubleshoot-mcp:1.0.0KUBECONFIGPath to the kubeconfig inside the container. Must match the mount destination. There is no fallback to ~/.kube/config and no in-cluster config: a missing, unreadable or malformed file is a startup failure rather than a silent downgrade to an ambient credential.ALLOWED_NAMESPACESComma-separated namespaces the server may read, e.g. staging,production. Wildcards (* and all) are rejected at startup, and kube-system and kube-public are stripped even if listed. This is defense-in-depth, not the security boundary — the boundary is the ServiceAccount's RBAC bindings.LOG_LEVELDEBUG, INFO, WARNING or ERROR. All logging goes to stderr; stdout carries the JSON-RPC stream exclusively.API_TIMEOUT_SECONDSKubernetes API request timeout. Must be a positive integer.MAX_LOG_LINESMaximum log lines any single get_pod_logs call may return. Must be a positive integer; clamped to a hard ceiling of 1000 with a warning.本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
k8s-troubleshoot-mcp
一个只读的 MCP(Model Context Protocol)
服务,可让 AI 助手诊断 Kubernetes 集群。无需运行六条 kubectl 命令并手动关联输出,只需询问 Pod 为何陷入崩溃循环。
只读是结构性属性,而非承诺。 服务没有任何写入工具,RBAC 清单也未授予任何写入动词。请参阅安全模型。
功能介绍
该服务通过 stdio 提供 16 个诊断工具。将其连接到 Claude Desktop、 VS Code 或 Kiro,即可询问如下问题:
- “为什么
staging中的checkoutPod 尚未就绪?” - “显示
api容器最近 50 行日志” - “哪些节点未处于 Ready 状态?它们有哪些污点?”
- “
webHPA 是否正在扩缩容?其当前指标是什么?” - “最近几分钟
production中触发了哪些事件?”
助手调用这些工具,服务使用受限的 ServiceAccount 令牌查询 Kubernetes API,每个响应都会以结构化字典的形式返回—— 包括错误;错误绝不会作为异常抛入 MCP 层。
架构
┌──────────────────┐ stdio (JSON-RPC) ┌──────────────────────────┐
│ MCP Client │◄─────────────────────►│ MCP Server │
│ Claude Desktop │ │ (this project) │
│ VS Code / Kiro │ │ │
└──────────────────┘ │ config.py ─ validate │
│ server.py ─ 16 tools │
│ tools/*.py ─ read+shape│
│ response.py ─ escape + │
│ structure │
└───────────┬──────────────┘
│ HTTPS, explicit
│ KUBECONFIG only
┌───────────▼──────────────┐
│ Kubernetes API server │
│ ── RBAC boundary ── │
│ ServiceAccount: │
│ get/list/watch only │
└──────────────────────────┘
配置会在启动时验证一次。如果有任何问题——KUBECONFIG
未设置、文件无法读取或格式错误、ALLOWED_NAMESPACES 缺失或
包含通配符——进程会向 stderr 写入一行内容并以状态码 1 退出。它
绝不会在配置部分有效的状态下启动。
可用工具
标记为 ? 的参数为可选参数。
| 工具 | 描述 | 参数 |
|---|---|---|
get_pod_status | Pod 的阶段、条件、容器状态、QoS 类别和节点 | pod_name、namespace |
get_pod_logs | Pod 容器的近期日志行。内容不可信——请参阅读取 get_pod_logs 输出 | pod_name、namespace、container?、previous?、tail_lines? |
get_pod_events | Pod 的近期事件,按时间从新到旧排列,并包含 total_available | pod_name、namespace |
list_pods | 命名空间中的 Pod,包含阶段、重启次数和就绪状态 | namespace、label_selector? |
get_node_status | 节点的条件、容量、可分配资源、污点和角色 | node_name |
list_nodes | 集群节点的就绪状态、角色、运行时长和 kubelet 版本 | 无 |
get_deployment_status | 副本数、条件和滚动更新策略 | deployment_name、namespace |
list_deployments | 命名空间中的 Deployment,包含副本数和可用性 | namespace |
get_statefulset_status | 副本数、修订版本和更新策略 | statefulset_name、namespace |
get_daemonset_status | 调度数量和更新策略 | daemonset_name、namespace |
get_service | 类型、ClusterIP、端口、选择器和就绪端点数量 | service_name、namespace |
get_endpoints | 为服务提供后端支持的就绪和未就绪端点地址 | service_name、namespace |
get_pvc_status | PVC 的阶段、容量、绑定状态和扩容状态 | pvc_name、namespace |
get_hpa_status | HPA 的副本数上下限、当前指标和条件 | hpa_name、namespace |
get_namespace_events | 命名空间中的近期事件,按时间从新到旧排列,并包含 total_available | namespace、limit? |
list_namespaces | 此服务获准读取的命名空间 | 无 |
每个带命名空间的工具都会在发起任何 API 调用之前验证其 namespace 参数,因此不允许的命名空间会返回结构化错误,且不会发出网络请求。
明确不提供的功能
不提供 get_secrets、get_configmap、exec_into_pod、port_forward,也不提供任何
create/update/patch/delete 工具。除非进行新的威胁模型审查并记录结果,否则所有版本都不会包含这些工具——它们不是待办事项。每项排除的原因请参阅
SECURITY.md。
安全模型
完整详情请参阅 SECURITY.md。概要如下:
安全边界是 Kubernetes RBAC
此服务在应用代码中的所有操作都属于纵深防御。执行边界是 ServiceAccount 的 RBAC 绑定。 如果绑定授予的权限超出预期,那么应用层允许列表就是唯一的防线,不应依赖它作为安全边界。
配置按作用域拆分,以尽量缩小集群级授权:
| 清单 | 作用域 | 授予的权限 |
|---|---|---|
clusterrole.yaml + clusterrolebinding.yaml | 集群 | 仅对 nodes 和 namespaces 授予 get/list/watch 权限 |
role.yaml | 命名空间 | 对诊断资源授予 get/list/watch 权限 |
rolebinding.yaml.template | 命名空间 | 每次在一个命名空间中绑定 Role |
应用集群级的这一对配置不会让任何命名空间变得可读。只有在某个命名空间中同时存在 Role 和 RoleBinding 时,该命名空间才会变得可读。列在 ALLOWED_NAMESPACES 中但从未绑定的命名空间仍不可读——RBAC 优先。
pods/log 会在单独的规则块中授予权限,绝不会并入 pods 规则,因为 Kubernetes 子资源不会继承其父资源的权限。
纵深防御层
| 层 | 作用 | 不是什么 |
|---|---|---|
| RBAC | 仅在已绑定的命名空间中为诊断资源授予读取动词 | — 这就是安全边界 |
| 显式 kubeconfig | 从指定的精确路径读取 KUBECONFIG;不读取 ~/.kube/config,不使用集群内配置,也没有回退链 | 不是权限检查——它可防止意外使用环境中的凭据 |
| 命名空间允许列表 | 拒绝通配符,排除 kube-system/kube-public,并在每次调用前验证 | 仅供参考;此处的错误由 RBAC 兜底 |
| 输出转义 | 所有由集群写入的自由文本都会经过 serialize_log_content | 可防止内容逸出 JSON 字符串;无法阻止模型遵循可读指令行事 |
| 结构化错误 | 每次失败都会返回一个 dict;异常不会传递到 MCP 层 | — |
提示词注入可以缓解,但无法消除
Pod 日志和事件消息由集群中的工作负载写入。容器可以输出任何内容,包括伪装成指令、试图影响读取内容的模型的文本。转义可将注入文本限制在其 JSON 字符串中;但无法阻止模型将其作为数据读取并遵循其中的指令行事。请将工具输出视为任何下游使用者的不可信输入。 我们接受并记录了这一残余风险。
前置条件
- 一个 Kubernetes 集群,以及一个拥有足够权限的
kubectl上下文,可用于创建 ServiceAccount、Role、RoleBinding、ClusterRole 和 ClusterRoleBinding——配置时只需执行一次。服务本身绝不会使用你的管理员凭据。 - Kubernetes 1.24+——
scripts/generate-kubeconfig.sh通过 TokenRequest API 生成令牌,而不是使用旧式自动挂载的 Secret。 - Python 3.11+
- uv——运行
curl -LsSf https://astral.sh/uv/install.sh | sh安装(也可使用 Docker,这样主机上无需安装 Python 或 uv)
设置
git clone https://github.com/NanaGyamfiPrempeh30/k8s-troubleshoot-mcp.git
cd k8s-troubleshoot-mcp
# Install dependencies (uv creates .venv automatically)
uv sync
# Run the test suite
uv run pytest tests/ -q
配置 RBAC 并生成 kubeconfig
scripts/generate-kubeconfig.sh /secure/path/k8s-mcp-kubeconfig.yaml staging production
第一个参数指定 kubeconfig 的写入位置;其余参数为服务可以读取的命名空间。
传入的命名空间集合应与计划填入
ALLOWED_NAMESPACES 的集合一致——RBAC 是强制执行边界,而在此处绑定
但未列入允许列表(或反过来)的命名空间,会导致实际权限与配置能力不一致。
脚本会一并应用集群范围的清单,然后使用显式的 -n <namespace> 应用
role.yaml,并为每个命名空间生成一个 RoleBinding。成功后,它只会将
kubeconfig 路径输出到 stdout,不输出其他内容;所有诊断信息都写入 stderr。
此外,配置完成后,脚本还会断言 kubectl auth can-i get secrets 返回
no;如果不是,则中止。
不要运行
kubectl apply -f kubernetes/。 该命令不会报错——它会报告 成功,同时在当前命名空间中创建role.yaml,并完全跳过rolebinding.yaml.template,因为kubectl apply -f <dir>只会读取.yaml/.yml/.json文件。最终得到的服务看似已完成配置,实际上却无法读取任何内容。 已使用--dry-run=server在 v1.35 API server 上验证:应用了 5 个资源,而非 6 个。
生成的 kubeconfig 会使用 umask 077 写入,并设置 chmod 600。请勿将其放入
代码库 — 如果输出路径位于代码库内且未被 .gitignore 覆盖,脚本会发出警告。
运行
KUBECONFIG=/secure/path/k8s-mcp-kubeconfig.yaml \
ALLOWED_NAMESPACES=staging,production \
uv run k8s-troubleshoot-mcp
该服务通过 stdin/stdout 使用 JSON-RPC,因此看起来会一直挂起——这是正常的。 它正在等待客户端连接。
连接到 Claude Desktop
将以下内容添加到 claude_desktop_config.json
(Windows 上为 %APPDATA%\Claude\claude_desktop_config.json,
macOS 上为 ~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"k8s-troubleshoot": {
"command": "uv",
"args": ["run", "k8s-troubleshoot-mcp"],
"cwd": "/path/to/k8s-troubleshoot-mcp",
"env": {
"KUBECONFIG": "/secure/path/k8s-mcp-kubeconfig.yaml",
"ALLOWED_NAMESPACES": "staging,production"
}
}
}
}
完全重启 Claude Desktop(从系统托盘退出后重新打开),然后检查
Settings → Developer 中的 k8s-troubleshoot 是否显示 running。
在 Windows 上,如果服务显示为已断开连接,请使用批处理文件包装器 — Claude Desktop 直接调用解释器时存在工作目录问题:
@echo off
cd /d C:\Users\YourUsername\k8s-troubleshoot-mcp
uv run k8s-troubleshoot-mcp
{
"mcpServers": {
"k8s-troubleshoot": {
"command": "cmd.exe",
"args": ["/c", "C:\\Users\\YourUsername\\k8s-troubleshoot-mcp\\run_mcp.bat"],
"env": {
"KUBECONFIG": "C:\\secure\\path\\k8s-mcp-kubeconfig.yaml",
"ALLOWED_NAMESPACES": "staging,production"
}
}
}
}
使用 Docker 运行
docker build -t k8s-troubleshoot-mcp .
docker run -i --rm \
-v /secure/path/k8s-mcp-kubeconfig.yaml:/kubeconfig:ro \
-e KUBECONFIG=/kubeconfig \
-e ALLOWED_NAMESPACES=staging,production \
k8s-troubleshoot-mcp
必须使用 -i — JSON-RPC 通过 stdin/stdout 传输。:ro 不是装饰:
此服务不会执行任何写入操作,因此可写挂载只会授予它用不上的权限。
该镜像以 非 root 用户 UID 10001 运行,且不包含任何凭据。
以下两点可能会给你带来麻烦:
- UID 10001 必须能够读取 kubeconfig。 以
600权限创建且归主机用户所有的文件无法满足这一点,而且绑定挂载会保留主机上的所有权。 可以授予组/其他用户读取权限,或使用--user "$(id -u)"运行。 - 集群位于主机的环回地址上(minikube、kind)时,在 Linux 上需要使用
--network host。 在 Docker Desktop 上这还不够 — 请参阅 使用 minikube 进行本地测试。
要通过 Claude Desktop 使用容器,请将 "command": "docker" 设为命令,并将完整的
run -i --rm … 调用放入 "args"。
环境变量
| 变量 | 必需 | 默认值 | 描述 |
|---|---|---|---|
KUBECONFIG | 是 | — | kubeconfig 的确切路径。不回退到 ~/.kube/config,也不使用集群内配置;文件缺失、不可读或格式错误都会导致启动失败 |
ALLOWED_NAMESPACES | 是 | — | 服务可读取的逗号分隔命名空间。拒绝通配符(*、all);即使列出,也会在警告后剔除 kube-system 和 kube-public |
LOG_LEVEL | 否 | INFO | DEBUG、INFO、WARNING、ERROR。无法识别的值会回退到 INFO |
API_TIMEOUT_SECONDS | 否 | 30 | 必须为正整数 |
MAX_LOG_LINES | 否 | 200 | 必须为正整数。若超过硬性上限 1000,则会截断到该上限并发出警告 |
所有日志均写入 stderr。stdout 专用于 JSON-RPC 流 —
哪怕一个多余的 print() 都会破坏协议,因此 src/ 中没有此类调用。
阅读 get_pod_logs 输出
content 字段是经过转义的日志文本,而非日志文本。仅对 MCP 响应执行一次 json.loads,不足以得到可打印的输出。
serialize_log_content 会在响应封装进行 JSON 编码之前转义日志,因此转义会在传输过程中保留下来。解码响应只会撤销传输层的转义:
resp = json.loads(raw_mcp_response)
content = resp["data"]["content"]
print(content)
# line one\nline two\tsaid \"hi\" \u003cb\u003e\n <- one physical line
content.count("\n") # 0 -- there are no real newlines in it
要还原原始文本,请对该字段再解码一次:
text = json.loads('"' + content + '"')
print(text)
# line one
# line two said "hi" <b>
这是有意为之,并非错误。转义是结构性提示词注入缓解措施(REQ-020、REQ-027)——如果一次解码就还原了真正的控制字符和原始 <,那么内容到达模型之前,这项缓解措施就失效了。只有在输出不会被解释的地方才进行第二次解码,例如终端或日志文件。
在 MCP Inspector 的原始视图中,你会看到 \\n(两个反斜杠)。这是正确的:传输层转义了已转义的 \n 中的反斜杠。
不要根据 content 计算行数。 按换行符拆分后,无论日志有多长,都会返回 1。请使用 lines_returned,这正是它存在的原因。
使用 minikube 进行本地测试
minikube 重启后 kubeconfig 过期
minikube 通过代理地址暴露 API 服务器,该地址的端口按会话分配。该地址可通过 kubectl cluster-info 查看,在 minikube stop / minikube start 周期之间以及 WSL2 重启后可能会发生变化。
在之前的会话中由 scripts/generate-kubeconfig.sh 生成的 kubeconfig 会固定旧的 host:port。服务会正常启动,随后每次工具调用都会失败,并报告 connection_error,指出的端点上没有任何服务在监听:
Kubernetes control plane is running at https://127.0.0.1:54489
^^^^^ per-session
恢复方法:
-
确认当前端点:
kubectl cluster-info -
针对该端点生成新的 kubeconfig:
scripts/generate-kubeconfig.sh /path/to/kubeconfig.yaml <namespace> [namespace...] -
彻底重启 MCP 服务进程。 服务会在启动时读取一次
KUBECONFIG(REQ-002),之后不会重新读取,因此在运行中的服务下方覆盖文件不会产生任何影响。此外,服务不会回退到~/.kube/config(REQ-003)——同一台机器上可正常工作的kubectl也无法挽救过期的 kubeconfig。
无论原因是端口过期、令牌被撤销,还是集群确实无法访问,出现的故障都完全相同。kubectl cluster-info 可以区分这些情况:如果它成功,而服务报告 connection_error,则说明 kubeconfig 已过期。
从 Docker Desktop 上的容器访问 minikube
--network host 加入的是Docker VM 的网络命名空间,而不是 WSL 发行版的网络命名空间,因此已发布的 minikube 端口不在该回环地址上,容器会收到 ConnectionRefused。虽然可以访问 host.docker.internal,但将 kubeconfig 中的 server 改为该地址会导致 TLS 验证失败——host.docker.internal 不在 minikube API 服务器证书的 SAN 中。
将 URL 指向 host.docker.internal,并且在集群条目中添加 tls-server-name。这个 kubeconfig 字段正是为此用途而存在的:
clusters:
- name: minikube
cluster:
server: https://host.docker.internal:54489
tls-server-name: localhost
certificate-authority-data: ...
项目结构
k8s-troubleshoot-mcp/
├── .github/workflows/
│ ├── build-and-push.yml # Docker build + push to Docker Hub
│ └── secret-scan.yml # TruffleHog secret scanning
├── kubernetes/
│ ├── namespace.yaml
│ ├── serviceaccount.yaml # automountServiceAccountToken: false
│ ├── clusterrole.yaml # nodes + namespaces, read verbs only
│ ├── clusterrolebinding.yaml
│ ├── role.yaml # pods/log in its own rule block
│ └── rolebinding.yaml.template
├── scripts/
│ ├── generate-kubeconfig.sh # provisions RBAC, mints a scoped token
│ └── check-namespaces.py # CI guard: GitHub vs Docker Hub handles
├── docs/
│ ├── PUBLISHING.md # Docker Hub + MCP Registry release runbook
│ └── dockerhub-overview.md # Docker Hub repository description
├── server.json # MCP Registry listing metadata
├── src/k8s_troubleshoot_mcp/
│ ├── __main__.py # startup sequence, fail-closed
│ ├── config.py # env validation (REQ-001..010, 069..071)
│ ├── k8s_client.py # explicit-path client factory
│ ├── server.py # FastMCP instance + 16 tool registrations
│ ├── response.py # serialize_log_content + structured errors
│ ├── pagination.py # total_available / continue-token detection
│ └── tools/ # pods, nodes, workloads, services,
│ # storage, autoscaling, events, namespaces
├── tests/
│ ├── unit/
│ └── property/ # P1-P18, Hypothesis-driven
├── requirements.md # EARS-format requirements
├── design.md # architecture + 18 correctness properties
├── SECURITY.md # threat model, RBAC boundary, exclusions
├── DEBUG_LOG.md # every error found, root cause, resolution
├── Dockerfile # multi-stage, non-root UID 10001
├── uv.lock # 67 pinned dependencies
└── README.md # this file
故障排除
KUBECONFIG environment variable is not set — 服务器拒绝在未指定显式路径的情况下启动,也不会回退到 ~/.kube/config。这是有意为之(REQ-003)。如果尚未运行,请执行 scripts/generate-kubeconfig.sh。
is not a valid kubeconfig — 文件存在且可读,但格式错误。消息会指出路径、行号和列号,绝不会包含文件内容;kubeconfig 中包含 bearer token。
所有工具都返回 connection_error,但 kubectl 可以正常工作 — kubeconfig 已过期。请参阅使用 minikube 进行本地测试。
namespace_not_allowed — 命名空间不在 ALLOWED_NAMESPACES 中,或者它是 kube-system/kube-public,即使列出也会在启动时被剔除。
命名空间已获准,但返回 kubernetes_api_error,且 http_status: 403 — 允许列表与 RBAC 绑定不一致。ALLOWED_NAMESPACES 不会授予任何权限;只有在命名空间中存在 Role 和 RoleBinding 后,才能读取该命名空间。请使用完整的命名空间集重新运行 generate-kubeconfig.sh。
错误会以以下三种代码之一返回:namespace_not_allowed(在任何 API 调用前被拒绝)、kubernetes_api_error(包含来自 API 服务器的 http_status 和 reason)以及 connection_error。
在 Docker 中读取 kubeconfig 时权限被拒绝 — 容器以 UID 10001 运行,而绑定挂载会保留主机上的所有权。请参阅使用 Docker 运行。
日志内容看起来是一长行,里面带有 \n — 这说明转义正常工作。请参阅读取 get_pod_logs 输出。
MCP Inspector 显示 Logging ✗ — 这是预期行为,并非缺陷。FastMCP 不会注册 set_logging_level 处理器,因此 get_capabilities() 会省略此能力。该 MCP 功能会将日志记录发送给客户端;此服务器将日志写入 stderr,两者无关。Inspector 显示 Resources 和 Prompts 受支持,也是出于相反的原因——即使没有定义这些处理器,FastMCP 也会无条件注册它们。
构建过程
DEBUG_LOG.md 记录了开发期间遇到的每个错误,包括每个错误的根本原因和解决方案,其中有几个错误是在整个测试套件全部通过后,通过在真实集群上运行才发现的。
如果你正在评估是否要将集群凭据交给此服务器,值得阅读这份记录,因为其中贯穿着一个反复出现的问题:测试 Mock 彼此一致,却与集群不一致。 由此发现了五个独立缺陷,每个缺陷都有一个通过的测试断言了相反的结果。对此采取的验证步骤总结在 SECURITY.md 中。
开发
uv sync --extra dev
uv run pytest tests/ -q
属性测试(tests/property/)会从共享注册表 tests/property/strategies.py 中的 NAMESPACED_TOOLS 枚举工具。任何新工具都必须在同一次变更中添加到该注册表——如果工具未加入注册表,P4/P6/P7 就会悄无声息地停止覆盖它,但仍会报告全部通过。
首次提交前,请安装 TruffleHog pre-commit 钩子(pre-commit 不是项目依赖项,而是随开发工具一同安装的工具):
pip install pre-commit
pre-commit install
路线图
- 16 个只读诊断工具
- 18 个正确性属性(P1-P18),由 Hypothesis 驱动
- RBAC 清单 + 限定范围的 kubeconfig 生成
- 实时集群验证
- Docker 打包(多阶段、非 root、不内置凭据)
- TruffleHog 密钥扫描(pre-commit + GitHub Actions)
- 已准备好 Docker Hub + MCP Registry 列表(docs/PUBLISHING.md)
- 将
get_endpoints迁移到discovery.k8s.io/v1EndpointSlice - 发布到 Smithery
-
get_ingress_status和get_networkpolicy工具 - 用于基于网络部署的 HTTP 传输
致谢
许可证
MIT
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。