kubectl-mcp-server 1.13.0__py3-none-any.whl → 1.15.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.
@@ -2,13 +2,15 @@
2
2
  Kubernetes configuration loader utility.
3
3
 
4
4
  Handles both in-cluster and out-of-cluster (kubeconfig) configurations.
5
+ Supports multi-cluster operations with context targeting.
5
6
 
6
- This module patches the kubernetes.config.load_kube_config function to
7
- automatically try in-cluster config first when running inside a pod.
7
+ This module provides context-aware client creation for multi-cluster support.
8
+ All get_*_client() functions accept an optional 'context' parameter.
8
9
  """
9
10
 
10
11
  import os
11
12
  import logging
13
+ from typing import Optional, Any
12
14
 
13
15
  logger = logging.getLogger("mcp-server")
14
16
 
@@ -16,20 +18,20 @@ _config_loaded = False
16
18
  _original_load_kube_config = None
17
19
 
18
20
 
19
- def load_kubernetes_config():
21
+ def load_kubernetes_config(context: str = ""):
20
22
  """Load Kubernetes configuration.
21
23
 
22
24
  Tries in-cluster config first (when running inside a pod),
23
25
  then falls back to kubeconfig file.
24
26
 
27
+ Args:
28
+ context: Optional context name for kubeconfig provider
29
+
25
30
  Returns:
26
31
  bool: True if config loaded successfully, False otherwise
27
32
  """
28
33
  global _config_loaded
29
34
 
30
- if _config_loaded:
31
- return True
32
-
33
35
  from kubernetes import config
34
36
  from kubernetes.config.config_exception import ConfigException
35
37
 
@@ -46,8 +48,14 @@ def load_kubernetes_config():
46
48
  try:
47
49
  kubeconfig_path = os.environ.get('KUBECONFIG', '~/.kube/config')
48
50
  kubeconfig_path = os.path.expanduser(kubeconfig_path)
49
- config.load_kube_config(config_file=kubeconfig_path)
50
- logger.info(f"Loaded kubeconfig from {kubeconfig_path}")
51
+
52
+ if context:
53
+ config.load_kube_config(config_file=kubeconfig_path, context=context)
54
+ logger.info(f"Loaded kubeconfig context '{context}' from {kubeconfig_path}")
55
+ else:
56
+ config.load_kube_config(config_file=kubeconfig_path)
57
+ logger.info(f"Loaded kubeconfig from {kubeconfig_path}")
58
+
51
59
  _config_loaded = True
52
60
  return True
53
61
  except ConfigException as e:
@@ -102,8 +110,52 @@ def patch_kubernetes_config():
102
110
  patch_kubernetes_config()
103
111
 
104
112
 
105
- def get_k8s_client():
106
- """Get a configured Kubernetes API client.
113
+ def _load_config_for_context(context: str = "") -> Any:
114
+ """
115
+ Load kubernetes config for a specific context and return ApiClient.
116
+
117
+ Args:
118
+ context: Context name (empty for default)
119
+
120
+ Returns:
121
+ kubernetes.client.ApiClient configured for the context
122
+ """
123
+ from kubernetes import client, config
124
+ from kubernetes.config.config_exception import ConfigException
125
+
126
+ # Try in-cluster first
127
+ try:
128
+ config.load_incluster_config()
129
+ return client.ApiClient()
130
+ except ConfigException:
131
+ pass
132
+
133
+ # Load kubeconfig with optional context
134
+ kubeconfig_path = os.environ.get('KUBECONFIG', '~/.kube/config')
135
+ kubeconfig_path = os.path.expanduser(kubeconfig_path)
136
+
137
+ api_config = client.Configuration()
138
+
139
+ if context:
140
+ config.load_kube_config(
141
+ config_file=kubeconfig_path,
142
+ context=context,
143
+ client_configuration=api_config
144
+ )
145
+ else:
146
+ config.load_kube_config(
147
+ config_file=kubeconfig_path,
148
+ client_configuration=api_config
149
+ )
150
+
151
+ return client.ApiClient(configuration=api_config)
152
+
153
+
154
+ def get_k8s_client(context: str = ""):
155
+ """Get a configured Kubernetes Core API client.
156
+
157
+ Args:
158
+ context: Optional context name for multi-cluster support
107
159
 
108
160
  Returns:
109
161
  kubernetes.client.CoreV1Api: Configured Kubernetes client
@@ -113,15 +165,19 @@ def get_k8s_client():
113
165
  """
114
166
  from kubernetes import client
115
167
 
116
- if not load_kubernetes_config():
117
- raise RuntimeError("Invalid kube-config file. No configuration found.")
118
-
119
- return client.CoreV1Api()
168
+ try:
169
+ api_client = _load_config_for_context(context)
170
+ return client.CoreV1Api(api_client=api_client)
171
+ except Exception as e:
172
+ raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
120
173
 
121
174
 
122
- def get_apps_client():
175
+ def get_apps_client(context: str = ""):
123
176
  """Get a configured Kubernetes Apps API client.
124
177
 
178
+ Args:
179
+ context: Optional context name for multi-cluster support
180
+
125
181
  Returns:
126
182
  kubernetes.client.AppsV1Api: Configured Apps API client
127
183
 
@@ -130,15 +186,19 @@ def get_apps_client():
130
186
  """
131
187
  from kubernetes import client
132
188
 
133
- if not load_kubernetes_config():
134
- raise RuntimeError("Invalid kube-config file. No configuration found.")
135
-
136
- return client.AppsV1Api()
189
+ try:
190
+ api_client = _load_config_for_context(context)
191
+ return client.AppsV1Api(api_client=api_client)
192
+ except Exception as e:
193
+ raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
137
194
 
138
195
 
139
- def get_rbac_client():
196
+ def get_rbac_client(context: str = ""):
140
197
  """Get a configured Kubernetes RBAC API client.
141
198
 
199
+ Args:
200
+ context: Optional context name for multi-cluster support
201
+
142
202
  Returns:
143
203
  kubernetes.client.RbacAuthorizationV1Api: Configured RBAC client
144
204
 
@@ -147,15 +207,19 @@ def get_rbac_client():
147
207
  """
148
208
  from kubernetes import client
149
209
 
150
- if not load_kubernetes_config():
151
- raise RuntimeError("Invalid kube-config file. No configuration found.")
152
-
153
- return client.RbacAuthorizationV1Api()
210
+ try:
211
+ api_client = _load_config_for_context(context)
212
+ return client.RbacAuthorizationV1Api(api_client=api_client)
213
+ except Exception as e:
214
+ raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
154
215
 
155
216
 
156
- def get_networking_client():
217
+ def get_networking_client(context: str = ""):
157
218
  """Get a configured Kubernetes Networking API client.
158
219
 
220
+ Args:
221
+ context: Optional context name for multi-cluster support
222
+
159
223
  Returns:
160
224
  kubernetes.client.NetworkingV1Api: Configured Networking client
161
225
 
@@ -164,15 +228,19 @@ def get_networking_client():
164
228
  """
165
229
  from kubernetes import client
166
230
 
167
- if not load_kubernetes_config():
168
- raise RuntimeError("Invalid kube-config file. No configuration found.")
169
-
170
- return client.NetworkingV1Api()
231
+ try:
232
+ api_client = _load_config_for_context(context)
233
+ return client.NetworkingV1Api(api_client=api_client)
234
+ except Exception as e:
235
+ raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
171
236
 
172
237
 
173
- def get_storage_client():
238
+ def get_storage_client(context: str = ""):
174
239
  """Get a configured Kubernetes Storage API client.
175
240
 
241
+ Args:
242
+ context: Optional context name for multi-cluster support
243
+
176
244
  Returns:
177
245
  kubernetes.client.StorageV1Api: Configured Storage client
178
246
 
@@ -181,15 +249,19 @@ def get_storage_client():
181
249
  """
182
250
  from kubernetes import client
183
251
 
184
- if not load_kubernetes_config():
185
- raise RuntimeError("Invalid kube-config file. No configuration found.")
186
-
187
- return client.StorageV1Api()
252
+ try:
253
+ api_client = _load_config_for_context(context)
254
+ return client.StorageV1Api(api_client=api_client)
255
+ except Exception as e:
256
+ raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
188
257
 
189
258
 
190
- def get_batch_client():
259
+ def get_batch_client(context: str = ""):
191
260
  """Get a configured Kubernetes Batch API client.
192
261
 
262
+ Args:
263
+ context: Optional context name for multi-cluster support
264
+
193
265
  Returns:
194
266
  kubernetes.client.BatchV1Api: Configured Batch client
195
267
 
@@ -198,15 +270,19 @@ def get_batch_client():
198
270
  """
199
271
  from kubernetes import client
200
272
 
201
- if not load_kubernetes_config():
202
- raise RuntimeError("Invalid kube-config file. No configuration found.")
203
-
204
- return client.BatchV1Api()
273
+ try:
274
+ api_client = _load_config_for_context(context)
275
+ return client.BatchV1Api(api_client=api_client)
276
+ except Exception as e:
277
+ raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
205
278
 
206
279
 
207
- def get_autoscaling_client():
280
+ def get_autoscaling_client(context: str = ""):
208
281
  """Get a configured Kubernetes Autoscaling API client.
209
282
 
283
+ Args:
284
+ context: Optional context name for multi-cluster support
285
+
210
286
  Returns:
211
287
  kubernetes.client.AutoscalingV1Api: Configured Autoscaling client
212
288
 
@@ -215,15 +291,19 @@ def get_autoscaling_client():
215
291
  """
216
292
  from kubernetes import client
217
293
 
218
- if not load_kubernetes_config():
219
- raise RuntimeError("Invalid kube-config file. No configuration found.")
220
-
221
- return client.AutoscalingV1Api()
294
+ try:
295
+ api_client = _load_config_for_context(context)
296
+ return client.AutoscalingV1Api(api_client=api_client)
297
+ except Exception as e:
298
+ raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
222
299
 
223
300
 
224
- def get_policy_client():
301
+ def get_policy_client(context: str = ""):
225
302
  """Get a configured Kubernetes Policy API client.
226
303
 
304
+ Args:
305
+ context: Optional context name for multi-cluster support
306
+
227
307
  Returns:
228
308
  kubernetes.client.PolicyV1Api: Configured Policy client
229
309
 
@@ -232,15 +312,19 @@ def get_policy_client():
232
312
  """
233
313
  from kubernetes import client
234
314
 
235
- if not load_kubernetes_config():
236
- raise RuntimeError("Invalid kube-config file. No configuration found.")
237
-
238
- return client.PolicyV1Api()
315
+ try:
316
+ api_client = _load_config_for_context(context)
317
+ return client.PolicyV1Api(api_client=api_client)
318
+ except Exception as e:
319
+ raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
239
320
 
240
321
 
241
- def get_custom_objects_client():
322
+ def get_custom_objects_client(context: str = ""):
242
323
  """Get a configured Kubernetes Custom Objects API client.
243
324
 
325
+ Args:
326
+ context: Optional context name for multi-cluster support
327
+
244
328
  Returns:
245
329
  kubernetes.client.CustomObjectsApi: Configured Custom Objects client
246
330
 
@@ -249,15 +333,19 @@ def get_custom_objects_client():
249
333
  """
250
334
  from kubernetes import client
251
335
 
252
- if not load_kubernetes_config():
253
- raise RuntimeError("Invalid kube-config file. No configuration found.")
254
-
255
- return client.CustomObjectsApi()
336
+ try:
337
+ api_client = _load_config_for_context(context)
338
+ return client.CustomObjectsApi(api_client=api_client)
339
+ except Exception as e:
340
+ raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
256
341
 
257
342
 
258
- def get_version_client():
343
+ def get_version_client(context: str = ""):
259
344
  """Get a configured Kubernetes Version API client.
260
345
 
346
+ Args:
347
+ context: Optional context name for multi-cluster support
348
+
261
349
  Returns:
262
350
  kubernetes.client.VersionApi: Configured Version client
263
351
 
@@ -266,15 +354,19 @@ def get_version_client():
266
354
  """
267
355
  from kubernetes import client
268
356
 
269
- if not load_kubernetes_config():
270
- raise RuntimeError("Invalid kube-config file. No configuration found.")
271
-
272
- return client.VersionApi()
357
+ try:
358
+ api_client = _load_config_for_context(context)
359
+ return client.VersionApi(api_client=api_client)
360
+ except Exception as e:
361
+ raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
273
362
 
274
363
 
275
- def get_admissionregistration_client():
364
+ def get_admissionregistration_client(context: str = ""):
276
365
  """Get a configured Kubernetes Admission Registration API client.
277
366
 
367
+ Args:
368
+ context: Optional context name for multi-cluster support
369
+
278
370
  Returns:
279
371
  kubernetes.client.AdmissionregistrationV1Api: Configured Admission client
280
372
 
@@ -283,7 +375,137 @@ def get_admissionregistration_client():
283
375
  """
284
376
  from kubernetes import client
285
377
 
286
- if not load_kubernetes_config():
287
- raise RuntimeError("Invalid kube-config file. No configuration found.")
378
+ try:
379
+ api_client = _load_config_for_context(context)
380
+ return client.AdmissionregistrationV1Api(api_client=api_client)
381
+ except Exception as e:
382
+ raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
288
383
 
289
- return client.AdmissionregistrationV1Api()
384
+
385
+ def get_apiextensions_client(context: str = ""):
386
+ """Get a configured Kubernetes API Extensions client.
387
+
388
+ Args:
389
+ context: Optional context name for multi-cluster support
390
+
391
+ Returns:
392
+ kubernetes.client.ApiextensionsV1Api: Configured API Extensions client
393
+
394
+ Raises:
395
+ RuntimeError: If Kubernetes config cannot be loaded
396
+ """
397
+ from kubernetes import client
398
+
399
+ try:
400
+ api_client = _load_config_for_context(context)
401
+ return client.ApiextensionsV1Api(api_client=api_client)
402
+ except Exception as e:
403
+ raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
404
+
405
+
406
+ def get_coordination_client(context: str = ""):
407
+ """Get a configured Kubernetes Coordination API client.
408
+
409
+ Args:
410
+ context: Optional context name for multi-cluster support
411
+
412
+ Returns:
413
+ kubernetes.client.CoordinationV1Api: Configured Coordination client
414
+
415
+ Raises:
416
+ RuntimeError: If Kubernetes config cannot be loaded
417
+ """
418
+ from kubernetes import client
419
+
420
+ try:
421
+ api_client = _load_config_for_context(context)
422
+ return client.CoordinationV1Api(api_client=api_client)
423
+ except Exception as e:
424
+ raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
425
+
426
+
427
+ def get_events_client(context: str = ""):
428
+ """Get a configured Kubernetes Events API client.
429
+
430
+ Args:
431
+ context: Optional context name for multi-cluster support
432
+
433
+ Returns:
434
+ kubernetes.client.EventsV1Api: Configured Events client
435
+
436
+ Raises:
437
+ RuntimeError: If Kubernetes config cannot be loaded
438
+ """
439
+ from kubernetes import client
440
+
441
+ try:
442
+ api_client = _load_config_for_context(context)
443
+ return client.EventsV1Api(api_client=api_client)
444
+ except Exception as e:
445
+ raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
446
+
447
+
448
+ # Utility functions for context management
449
+
450
+ def list_contexts() -> list:
451
+ """
452
+ List all available kubeconfig contexts.
453
+
454
+ Returns:
455
+ List of context dictionaries with name, cluster, user, namespace
456
+ """
457
+ from kubernetes import config
458
+
459
+ try:
460
+ kubeconfig_path = os.environ.get('KUBECONFIG', '~/.kube/config')
461
+ kubeconfig_path = os.path.expanduser(kubeconfig_path)
462
+
463
+ contexts, active = config.list_kube_config_contexts(config_file=kubeconfig_path)
464
+
465
+ return [
466
+ {
467
+ "name": ctx.get("name"),
468
+ "cluster": ctx.get("context", {}).get("cluster"),
469
+ "user": ctx.get("context", {}).get("user"),
470
+ "namespace": ctx.get("context", {}).get("namespace", "default"),
471
+ "active": ctx.get("name") == (active.get("name") if active else None)
472
+ }
473
+ for ctx in contexts
474
+ ]
475
+ except Exception as e:
476
+ logger.error(f"Error listing contexts: {e}")
477
+ return []
478
+
479
+
480
+ def get_active_context() -> Optional[str]:
481
+ """
482
+ Get the current active context name.
483
+
484
+ Returns:
485
+ Active context name or None
486
+ """
487
+ from kubernetes import config
488
+
489
+ try:
490
+ kubeconfig_path = os.environ.get('KUBECONFIG', '~/.kube/config')
491
+ kubeconfig_path = os.path.expanduser(kubeconfig_path)
492
+
493
+ _, active = config.list_kube_config_contexts(config_file=kubeconfig_path)
494
+ return active.get("name") if active else None
495
+ except Exception as e:
496
+ logger.error(f"Error getting active context: {e}")
497
+ return None
498
+
499
+
500
+ def context_exists(context: str) -> bool:
501
+ """
502
+ Check if a context exists in kubeconfig.
503
+
504
+ Args:
505
+ context: Context name to check
506
+
507
+ Returns:
508
+ True if context exists
509
+ """
510
+ contexts = list_contexts()
511
+ return any(ctx["name"] == context for ctx in contexts)