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,12 +1,21 @@
1
1
  import logging
2
2
  import subprocess
3
- from typing import Any, Dict, Optional
3
+ from typing import Any, Dict, List, Optional
4
4
 
5
5
  from mcp.types import ToolAnnotations
6
6
 
7
+ from ..k8s_config import get_k8s_client, get_networking_client
8
+
7
9
  logger = logging.getLogger("mcp-server")
8
10
 
9
11
 
12
+ def _get_kubectl_context_args(context: str) -> List[str]:
13
+ """Get kubectl context arguments if context is specified."""
14
+ if context:
15
+ return ["--context", context]
16
+ return []
17
+
18
+
10
19
  def register_networking_tools(server, non_destructive: bool):
11
20
  """Register networking-related tools."""
12
21
 
@@ -16,12 +25,18 @@ def register_networking_tools(server, non_destructive: bool):
16
25
  readOnlyHint=True,
17
26
  ),
18
27
  )
19
- def get_ingress(namespace: Optional[str] = None) -> Dict[str, Any]:
20
- """Get Ingress resources in a namespace or cluster-wide."""
28
+ def get_ingress(
29
+ namespace: Optional[str] = None,
30
+ context: str = ""
31
+ ) -> Dict[str, Any]:
32
+ """Get Ingress resources in a namespace or cluster-wide.
33
+
34
+ Args:
35
+ namespace: Namespace to list ingresses from (all namespaces if not specified)
36
+ context: Kubernetes context to use (uses current context if not specified)
37
+ """
21
38
  try:
22
- from kubernetes import client, config
23
- config.load_kube_config()
24
- networking = client.NetworkingV1Api()
39
+ networking = get_networking_client(context)
25
40
 
26
41
  if namespace:
27
42
  ingresses = networking.list_namespaced_ingress(namespace)
@@ -30,6 +45,7 @@ def register_networking_tools(server, non_destructive: bool):
30
45
 
31
46
  return {
32
47
  "success": True,
48
+ "context": context or "current",
33
49
  "ingresses": [
34
50
  {
35
51
  "name": ing.metadata.name,
@@ -70,12 +86,20 @@ def register_networking_tools(server, non_destructive: bool):
70
86
  readOnlyHint=True,
71
87
  ),
72
88
  )
73
- def get_endpoints(namespace: Optional[str] = None, service_name: Optional[str] = None) -> Dict[str, Any]:
74
- """Get Endpoints for services."""
89
+ def get_endpoints(
90
+ namespace: Optional[str] = None,
91
+ service_name: Optional[str] = None,
92
+ context: str = ""
93
+ ) -> Dict[str, Any]:
94
+ """Get Endpoints for services.
95
+
96
+ Args:
97
+ namespace: Namespace to list endpoints from (all namespaces if not specified)
98
+ service_name: Filter by specific service name
99
+ context: Kubernetes context to use (uses current context if not specified)
100
+ """
75
101
  try:
76
- from kubernetes import client, config
77
- config.load_kube_config()
78
- v1 = client.CoreV1Api()
102
+ v1 = get_k8s_client(context)
79
103
 
80
104
  if namespace:
81
105
  endpoints = v1.list_namespaced_endpoints(namespace)
@@ -107,7 +131,11 @@ def register_networking_tools(server, non_destructive: bool):
107
131
  "addresses": addresses
108
132
  })
109
133
 
110
- return {"success": True, "endpoints": result}
134
+ return {
135
+ "success": True,
136
+ "context": context or "current",
137
+ "endpoints": result
138
+ }
111
139
  except Exception as e:
112
140
  logger.error(f"Error getting Endpoints: {e}")
113
141
  return {"success": False, "error": str(e)}
@@ -122,14 +150,24 @@ def register_networking_tools(server, non_destructive: bool):
122
150
  source_pod: str,
123
151
  target: str,
124
152
  source_namespace: str = "default",
125
- port: Optional[int] = None
153
+ port: Optional[int] = None,
154
+ context: str = ""
126
155
  ) -> Dict[str, Any]:
127
- """Diagnose network connectivity between pods or to external endpoints."""
156
+ """Diagnose network connectivity between pods or to external endpoints.
157
+
158
+ Args:
159
+ source_pod: Name of the pod to test from
160
+ target: Target hostname or IP to test connectivity to
161
+ source_namespace: Namespace of the source pod
162
+ port: Optional port number to test
163
+ context: Kubernetes context to use (uses current context if not specified)
164
+ """
128
165
  try:
129
- results = {"success": True, "tests": []}
166
+ results = {"success": True, "context": context or "current", "tests": []}
167
+ ctx_args = _get_kubectl_context_args(context)
130
168
 
131
169
  # Test DNS resolution
132
- dns_cmd = ["kubectl", "exec", source_pod, "-n", source_namespace, "--", "nslookup", target]
170
+ dns_cmd = ["kubectl"] + ctx_args + ["exec", source_pod, "-n", source_namespace, "--", "nslookup", target]
133
171
  dns_result = subprocess.run(dns_cmd, capture_output=True, text=True, timeout=30)
134
172
  results["tests"].append({
135
173
  "test": "DNS Resolution",
@@ -139,10 +177,10 @@ def register_networking_tools(server, non_destructive: bool):
139
177
 
140
178
  # Test connectivity
141
179
  if port:
142
- conn_cmd = ["kubectl", "exec", source_pod, "-n", source_namespace, "--",
180
+ conn_cmd = ["kubectl"] + ctx_args + ["exec", source_pod, "-n", source_namespace, "--",
143
181
  "nc", "-zv", "-w", "5", target, str(port)]
144
182
  else:
145
- conn_cmd = ["kubectl", "exec", source_pod, "-n", source_namespace, "--",
183
+ conn_cmd = ["kubectl"] + ctx_args + ["exec", source_pod, "-n", source_namespace, "--",
146
184
  "ping", "-c", "3", target]
147
185
 
148
186
  conn_result = subprocess.run(conn_cmd, capture_output=True, text=True, timeout=30)
@@ -166,24 +204,35 @@ def register_networking_tools(server, non_destructive: bool):
166
204
  readOnlyHint=True,
167
205
  ),
168
206
  )
169
- def check_dns_resolution(hostname: str, namespace: str = "default", pod_name: Optional[str] = None) -> Dict[str, Any]:
170
- """Check DNS resolution from within the cluster."""
207
+ def check_dns_resolution(
208
+ hostname: str,
209
+ namespace: str = "default",
210
+ pod_name: Optional[str] = None,
211
+ context: str = ""
212
+ ) -> Dict[str, Any]:
213
+ """Check DNS resolution from within the cluster.
214
+
215
+ Args:
216
+ hostname: Hostname to resolve
217
+ namespace: Namespace to find a pod in
218
+ pod_name: Specific pod to use for testing (optional)
219
+ context: Kubernetes context to use (uses current context if not specified)
220
+ """
171
221
  try:
172
222
  if not pod_name:
173
223
  # Find a running pod in the namespace
174
- from kubernetes import client, config
175
- config.load_kube_config()
176
- v1 = client.CoreV1Api()
224
+ v1 = get_k8s_client(context)
177
225
  pods = v1.list_namespaced_pod(namespace, field_selector="status.phase=Running")
178
226
  if not pods.items:
179
227
  return {"success": False, "error": f"No running pods in namespace {namespace}"}
180
228
  pod_name = pods.items[0].metadata.name
181
229
 
182
- cmd = ["kubectl", "exec", pod_name, "-n", namespace, "--", "nslookup", hostname]
230
+ cmd = ["kubectl"] + _get_kubectl_context_args(context) + ["exec", pod_name, "-n", namespace, "--", "nslookup", hostname]
183
231
  result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
184
232
 
185
233
  return {
186
234
  "success": result.returncode == 0,
235
+ "context": context or "current",
187
236
  "hostname": hostname,
188
237
  "pod": pod_name,
189
238
  "namespace": namespace,
@@ -201,12 +250,20 @@ def register_networking_tools(server, non_destructive: bool):
201
250
  readOnlyHint=True,
202
251
  ),
203
252
  )
204
- def trace_service_chain(service_name: str, namespace: str = "default") -> Dict[str, Any]:
205
- """Trace the connection chain from service to pods."""
253
+ def trace_service_chain(
254
+ service_name: str,
255
+ namespace: str = "default",
256
+ context: str = ""
257
+ ) -> Dict[str, Any]:
258
+ """Trace the connection chain from service to pods.
259
+
260
+ Args:
261
+ service_name: Name of the service to trace
262
+ namespace: Namespace of the service
263
+ context: Kubernetes context to use (uses current context if not specified)
264
+ """
206
265
  try:
207
- from kubernetes import client, config
208
- config.load_kube_config()
209
- v1 = client.CoreV1Api()
266
+ v1 = get_k8s_client(context)
210
267
 
211
268
  # Get service
212
269
  service = v1.read_namespaced_service(service_name, namespace)
@@ -227,6 +284,7 @@ def register_networking_tools(server, non_destructive: bool):
227
284
 
228
285
  result = {
229
286
  "success": True,
287
+ "context": context or "current",
230
288
  "service": {
231
289
  "name": service.metadata.name,
232
290
  "type": service.spec.type,
@@ -280,14 +338,30 @@ def register_networking_tools(server, non_destructive: bool):
280
338
  destructiveHint=True,
281
339
  ),
282
340
  )
283
- def port_forward(pod_name: str, local_port: int, pod_port: int, namespace: Optional[str] = "default") -> Dict[str, Any]:
284
- """Start port forwarding to a pod (note: this starts a background process)."""
341
+ def port_forward(
342
+ pod_name: str,
343
+ local_port: int,
344
+ pod_port: int,
345
+ namespace: Optional[str] = "default",
346
+ context: str = ""
347
+ ) -> Dict[str, Any]:
348
+ """Start port forwarding to a pod (note: this starts a background process).
349
+
350
+ Args:
351
+ pod_name: Name of the pod to forward to
352
+ local_port: Local port to listen on
353
+ pod_port: Pod port to forward to
354
+ namespace: Namespace of the pod
355
+ context: Kubernetes context to use (uses current context if not specified)
356
+ """
285
357
  try:
286
358
  import os
287
- cmd = f"kubectl port-forward {pod_name} {local_port}:{pod_port} -n {namespace} &"
359
+ ctx_args = " ".join(_get_kubectl_context_args(context))
360
+ cmd = f"kubectl {ctx_args} port-forward {pod_name} {local_port}:{pod_port} -n {namespace} &"
288
361
  os.system(cmd)
289
362
  return {
290
363
  "success": True,
364
+ "context": context or "current",
291
365
  "message": f"Port forwarding started: localhost:{local_port} -> {pod_name}:{pod_port}",
292
366
  "note": "Port forwarding is running in background. Use 'pkill -f port-forward' to stop."
293
367
  }