kubectl-mcp-server 1.14.0__py3-none-any.whl → 1.16.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kubectl_mcp_server-1.16.0.dist-info/METADATA +1047 -0
- kubectl_mcp_server-1.16.0.dist-info/RECORD +61 -0
- kubectl_mcp_tool/__init__.py +1 -1
- kubectl_mcp_tool/crd_detector.py +247 -0
- kubectl_mcp_tool/k8s_config.py +304 -63
- kubectl_mcp_tool/mcp_server.py +27 -0
- kubectl_mcp_tool/tools/__init__.py +20 -0
- kubectl_mcp_tool/tools/backup.py +881 -0
- kubectl_mcp_tool/tools/capi.py +727 -0
- kubectl_mcp_tool/tools/certs.py +709 -0
- kubectl_mcp_tool/tools/cilium.py +582 -0
- kubectl_mcp_tool/tools/cluster.py +395 -121
- kubectl_mcp_tool/tools/core.py +157 -60
- kubectl_mcp_tool/tools/cost.py +97 -41
- kubectl_mcp_tool/tools/deployments.py +173 -56
- kubectl_mcp_tool/tools/diagnostics.py +40 -13
- kubectl_mcp_tool/tools/gitops.py +552 -0
- kubectl_mcp_tool/tools/helm.py +133 -46
- kubectl_mcp_tool/tools/keda.py +464 -0
- kubectl_mcp_tool/tools/kiali.py +652 -0
- kubectl_mcp_tool/tools/kubevirt.py +803 -0
- kubectl_mcp_tool/tools/networking.py +106 -32
- kubectl_mcp_tool/tools/operations.py +176 -50
- kubectl_mcp_tool/tools/pods.py +162 -50
- kubectl_mcp_tool/tools/policy.py +554 -0
- kubectl_mcp_tool/tools/rollouts.py +790 -0
- kubectl_mcp_tool/tools/security.py +89 -36
- kubectl_mcp_tool/tools/storage.py +35 -16
- tests/test_browser.py +2 -2
- tests/test_ecosystem.py +331 -0
- tests/test_tools.py +73 -10
- kubectl_mcp_server-1.14.0.dist-info/METADATA +0 -780
- kubectl_mcp_server-1.14.0.dist-info/RECORD +0 -49
- {kubectl_mcp_server-1.14.0.dist-info → kubectl_mcp_server-1.16.0.dist-info}/WHEEL +0 -0
- {kubectl_mcp_server-1.14.0.dist-info → kubectl_mcp_server-1.16.0.dist-info}/entry_points.txt +0 -0
- {kubectl_mcp_server-1.14.0.dist-info → kubectl_mcp_server-1.16.0.dist-info}/licenses/LICENSE +0 -0
- {kubectl_mcp_server-1.14.0.dist-info → kubectl_mcp_server-1.16.0.dist-info}/top_level.txt +0 -0
kubectl_mcp_tool/tools/pods.py
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pod management tools for kubectl-mcp-server.
|
|
3
|
+
|
|
4
|
+
All tools support multi-cluster operations via the optional 'context' parameter.
|
|
5
|
+
"""
|
|
6
|
+
|
|
1
7
|
import json
|
|
2
8
|
import logging
|
|
3
9
|
import shlex
|
|
@@ -6,9 +12,18 @@ from typing import Any, Callable, Dict, List, Optional
|
|
|
6
12
|
|
|
7
13
|
from mcp.types import ToolAnnotations
|
|
8
14
|
|
|
15
|
+
from kubectl_mcp_tool.k8s_config import get_k8s_client
|
|
16
|
+
|
|
9
17
|
logger = logging.getLogger("mcp-server")
|
|
10
18
|
|
|
11
19
|
|
|
20
|
+
def _get_kubectl_context_args(context: str = "") -> List[str]:
|
|
21
|
+
"""Get kubectl context arguments."""
|
|
22
|
+
if context:
|
|
23
|
+
return ["--context", context]
|
|
24
|
+
return []
|
|
25
|
+
|
|
26
|
+
|
|
12
27
|
def register_pod_tools(
|
|
13
28
|
server,
|
|
14
29
|
non_destructive: bool
|
|
@@ -26,12 +41,18 @@ def register_pod_tools(
|
|
|
26
41
|
readOnlyHint=True,
|
|
27
42
|
),
|
|
28
43
|
)
|
|
29
|
-
def get_pods(
|
|
30
|
-
|
|
44
|
+
def get_pods(
|
|
45
|
+
namespace: Optional[str] = None,
|
|
46
|
+
context: str = ""
|
|
47
|
+
) -> Dict[str, Any]:
|
|
48
|
+
"""Get all pods in the specified namespace.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
namespace: Namespace to list pods from (all namespaces if not specified)
|
|
52
|
+
context: Kubernetes context to use (uses current context if not specified)
|
|
53
|
+
"""
|
|
31
54
|
try:
|
|
32
|
-
|
|
33
|
-
config.load_kube_config()
|
|
34
|
-
v1 = client.CoreV1Api()
|
|
55
|
+
v1 = get_k8s_client(context)
|
|
35
56
|
|
|
36
57
|
if namespace:
|
|
37
58
|
pods = v1.list_namespaced_pod(namespace)
|
|
@@ -40,6 +61,7 @@ def register_pod_tools(
|
|
|
40
61
|
|
|
41
62
|
return {
|
|
42
63
|
"success": True,
|
|
64
|
+
"context": context or "current",
|
|
43
65
|
"pods": [
|
|
44
66
|
{
|
|
45
67
|
"name": pod.metadata.name,
|
|
@@ -64,13 +86,20 @@ def register_pod_tools(
|
|
|
64
86
|
pod_name: str,
|
|
65
87
|
namespace: Optional[str] = "default",
|
|
66
88
|
container: Optional[str] = None,
|
|
67
|
-
tail: Optional[int] = None
|
|
89
|
+
tail: Optional[int] = None,
|
|
90
|
+
context: str = ""
|
|
68
91
|
) -> Dict[str, Any]:
|
|
69
|
-
"""Get logs from a pod.
|
|
92
|
+
"""Get logs from a pod.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
pod_name: Name of the pod
|
|
96
|
+
namespace: Namespace of the pod
|
|
97
|
+
container: Container name (for multi-container pods)
|
|
98
|
+
tail: Number of lines to show from end of logs
|
|
99
|
+
context: Kubernetes context to use (uses current context if not specified)
|
|
100
|
+
"""
|
|
70
101
|
try:
|
|
71
|
-
|
|
72
|
-
config.load_kube_config()
|
|
73
|
-
v1 = client.CoreV1Api()
|
|
102
|
+
v1 = get_k8s_client(context)
|
|
74
103
|
|
|
75
104
|
logs = v1.read_namespaced_pod_log(
|
|
76
105
|
name=pod_name,
|
|
@@ -81,6 +110,7 @@ def register_pod_tools(
|
|
|
81
110
|
|
|
82
111
|
return {
|
|
83
112
|
"success": True,
|
|
113
|
+
"context": context or "current",
|
|
84
114
|
"logs": logs
|
|
85
115
|
}
|
|
86
116
|
except Exception as e:
|
|
@@ -93,16 +123,25 @@ def register_pod_tools(
|
|
|
93
123
|
readOnlyHint=True,
|
|
94
124
|
),
|
|
95
125
|
)
|
|
96
|
-
def get_pod_events(
|
|
97
|
-
|
|
126
|
+
def get_pod_events(
|
|
127
|
+
pod_name: str,
|
|
128
|
+
namespace: str = "default",
|
|
129
|
+
context: str = ""
|
|
130
|
+
) -> Dict[str, Any]:
|
|
131
|
+
"""Get events for a specific pod.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
pod_name: Name of the pod
|
|
135
|
+
namespace: Namespace of the pod
|
|
136
|
+
context: Kubernetes context to use (uses current context if not specified)
|
|
137
|
+
"""
|
|
98
138
|
try:
|
|
99
|
-
|
|
100
|
-
config.load_kube_config()
|
|
101
|
-
v1 = client.CoreV1Api()
|
|
139
|
+
v1 = get_k8s_client(context)
|
|
102
140
|
field_selector = f"involvedObject.name={pod_name}"
|
|
103
141
|
events = v1.list_namespaced_event(namespace, field_selector=field_selector)
|
|
104
142
|
return {
|
|
105
143
|
"success": True,
|
|
144
|
+
"context": context or "current",
|
|
106
145
|
"events": [
|
|
107
146
|
{
|
|
108
147
|
"name": event.metadata.name,
|
|
@@ -123,16 +162,25 @@ def register_pod_tools(
|
|
|
123
162
|
readOnlyHint=True,
|
|
124
163
|
),
|
|
125
164
|
)
|
|
126
|
-
def check_pod_health(
|
|
127
|
-
|
|
165
|
+
def check_pod_health(
|
|
166
|
+
pod_name: str,
|
|
167
|
+
namespace: str = "default",
|
|
168
|
+
context: str = ""
|
|
169
|
+
) -> Dict[str, Any]:
|
|
170
|
+
"""Check the health status of a pod.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
pod_name: Name of the pod
|
|
174
|
+
namespace: Namespace of the pod
|
|
175
|
+
context: Kubernetes context to use (uses current context if not specified)
|
|
176
|
+
"""
|
|
128
177
|
try:
|
|
129
|
-
|
|
130
|
-
config.load_kube_config()
|
|
131
|
-
v1 = client.CoreV1Api()
|
|
178
|
+
v1 = get_k8s_client(context)
|
|
132
179
|
pod = v1.read_namespaced_pod(pod_name, namespace)
|
|
133
180
|
status = pod.status
|
|
134
181
|
return {
|
|
135
182
|
"success": True,
|
|
183
|
+
"context": context or "current",
|
|
136
184
|
"phase": status.phase,
|
|
137
185
|
"conditions": [c.type for c in status.conditions] if status.conditions else []
|
|
138
186
|
}
|
|
@@ -150,11 +198,22 @@ def register_pod_tools(
|
|
|
150
198
|
pod_name: str,
|
|
151
199
|
command: str,
|
|
152
200
|
namespace: Optional[str] = "default",
|
|
153
|
-
container: Optional[str] = None
|
|
201
|
+
container: Optional[str] = None,
|
|
202
|
+
context: str = ""
|
|
154
203
|
) -> Dict[str, Any]:
|
|
155
|
-
"""Execute a command inside a pod.
|
|
204
|
+
"""Execute a command inside a pod.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
pod_name: Name of the pod
|
|
208
|
+
command: Command to execute
|
|
209
|
+
namespace: Namespace of the pod
|
|
210
|
+
container: Container name (for multi-container pods)
|
|
211
|
+
context: Kubernetes context to use (uses current context if not specified)
|
|
212
|
+
"""
|
|
156
213
|
try:
|
|
157
|
-
cmd = ["kubectl"
|
|
214
|
+
cmd = ["kubectl"] + _get_kubectl_context_args(context) + [
|
|
215
|
+
"exec", pod_name, "-n", namespace
|
|
216
|
+
]
|
|
158
217
|
if container:
|
|
159
218
|
cmd.extend(["-c", container])
|
|
160
219
|
cmd.append("--")
|
|
@@ -164,6 +223,7 @@ def register_pod_tools(
|
|
|
164
223
|
|
|
165
224
|
return {
|
|
166
225
|
"success": result.returncode == 0,
|
|
226
|
+
"context": context or "current",
|
|
167
227
|
"stdout": result.stdout,
|
|
168
228
|
"stderr": result.stderr,
|
|
169
229
|
"exit_code": result.returncode
|
|
@@ -180,21 +240,29 @@ def register_pod_tools(
|
|
|
180
240
|
)
|
|
181
241
|
def cleanup_pods(
|
|
182
242
|
namespace: Optional[str] = None,
|
|
183
|
-
states: Optional[List[str]] = None
|
|
243
|
+
states: Optional[List[str]] = None,
|
|
244
|
+
context: str = ""
|
|
184
245
|
) -> Dict[str, Any]:
|
|
185
|
-
"""Clean up pods in problematic states (Evicted, Error, Completed, etc.).
|
|
246
|
+
"""Clean up pods in problematic states (Evicted, Error, Completed, etc.).
|
|
247
|
+
|
|
248
|
+
Args:
|
|
249
|
+
namespace: Namespace to clean up (all namespaces if not specified)
|
|
250
|
+
states: List of states to clean up (default: Evicted, Error, Completed, ContainerStatusUnknown)
|
|
251
|
+
context: Kubernetes context to use (uses current context if not specified)
|
|
252
|
+
"""
|
|
186
253
|
if non_destructive:
|
|
187
254
|
return {"success": False, "error": "Blocked: non-destructive mode"}
|
|
188
255
|
try:
|
|
189
256
|
if states is None:
|
|
190
257
|
states = ["Evicted", "Error", "Completed", "ContainerStatusUnknown"]
|
|
191
258
|
|
|
259
|
+
ctx_args = _get_kubectl_context_args(context)
|
|
192
260
|
ns_flag = ["-n", namespace] if namespace else ["--all-namespaces"]
|
|
193
261
|
|
|
194
262
|
deleted_pods = []
|
|
195
263
|
for state in states:
|
|
196
264
|
if state == "Evicted":
|
|
197
|
-
cmd = ["kubectl"
|
|
265
|
+
cmd = ["kubectl"] + ctx_args + ["get", "pods"] + ns_flag + ["-o", "json"]
|
|
198
266
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
|
|
199
267
|
if result.returncode == 0:
|
|
200
268
|
try:
|
|
@@ -203,7 +271,7 @@ def register_pod_tools(
|
|
|
203
271
|
if pod.get("status", {}).get("reason") == "Evicted":
|
|
204
272
|
pod_name = pod["metadata"]["name"]
|
|
205
273
|
pod_ns = pod["metadata"]["namespace"]
|
|
206
|
-
del_cmd = ["kubectl"
|
|
274
|
+
del_cmd = ["kubectl"] + ctx_args + ["delete", "pod", pod_name, "-n", pod_ns]
|
|
207
275
|
subprocess.run(del_cmd, capture_output=True, timeout=10)
|
|
208
276
|
deleted_pods.append(f"{pod_ns}/{pod_name}")
|
|
209
277
|
except json.JSONDecodeError:
|
|
@@ -211,6 +279,7 @@ def register_pod_tools(
|
|
|
211
279
|
|
|
212
280
|
return {
|
|
213
281
|
"success": True,
|
|
282
|
+
"context": context or "current",
|
|
214
283
|
"deleted_count": len(deleted_pods),
|
|
215
284
|
"deleted_pods": deleted_pods[:20]
|
|
216
285
|
}
|
|
@@ -224,12 +293,20 @@ def register_pod_tools(
|
|
|
224
293
|
readOnlyHint=True,
|
|
225
294
|
),
|
|
226
295
|
)
|
|
227
|
-
def get_pod_conditions(
|
|
228
|
-
|
|
296
|
+
def get_pod_conditions(
|
|
297
|
+
pod_name: str,
|
|
298
|
+
namespace: str = "default",
|
|
299
|
+
context: str = ""
|
|
300
|
+
) -> Dict[str, Any]:
|
|
301
|
+
"""Get detailed pod conditions breakdown.
|
|
302
|
+
|
|
303
|
+
Args:
|
|
304
|
+
pod_name: Name of the pod
|
|
305
|
+
namespace: Namespace of the pod
|
|
306
|
+
context: Kubernetes context to use (uses current context if not specified)
|
|
307
|
+
"""
|
|
229
308
|
try:
|
|
230
|
-
|
|
231
|
-
config.load_kube_config()
|
|
232
|
-
v1 = client.CoreV1Api()
|
|
309
|
+
v1 = get_k8s_client(context)
|
|
233
310
|
|
|
234
311
|
pod = v1.read_namespaced_pod(pod_name, namespace)
|
|
235
312
|
|
|
@@ -279,6 +356,7 @@ def register_pod_tools(
|
|
|
279
356
|
|
|
280
357
|
return {
|
|
281
358
|
"success": True,
|
|
359
|
+
"context": context or "current",
|
|
282
360
|
"pod": pod_name,
|
|
283
361
|
"namespace": namespace,
|
|
284
362
|
"phaseAnalysis": phase_analysis,
|
|
@@ -299,11 +377,21 @@ def register_pod_tools(
|
|
|
299
377
|
pod_name: str,
|
|
300
378
|
namespace: str = "default",
|
|
301
379
|
container: Optional[str] = None,
|
|
302
|
-
tail: int = 100
|
|
380
|
+
tail: int = 100,
|
|
381
|
+
context: str = ""
|
|
303
382
|
) -> Dict[str, Any]:
|
|
304
|
-
"""Get logs from the previous container instance (useful for crash debugging).
|
|
383
|
+
"""Get logs from the previous container instance (useful for crash debugging).
|
|
384
|
+
|
|
385
|
+
Args:
|
|
386
|
+
pod_name: Name of the pod
|
|
387
|
+
namespace: Namespace of the pod
|
|
388
|
+
container: Container name (for multi-container pods)
|
|
389
|
+
tail: Number of lines to show from end of logs
|
|
390
|
+
context: Kubernetes context to use (uses current context if not specified)
|
|
391
|
+
"""
|
|
305
392
|
try:
|
|
306
|
-
|
|
393
|
+
ctx_args = _get_kubectl_context_args(context)
|
|
394
|
+
cmd = ["kubectl"] + ctx_args + ["logs", pod_name, "-n", namespace, "--previous", f"--tail={tail}"]
|
|
307
395
|
if container:
|
|
308
396
|
cmd.extend(["-c", container])
|
|
309
397
|
|
|
@@ -316,6 +404,7 @@ def register_pod_tools(
|
|
|
316
404
|
|
|
317
405
|
return {
|
|
318
406
|
"success": True,
|
|
407
|
+
"context": context or "current",
|
|
319
408
|
"pod": pod_name,
|
|
320
409
|
"namespace": namespace,
|
|
321
410
|
"container": container,
|
|
@@ -334,18 +423,27 @@ def register_pod_tools(
|
|
|
334
423
|
readOnlyHint=True,
|
|
335
424
|
),
|
|
336
425
|
)
|
|
337
|
-
def diagnose_pod_crash(
|
|
338
|
-
|
|
426
|
+
def diagnose_pod_crash(
|
|
427
|
+
pod_name: str,
|
|
428
|
+
namespace: str = "default",
|
|
429
|
+
context: str = ""
|
|
430
|
+
) -> Dict[str, Any]:
|
|
431
|
+
"""Automated diagnosis of pod crash loops and failures.
|
|
432
|
+
|
|
433
|
+
Args:
|
|
434
|
+
pod_name: Name of the pod
|
|
435
|
+
namespace: Namespace of the pod
|
|
436
|
+
context: Kubernetes context to use (uses current context if not specified)
|
|
437
|
+
"""
|
|
339
438
|
try:
|
|
340
|
-
|
|
341
|
-
config.load_kube_config()
|
|
342
|
-
v1 = client.CoreV1Api()
|
|
439
|
+
v1 = get_k8s_client(context)
|
|
343
440
|
|
|
344
441
|
pod = v1.read_namespaced_pod(pod_name, namespace)
|
|
345
442
|
|
|
346
443
|
diagnosis = {
|
|
347
444
|
"pod": pod_name,
|
|
348
445
|
"namespace": namespace,
|
|
446
|
+
"context": context or "current",
|
|
349
447
|
"phase": pod.status.phase,
|
|
350
448
|
"issues": [],
|
|
351
449
|
"recommendations": [],
|
|
@@ -447,12 +545,18 @@ def register_pod_tools(
|
|
|
447
545
|
readOnlyHint=True,
|
|
448
546
|
),
|
|
449
547
|
)
|
|
450
|
-
def detect_pending_pods(
|
|
451
|
-
|
|
548
|
+
def detect_pending_pods(
|
|
549
|
+
namespace: Optional[str] = None,
|
|
550
|
+
context: str = ""
|
|
551
|
+
) -> Dict[str, Any]:
|
|
552
|
+
"""Find pending pods and explain why they are not scheduled.
|
|
553
|
+
|
|
554
|
+
Args:
|
|
555
|
+
namespace: Namespace to search (all namespaces if not specified)
|
|
556
|
+
context: Kubernetes context to use (uses current context if not specified)
|
|
557
|
+
"""
|
|
452
558
|
try:
|
|
453
|
-
|
|
454
|
-
config.load_kube_config()
|
|
455
|
-
v1 = client.CoreV1Api()
|
|
559
|
+
v1 = get_k8s_client(context)
|
|
456
560
|
|
|
457
561
|
if namespace:
|
|
458
562
|
pods = v1.list_namespaced_pod(namespace, field_selector="status.phase=Pending")
|
|
@@ -514,6 +618,7 @@ def register_pod_tools(
|
|
|
514
618
|
|
|
515
619
|
return {
|
|
516
620
|
"success": True,
|
|
621
|
+
"context": context or "current",
|
|
517
622
|
"pendingCount": len(pending_pods),
|
|
518
623
|
"pendingPods": pending_pods
|
|
519
624
|
}
|
|
@@ -527,12 +632,18 @@ def register_pod_tools(
|
|
|
527
632
|
readOnlyHint=True,
|
|
528
633
|
),
|
|
529
634
|
)
|
|
530
|
-
def get_evicted_pods(
|
|
531
|
-
|
|
635
|
+
def get_evicted_pods(
|
|
636
|
+
namespace: Optional[str] = None,
|
|
637
|
+
context: str = ""
|
|
638
|
+
) -> Dict[str, Any]:
|
|
639
|
+
"""Find evicted pods with their eviction reasons.
|
|
640
|
+
|
|
641
|
+
Args:
|
|
642
|
+
namespace: Namespace to search (all namespaces if not specified)
|
|
643
|
+
context: Kubernetes context to use (uses current context if not specified)
|
|
644
|
+
"""
|
|
532
645
|
try:
|
|
533
|
-
|
|
534
|
-
config.load_kube_config()
|
|
535
|
-
v1 = client.CoreV1Api()
|
|
646
|
+
v1 = get_k8s_client(context)
|
|
536
647
|
|
|
537
648
|
if namespace:
|
|
538
649
|
pods = v1.list_namespaced_pod(namespace)
|
|
@@ -567,6 +678,7 @@ def register_pod_tools(
|
|
|
567
678
|
|
|
568
679
|
return {
|
|
569
680
|
"success": True,
|
|
681
|
+
"context": context or "current",
|
|
570
682
|
"summary": {
|
|
571
683
|
"totalEvicted": len(evicted),
|
|
572
684
|
"byReason": {k: len(v) for k, v in by_reason.items()}
|