kubectl-mcp-server 1.18.0__py3-none-any.whl → 1.19.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.18.0.dist-info → kubectl_mcp_server-1.19.0.dist-info}/METADATA +1 -48
- {kubectl_mcp_server-1.18.0.dist-info → kubectl_mcp_server-1.19.0.dist-info}/RECORD +8 -9
- kubectl_mcp_tool/__init__.py +1 -1
- kubectl_mcp_tool/k8s_config.py +1 -127
- kubectl_mcp_tool/providers.py +0 -347
- {kubectl_mcp_server-1.18.0.dist-info → kubectl_mcp_server-1.19.0.dist-info}/WHEEL +0 -0
- {kubectl_mcp_server-1.18.0.dist-info → kubectl_mcp_server-1.19.0.dist-info}/entry_points.txt +0 -0
- {kubectl_mcp_server-1.18.0.dist-info → kubectl_mcp_server-1.19.0.dist-info}/licenses/LICENSE +0 -0
- {kubectl_mcp_server-1.18.0.dist-info → kubectl_mcp_server-1.19.0.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kubectl-mcp-server
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.19.0
|
|
4
4
|
Summary: A Model Context Protocol (MCP) server for Kubernetes with 220+ tools, 8 resources, and 8 prompts
|
|
5
5
|
Home-page: https://github.com/rohitg00/kubectl-mcp-server
|
|
6
6
|
Author: Rohit Ghumare
|
|
@@ -121,7 +121,6 @@ pip install kubectl-mcp-server[ui]
|
|
|
121
121
|
- [In-Cluster Deployment](#in-cluster-deployment)
|
|
122
122
|
- [Multi-Cluster Support](#multi-cluster-support)
|
|
123
123
|
- [Architecture](#architecture)
|
|
124
|
-
- [Agent Skills](#agent-skills-24-skills-for-ai-coding-agents)
|
|
125
124
|
- [Development & Testing](#development--testing)
|
|
126
125
|
- [Contributing](#contributing)
|
|
127
126
|
- [Support & Community](#support--community)
|
|
@@ -876,52 +875,6 @@ kubectl_mcp_tool/
|
|
|
876
875
|
└── cli/ # CLI interface
|
|
877
876
|
```
|
|
878
877
|
|
|
879
|
-
## Agent Skills (24 Skills for AI Coding Agents)
|
|
880
|
-
|
|
881
|
-
Extend your AI coding agent with Kubernetes expertise using our [Agent Skills](https://agentskills.io) library. Skills provide specialized knowledge and workflows that agents can load on demand.
|
|
882
|
-
|
|
883
|
-
### Quick Install
|
|
884
|
-
|
|
885
|
-
```bash
|
|
886
|
-
# Copy all skills to Claude
|
|
887
|
-
cp -r kubernetes-skills/claude/* ~/.claude/skills/
|
|
888
|
-
|
|
889
|
-
# Or install specific skills
|
|
890
|
-
cp -r kubernetes-skills/claude/k8s-helm ~/.claude/skills/
|
|
891
|
-
```
|
|
892
|
-
|
|
893
|
-
### Available Skills (24)
|
|
894
|
-
|
|
895
|
-
| Category | Skills |
|
|
896
|
-
|----------|--------|
|
|
897
|
-
| **Core Resources** | k8s-core, k8s-networking, k8s-storage |
|
|
898
|
-
| **Workloads** | k8s-deploy, k8s-operations, k8s-helm |
|
|
899
|
-
| **Observability** | k8s-diagnostics, k8s-troubleshoot, k8s-incident |
|
|
900
|
-
| **Security** | k8s-security, k8s-policy, k8s-certs |
|
|
901
|
-
| **GitOps** | k8s-gitops, k8s-rollouts |
|
|
902
|
-
| **Scaling** | k8s-autoscaling, k8s-cost, k8s-backup |
|
|
903
|
-
| **Multi-Cluster** | k8s-multicluster, k8s-capi, k8s-kubevirt |
|
|
904
|
-
| **Networking** | k8s-service-mesh, k8s-cilium |
|
|
905
|
-
| **Tools** | k8s-browser, k8s-cli |
|
|
906
|
-
|
|
907
|
-
### Convert to Other Agents
|
|
908
|
-
|
|
909
|
-
Use [SkillKit](https://github.com/rohitg00/skillkit) to convert skills to your preferred AI agent format:
|
|
910
|
-
|
|
911
|
-
```bash
|
|
912
|
-
npm install -g skillkit
|
|
913
|
-
|
|
914
|
-
# Convert to Cursor format
|
|
915
|
-
skillkit translate kubernetes-skills/claude --to cursor --output .cursor/rules/
|
|
916
|
-
|
|
917
|
-
# Convert to Codex format
|
|
918
|
-
skillkit translate kubernetes-skills/claude --to codex --output ./
|
|
919
|
-
```
|
|
920
|
-
|
|
921
|
-
**Supported agents:** Claude, Cursor, Codex, Gemini CLI, GitHub Copilot, Goose, Windsurf, Roo, Amp, and more.
|
|
922
|
-
|
|
923
|
-
See [kubernetes-skills/README.md](kubernetes-skills/README.md) for full documentation.
|
|
924
|
-
|
|
925
878
|
## Multi-Cluster Support
|
|
926
879
|
|
|
927
880
|
Seamlessly manage multiple Kubernetes clusters through natural language. **Every tool** supports an optional `context` parameter to target any cluster without switching contexts.
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
kubectl_mcp_server-1.
|
|
2
|
-
kubectl_mcp_tool/__init__.py,sha256=
|
|
1
|
+
kubectl_mcp_server-1.19.0.dist-info/licenses/LICENSE,sha256=nH9Z0W0WNH2oQ4cPrBAU8ldDcHfeI6NUbkSGiazYWgQ,1070
|
|
2
|
+
kubectl_mcp_tool/__init__.py,sha256=e_8lin1zD6cAw_MY-Uvgql-zjjoKlcF9zuO6kzCPbaU,580
|
|
3
3
|
kubectl_mcp_tool/__main__.py,sha256=CE6cTD6PA71Ap0i5_gE17Pb9FcedOJmtGRNzZ5-TFSc,1490
|
|
4
4
|
kubectl_mcp_tool/crd_detector.py,sha256=xLZSyg3iIe_MjSMI3mESe2VK8wGJzu40rJ7F72fULBs,7139
|
|
5
5
|
kubectl_mcp_tool/diagnostics.py,sha256=uwolSoHadRkB-J8PAsabbexfj6sTNCIIRRrABBRXoTU,11776
|
|
6
|
-
kubectl_mcp_tool/k8s_config.py,sha256=
|
|
6
|
+
kubectl_mcp_tool/k8s_config.py,sha256=tjP_VONsoqhd5qn0LPB25aKhiPw2qCYhJ_B52NbRYDo,15746
|
|
7
7
|
kubectl_mcp_tool/mcp_server.py,sha256=-p-r-NRaX6UiHUebvO4OIozRlA2PX851rmAlW_rO3Tk,31112
|
|
8
|
-
kubectl_mcp_tool/providers.py,sha256=_LMpVbCWgFrsRO_u4krSw7wFNYapzp29WtiEh-sbBeY,11521
|
|
9
8
|
kubectl_mcp_tool/safety.py,sha256=7bx7tUH_hT4Y_eTnpH5knnHTsvnIZvI_0oo9MSBe_z8,5036
|
|
10
9
|
kubectl_mcp_tool/auth/__init__.py,sha256=ot8ivZZkDtV8Rg0y1UYruwobKCPyxX1svqh35wWxKvY,347
|
|
11
10
|
kubectl_mcp_tool/auth/config.py,sha256=wi3wuJNMyDqMeluDHL0MaJyedIFv5CFVxiUaEVaTvzk,2267
|
|
@@ -69,8 +68,8 @@ tests/test_resources.py,sha256=Z0Ex8WdRz-B3VZa1s0eAaDDGbhy7dRdqy1uFVOe2Qbo,12689
|
|
|
69
68
|
tests/test_safety.py,sha256=5IOBQbcH2r8tANQD7ymsR6pBc6O50IspcFsuwrnwh1M,8292
|
|
70
69
|
tests/test_server.py,sha256=lLvgbqutnivSgQMNrki0O48whBQt0UXjdwT047nf0nw,14415
|
|
71
70
|
tests/test_tools.py,sha256=g3moVSpiFgqC107M4tBAQ6xU600rlYMPhnknyYhnTIc,34792
|
|
72
|
-
kubectl_mcp_server-1.
|
|
73
|
-
kubectl_mcp_server-1.
|
|
74
|
-
kubectl_mcp_server-1.
|
|
75
|
-
kubectl_mcp_server-1.
|
|
76
|
-
kubectl_mcp_server-1.
|
|
71
|
+
kubectl_mcp_server-1.19.0.dist-info/METADATA,sha256=Ly4r-eDtkBYbDXixx9wSONKOt2S00GhAWwoCwp4KUgE,38116
|
|
72
|
+
kubectl_mcp_server-1.19.0.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
|
|
73
|
+
kubectl_mcp_server-1.19.0.dist-info/entry_points.txt,sha256=zeOxQGaNC4r58deEmqsLCU5hfMjF0VqFUt9P5wWsKEM,109
|
|
74
|
+
kubectl_mcp_server-1.19.0.dist-info/top_level.txt,sha256=o5IpfOGG-lqU8rVWJeK9aYC0r4f6qEX09QiBhZlYbkQ,23
|
|
75
|
+
kubectl_mcp_server-1.19.0.dist-info/RECORD,,
|
kubectl_mcp_tool/__init__.py
CHANGED
|
@@ -7,7 +7,7 @@ with Kubernetes clusters through natural language commands.
|
|
|
7
7
|
For more information, see: https://github.com/rohitg00/kubectl-mcp-server
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
__version__ = "1.
|
|
10
|
+
__version__ = "1.19.0"
|
|
11
11
|
|
|
12
12
|
from .mcp_server import MCPServer
|
|
13
13
|
from .diagnostics import run_diagnostics, check_kubectl_installation, check_cluster_connection
|
kubectl_mcp_tool/k8s_config.py
CHANGED
|
@@ -6,39 +6,14 @@ Supports multi-cluster operations with context targeting.
|
|
|
6
6
|
|
|
7
7
|
This module provides context-aware client creation for multi-cluster support.
|
|
8
8
|
All get_*_client() functions accept an optional 'context' parameter.
|
|
9
|
-
|
|
10
|
-
Environment Variables:
|
|
11
|
-
MCP_K8S_PROVIDER: Provider type (kubeconfig, in-cluster, single)
|
|
12
|
-
MCP_K8S_KUBECONFIG: Path to kubeconfig file
|
|
13
|
-
MCP_K8S_CONTEXT: Default context for single provider
|
|
14
|
-
MCP_K8S_QPS: API rate limit (default: 100)
|
|
15
|
-
MCP_K8S_BURST: API burst limit (default: 200)
|
|
16
|
-
MCP_K8S_TIMEOUT: Request timeout in seconds (default: 30)
|
|
17
9
|
"""
|
|
18
10
|
|
|
19
11
|
import os
|
|
20
12
|
import logging
|
|
21
|
-
from typing import Optional, Any
|
|
13
|
+
from typing import Optional, Any
|
|
22
14
|
|
|
23
15
|
logger = logging.getLogger("mcp-server")
|
|
24
16
|
|
|
25
|
-
# Try to import provider module for enhanced features
|
|
26
|
-
try:
|
|
27
|
-
from .providers import (
|
|
28
|
-
KubernetesProvider,
|
|
29
|
-
ProviderConfig,
|
|
30
|
-
ProviderType,
|
|
31
|
-
UnknownContextError,
|
|
32
|
-
get_provider,
|
|
33
|
-
get_context_names,
|
|
34
|
-
get_current_context as provider_get_current_context,
|
|
35
|
-
validate_context,
|
|
36
|
-
)
|
|
37
|
-
_HAS_PROVIDER = True
|
|
38
|
-
except ImportError:
|
|
39
|
-
_HAS_PROVIDER = False
|
|
40
|
-
logger.debug("Provider module not available, using basic config")
|
|
41
|
-
|
|
42
17
|
_config_loaded = False
|
|
43
18
|
_original_load_kube_config = None
|
|
44
19
|
|
|
@@ -139,29 +114,12 @@ def _load_config_for_context(context: str = "") -> Any:
|
|
|
139
114
|
"""
|
|
140
115
|
Load kubernetes config for a specific context and return ApiClient.
|
|
141
116
|
|
|
142
|
-
Uses the provider module for caching when available.
|
|
143
|
-
|
|
144
117
|
Args:
|
|
145
118
|
context: Context name (empty for default)
|
|
146
119
|
|
|
147
120
|
Returns:
|
|
148
121
|
kubernetes.client.ApiClient configured for the context
|
|
149
|
-
|
|
150
|
-
Raises:
|
|
151
|
-
UnknownContextError: If context is not found (when provider available)
|
|
152
|
-
RuntimeError: If config cannot be loaded
|
|
153
122
|
"""
|
|
154
|
-
# Use provider module if available (provides caching and validation)
|
|
155
|
-
if _HAS_PROVIDER:
|
|
156
|
-
try:
|
|
157
|
-
provider = get_provider()
|
|
158
|
-
return provider.get_api_client(context)
|
|
159
|
-
except UnknownContextError:
|
|
160
|
-
raise
|
|
161
|
-
except Exception as e:
|
|
162
|
-
logger.warning(f"Provider failed, falling back to basic config: {e}")
|
|
163
|
-
|
|
164
|
-
# Fallback to basic config loading
|
|
165
123
|
from kubernetes import client, config
|
|
166
124
|
from kubernetes.config.config_exception import ConfigException
|
|
167
125
|
|
|
@@ -496,25 +454,6 @@ def list_contexts() -> list:
|
|
|
496
454
|
Returns:
|
|
497
455
|
List of context dictionaries with name, cluster, user, namespace
|
|
498
456
|
"""
|
|
499
|
-
# Use provider if available
|
|
500
|
-
if _HAS_PROVIDER:
|
|
501
|
-
try:
|
|
502
|
-
provider = get_provider()
|
|
503
|
-
contexts = provider.list_contexts()
|
|
504
|
-
return [
|
|
505
|
-
{
|
|
506
|
-
"name": ctx.name,
|
|
507
|
-
"cluster": ctx.cluster,
|
|
508
|
-
"user": ctx.user,
|
|
509
|
-
"namespace": ctx.namespace,
|
|
510
|
-
"active": ctx.is_active
|
|
511
|
-
}
|
|
512
|
-
for ctx in contexts
|
|
513
|
-
]
|
|
514
|
-
except Exception as e:
|
|
515
|
-
logger.warning(f"Provider list_contexts failed: {e}")
|
|
516
|
-
|
|
517
|
-
# Fallback to direct kubeconfig reading
|
|
518
457
|
from kubernetes import config
|
|
519
458
|
|
|
520
459
|
try:
|
|
@@ -545,14 +484,6 @@ def get_active_context() -> Optional[str]:
|
|
|
545
484
|
Returns:
|
|
546
485
|
Active context name or None
|
|
547
486
|
"""
|
|
548
|
-
# Use provider if available
|
|
549
|
-
if _HAS_PROVIDER:
|
|
550
|
-
try:
|
|
551
|
-
return provider_get_current_context()
|
|
552
|
-
except Exception as e:
|
|
553
|
-
logger.warning(f"Provider get_current_context failed: {e}")
|
|
554
|
-
|
|
555
|
-
# Fallback to direct kubeconfig reading
|
|
556
487
|
from kubernetes import config
|
|
557
488
|
|
|
558
489
|
try:
|
|
@@ -597,60 +528,3 @@ def _get_kubectl_context_args(context: str = "") -> list:
|
|
|
597
528
|
if context and context.strip():
|
|
598
529
|
return ["--context", context.strip()]
|
|
599
530
|
return []
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
# Re-export provider types for convenience
|
|
603
|
-
if _HAS_PROVIDER:
|
|
604
|
-
__all__ = [
|
|
605
|
-
# Client functions
|
|
606
|
-
"get_k8s_client",
|
|
607
|
-
"get_apps_client",
|
|
608
|
-
"get_rbac_client",
|
|
609
|
-
"get_networking_client",
|
|
610
|
-
"get_storage_client",
|
|
611
|
-
"get_batch_client",
|
|
612
|
-
"get_autoscaling_client",
|
|
613
|
-
"get_policy_client",
|
|
614
|
-
"get_custom_objects_client",
|
|
615
|
-
"get_version_client",
|
|
616
|
-
"get_admissionregistration_client",
|
|
617
|
-
"get_apiextensions_client",
|
|
618
|
-
"get_coordination_client",
|
|
619
|
-
"get_events_client",
|
|
620
|
-
# Config functions
|
|
621
|
-
"load_kubernetes_config",
|
|
622
|
-
"patch_kubernetes_config",
|
|
623
|
-
# Context functions
|
|
624
|
-
"list_contexts",
|
|
625
|
-
"get_active_context",
|
|
626
|
-
"context_exists",
|
|
627
|
-
# Provider types (when available)
|
|
628
|
-
"KubernetesProvider",
|
|
629
|
-
"ProviderConfig",
|
|
630
|
-
"ProviderType",
|
|
631
|
-
"UnknownContextError",
|
|
632
|
-
"get_provider",
|
|
633
|
-
"validate_context",
|
|
634
|
-
]
|
|
635
|
-
else:
|
|
636
|
-
__all__ = [
|
|
637
|
-
"get_k8s_client",
|
|
638
|
-
"get_apps_client",
|
|
639
|
-
"get_rbac_client",
|
|
640
|
-
"get_networking_client",
|
|
641
|
-
"get_storage_client",
|
|
642
|
-
"get_batch_client",
|
|
643
|
-
"get_autoscaling_client",
|
|
644
|
-
"get_policy_client",
|
|
645
|
-
"get_custom_objects_client",
|
|
646
|
-
"get_version_client",
|
|
647
|
-
"get_admissionregistration_client",
|
|
648
|
-
"get_apiextensions_client",
|
|
649
|
-
"get_coordination_client",
|
|
650
|
-
"get_events_client",
|
|
651
|
-
"load_kubernetes_config",
|
|
652
|
-
"patch_kubernetes_config",
|
|
653
|
-
"list_contexts",
|
|
654
|
-
"get_active_context",
|
|
655
|
-
"context_exists",
|
|
656
|
-
]
|
kubectl_mcp_tool/providers.py
DELETED
|
@@ -1,347 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import logging
|
|
3
|
-
from enum import Enum
|
|
4
|
-
from typing import Any, Dict, List, Optional, Tuple
|
|
5
|
-
from dataclasses import dataclass, field
|
|
6
|
-
from functools import lru_cache
|
|
7
|
-
|
|
8
|
-
logger = logging.getLogger("mcp-server")
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class ProviderType(Enum):
|
|
12
|
-
"""Kubernetes provider types."""
|
|
13
|
-
KUBECONFIG = "kubeconfig"
|
|
14
|
-
IN_CLUSTER = "in-cluster"
|
|
15
|
-
SINGLE = "single"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class UnknownContextError(Exception):
|
|
19
|
-
"""Raised when a requested context is not found."""
|
|
20
|
-
def __init__(self, context: str, available: List[str] = None):
|
|
21
|
-
self.context = context
|
|
22
|
-
self.available = available or []
|
|
23
|
-
msg = f"Context '{context}' not found"
|
|
24
|
-
if self.available:
|
|
25
|
-
msg += f". Available contexts: {', '.join(self.available)}"
|
|
26
|
-
super().__init__(msg)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
class ProviderError(Exception):
|
|
30
|
-
"""Raised when provider configuration is invalid."""
|
|
31
|
-
pass
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
@dataclass
|
|
35
|
-
class ProviderConfig:
|
|
36
|
-
"""Configuration for Kubernetes provider."""
|
|
37
|
-
provider_type: ProviderType = ProviderType.KUBECONFIG
|
|
38
|
-
kubeconfig_path: str = ""
|
|
39
|
-
context: str = ""
|
|
40
|
-
qps: float = 100.0
|
|
41
|
-
burst: int = 200
|
|
42
|
-
timeout: int = 30
|
|
43
|
-
|
|
44
|
-
@classmethod
|
|
45
|
-
def from_env(cls) -> "ProviderConfig":
|
|
46
|
-
"""Create config from environment variables."""
|
|
47
|
-
provider_str = os.environ.get("MCP_K8S_PROVIDER", "kubeconfig").lower()
|
|
48
|
-
|
|
49
|
-
try:
|
|
50
|
-
provider_type = ProviderType(provider_str)
|
|
51
|
-
except ValueError:
|
|
52
|
-
logger.warning(f"Unknown provider type '{provider_str}', using kubeconfig")
|
|
53
|
-
provider_type = ProviderType.KUBECONFIG
|
|
54
|
-
|
|
55
|
-
kubeconfig_path = os.environ.get(
|
|
56
|
-
"MCP_K8S_KUBECONFIG",
|
|
57
|
-
os.environ.get("KUBECONFIG", "~/.kube/config")
|
|
58
|
-
)
|
|
59
|
-
kubeconfig_path = os.path.expanduser(kubeconfig_path)
|
|
60
|
-
|
|
61
|
-
return cls(
|
|
62
|
-
provider_type=provider_type,
|
|
63
|
-
kubeconfig_path=kubeconfig_path,
|
|
64
|
-
context=os.environ.get("MCP_K8S_CONTEXT", ""),
|
|
65
|
-
qps=float(os.environ.get("MCP_K8S_QPS", "100")),
|
|
66
|
-
burst=int(os.environ.get("MCP_K8S_BURST", "200")),
|
|
67
|
-
timeout=int(os.environ.get("MCP_K8S_TIMEOUT", "30")),
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
@dataclass
|
|
72
|
-
class ContextInfo:
|
|
73
|
-
"""Information about a kubeconfig context."""
|
|
74
|
-
name: str
|
|
75
|
-
cluster: str
|
|
76
|
-
user: str
|
|
77
|
-
namespace: str = "default"
|
|
78
|
-
is_active: bool = False
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
class KubernetesProvider:
|
|
82
|
-
"""
|
|
83
|
-
Multi-cluster Kubernetes provider.
|
|
84
|
-
|
|
85
|
-
Manages connections to multiple Kubernetes clusters based on
|
|
86
|
-
kubeconfig contexts or in-cluster configuration.
|
|
87
|
-
"""
|
|
88
|
-
|
|
89
|
-
_instance: Optional["KubernetesProvider"] = None
|
|
90
|
-
|
|
91
|
-
def __init__(self, config: Optional[ProviderConfig] = None):
|
|
92
|
-
"""Initialize provider with configuration."""
|
|
93
|
-
self.config = config or ProviderConfig.from_env()
|
|
94
|
-
self._api_clients: Dict[str, Any] = {}
|
|
95
|
-
self._in_cluster = False
|
|
96
|
-
self._contexts_cache: Optional[List[ContextInfo]] = None
|
|
97
|
-
self._active_context: Optional[str] = None
|
|
98
|
-
|
|
99
|
-
self._initialize()
|
|
100
|
-
|
|
101
|
-
@classmethod
|
|
102
|
-
def get_instance(cls) -> "KubernetesProvider":
|
|
103
|
-
"""Get singleton provider instance."""
|
|
104
|
-
if cls._instance is None:
|
|
105
|
-
cls._instance = cls()
|
|
106
|
-
return cls._instance
|
|
107
|
-
|
|
108
|
-
@classmethod
|
|
109
|
-
def reset_instance(cls):
|
|
110
|
-
"""Reset singleton instance (for testing)."""
|
|
111
|
-
cls._instance = None
|
|
112
|
-
|
|
113
|
-
def _initialize(self):
|
|
114
|
-
"""Initialize the provider based on type."""
|
|
115
|
-
if self.config.provider_type == ProviderType.IN_CLUSTER:
|
|
116
|
-
self._initialize_in_cluster()
|
|
117
|
-
elif self.config.provider_type == ProviderType.SINGLE:
|
|
118
|
-
self._initialize_single()
|
|
119
|
-
else:
|
|
120
|
-
self._initialize_kubeconfig()
|
|
121
|
-
|
|
122
|
-
def _initialize_in_cluster(self):
|
|
123
|
-
"""Initialize for in-cluster provider."""
|
|
124
|
-
from kubernetes import config
|
|
125
|
-
from kubernetes.config.config_exception import ConfigException
|
|
126
|
-
|
|
127
|
-
try:
|
|
128
|
-
config.load_incluster_config()
|
|
129
|
-
self._in_cluster = True
|
|
130
|
-
self._active_context = "in-cluster"
|
|
131
|
-
logger.info("Initialized in-cluster Kubernetes provider")
|
|
132
|
-
except ConfigException as e:
|
|
133
|
-
raise ProviderError(f"Failed to load in-cluster config: {e}")
|
|
134
|
-
|
|
135
|
-
def _initialize_single(self):
|
|
136
|
-
"""Initialize for single-context provider."""
|
|
137
|
-
if not self.config.context:
|
|
138
|
-
raise ProviderError("MCP_K8S_CONTEXT must be set for 'single' provider")
|
|
139
|
-
|
|
140
|
-
from kubernetes import config
|
|
141
|
-
|
|
142
|
-
try:
|
|
143
|
-
config.load_kube_config(
|
|
144
|
-
config_file=self.config.kubeconfig_path,
|
|
145
|
-
context=self.config.context
|
|
146
|
-
)
|
|
147
|
-
self._active_context = self.config.context
|
|
148
|
-
logger.info(f"Initialized single-context provider: {self.config.context}")
|
|
149
|
-
except Exception as e:
|
|
150
|
-
raise ProviderError(f"Failed to load context '{self.config.context}': {e}")
|
|
151
|
-
|
|
152
|
-
def _initialize_kubeconfig(self):
|
|
153
|
-
"""Initialize for multi-cluster kubeconfig provider."""
|
|
154
|
-
from kubernetes import config
|
|
155
|
-
|
|
156
|
-
try:
|
|
157
|
-
contexts, active = config.list_kube_config_contexts(
|
|
158
|
-
config_file=self.config.kubeconfig_path
|
|
159
|
-
)
|
|
160
|
-
|
|
161
|
-
if active:
|
|
162
|
-
self._active_context = active.get("name")
|
|
163
|
-
|
|
164
|
-
self._contexts_cache = [
|
|
165
|
-
ContextInfo(
|
|
166
|
-
name=ctx.get("name", ""),
|
|
167
|
-
cluster=ctx.get("context", {}).get("cluster", ""),
|
|
168
|
-
user=ctx.get("context", {}).get("user", ""),
|
|
169
|
-
namespace=ctx.get("context", {}).get("namespace", "default"),
|
|
170
|
-
is_active=ctx.get("name") == self._active_context
|
|
171
|
-
)
|
|
172
|
-
for ctx in contexts
|
|
173
|
-
]
|
|
174
|
-
|
|
175
|
-
logger.info(
|
|
176
|
-
f"Initialized kubeconfig provider with {len(self._contexts_cache)} contexts, "
|
|
177
|
-
f"active: {self._active_context}"
|
|
178
|
-
)
|
|
179
|
-
except Exception as e:
|
|
180
|
-
logger.warning(f"Failed to list contexts: {e}")
|
|
181
|
-
self._contexts_cache = []
|
|
182
|
-
|
|
183
|
-
def list_contexts(self) -> List[ContextInfo]:
|
|
184
|
-
"""
|
|
185
|
-
List all available contexts.
|
|
186
|
-
|
|
187
|
-
Returns:
|
|
188
|
-
List of ContextInfo objects
|
|
189
|
-
"""
|
|
190
|
-
if self._in_cluster:
|
|
191
|
-
return [ContextInfo(
|
|
192
|
-
name="in-cluster",
|
|
193
|
-
cluster="in-cluster",
|
|
194
|
-
user="service-account",
|
|
195
|
-
namespace="default",
|
|
196
|
-
is_active=True
|
|
197
|
-
)]
|
|
198
|
-
|
|
199
|
-
if self.config.provider_type == ProviderType.SINGLE:
|
|
200
|
-
from kubernetes import config
|
|
201
|
-
try:
|
|
202
|
-
contexts, _ = config.list_kube_config_contexts(
|
|
203
|
-
config_file=self.config.kubeconfig_path
|
|
204
|
-
)
|
|
205
|
-
for ctx in contexts:
|
|
206
|
-
if ctx.get("name") == self.config.context:
|
|
207
|
-
return [ContextInfo(
|
|
208
|
-
name=ctx.get("name", ""),
|
|
209
|
-
cluster=ctx.get("context", {}).get("cluster", ""),
|
|
210
|
-
user=ctx.get("context", {}).get("user", ""),
|
|
211
|
-
namespace=ctx.get("context", {}).get("namespace", "default"),
|
|
212
|
-
is_active=True
|
|
213
|
-
)]
|
|
214
|
-
except Exception:
|
|
215
|
-
pass
|
|
216
|
-
return []
|
|
217
|
-
|
|
218
|
-
self._refresh_contexts_cache()
|
|
219
|
-
return self._contexts_cache or []
|
|
220
|
-
|
|
221
|
-
def _refresh_contexts_cache(self):
|
|
222
|
-
"""Refresh the contexts cache from kubeconfig."""
|
|
223
|
-
if self._in_cluster or self.config.provider_type == ProviderType.SINGLE:
|
|
224
|
-
return
|
|
225
|
-
|
|
226
|
-
from kubernetes import config
|
|
227
|
-
try:
|
|
228
|
-
contexts, active = config.list_kube_config_contexts(
|
|
229
|
-
config_file=self.config.kubeconfig_path
|
|
230
|
-
)
|
|
231
|
-
self._active_context = active.get("name") if active else None
|
|
232
|
-
self._contexts_cache = [
|
|
233
|
-
ContextInfo(
|
|
234
|
-
name=ctx.get("name", ""),
|
|
235
|
-
cluster=ctx.get("context", {}).get("cluster", ""),
|
|
236
|
-
user=ctx.get("context", {}).get("user", ""),
|
|
237
|
-
namespace=ctx.get("context", {}).get("namespace", "default"),
|
|
238
|
-
is_active=ctx.get("name") == self._active_context
|
|
239
|
-
)
|
|
240
|
-
for ctx in contexts
|
|
241
|
-
]
|
|
242
|
-
except Exception as e:
|
|
243
|
-
logger.warning(f"Failed to refresh contexts: {e}")
|
|
244
|
-
|
|
245
|
-
def get_current_context(self) -> Optional[str]:
|
|
246
|
-
"""Get the current active context name."""
|
|
247
|
-
if self._in_cluster:
|
|
248
|
-
return "in-cluster"
|
|
249
|
-
return self._active_context
|
|
250
|
-
|
|
251
|
-
def _get_context_names(self) -> List[str]:
|
|
252
|
-
"""Get list of available context names."""
|
|
253
|
-
contexts = self.list_contexts()
|
|
254
|
-
return [ctx.name for ctx in contexts]
|
|
255
|
-
|
|
256
|
-
def validate_context(self, context: str) -> str:
|
|
257
|
-
"""
|
|
258
|
-
Validate and resolve a context name.
|
|
259
|
-
|
|
260
|
-
Args:
|
|
261
|
-
context: Context name (empty string uses default)
|
|
262
|
-
|
|
263
|
-
Returns:
|
|
264
|
-
Resolved context name
|
|
265
|
-
|
|
266
|
-
Raises:
|
|
267
|
-
UnknownContextError: If context is not found
|
|
268
|
-
"""
|
|
269
|
-
if self._in_cluster:
|
|
270
|
-
return "in-cluster"
|
|
271
|
-
|
|
272
|
-
if self.config.provider_type == ProviderType.SINGLE:
|
|
273
|
-
if context and context != self.config.context:
|
|
274
|
-
raise UnknownContextError(
|
|
275
|
-
context,
|
|
276
|
-
[self.config.context]
|
|
277
|
-
)
|
|
278
|
-
return self.config.context
|
|
279
|
-
|
|
280
|
-
if not context:
|
|
281
|
-
return self._active_context or ""
|
|
282
|
-
|
|
283
|
-
available = self._get_context_names()
|
|
284
|
-
if context not in available:
|
|
285
|
-
raise UnknownContextError(context, available)
|
|
286
|
-
|
|
287
|
-
return context
|
|
288
|
-
|
|
289
|
-
def get_api_client(self, context: str = "") -> Any:
|
|
290
|
-
"""
|
|
291
|
-
Get an API client configuration for a specific context.
|
|
292
|
-
|
|
293
|
-
Args:
|
|
294
|
-
context: Context name (empty uses default)
|
|
295
|
-
|
|
296
|
-
Returns:
|
|
297
|
-
kubernetes.client.ApiClient configured for the context
|
|
298
|
-
"""
|
|
299
|
-
from kubernetes import client, config
|
|
300
|
-
|
|
301
|
-
resolved_context = self.validate_context(context)
|
|
302
|
-
|
|
303
|
-
if resolved_context in self._api_clients:
|
|
304
|
-
return self._api_clients[resolved_context]
|
|
305
|
-
|
|
306
|
-
if self._in_cluster:
|
|
307
|
-
api_client = client.ApiClient()
|
|
308
|
-
else:
|
|
309
|
-
api_config = client.Configuration()
|
|
310
|
-
config.load_kube_config(
|
|
311
|
-
config_file=self.config.kubeconfig_path,
|
|
312
|
-
context=resolved_context,
|
|
313
|
-
client_configuration=api_config
|
|
314
|
-
)
|
|
315
|
-
api_client = client.ApiClient(configuration=api_config)
|
|
316
|
-
|
|
317
|
-
self._api_clients[resolved_context] = api_client
|
|
318
|
-
|
|
319
|
-
return api_client
|
|
320
|
-
|
|
321
|
-
def clear_client_cache(self, context: str = ""):
|
|
322
|
-
"""Clear cached API client(s)."""
|
|
323
|
-
if context:
|
|
324
|
-
self._api_clients.pop(context, None)
|
|
325
|
-
else:
|
|
326
|
-
self._api_clients.clear()
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
def get_provider() -> KubernetesProvider:
|
|
330
|
-
"""Get the global Kubernetes provider instance."""
|
|
331
|
-
return KubernetesProvider.get_instance()
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
def get_context_names() -> List[str]:
|
|
335
|
-
"""Get list of available context names."""
|
|
336
|
-
provider = get_provider()
|
|
337
|
-
return [ctx.name for ctx in provider.list_contexts()]
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
def get_current_context() -> Optional[str]:
|
|
341
|
-
"""Get the current active context name."""
|
|
342
|
-
return get_provider().get_current_context()
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
def validate_context(context: str) -> str:
|
|
346
|
-
"""Validate and resolve a context name."""
|
|
347
|
-
return get_provider().validate_context(context)
|
|
File without changes
|
{kubectl_mcp_server-1.18.0.dist-info → kubectl_mcp_server-1.19.0.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{kubectl_mcp_server-1.18.0.dist-info → kubectl_mcp_server-1.19.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|