内容
# kops
通过 MCP 向 Claude Code 暴露的只读 `kubectl` 助手。六种工具,全部严格只读 — 动词(`get`、`describe`、`logs`)是硬编码的;用户输入仅填充参数值,永远不会是动词本身。
| 工具 | 功能 |
|------|------|
| `k8s_get` | 列出/获取资源,返回每种资源类型的关键字段摘要 |
| `k8s_describe` | 单个资源的 `kubectl describe` 文本输出 |
| `k8s_logs` | 带有 `tail` / `since` / `previous` 标志的 Pod 日志 |
| `k8s_events` | 按命名空间/类型/名称过滤的近期事件 |
| `k8s_triage` | ⭐ 一键集群健康扫描 — 从这里开始进行诊断 |
| `k8s_inventory` | ⭐ 一键全面快照 — 从这里开始进行文档/审计 |
## 为什么
`kubectl` 通过 Bash 提供给 Claude 的是需要每次都重新解析的文本表格。将它作为 MCP 包装返回 **结构化 JSON**,Claude 可以直接推理 — 更少的 tokens,更少的解析错误,以及内置的安全边界(只读动词,名称验证,输出大小上限)。
两种聚合工具(`k8s_triage`、`k8s_inventory`)将常见的多步骤查询压缩为单次请求:
- `k8s_triage` — "有什么问题?" → 4 个并发的 kubectl 调用,返回有问题的 Pod + 警告事件 + 不健康的节点 + 停滞的部署
- `k8s_inventory` — "给我看所有东西" → 14+ 个并发的 kubectl 调用,返回按命名空间分组的集群范围快照。替换约 50 个单独的 `k8s_get` 调用(对集群文档来说速度快约 6 倍)。
## 安装
```bash
cd /Users/kaka/claude/kops
uv sync
```
## 烟雾测试(无需集群)
MCP 需要在任何业务请求之前进行握手(`initialize` → `notifications/initialized`),因此直接通过 stdin 发送 `tools/list` 请求会被拒绝,并提示 `Received request before initialization was complete`。按顺序发送以下三个消息:
```bash
{
printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"smoke","version":"0"}}}'
printf '%s\n' '{"jsonrpc":"2.0","method":"notifications/initialized"}'
printf '%s\n' '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
} | uv run kops
```
预期:`initialize` 响应,然后是 `tools/list` 响应,列出 **6 种工具** 及其输入模式。(通知没有 `id` 且不产生回复。)
## 使用 MCP Inspector 进行可视化调试
对于交互式调试,跳过原始的 stdio 操作,使用官方工具 — 它们为您处理握手:
```bash
# 选项 A:MCP Inspector(浏览器 UI)
npx @modelcontextprotocol/inspector uv --directory /Users/kaka/claude/kops run kops
# 选项 B:mcp dev(已在 deps 中与 mcp[cli] 捆绑)
uv run mcp dev src/kops/server.py
```
打开每个命令打印的 URL,点击工具,操作其参数。
## 在 Claude Code 中注册
```bash
claude mcp add -s user kops -- uv --directory /Users/kaka/claude/kops run kops
```
或在 `~/.claude.json` 的 `mcpServers` 下手动:
```json
{
"mcpServers": {
"kops": {
"command": "uv",
"args": ["--directory", "/Users/kaka/claude/kops", "run", "kops"]
}
}
}
```
重新加载 Claude Code(或打开新会话)。工具表现为 `mcp__kops__k8s_get`、`mcp__kops__k8s_triage`、`mcp__kops__k8s_inventory` 等。
### 多集群(kubeconfig 隔离)
要与外部集群通信而不污染 `~/.kube/config`,请使用自己的 `KUBECONFIG` 注册单独的服务器条目:
```bash
claude mcp add -s user -e KUBECONFIG=/path/to/qa-cluster.yaml \
kops-qa -- uv --directory /Users/kaka/claude/kops run kops
```
然后工具表现为 `mcp__kops_qa__k8s_triage` 等,完全隔离。
## 端到端烟雾测试(带有 kind 集群)
```bash
kind create cluster --name kops-test
kubectl run broken --image=nonexistent:fake --restart=Never
sleep 30
```
然后在 Claude Code 中询问: "这个集群有问题,哪里出了问题?"
预期:Claude 首先调用 `mcp__kops__k8s_triage`,看到 `broken` Pod 处于 `ImagePullBackOff` 状态,然后调用 `k8s_describe` 获取根本原因。
对于文档示例,询问: "给我这个集群的完整报告"。
预期:Claude 调用 `mcp__kops__k8s_inventory` 一次,并组装一个结构化的 Markdown 报告,涵盖节点、命名空间、工作负载、服务、暴露面和配置计数。
## `k8s_get` 按类型返回的内容
`_summarize_resource` 提取最有助于诊断和文档的字段。避免转储完整的 `spec` 以保持 token 使用合理。
| 类型 | 摘要字段 |
|------|------|
| Pod | `phase`、`ready`、`restarts`、`node`、`podIP`、`images`、`containerCount`、`resources`(当声明时)、`reason`(当卡住时) |
| Service | `type`、`clusterIP`、`externalIPs`、`loadBalancer`、`ports[]`(包括 `nodePort`) |
| Deployment | `desired`、`available`、`updated`、`ready`、`images`、`containerCount`、`resources` |
| StatefulSet / DaemonSet / ReplicaSet | `desired`、`ready`、`images`、`containerCount`、`resources` |
| Node | `ready`、`kubeletVersion`、`internalIP`、`pressures`(仅当为 True 时) |
| Ingress | `hosts[]` |
| Namespace / 通用 | `name`、`namespace`、`kind`、`age`、`labels`(前 5 个) |
其中 `resources` 是 **所有主容器** 的 `requests` 和 `limits` 之和(排除 init 容器 — 它们不会与稳定状态并发运行,因此不会增加调度占用)。CPU 归一化为毫核心,内存归一化为二进制单位(`Ki/Mi/Gi`)。Init 容器仍在 `images[]` 中出现,带有 `init: True` 标志。
## `k8s_inventory` 返回的内容
```python
{
"summary": {
"scope": "cluster-wide" | "namespace=<name>",
"namespaces": int, "nodes": int, "pods_total": int,
"by_kind_counts": {"deployments": N, "services": N, ...},
"istio_present": bool,
"include_istio": bool,
},
"nodes": [<summarized node>, ...], # 仅集群范围
"namespaces": [
{
"name": "...", "age": "...", "labels": {...},
"pods": int, # 仅计数(不包括完整 Pod 列表)
"deployments": [<summarized>, ...],
"statefulsets": [...], "daemonsets": [...],
"services": [...], "ingresses": [...], "hpa": [...],
"pvcs": [...], "configmaps": [...], "secrets": [...],
"jobs": [...], "cronjobs": [...],
"istio_gateways": [...], "istio_virtualservices": [...], "istio_destinationrules": [...],
},
...
]
}
```
ConfigMap 数据和 Secret 值 **永远不会** 返回 — 只有元数据(名称、键列表、年龄)。这是硬安全边界;如果您需要实际配置内容,请通过 Bash + `kubectl` 在明确许可下进行。
Pod 不作为列表包含(可能非常庞大)。使用 `k8s_triage` 进行 Pod 健康检查,使用 `k8s_get pod` 获取特定 Pod。
## 安全
- **变异防御**:动词在每个工具函数内硬编码。用户输入仅填充参数值,永远不是动词。没有任何路径可以通过输入到达 `delete` / `apply` / `patch` / `scale` / `exec`。
- **注入防御**:`subprocess.run([...], shell=False)` 在所有地方。名称/命名空间/容器针对 `^[a-zA-Z0-9._-]{1,253}$` 进行验证。选择器针对 Kubernetes 标签选择器字符集进行验证。
- **资源限制**:`kubectl` 调用时带有 30s 超时。日志 `tail` 限制为 1000 行。输出大小上限(30KB describe,50KB logs)。
- **上下文隔离**:默认使用 `kubectl config current-context`。可选的 `context` 参数可以覆盖,但不能注入 `KUBECONFIG` 路径。对于跨集群的完全隔离,请使用自己的 `KUBECONFIG` 环境变量注册单独的 MCP 服务器条目。
## 扩展
通过在 `src/kops/server.py` 中编写新的 `@mcp.tool()` 函数来添加另一个工具:
- 硬编码动词。
- 使用现有的帮助函数验证输入(`_validate_kind`、`_validate_name`、`_validate_selector`、`_validate_since`)。
- 通过 `_run_kubectl([...])` 调用 kubectl。
- 如果您的工具返回资源,请重用 `_summarize_resource` 进行输出塑形。
- 函数签名上的类型提示自动成为 JSON-RPC 输入模式(FastMCP 处理此操作)。
对于聚合工具(`triage` / `inventory` 样式),遵循模式:构建一个 `kubectl get -A -o json` 参数向量列表,通过 `ThreadPoolExecutor(max_workers=8)` 进行扇出,本地处理和分组。CRD 检测是宽容的 — 将每个类型的 kubectl 调用包装在 `try/except RuntimeError` 中,并静默跳过缺失的类型。