kubectl-mcp-server 1.12.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.
Files changed (45) hide show
  1. kubectl_mcp_server-1.12.0.dist-info/METADATA +711 -0
  2. kubectl_mcp_server-1.12.0.dist-info/RECORD +45 -0
  3. kubectl_mcp_server-1.12.0.dist-info/WHEEL +5 -0
  4. kubectl_mcp_server-1.12.0.dist-info/entry_points.txt +3 -0
  5. kubectl_mcp_server-1.12.0.dist-info/licenses/LICENSE +21 -0
  6. kubectl_mcp_server-1.12.0.dist-info/top_level.txt +2 -0
  7. kubectl_mcp_tool/__init__.py +21 -0
  8. kubectl_mcp_tool/__main__.py +46 -0
  9. kubectl_mcp_tool/auth/__init__.py +13 -0
  10. kubectl_mcp_tool/auth/config.py +71 -0
  11. kubectl_mcp_tool/auth/scopes.py +148 -0
  12. kubectl_mcp_tool/auth/verifier.py +82 -0
  13. kubectl_mcp_tool/cli/__init__.py +9 -0
  14. kubectl_mcp_tool/cli/__main__.py +10 -0
  15. kubectl_mcp_tool/cli/cli.py +111 -0
  16. kubectl_mcp_tool/diagnostics.py +355 -0
  17. kubectl_mcp_tool/k8s_config.py +289 -0
  18. kubectl_mcp_tool/mcp_server.py +530 -0
  19. kubectl_mcp_tool/prompts/__init__.py +5 -0
  20. kubectl_mcp_tool/prompts/prompts.py +823 -0
  21. kubectl_mcp_tool/resources/__init__.py +5 -0
  22. kubectl_mcp_tool/resources/resources.py +305 -0
  23. kubectl_mcp_tool/tools/__init__.py +28 -0
  24. kubectl_mcp_tool/tools/browser.py +371 -0
  25. kubectl_mcp_tool/tools/cluster.py +315 -0
  26. kubectl_mcp_tool/tools/core.py +421 -0
  27. kubectl_mcp_tool/tools/cost.py +680 -0
  28. kubectl_mcp_tool/tools/deployments.py +381 -0
  29. kubectl_mcp_tool/tools/diagnostics.py +174 -0
  30. kubectl_mcp_tool/tools/helm.py +1561 -0
  31. kubectl_mcp_tool/tools/networking.py +296 -0
  32. kubectl_mcp_tool/tools/operations.py +501 -0
  33. kubectl_mcp_tool/tools/pods.py +582 -0
  34. kubectl_mcp_tool/tools/security.py +333 -0
  35. kubectl_mcp_tool/tools/storage.py +133 -0
  36. kubectl_mcp_tool/utils/__init__.py +17 -0
  37. kubectl_mcp_tool/utils/helpers.py +80 -0
  38. tests/__init__.py +9 -0
  39. tests/conftest.py +379 -0
  40. tests/test_auth.py +256 -0
  41. tests/test_browser.py +349 -0
  42. tests/test_prompts.py +536 -0
  43. tests/test_resources.py +343 -0
  44. tests/test_server.py +384 -0
  45. tests/test_tools.py +659 -0
tests/test_server.py ADDED
@@ -0,0 +1,384 @@
1
+ """
2
+ Unit tests for MCP Server initialization and configuration.
3
+
4
+ This module tests:
5
+ - Server initialization
6
+ - Configuration options
7
+ - Transport methods
8
+ - Dependency checking
9
+ """
10
+
11
+ import pytest
12
+ import asyncio
13
+ from unittest.mock import patch, MagicMock, AsyncMock
14
+
15
+
16
+ class TestServerInitialization:
17
+ """Tests for MCPServer initialization."""
18
+
19
+ @pytest.mark.unit
20
+ def test_server_creates_successfully(self):
21
+ """Test that server creates successfully."""
22
+ from kubectl_mcp_tool.mcp_server import MCPServer
23
+
24
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
25
+ with patch("kubernetes.config.load_kube_config"):
26
+ server = MCPServer(name="test-server")
27
+
28
+ assert server is not None
29
+ assert server.name == "test-server"
30
+
31
+ @pytest.mark.unit
32
+ def test_server_name_is_set(self):
33
+ """Test that server name is properly set."""
34
+ from kubectl_mcp_tool.mcp_server import MCPServer
35
+
36
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
37
+ with patch("kubernetes.config.load_kube_config"):
38
+ server = MCPServer(name="my-custom-server")
39
+
40
+ assert server.name == "my-custom-server"
41
+
42
+ @pytest.mark.unit
43
+ def test_non_destructive_mode_default(self):
44
+ """Test that non-destructive mode is disabled by default."""
45
+ from kubectl_mcp_tool.mcp_server import MCPServer
46
+
47
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
48
+ with patch("kubernetes.config.load_kube_config"):
49
+ server = MCPServer(name="test")
50
+
51
+ assert server.non_destructive is False
52
+
53
+ @pytest.mark.unit
54
+ def test_non_destructive_mode_enabled(self):
55
+ """Test that non_destructive mode can be enabled."""
56
+ from kubectl_mcp_tool.mcp_server import MCPServer
57
+
58
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
59
+ with patch("kubernetes.config.load_kube_config"):
60
+ server = MCPServer(name="test", non_destructive=True)
61
+
62
+ assert server.non_destructive is True
63
+
64
+ @pytest.mark.unit
65
+ def test_fastmcp_server_instance(self):
66
+ """Test that FastMCP server instance is created."""
67
+ from kubectl_mcp_tool.mcp_server import MCPServer
68
+
69
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
70
+ with patch("kubernetes.config.load_kube_config"):
71
+ server = MCPServer(name="test")
72
+
73
+ assert hasattr(server, 'server')
74
+ assert server.server is not None
75
+
76
+
77
+ class TestToolRegistration:
78
+ """Tests for tool registration."""
79
+
80
+ @pytest.mark.unit
81
+ def test_tools_are_registered(self):
82
+ """Test that tools are registered during initialization."""
83
+ from kubectl_mcp_tool.mcp_server import MCPServer
84
+
85
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
86
+ with patch("kubernetes.config.load_kube_config"):
87
+ server = MCPServer(name="test")
88
+
89
+ # Server should have tools
90
+ assert hasattr(server, 'server')
91
+
92
+ @pytest.mark.unit
93
+ def test_setup_tools_called(self):
94
+ """Test that setup_tools is called during initialization."""
95
+ from kubectl_mcp_tool.mcp_server import MCPServer
96
+
97
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
98
+ with patch("kubernetes.config.load_kube_config"):
99
+ with patch.object(MCPServer, 'setup_tools') as mock_setup:
100
+ # Create server - setup_tools is called in __init__
101
+ # We can't easily patch it before __init__, so just verify the method exists
102
+ pass
103
+
104
+ # Verify setup_tools method exists
105
+ assert hasattr(MCPServer, 'setup_tools')
106
+
107
+
108
+ class TestResourceRegistration:
109
+ """Tests for resource registration."""
110
+
111
+ @pytest.mark.unit
112
+ def test_resources_are_registered(self):
113
+ """Test that resources are registered during initialization."""
114
+ from kubectl_mcp_tool.mcp_server import MCPServer
115
+
116
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
117
+ with patch("kubernetes.config.load_kube_config"):
118
+ server = MCPServer(name="test")
119
+
120
+ assert hasattr(server, 'server')
121
+
122
+ @pytest.mark.unit
123
+ def test_setup_resources_method_exists(self):
124
+ """Test that setup_resources method exists."""
125
+ from kubectl_mcp_tool.mcp_server import MCPServer
126
+
127
+ assert hasattr(MCPServer, 'setup_resources')
128
+
129
+
130
+ class TestPromptRegistration:
131
+ """Tests for prompt registration."""
132
+
133
+ @pytest.mark.unit
134
+ def test_prompts_are_registered(self):
135
+ """Test that prompts are registered during initialization."""
136
+ from kubectl_mcp_tool.mcp_server import MCPServer
137
+
138
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
139
+ with patch("kubernetes.config.load_kube_config"):
140
+ server = MCPServer(name="test")
141
+
142
+ assert hasattr(server, 'server')
143
+
144
+ @pytest.mark.unit
145
+ def test_setup_prompts_method_exists(self):
146
+ """Test that setup_prompts method exists."""
147
+ from kubectl_mcp_tool.mcp_server import MCPServer
148
+
149
+ assert hasattr(MCPServer, 'setup_prompts')
150
+
151
+
152
+ class TestDependencyChecking:
153
+ """Tests for dependency checking."""
154
+
155
+ @pytest.mark.unit
156
+ def test_dependencies_checked_lazily(self):
157
+ """Test that dependencies are checked lazily."""
158
+ from kubectl_mcp_tool.mcp_server import MCPServer
159
+
160
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
161
+ with patch("kubernetes.config.load_kube_config"):
162
+ server = MCPServer(name="test")
163
+
164
+ # Dependencies should not be checked until accessed
165
+ assert server._dependencies_checked is False
166
+
167
+ @pytest.mark.unit
168
+ def test_dependencies_checked_on_access(self):
169
+ """Test that dependencies are checked on first access."""
170
+ from kubectl_mcp_tool.mcp_server import MCPServer
171
+
172
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True) as mock_check:
173
+ with patch("kubernetes.config.load_kube_config"):
174
+ server = MCPServer(name="test")
175
+ # Access the property
176
+ _ = server.dependencies_available
177
+
178
+ assert server._dependencies_checked is True
179
+
180
+ @pytest.mark.unit
181
+ def test_check_tool_availability(self):
182
+ """Test tool availability checking."""
183
+ from kubectl_mcp_tool.mcp_server import MCPServer
184
+
185
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
186
+ with patch("kubernetes.config.load_kube_config"):
187
+ server = MCPServer(name="test")
188
+
189
+ with patch("shutil.which", return_value="/usr/bin/kubectl"):
190
+ with patch("subprocess.check_output", return_value=b'{"clientVersion": {}}'):
191
+ result = server._check_tool_availability("kubectl")
192
+ assert result is True
193
+
194
+ @pytest.mark.unit
195
+ def test_check_tool_not_available(self):
196
+ """Test tool availability when tool is not found."""
197
+ from kubectl_mcp_tool.mcp_server import MCPServer
198
+
199
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
200
+ with patch("kubernetes.config.load_kube_config"):
201
+ server = MCPServer(name="test")
202
+
203
+ with patch("shutil.which", return_value=None):
204
+ result = server._check_tool_availability("nonexistent-tool")
205
+ assert result is False
206
+
207
+
208
+ class TestNonDestructiveMode:
209
+ """Tests for non-destructive mode."""
210
+
211
+ @pytest.mark.unit
212
+ def test_check_destructive_returns_none_when_allowed(self):
213
+ """Test that _check_destructive returns None when destructive ops are allowed."""
214
+ from kubectl_mcp_tool.mcp_server import MCPServer
215
+
216
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
217
+ with patch("kubernetes.config.load_kube_config"):
218
+ server = MCPServer(name="test", non_destructive=False)
219
+
220
+ result = server._check_destructive()
221
+ assert result is None
222
+
223
+ @pytest.mark.unit
224
+ def test_check_destructive_returns_error_when_blocked(self):
225
+ """Test that _check_destructive returns error when destructive ops are blocked."""
226
+ from kubectl_mcp_tool.mcp_server import MCPServer
227
+
228
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
229
+ with patch("kubernetes.config.load_kube_config"):
230
+ server = MCPServer(name="test", non_destructive=True)
231
+
232
+ result = server._check_destructive()
233
+ assert result is not None
234
+ assert result["success"] is False
235
+ assert "non-destructive mode" in result["error"]
236
+
237
+
238
+ class TestSecretMasking:
239
+ """Tests for secret masking."""
240
+
241
+ @pytest.mark.unit
242
+ def test_mask_secrets_method_exists(self):
243
+ """Test that _mask_secrets method exists."""
244
+ from kubectl_mcp_tool.mcp_server import MCPServer
245
+
246
+ assert hasattr(MCPServer, '_mask_secrets')
247
+
248
+ @pytest.mark.unit
249
+ def test_masks_base64_secrets(self):
250
+ """Test that base64-encoded secrets are masked."""
251
+ from kubectl_mcp_tool.mcp_server import MCPServer
252
+
253
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
254
+ with patch("kubernetes.config.load_kube_config"):
255
+ server = MCPServer(name="test")
256
+
257
+ text = """
258
+ data:
259
+ password: c2VjcmV0UGFzc3dvcmQxMjM0NTY3ODkw
260
+ """
261
+ masked = server._mask_secrets(text)
262
+ assert "[MASKED]" in masked
263
+
264
+ @pytest.mark.unit
265
+ def test_masks_password_fields(self):
266
+ """Test that password fields are masked."""
267
+ from kubectl_mcp_tool.mcp_server import MCPServer
268
+
269
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
270
+ with patch("kubernetes.config.load_kube_config"):
271
+ server = MCPServer(name="test")
272
+
273
+ text = 'password: "supersecretpassword"'
274
+ masked = server._mask_secrets(text)
275
+ assert "[MASKED]" in masked
276
+
277
+ @pytest.mark.unit
278
+ def test_masks_token_fields(self):
279
+ """Test that token fields are masked."""
280
+ from kubectl_mcp_tool.mcp_server import MCPServer
281
+
282
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
283
+ with patch("kubernetes.config.load_kube_config"):
284
+ server = MCPServer(name="test")
285
+
286
+ text = 'token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.test"'
287
+ masked = server._mask_secrets(text)
288
+ assert "[MASKED]" in masked
289
+
290
+
291
+ class TestTransportMethods:
292
+ """Tests for transport methods."""
293
+
294
+ @pytest.mark.unit
295
+ def test_serve_stdio_method_exists(self):
296
+ """Test that serve_stdio method exists."""
297
+ from kubectl_mcp_tool.mcp_server import MCPServer
298
+
299
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
300
+ with patch("kubernetes.config.load_kube_config"):
301
+ server = MCPServer(name="test")
302
+
303
+ assert hasattr(server, 'serve_stdio')
304
+
305
+ @pytest.mark.unit
306
+ def test_serve_sse_method_exists(self):
307
+ """Test that serve_sse method exists."""
308
+ from kubectl_mcp_tool.mcp_server import MCPServer
309
+
310
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
311
+ with patch("kubernetes.config.load_kube_config"):
312
+ server = MCPServer(name="test")
313
+
314
+ assert hasattr(server, 'serve_sse')
315
+
316
+ @pytest.mark.unit
317
+ def test_serve_http_method_exists(self):
318
+ """Test that serve_http method exists."""
319
+ from kubectl_mcp_tool.mcp_server import MCPServer
320
+
321
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
322
+ with patch("kubernetes.config.load_kube_config"):
323
+ server = MCPServer(name="test")
324
+
325
+ assert hasattr(server, 'serve_http')
326
+
327
+
328
+ class TestServerConfiguration:
329
+ """Tests for server configuration."""
330
+
331
+ @pytest.mark.unit
332
+ def test_server_with_default_config(self):
333
+ """Test server with default configuration."""
334
+ from kubectl_mcp_tool.mcp_server import MCPServer
335
+
336
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
337
+ with patch("kubernetes.config.load_kube_config"):
338
+ server = MCPServer(name="default-server")
339
+
340
+ assert server.name == "default-server"
341
+ assert server.non_destructive is False
342
+
343
+ @pytest.mark.unit
344
+ def test_server_with_custom_config(self):
345
+ """Test server with custom configuration."""
346
+ from kubectl_mcp_tool.mcp_server import MCPServer
347
+
348
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
349
+ with patch("kubernetes.config.load_kube_config"):
350
+ server = MCPServer(
351
+ name="custom-server",
352
+ non_destructive=True
353
+ )
354
+
355
+ assert server.name == "custom-server"
356
+ assert server.non_destructive is True
357
+
358
+
359
+ class TestModuleExports:
360
+ """Tests for module exports."""
361
+
362
+ @pytest.mark.unit
363
+ def test_mcpserver_exported(self):
364
+ """Test that MCPServer is exported from module."""
365
+ from kubectl_mcp_tool import MCPServer
366
+
367
+ assert MCPServer is not None
368
+
369
+ @pytest.mark.unit
370
+ def test_version_exported(self):
371
+ """Test that __version__ is exported from module."""
372
+ from kubectl_mcp_tool import __version__
373
+
374
+ assert __version__ is not None
375
+ assert isinstance(__version__, str)
376
+
377
+ @pytest.mark.unit
378
+ def test_diagnostics_exported(self):
379
+ """Test that diagnostics functions are exported."""
380
+ from kubectl_mcp_tool import run_diagnostics, check_kubectl_installation, check_cluster_connection
381
+
382
+ assert run_diagnostics is not None
383
+ assert check_kubectl_installation is not None
384
+ assert check_cluster_connection is not None