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.
Files changed (37) hide show
  1. kubectl_mcp_server-1.16.0.dist-info/METADATA +1047 -0
  2. kubectl_mcp_server-1.16.0.dist-info/RECORD +61 -0
  3. kubectl_mcp_tool/__init__.py +1 -1
  4. kubectl_mcp_tool/crd_detector.py +247 -0
  5. kubectl_mcp_tool/k8s_config.py +304 -63
  6. kubectl_mcp_tool/mcp_server.py +27 -0
  7. kubectl_mcp_tool/tools/__init__.py +20 -0
  8. kubectl_mcp_tool/tools/backup.py +881 -0
  9. kubectl_mcp_tool/tools/capi.py +727 -0
  10. kubectl_mcp_tool/tools/certs.py +709 -0
  11. kubectl_mcp_tool/tools/cilium.py +582 -0
  12. kubectl_mcp_tool/tools/cluster.py +395 -121
  13. kubectl_mcp_tool/tools/core.py +157 -60
  14. kubectl_mcp_tool/tools/cost.py +97 -41
  15. kubectl_mcp_tool/tools/deployments.py +173 -56
  16. kubectl_mcp_tool/tools/diagnostics.py +40 -13
  17. kubectl_mcp_tool/tools/gitops.py +552 -0
  18. kubectl_mcp_tool/tools/helm.py +133 -46
  19. kubectl_mcp_tool/tools/keda.py +464 -0
  20. kubectl_mcp_tool/tools/kiali.py +652 -0
  21. kubectl_mcp_tool/tools/kubevirt.py +803 -0
  22. kubectl_mcp_tool/tools/networking.py +106 -32
  23. kubectl_mcp_tool/tools/operations.py +176 -50
  24. kubectl_mcp_tool/tools/pods.py +162 -50
  25. kubectl_mcp_tool/tools/policy.py +554 -0
  26. kubectl_mcp_tool/tools/rollouts.py +790 -0
  27. kubectl_mcp_tool/tools/security.py +89 -36
  28. kubectl_mcp_tool/tools/storage.py +35 -16
  29. tests/test_browser.py +2 -2
  30. tests/test_ecosystem.py +331 -0
  31. tests/test_tools.py +73 -10
  32. kubectl_mcp_server-1.14.0.dist-info/METADATA +0 -780
  33. kubectl_mcp_server-1.14.0.dist-info/RECORD +0 -49
  34. {kubectl_mcp_server-1.14.0.dist-info → kubectl_mcp_server-1.16.0.dist-info}/WHEEL +0 -0
  35. {kubectl_mcp_server-1.14.0.dist-info → kubectl_mcp_server-1.16.0.dist-info}/entry_points.txt +0 -0
  36. {kubectl_mcp_server-1.14.0.dist-info → kubectl_mcp_server-1.16.0.dist-info}/licenses/LICENSE +0 -0
  37. {kubectl_mcp_server-1.14.0.dist-info → kubectl_mcp_server-1.16.0.dist-info}/top_level.txt +0 -0
@@ -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(namespace: Optional[str] = None) -> Dict[str, Any]:
30
- """Get all pods in the specified namespace."""
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
- from kubernetes import client, config
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
- from kubernetes import client, config
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(pod_name: str, namespace: str = "default") -> Dict[str, Any]:
97
- """Get events for a specific pod."""
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
- from kubernetes import client, config
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(pod_name: str, namespace: str = "default") -> Dict[str, Any]:
127
- """Check the health status of a pod."""
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
- from kubernetes import client, config
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", "exec", pod_name, "-n", namespace]
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", "get", "pods"] + ns_flag + ["-o", "json"]
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", "delete", "pod", pod_name, "-n", pod_ns]
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(pod_name: str, namespace: str = "default") -> Dict[str, Any]:
228
- """Get detailed pod conditions breakdown."""
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
- from kubernetes import client, config
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
- cmd = ["kubectl", "logs", pod_name, "-n", namespace, "--previous", f"--tail={tail}"]
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(pod_name: str, namespace: str = "default") -> Dict[str, Any]:
338
- """Automated diagnosis of pod crash loops and failures."""
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
- from kubernetes import client, config
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(namespace: Optional[str] = None) -> Dict[str, Any]:
451
- """Find pending pods and explain why they are not scheduled."""
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
- from kubernetes import client, config
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(namespace: Optional[str] = None) -> Dict[str, Any]:
531
- """Find evicted pods with their eviction reasons."""
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
- from kubernetes import client, config
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()}