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.
- kubectl_mcp_server-1.15.0.dist-info/METADATA +1026 -0
- {kubectl_mcp_server-1.14.0.dist-info → kubectl_mcp_server-1.15.0.dist-info}/RECORD +21 -21
- kubectl_mcp_tool/__init__.py +1 -1
- kubectl_mcp_tool/k8s_config.py +285 -63
- kubectl_mcp_tool/tools/cluster.py +395 -121
- kubectl_mcp_tool/tools/core.py +157 -60
- kubectl_mcp_tool/tools/cost.py +97 -41
- kubectl_mcp_tool/tools/deployments.py +173 -56
- kubectl_mcp_tool/tools/diagnostics.py +40 -13
- kubectl_mcp_tool/tools/helm.py +133 -46
- kubectl_mcp_tool/tools/networking.py +106 -32
- kubectl_mcp_tool/tools/operations.py +176 -50
- kubectl_mcp_tool/tools/pods.py +162 -50
- kubectl_mcp_tool/tools/security.py +89 -36
- kubectl_mcp_tool/tools/storage.py +35 -16
- tests/test_browser.py +2 -2
- tests/test_tools.py +10 -9
- kubectl_mcp_server-1.14.0.dist-info/METADATA +0 -780
- {kubectl_mcp_server-1.14.0.dist-info → kubectl_mcp_server-1.15.0.dist-info}/WHEEL +0 -0
- {kubectl_mcp_server-1.14.0.dist-info → kubectl_mcp_server-1.15.0.dist-info}/entry_points.txt +0 -0
- {kubectl_mcp_server-1.14.0.dist-info → kubectl_mcp_server-1.15.0.dist-info}/licenses/LICENSE +0 -0
- {kubectl_mcp_server-1.14.0.dist-info → kubectl_mcp_server-1.15.0.dist-info}/top_level.txt +0 -0
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from typing import Any, Dict, Optional
|
|
2
|
+
from typing import Any, Dict, List, Optional
|
|
3
3
|
|
|
4
4
|
from mcp.types import ToolAnnotations
|
|
5
5
|
|
|
6
|
+
from ..k8s_config import get_k8s_client, get_storage_client
|
|
7
|
+
|
|
6
8
|
logger = logging.getLogger("mcp-server")
|
|
7
9
|
|
|
8
10
|
|
|
@@ -15,12 +17,18 @@ def register_storage_tools(server, non_destructive: bool):
|
|
|
15
17
|
readOnlyHint=True,
|
|
16
18
|
),
|
|
17
19
|
)
|
|
18
|
-
def get_persistent_volumes(
|
|
19
|
-
|
|
20
|
+
def get_persistent_volumes(
|
|
21
|
+
name: Optional[str] = None,
|
|
22
|
+
context: str = ""
|
|
23
|
+
) -> Dict[str, Any]:
|
|
24
|
+
"""Get Persistent Volumes in the cluster.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
name: Specific PV name to get (all PVs if not specified)
|
|
28
|
+
context: Kubernetes context to use (uses current context if not specified)
|
|
29
|
+
"""
|
|
20
30
|
try:
|
|
21
|
-
|
|
22
|
-
config.load_kube_config()
|
|
23
|
-
v1 = client.CoreV1Api()
|
|
31
|
+
v1 = get_k8s_client(context)
|
|
24
32
|
|
|
25
33
|
if name:
|
|
26
34
|
pv = v1.read_persistent_volume(name)
|
|
@@ -39,6 +47,7 @@ def register_storage_tools(server, non_destructive: bool):
|
|
|
39
47
|
|
|
40
48
|
return {
|
|
41
49
|
"success": True,
|
|
50
|
+
"context": context or "current",
|
|
42
51
|
"persistentVolumes": [
|
|
43
52
|
{
|
|
44
53
|
"name": pv.metadata.name,
|
|
@@ -66,12 +75,18 @@ def register_storage_tools(server, non_destructive: bool):
|
|
|
66
75
|
readOnlyHint=True,
|
|
67
76
|
),
|
|
68
77
|
)
|
|
69
|
-
def get_pvcs(
|
|
70
|
-
|
|
78
|
+
def get_pvcs(
|
|
79
|
+
namespace: Optional[str] = None,
|
|
80
|
+
context: str = ""
|
|
81
|
+
) -> Dict[str, Any]:
|
|
82
|
+
"""Get Persistent Volume Claims in a namespace or cluster-wide.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
namespace: Namespace to list PVCs from (all namespaces if not specified)
|
|
86
|
+
context: Kubernetes context to use (uses current context if not specified)
|
|
87
|
+
"""
|
|
71
88
|
try:
|
|
72
|
-
|
|
73
|
-
config.load_kube_config()
|
|
74
|
-
v1 = client.CoreV1Api()
|
|
89
|
+
v1 = get_k8s_client(context)
|
|
75
90
|
|
|
76
91
|
if namespace:
|
|
77
92
|
pvcs = v1.list_namespaced_persistent_volume_claim(namespace)
|
|
@@ -80,6 +95,7 @@ def register_storage_tools(server, non_destructive: bool):
|
|
|
80
95
|
|
|
81
96
|
return {
|
|
82
97
|
"success": True,
|
|
98
|
+
"context": context or "current",
|
|
83
99
|
"pvcs": [
|
|
84
100
|
{
|
|
85
101
|
"name": pvc.metadata.name,
|
|
@@ -103,17 +119,20 @@ def register_storage_tools(server, non_destructive: bool):
|
|
|
103
119
|
readOnlyHint=True,
|
|
104
120
|
),
|
|
105
121
|
)
|
|
106
|
-
def get_storage_classes() -> Dict[str, Any]:
|
|
107
|
-
"""Get Storage Classes in the cluster.
|
|
122
|
+
def get_storage_classes(context: str = "") -> Dict[str, Any]:
|
|
123
|
+
"""Get Storage Classes in the cluster.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
context: Kubernetes context to use (uses current context if not specified)
|
|
127
|
+
"""
|
|
108
128
|
try:
|
|
109
|
-
|
|
110
|
-
config.load_kube_config()
|
|
111
|
-
storage = client.StorageV1Api()
|
|
129
|
+
storage = get_storage_client(context)
|
|
112
130
|
|
|
113
131
|
scs = storage.list_storage_class()
|
|
114
132
|
|
|
115
133
|
return {
|
|
116
134
|
"success": True,
|
|
135
|
+
"context": context or "current",
|
|
117
136
|
"storageClasses": [
|
|
118
137
|
{
|
|
119
138
|
"name": sc.metadata.name,
|
tests/test_browser.py
CHANGED
|
@@ -501,11 +501,11 @@ class TestServerIntegration:
|
|
|
501
501
|
tools = asyncio.run(server.server.list_tools())
|
|
502
502
|
tool_names = [t.name for t in tools]
|
|
503
503
|
|
|
504
|
-
# Should have browser tools (
|
|
504
|
+
# Should have browser tools (131 + 26 = 157)
|
|
505
505
|
assert "browser_open" in tool_names
|
|
506
506
|
assert "browser_screenshot" in tool_names
|
|
507
507
|
assert "browser_connect_cdp" in tool_names # v0.7 tool
|
|
508
|
-
assert len(tools) ==
|
|
508
|
+
assert len(tools) == 157, f"Expected 157 tools (131 + 26), got {len(tools)}"
|
|
509
509
|
|
|
510
510
|
|
|
511
511
|
import asyncio
|
tests/test_tools.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Unit tests for all MCP tools in kubectl-mcp-server.
|
|
3
3
|
|
|
4
|
-
This module contains comprehensive tests for all
|
|
5
|
-
provided by the MCP server.
|
|
4
|
+
This module contains comprehensive tests for all 125 Kubernetes tools
|
|
5
|
+
provided by the MCP server (131 total with UI tools).
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import pytest
|
|
@@ -12,7 +12,7 @@ from unittest.mock import patch, MagicMock
|
|
|
12
12
|
from datetime import datetime
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
# Complete list of all
|
|
15
|
+
# Complete list of all 131 tools that must be registered (125 core + 6 UI)
|
|
16
16
|
EXPECTED_TOOLS = [
|
|
17
17
|
# Pods (pods.py)
|
|
18
18
|
"get_pods", "get_logs", "get_pod_events", "check_pod_health", "exec_in_pod",
|
|
@@ -24,10 +24,11 @@ EXPECTED_TOOLS = [
|
|
|
24
24
|
# Core (core.py)
|
|
25
25
|
"get_namespaces", "get_configmaps", "get_secrets", "get_events",
|
|
26
26
|
"get_resource_quotas", "get_limit_ranges",
|
|
27
|
-
# Cluster (cluster.py)
|
|
28
|
-
"get_current_context", "switch_context", "
|
|
27
|
+
# Cluster (cluster.py) - includes multi-cluster config tools
|
|
28
|
+
"get_current_context", "switch_context", "list_contexts_tool",
|
|
29
29
|
"get_context_details", "set_namespace_for_context", "get_cluster_info",
|
|
30
30
|
"get_cluster_version", "get_nodes", "get_api_resources", "health_check",
|
|
31
|
+
"kubeconfig_view", "get_api_versions", "check_crd_exists", "list_crds", "get_nodes_summary",
|
|
31
32
|
# Networking (networking.py)
|
|
32
33
|
"get_services", "get_endpoints", "get_ingress", "port_forward",
|
|
33
34
|
"diagnose_network_connectivity", "check_dns_resolution", "trace_service_chain",
|
|
@@ -68,11 +69,11 @@ EXPECTED_TOOLS = [
|
|
|
68
69
|
|
|
69
70
|
|
|
70
71
|
class TestAllToolsRegistered:
|
|
71
|
-
"""Comprehensive tests to verify all
|
|
72
|
+
"""Comprehensive tests to verify all 131 tools are registered (125 core + 6 UI)."""
|
|
72
73
|
|
|
73
74
|
@pytest.mark.unit
|
|
74
75
|
def test_all_127_tools_registered(self):
|
|
75
|
-
"""Verify all
|
|
76
|
+
"""Verify all 131 expected tools are registered (excluding optional browser tools)."""
|
|
76
77
|
import os
|
|
77
78
|
from kubectl_mcp_tool.mcp_server import MCPServer
|
|
78
79
|
|
|
@@ -93,8 +94,8 @@ class TestAllToolsRegistered:
|
|
|
93
94
|
tools = asyncio.run(get_tools())
|
|
94
95
|
tool_names = {t.name for t in tools}
|
|
95
96
|
|
|
96
|
-
# Verify count (
|
|
97
|
-
assert len(tools) ==
|
|
97
|
+
# Verify count (131 tools = 125 core + 6 UI, browser tools disabled)
|
|
98
|
+
assert len(tools) == 131, f"Expected 131 tools, got {len(tools)}"
|
|
98
99
|
|
|
99
100
|
# Check for missing tools
|
|
100
101
|
missing_tools = set(EXPECTED_TOOLS) - tool_names
|