kubectl-mcp-server 1.14.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.
@@ -1,11 +1,24 @@
1
1
  import logging
2
+ import subprocess
2
3
  from typing import Any, Dict, List, Optional
3
4
 
4
5
  from mcp.types import ToolAnnotations
5
6
 
7
+ from ..k8s_config import (
8
+ get_k8s_client,
9
+ get_apiextensions_client,
10
+ )
11
+
6
12
  logger = logging.getLogger("mcp-server")
7
13
 
8
14
 
15
+ def _get_kubectl_context_args(context: str) -> List[str]:
16
+ """Get kubectl context arguments if context is specified."""
17
+ if context:
18
+ return ["--context", context]
19
+ return []
20
+
21
+
9
22
  def register_core_tools(server, non_destructive: bool):
10
23
  """Register core Kubernetes resource tools."""
11
24
 
@@ -15,15 +28,18 @@ def register_core_tools(server, non_destructive: bool):
15
28
  readOnlyHint=True,
16
29
  ),
17
30
  )
18
- def get_namespaces() -> Dict[str, Any]:
19
- """Get all Kubernetes namespaces."""
31
+ def get_namespaces(context: str = "") -> Dict[str, Any]:
32
+ """Get all Kubernetes namespaces.
33
+
34
+ Args:
35
+ context: Kubernetes context to use (uses current context if not specified)
36
+ """
20
37
  try:
21
- from kubernetes import client, config
22
- config.load_kube_config()
23
- v1 = client.CoreV1Api()
38
+ v1 = get_k8s_client(context)
24
39
  namespaces = v1.list_namespace()
25
40
  return {
26
41
  "success": True,
42
+ "context": context or "current",
27
43
  "namespaces": [ns.metadata.name for ns in namespaces.items]
28
44
  }
29
45
  except Exception as e:
@@ -36,18 +52,25 @@ def register_core_tools(server, non_destructive: bool):
36
52
  readOnlyHint=True,
37
53
  ),
38
54
  )
39
- def get_services(namespace: Optional[str] = None) -> Dict[str, Any]:
40
- """Get all services in the specified namespace."""
55
+ def get_services(
56
+ namespace: Optional[str] = None,
57
+ context: str = ""
58
+ ) -> Dict[str, Any]:
59
+ """Get all services in the specified namespace.
60
+
61
+ Args:
62
+ namespace: Namespace to list services from (all namespaces if not specified)
63
+ context: Kubernetes context to use (uses current context if not specified)
64
+ """
41
65
  try:
42
- from kubernetes import client, config
43
- config.load_kube_config()
44
- v1 = client.CoreV1Api()
66
+ v1 = get_k8s_client(context)
45
67
  if namespace:
46
68
  services = v1.list_namespaced_service(namespace)
47
69
  else:
48
70
  services = v1.list_service_for_all_namespaces()
49
71
  return {
50
72
  "success": True,
73
+ "context": context or "current",
51
74
  "services": [
52
75
  {
53
76
  "name": svc.metadata.name,
@@ -67,15 +90,18 @@ def register_core_tools(server, non_destructive: bool):
67
90
  readOnlyHint=True,
68
91
  ),
69
92
  )
70
- def get_nodes() -> Dict[str, Any]:
71
- """Get all nodes in the cluster."""
93
+ def get_nodes(context: str = "") -> Dict[str, Any]:
94
+ """Get all nodes in the cluster.
95
+
96
+ Args:
97
+ context: Kubernetes context to use (uses current context if not specified)
98
+ """
72
99
  try:
73
- from kubernetes import client, config
74
- config.load_kube_config()
75
- v1 = client.CoreV1Api()
100
+ v1 = get_k8s_client(context)
76
101
  nodes = v1.list_node()
77
102
  return {
78
103
  "success": True,
104
+ "context": context or "current",
79
105
  "nodes": [
80
106
  {
81
107
  "name": node.metadata.name,
@@ -104,18 +130,25 @@ def register_core_tools(server, non_destructive: bool):
104
130
  readOnlyHint=True,
105
131
  ),
106
132
  )
107
- def get_configmaps(namespace: Optional[str] = None) -> Dict[str, Any]:
108
- """Get all ConfigMaps in the specified namespace."""
133
+ def get_configmaps(
134
+ namespace: Optional[str] = None,
135
+ context: str = ""
136
+ ) -> Dict[str, Any]:
137
+ """Get all ConfigMaps in the specified namespace.
138
+
139
+ Args:
140
+ namespace: Namespace to list ConfigMaps from (all namespaces if not specified)
141
+ context: Kubernetes context to use (uses current context if not specified)
142
+ """
109
143
  try:
110
- from kubernetes import client, config
111
- config.load_kube_config()
112
- v1 = client.CoreV1Api()
144
+ v1 = get_k8s_client(context)
113
145
  if namespace:
114
146
  cms = v1.list_namespaced_config_map(namespace)
115
147
  else:
116
148
  cms = v1.list_config_map_for_all_namespaces()
117
149
  return {
118
150
  "success": True,
151
+ "context": context or "current",
119
152
  "configmaps": [
120
153
  {
121
154
  "name": cm.metadata.name,
@@ -134,18 +167,25 @@ def register_core_tools(server, non_destructive: bool):
134
167
  readOnlyHint=True,
135
168
  ),
136
169
  )
137
- def get_secrets(namespace: Optional[str] = None) -> Dict[str, Any]:
138
- """Get all Secrets in the specified namespace."""
170
+ def get_secrets(
171
+ namespace: Optional[str] = None,
172
+ context: str = ""
173
+ ) -> Dict[str, Any]:
174
+ """Get all Secrets in the specified namespace.
175
+
176
+ Args:
177
+ namespace: Namespace to list Secrets from (all namespaces if not specified)
178
+ context: Kubernetes context to use (uses current context if not specified)
179
+ """
139
180
  try:
140
- from kubernetes import client, config
141
- config.load_kube_config()
142
- v1 = client.CoreV1Api()
181
+ v1 = get_k8s_client(context)
143
182
  if namespace:
144
183
  secrets = v1.list_namespaced_secret(namespace)
145
184
  else:
146
185
  secrets = v1.list_secret_for_all_namespaces()
147
186
  return {
148
187
  "success": True,
188
+ "context": context or "current",
149
189
  "secrets": [
150
190
  {
151
191
  "name": secret.metadata.name,
@@ -164,18 +204,25 @@ def register_core_tools(server, non_destructive: bool):
164
204
  readOnlyHint=True,
165
205
  ),
166
206
  )
167
- def get_events(namespace: Optional[str] = None) -> Dict[str, Any]:
168
- """Get Kubernetes events."""
207
+ def get_events(
208
+ namespace: Optional[str] = None,
209
+ context: str = ""
210
+ ) -> Dict[str, Any]:
211
+ """Get Kubernetes events.
212
+
213
+ Args:
214
+ namespace: Namespace to list events from (all namespaces if not specified)
215
+ context: Kubernetes context to use (uses current context if not specified)
216
+ """
169
217
  try:
170
- from kubernetes import client, config
171
- config.load_kube_config()
172
- v1 = client.CoreV1Api()
218
+ v1 = get_k8s_client(context)
173
219
  if namespace:
174
220
  events = v1.list_namespaced_event(namespace)
175
221
  else:
176
222
  events = v1.list_event_for_all_namespaces()
177
223
  return {
178
224
  "success": True,
225
+ "context": context or "current",
179
226
  "events": [
180
227
  {
181
228
  "name": event.metadata.name,
@@ -197,11 +244,19 @@ def register_core_tools(server, non_destructive: bool):
197
244
  readOnlyHint=True,
198
245
  ),
199
246
  )
200
- def get_resource_usage(namespace: Optional[str] = None) -> Dict[str, Any]:
201
- """Get resource usage metrics for pods."""
247
+ def get_resource_usage(
248
+ namespace: Optional[str] = None,
249
+ context: str = ""
250
+ ) -> Dict[str, Any]:
251
+ """Get resource usage metrics for pods.
252
+
253
+ Args:
254
+ namespace: Namespace to get metrics from (all namespaces if not specified)
255
+ context: Kubernetes context to use (uses current context if not specified)
256
+ """
202
257
  try:
203
- import subprocess
204
258
  cmd = ["kubectl", "top", "pods"]
259
+ cmd.extend(_get_kubectl_context_args(context))
205
260
  if namespace:
206
261
  cmd.extend(["-n", namespace])
207
262
  else:
@@ -233,7 +288,11 @@ def register_core_tools(server, non_destructive: bool):
233
288
  "memory": parts[3]
234
289
  })
235
290
 
236
- return {"success": True, "pods": pods}
291
+ return {
292
+ "success": True,
293
+ "context": context or "current",
294
+ "pods": pods
295
+ }
237
296
  except subprocess.TimeoutExpired:
238
297
  return {"success": False, "error": "Metrics retrieval timed out"}
239
298
  except Exception as e:
@@ -246,12 +305,18 @@ def register_core_tools(server, non_destructive: bool):
246
305
  readOnlyHint=True,
247
306
  ),
248
307
  )
249
- def get_service_accounts(namespace: Optional[str] = None) -> Dict[str, Any]:
250
- """Get service accounts in a namespace or cluster-wide."""
308
+ def get_service_accounts(
309
+ namespace: Optional[str] = None,
310
+ context: str = ""
311
+ ) -> Dict[str, Any]:
312
+ """Get service accounts in a namespace or cluster-wide.
313
+
314
+ Args:
315
+ namespace: Namespace to list service accounts from (all namespaces if not specified)
316
+ context: Kubernetes context to use (uses current context if not specified)
317
+ """
251
318
  try:
252
- from kubernetes import client, config
253
- config.load_kube_config()
254
- v1 = client.CoreV1Api()
319
+ v1 = get_k8s_client(context)
255
320
 
256
321
  if namespace:
257
322
  sas = v1.list_namespaced_service_account(namespace)
@@ -260,6 +325,7 @@ def register_core_tools(server, non_destructive: bool):
260
325
 
261
326
  return {
262
327
  "success": True,
328
+ "context": context or "current",
263
329
  "serviceAccounts": [
264
330
  {
265
331
  "name": sa.metadata.name,
@@ -281,12 +347,18 @@ def register_core_tools(server, non_destructive: bool):
281
347
  readOnlyHint=True,
282
348
  ),
283
349
  )
284
- def get_crds(group: Optional[str] = None) -> Dict[str, Any]:
285
- """Get Custom Resource Definitions in the cluster."""
350
+ def get_crds(
351
+ group: Optional[str] = None,
352
+ context: str = ""
353
+ ) -> Dict[str, Any]:
354
+ """Get Custom Resource Definitions in the cluster.
355
+
356
+ Args:
357
+ group: Filter CRDs by API group
358
+ context: Kubernetes context to use (uses current context if not specified)
359
+ """
286
360
  try:
287
- from kubernetes import client, config
288
- config.load_kube_config()
289
- api = client.ApiextensionsV1Api()
361
+ api = get_apiextensions_client(context)
290
362
 
291
363
  crds = api.list_custom_resource_definition()
292
364
 
@@ -307,7 +379,11 @@ def register_core_tools(server, non_destructive: bool):
307
379
  )
308
380
  })
309
381
 
310
- return {"success": True, "crds": result}
382
+ return {
383
+ "success": True,
384
+ "context": context or "current",
385
+ "crds": result
386
+ }
311
387
  except Exception as e:
312
388
  logger.error(f"Error getting CRDs: {e}")
313
389
  return {"success": False, "error": str(e)}
@@ -318,12 +394,18 @@ def register_core_tools(server, non_destructive: bool):
318
394
  readOnlyHint=True,
319
395
  ),
320
396
  )
321
- def get_resource_quotas(namespace: Optional[str] = None) -> Dict[str, Any]:
322
- """Get resource quotas for namespaces."""
397
+ def get_resource_quotas(
398
+ namespace: Optional[str] = None,
399
+ context: str = ""
400
+ ) -> Dict[str, Any]:
401
+ """Get resource quotas for namespaces.
402
+
403
+ Args:
404
+ namespace: Namespace to list resource quotas from (all namespaces if not specified)
405
+ context: Kubernetes context to use (uses current context if not specified)
406
+ """
323
407
  try:
324
- from kubernetes import client, config
325
- config.load_kube_config()
326
- v1 = client.CoreV1Api()
408
+ v1 = get_k8s_client(context)
327
409
 
328
410
  if namespace:
329
411
  quotas = v1.list_namespaced_resource_quota(namespace)
@@ -332,6 +414,7 @@ def register_core_tools(server, non_destructive: bool):
332
414
 
333
415
  return {
334
416
  "success": True,
417
+ "context": context or "current",
335
418
  "quotas": [
336
419
  {
337
420
  "name": q.metadata.name,
@@ -352,12 +435,18 @@ def register_core_tools(server, non_destructive: bool):
352
435
  readOnlyHint=True,
353
436
  ),
354
437
  )
355
- def get_limit_ranges(namespace: Optional[str] = None) -> Dict[str, Any]:
356
- """Get limit ranges for namespaces."""
438
+ def get_limit_ranges(
439
+ namespace: Optional[str] = None,
440
+ context: str = ""
441
+ ) -> Dict[str, Any]:
442
+ """Get limit ranges for namespaces.
443
+
444
+ Args:
445
+ namespace: Namespace to list limit ranges from (all namespaces if not specified)
446
+ context: Kubernetes context to use (uses current context if not specified)
447
+ """
357
448
  try:
358
- from kubernetes import client, config
359
- config.load_kube_config()
360
- v1 = client.CoreV1Api()
449
+ v1 = get_k8s_client(context)
361
450
 
362
451
  if namespace:
363
452
  limits = v1.list_namespaced_limit_range(namespace)
@@ -366,6 +455,7 @@ def register_core_tools(server, non_destructive: bool):
366
455
 
367
456
  return {
368
457
  "success": True,
458
+ "context": context or "current",
369
459
  "limitRanges": [
370
460
  {
371
461
  "name": lr.metadata.name,
@@ -394,17 +484,24 @@ def register_core_tools(server, non_destructive: bool):
394
484
  readOnlyHint=True,
395
485
  ),
396
486
  )
397
- def get_priority_classes() -> Dict[str, Any]:
398
- """Get priority classes in the cluster."""
487
+ def get_priority_classes(context: str = "") -> Dict[str, Any]:
488
+ """Get priority classes in the cluster.
489
+
490
+ Args:
491
+ context: Kubernetes context to use (uses current context if not specified)
492
+ """
399
493
  try:
400
- from kubernetes import client, config
401
- config.load_kube_config()
402
- api = client.SchedulingV1Api()
494
+ from kubernetes import client
495
+ from ..k8s_config import _load_config_for_context
496
+
497
+ api_client = _load_config_for_context(context)
498
+ api = client.SchedulingV1Api(api_client=api_client)
403
499
 
404
500
  pcs = api.list_priority_class()
405
501
 
406
502
  return {
407
503
  "success": True,
504
+ "context": context or "current",
408
505
  "priorityClasses": [
409
506
  {
410
507
  "name": pc.metadata.name,
@@ -2,13 +2,22 @@ import logging
2
2
  import subprocess
3
3
  import re
4
4
  from datetime import datetime
5
- from typing import Any, Dict, Optional
5
+ from typing import Any, Dict, List, Optional
6
6
 
7
7
  from mcp.types import ToolAnnotations
8
8
 
9
+ from ..k8s_config import get_k8s_client, get_apps_client
10
+
9
11
  logger = logging.getLogger("mcp-server")
10
12
 
11
13
 
14
+ def _get_kubectl_context_args(context: str) -> List[str]:
15
+ """Get kubectl context arguments if context is specified."""
16
+ if context:
17
+ return ["--context", context]
18
+ return []
19
+
20
+
12
21
  def _parse_cpu(cpu_str: str) -> int:
13
22
  """Parse CPU string to millicores."""
14
23
  try:
@@ -67,13 +76,19 @@ def register_cost_tools(server, non_destructive: bool):
67
76
  )
68
77
  def get_resource_recommendations(
69
78
  namespace: Optional[str] = None,
70
- resource_type: str = "all"
79
+ resource_type: str = "all",
80
+ context: str = ""
71
81
  ) -> Dict[str, Any]:
72
- """Analyze resource usage and provide optimization recommendations for pods/deployments."""
82
+ """Analyze resource usage and provide optimization recommendations for pods/deployments.
83
+
84
+ Args:
85
+ namespace: Target namespace (optional, all namespaces if not specified)
86
+ resource_type: Type of resource to analyze
87
+ context: Kubernetes context to use (optional, uses current context if not specified)
88
+ """
73
89
  try:
74
- from kubernetes import client, config
75
- config.load_kube_config()
76
- v1 = client.CoreV1Api()
90
+ from kubernetes import client
91
+ v1 = get_k8s_client(context)
77
92
 
78
93
  recommendations = []
79
94
 
@@ -135,6 +150,7 @@ def register_cost_tools(server, non_destructive: bool):
135
150
 
136
151
  return {
137
152
  "success": True,
153
+ "context": context or "current",
138
154
  "totalAnalyzed": len(pods),
139
155
  "issuesFound": len(recommendations),
140
156
  "recommendations": recommendations[:50]
@@ -152,11 +168,19 @@ def register_cost_tools(server, non_destructive: bool):
152
168
  def get_idle_resources(
153
169
  namespace: Optional[str] = None,
154
170
  cpu_threshold: float = 10.0,
155
- memory_threshold: float = 10.0
171
+ memory_threshold: float = 10.0,
172
+ context: str = ""
156
173
  ) -> Dict[str, Any]:
157
- """Find underutilized pods using less than threshold percentage of requested resources."""
174
+ """Find underutilized pods using less than threshold percentage of requested resources.
175
+
176
+ Args:
177
+ namespace: Target namespace (optional, all namespaces if not specified)
178
+ cpu_threshold: CPU usage threshold percentage
179
+ memory_threshold: Memory usage threshold percentage
180
+ context: Kubernetes context to use (optional, uses current context if not specified)
181
+ """
158
182
  try:
159
- cmd = ["kubectl", "top", "pods", "--no-headers"]
183
+ cmd = ["kubectl"] + _get_kubectl_context_args(context) + ["top", "pods", "--no-headers"]
160
184
  if namespace:
161
185
  cmd.extend(["-n", namespace])
162
186
  else:
@@ -196,6 +220,7 @@ def register_cost_tools(server, non_destructive: bool):
196
220
 
197
221
  return {
198
222
  "success": True,
223
+ "context": context or "current",
199
224
  "thresholds": {
200
225
  "cpu": f"{cpu_threshold}%",
201
226
  "memory": f"{memory_threshold}%"
@@ -215,12 +240,15 @@ def register_cost_tools(server, non_destructive: bool):
215
240
  readOnlyHint=True,
216
241
  ),
217
242
  )
218
- def get_resource_quotas_usage(namespace: Optional[str] = None) -> Dict[str, Any]:
219
- """Show resource quota usage and availability across namespaces."""
243
+ def get_resource_quotas_usage(namespace: Optional[str] = None, context: str = "") -> Dict[str, Any]:
244
+ """Show resource quota usage and availability across namespaces.
245
+
246
+ Args:
247
+ namespace: Target namespace (optional, all namespaces if not specified)
248
+ context: Kubernetes context to use (optional, uses current context if not specified)
249
+ """
220
250
  try:
221
- from kubernetes import client, config
222
- config.load_kube_config()
223
- v1 = client.CoreV1Api()
251
+ v1 = get_k8s_client(context)
224
252
 
225
253
  if namespace:
226
254
  quotas = v1.list_namespaced_resource_quota(namespace).items
@@ -250,6 +278,7 @@ def register_cost_tools(server, non_destructive: bool):
250
278
 
251
279
  return {
252
280
  "success": True,
281
+ "context": context or "current",
253
282
  "count": len(quota_usage),
254
283
  "quotas": quota_usage
255
284
  }
@@ -263,12 +292,15 @@ def register_cost_tools(server, non_destructive: bool):
263
292
  readOnlyHint=True,
264
293
  ),
265
294
  )
266
- def get_cost_analysis(namespace: Optional[str] = None) -> Dict[str, Any]:
267
- """Analyze resource costs by namespace and workload based on resource requests."""
295
+ def get_cost_analysis(namespace: Optional[str] = None, context: str = "") -> Dict[str, Any]:
296
+ """Analyze resource costs by namespace and workload based on resource requests.
297
+
298
+ Args:
299
+ namespace: Target namespace (optional, all namespaces if not specified)
300
+ context: Kubernetes context to use (optional, uses current context if not specified)
301
+ """
268
302
  try:
269
- from kubernetes import client, config
270
- config.load_kube_config()
271
- v1 = client.CoreV1Api()
303
+ v1 = get_k8s_client(context)
272
304
 
273
305
  if namespace:
274
306
  pods = v1.list_namespaced_pod(namespace).items
@@ -325,6 +357,7 @@ def register_cost_tools(server, non_destructive: bool):
325
357
 
326
358
  return {
327
359
  "success": True,
360
+ "context": context or "current",
328
361
  "note": "Cost estimates based on resource requests. Integrate with cloud billing for actual costs.",
329
362
  "byNamespace": ns_summary,
330
363
  "topWorkloads": sorted(workload_costs, key=lambda x: x["cpuMillicores"], reverse=True)[:20]
@@ -341,15 +374,20 @@ def register_cost_tools(server, non_destructive: bool):
341
374
  )
342
375
  def get_overprovisioned_resources(
343
376
  namespace: Optional[str] = None,
344
- threshold: float = 50.0
377
+ threshold: float = 50.0,
378
+ context: str = ""
345
379
  ) -> Dict[str, Any]:
346
- """Find pods using significantly less resources than requested (over-provisioned)."""
380
+ """Find pods using significantly less resources than requested (over-provisioned).
381
+
382
+ Args:
383
+ namespace: Target namespace (optional, all namespaces if not specified)
384
+ threshold: Utilization threshold percentage below which resources are considered over-provisioned
385
+ context: Kubernetes context to use (optional, uses current context if not specified)
386
+ """
347
387
  try:
348
- from kubernetes import client, config
349
- config.load_kube_config()
350
- v1 = client.CoreV1Api()
388
+ v1 = get_k8s_client(context)
351
389
 
352
- cmd = ["kubectl", "top", "pods", "--no-headers"]
390
+ cmd = ["kubectl"] + _get_kubectl_context_args(context) + ["top", "pods", "--no-headers"]
353
391
  if namespace:
354
392
  cmd.extend(["-n", namespace])
355
393
  else:
@@ -418,6 +456,7 @@ def register_cost_tools(server, non_destructive: bool):
418
456
 
419
457
  return {
420
458
  "success": True,
459
+ "context": context or "current",
421
460
  "threshold": f"{threshold}%",
422
461
  "count": len(overprovisioned),
423
462
  "overprovisioned": overprovisioned[:50]
@@ -436,14 +475,21 @@ def register_cost_tools(server, non_destructive: bool):
436
475
  )
437
476
  def get_resource_trends(
438
477
  namespace: Optional[str] = None,
439
- resource_type: str = "pods"
478
+ resource_type: str = "pods",
479
+ context: str = ""
440
480
  ) -> Dict[str, Any]:
441
- """Get current resource usage snapshot for trend analysis (requires metrics-server)."""
481
+ """Get current resource usage snapshot for trend analysis (requires metrics-server).
482
+
483
+ Args:
484
+ namespace: Target namespace (optional, all namespaces if not specified)
485
+ resource_type: Type of resource to analyze (pods or nodes)
486
+ context: Kubernetes context to use (optional, uses current context if not specified)
487
+ """
442
488
  try:
443
489
  if resource_type == "nodes":
444
- cmd = ["kubectl", "top", "nodes", "--no-headers"]
490
+ cmd = ["kubectl"] + _get_kubectl_context_args(context) + ["top", "nodes", "--no-headers"]
445
491
  else:
446
- cmd = ["kubectl", "top", "pods", "--no-headers"]
492
+ cmd = ["kubectl"] + _get_kubectl_context_args(context) + ["top", "pods", "--no-headers"]
447
493
  if namespace:
448
494
  cmd.extend(["-n", namespace])
449
495
  else:
@@ -501,6 +547,7 @@ def register_cost_tools(server, non_destructive: bool):
501
547
 
502
548
  return {
503
549
  "success": True,
550
+ "context": context or "current",
504
551
  "timestamp": datetime.utcnow().isoformat() + "Z",
505
552
  "resourceType": resource_type,
506
553
  "summary": {
@@ -523,12 +570,14 @@ def register_cost_tools(server, non_destructive: bool):
523
570
  readOnlyHint=True,
524
571
  ),
525
572
  )
526
- def get_namespace_cost_allocation() -> Dict[str, Any]:
527
- """Calculate resource allocation percentages across all namespaces."""
573
+ def get_namespace_cost_allocation(context: str = "") -> Dict[str, Any]:
574
+ """Calculate resource allocation percentages across all namespaces.
575
+
576
+ Args:
577
+ context: Kubernetes context to use (optional, uses current context if not specified)
578
+ """
528
579
  try:
529
- from kubernetes import client, config
530
- config.load_kube_config()
531
- v1 = client.CoreV1Api()
580
+ v1 = get_k8s_client(context)
532
581
 
533
582
  pods = v1.list_pod_for_all_namespaces().items
534
583
 
@@ -573,6 +622,7 @@ def register_cost_tools(server, non_destructive: bool):
573
622
 
574
623
  return {
575
624
  "success": True,
625
+ "context": context or "current",
576
626
  "clusterTotals": {
577
627
  "totalCpuMillicores": total_cpu,
578
628
  "totalMemoryMi": round(total_memory / (1024 * 1024), 2),
@@ -592,21 +642,26 @@ def register_cost_tools(server, non_destructive: bool):
592
642
  )
593
643
  def optimize_resource_requests(
594
644
  namespace: str,
595
- deployment_name: Optional[str] = None
645
+ deployment_name: Optional[str] = None,
646
+ context: str = ""
596
647
  ) -> Dict[str, Any]:
597
- """Suggest optimal resource requests based on current usage patterns."""
648
+ """Suggest optimal resource requests based on current usage patterns.
649
+
650
+ Args:
651
+ namespace: Target namespace
652
+ deployment_name: Specific deployment to analyze (optional, all deployments if not specified)
653
+ context: Kubernetes context to use (optional, uses current context if not specified)
654
+ """
598
655
  try:
599
- from kubernetes import client, config
600
- config.load_kube_config()
601
- apps = client.AppsV1Api()
602
- v1 = client.CoreV1Api()
656
+ apps = get_apps_client(context)
657
+ v1 = get_k8s_client(context)
603
658
 
604
659
  if deployment_name:
605
660
  deployments = [apps.read_namespaced_deployment(deployment_name, namespace)]
606
661
  else:
607
662
  deployments = apps.list_namespaced_deployment(namespace).items
608
663
 
609
- cmd = ["kubectl", "top", "pods", "-n", namespace, "--no-headers"]
664
+ cmd = ["kubectl"] + _get_kubectl_context_args(context) + ["top", "pods", "-n", namespace, "--no-headers"]
610
665
  result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
611
666
 
612
667
  usage_map = {}
@@ -669,6 +724,7 @@ def register_cost_tools(server, non_destructive: bool):
669
724
 
670
725
  return {
671
726
  "success": True,
727
+ "context": context or "current",
672
728
  "namespace": namespace,
673
729
  "note": "Suggestions based on current usage + 20% buffer. Monitor over time for accuracy.",
674
730
  "suggestions": suggestions