container-manager-mcp 0.0.11__py3-none-any.whl → 0.0.12__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.
- container_manager_mcp/container_manager.py +218 -262
- {container_manager_mcp-0.0.11.dist-info → container_manager_mcp-0.0.12.dist-info}/METADATA +2 -2
- container_manager_mcp-0.0.12.dist-info/RECORD +10 -0
- container_manager_mcp-0.0.11.dist-info/RECORD +0 -10
- {container_manager_mcp-0.0.11.dist-info → container_manager_mcp-0.0.12.dist-info}/WHEEL +0 -0
- {container_manager_mcp-0.0.11.dist-info → container_manager_mcp-0.0.12.dist-info}/entry_points.txt +0 -0
- {container_manager_mcp-0.0.11.dist-info → container_manager_mcp-0.0.12.dist-info}/licenses/LICENSE +0 -0
- {container_manager_mcp-0.0.11.dist-info → container_manager_mcp-0.0.12.dist-info}/top_level.txt +0 -0
@@ -9,7 +9,8 @@ from typing import List, Dict, Optional, Any
|
|
9
9
|
import getopt
|
10
10
|
import json
|
11
11
|
import subprocess
|
12
|
-
from datetime import datetime
|
12
|
+
from datetime import datetime
|
13
|
+
import dateutil.parser
|
13
14
|
|
14
15
|
try:
|
15
16
|
import docker
|
@@ -27,6 +28,7 @@ except ImportError:
|
|
27
28
|
|
28
29
|
|
29
30
|
class ContainerManagerBase(ABC):
|
31
|
+
|
30
32
|
def __init__(self, silent: bool = False, log_file: str = None):
|
31
33
|
self.silent = silent
|
32
34
|
self.setup_logging(log_file)
|
@@ -66,6 +68,20 @@ class ContainerManagerBase(ABC):
|
|
66
68
|
size_bytes /= 1024.0
|
67
69
|
return f"{size_bytes:.2f}PB"
|
68
70
|
|
71
|
+
def _parse_timestamp(self, timestamp: Any) -> str:
|
72
|
+
"""Parse timestamp (integer or string) to ISO 8601 string."""
|
73
|
+
if not timestamp:
|
74
|
+
return "unknown"
|
75
|
+
if isinstance(timestamp, (int, float)):
|
76
|
+
return datetime.fromtimestamp(timestamp).strftime("%Y-%m-%dT%H:%M:%S")
|
77
|
+
if isinstance(timestamp, str):
|
78
|
+
try:
|
79
|
+
parsed = dateutil.parser.isoparse(timestamp)
|
80
|
+
return parsed.strftime("%Y-%m-%dT%H:%M:%S")
|
81
|
+
except ValueError:
|
82
|
+
return "unknown"
|
83
|
+
return "unknown"
|
84
|
+
|
69
85
|
@abstractmethod
|
70
86
|
def get_version(self) -> Dict:
|
71
87
|
pass
|
@@ -208,42 +224,6 @@ class DockerManager(ContainerManagerBase):
|
|
208
224
|
self.logger.error(f"Failed to connect to Docker daemon: {str(e)}")
|
209
225
|
raise RuntimeError(f"Failed to connect to Docker: {str(e)}")
|
210
226
|
|
211
|
-
def get_version(self) -> Dict:
|
212
|
-
params = {}
|
213
|
-
try:
|
214
|
-
version = self.client.version()
|
215
|
-
result = {
|
216
|
-
"version": version.get("Version", "unknown"),
|
217
|
-
"api_version": version.get("ApiVersion", "unknown"),
|
218
|
-
"os": version.get("Os", "unknown"),
|
219
|
-
"arch": version.get("Arch", "unknown"),
|
220
|
-
"build_time": version.get("BuildTime", "unknown"),
|
221
|
-
}
|
222
|
-
self.log_action("get_version", params, result)
|
223
|
-
return result
|
224
|
-
except Exception as e:
|
225
|
-
self.log_action("get_version", params, error=e)
|
226
|
-
raise RuntimeError(f"Failed to get version: {str(e)}")
|
227
|
-
|
228
|
-
def get_info(self) -> Dict:
|
229
|
-
params = {}
|
230
|
-
try:
|
231
|
-
info = self.client.info()
|
232
|
-
result = {
|
233
|
-
"containers_total": info.get("Containers", 0),
|
234
|
-
"containers_running": info.get("ContainersRunning", 0),
|
235
|
-
"images": info.get("Images", 0),
|
236
|
-
"driver": info.get("Driver", "unknown"),
|
237
|
-
"platform": f"{info.get('OperatingSystem', 'unknown')} {info.get('Architecture', 'unknown')}",
|
238
|
-
"memory_total": self._format_size(info.get("MemTotal", 0)),
|
239
|
-
"swap_total": self._format_size(info.get("SwapTotal", 0)),
|
240
|
-
}
|
241
|
-
self.log_action("get_info", params, result)
|
242
|
-
return result
|
243
|
-
except Exception as e:
|
244
|
-
self.log_action("get_info", params, error=e)
|
245
|
-
raise RuntimeError(f"Failed to get info: {str(e)}")
|
246
|
-
|
247
227
|
def list_images(self) -> List[Dict]:
|
248
228
|
params = {}
|
249
229
|
try:
|
@@ -257,12 +237,8 @@ class DockerManager(ContainerManagerBase):
|
|
257
237
|
repo_tag.rsplit(":", 1) if ":" in repo_tag else ("<none>", "<none>")
|
258
238
|
)
|
259
239
|
|
260
|
-
created = attrs.get("Created",
|
261
|
-
created_str = (
|
262
|
-
datetime.fromtimestamp(created).strftime("%Y-%m-%dT%H:%M:%S")
|
263
|
-
if created
|
264
|
-
else "unknown"
|
265
|
-
)
|
240
|
+
created = attrs.get("Created", None)
|
241
|
+
created_str = self._parse_timestamp(created)
|
266
242
|
|
267
243
|
size_bytes = attrs.get("Size", 0)
|
268
244
|
size_str = self._format_size(size_bytes) if size_bytes else "0B"
|
@@ -298,12 +274,8 @@ class DockerManager(ContainerManagerBase):
|
|
298
274
|
repository, tag = (
|
299
275
|
repo_tag.rsplit(":", 1) if ":" in repo_tag else (image, tag)
|
300
276
|
)
|
301
|
-
created = attrs.get("Created",
|
302
|
-
created_str = (
|
303
|
-
datetime.fromtimestamp(created).strftime("%Y-%m-%dT%H:%M:%S")
|
304
|
-
if created
|
305
|
-
else "unknown"
|
306
|
-
)
|
277
|
+
created = attrs.get("Created", None)
|
278
|
+
created_str = self._parse_timestamp(created)
|
307
279
|
size_bytes = attrs.get("Size", 0)
|
308
280
|
size_str = self._format_size(size_bytes) if size_bytes else "0B"
|
309
281
|
result = {
|
@@ -321,17 +293,6 @@ class DockerManager(ContainerManagerBase):
|
|
321
293
|
self.log_action("pull_image", params, error=e)
|
322
294
|
raise RuntimeError(f"Failed to pull image: {str(e)}")
|
323
295
|
|
324
|
-
def remove_image(self, image: str, force: bool = False) -> Dict:
|
325
|
-
params = {"image": image, "force": force}
|
326
|
-
try:
|
327
|
-
self.client.images.remove(image, force=force)
|
328
|
-
result = {"removed": image}
|
329
|
-
self.log_action("remove_image", params, result)
|
330
|
-
return result
|
331
|
-
except Exception as e:
|
332
|
-
self.log_action("remove_image", params, error=e)
|
333
|
-
raise RuntimeError(f"Failed to remove image: {str(e)}")
|
334
|
-
|
335
296
|
def list_containers(self, all: bool = False) -> List[Dict]:
|
336
297
|
params = {"all": all}
|
337
298
|
try:
|
@@ -347,12 +308,8 @@ class DockerManager(ContainerManagerBase):
|
|
347
308
|
port_mappings.append(
|
348
309
|
f"{hp.get('HostIp', '0.0.0.0')}:{hp.get('HostPort')}->{container_port}"
|
349
310
|
)
|
350
|
-
created = attrs.get("Created",
|
351
|
-
created_str = (
|
352
|
-
datetime.fromtimestamp(created).strftime("%Y-%m-%dT%H:%M:%S")
|
353
|
-
if created
|
354
|
-
else "unknown"
|
355
|
-
)
|
311
|
+
created = attrs.get("Created", None)
|
312
|
+
created_str = self._parse_timestamp(created)
|
356
313
|
simplified = {
|
357
314
|
"id": attrs.get("Id", "unknown")[7:19],
|
358
315
|
"image": attrs.get("Config", {}).get("Image", "unknown"),
|
@@ -410,12 +367,8 @@ class DockerManager(ContainerManagerBase):
|
|
410
367
|
port_mappings.append(
|
411
368
|
f"{hp.get('HostIp', '0.0.0.0')}:{hp.get('HostPort')}->{container_port}"
|
412
369
|
)
|
413
|
-
created = attrs.get("Created",
|
414
|
-
created_str = (
|
415
|
-
datetime.fromtimestamp(created).strftime("%Y-%m-%dT%H:%M:%S")
|
416
|
-
if created
|
417
|
-
else "unknown"
|
418
|
-
)
|
370
|
+
created = attrs.get("Created", None)
|
371
|
+
created_str = self._parse_timestamp(created)
|
419
372
|
result = {
|
420
373
|
"id": attrs.get("Id", "unknown")[7:19],
|
421
374
|
"image": attrs.get("Config", {}).get("Image", image),
|
@@ -430,6 +383,98 @@ class DockerManager(ContainerManagerBase):
|
|
430
383
|
self.log_action("run_container", params, error=e)
|
431
384
|
raise RuntimeError(f"Failed to run container: {str(e)}")
|
432
385
|
|
386
|
+
def list_networks(self) -> List[Dict]:
|
387
|
+
params = {}
|
388
|
+
try:
|
389
|
+
networks = self.client.networks.list()
|
390
|
+
result = []
|
391
|
+
for net in networks:
|
392
|
+
attrs = net.attrs
|
393
|
+
containers = len(attrs.get("Containers", {}))
|
394
|
+
created = attrs.get("Created", None)
|
395
|
+
created_str = self._parse_timestamp(created)
|
396
|
+
simplified = {
|
397
|
+
"id": attrs.get("Id", "unknown")[7:19],
|
398
|
+
"name": attrs.get("Name", "unknown"),
|
399
|
+
"driver": attrs.get("Driver", "unknown"),
|
400
|
+
"scope": attrs.get("Scope", "unknown"),
|
401
|
+
"containers": containers,
|
402
|
+
"created": created_str,
|
403
|
+
}
|
404
|
+
result.append(simplified)
|
405
|
+
self.log_action("list_networks", params, result)
|
406
|
+
return result
|
407
|
+
except Exception as e:
|
408
|
+
self.log_action("list_networks", params, error=e)
|
409
|
+
raise RuntimeError(f"Failed to list networks: {str(e)}")
|
410
|
+
|
411
|
+
def create_network(self, name: str, driver: str = "bridge") -> Dict:
|
412
|
+
params = {"name": name, "driver": driver}
|
413
|
+
try:
|
414
|
+
network = self.client.networks.create(name, driver=driver)
|
415
|
+
attrs = network.attrs
|
416
|
+
created = attrs.get("Created", None)
|
417
|
+
created_str = self._parse_timestamp(created)
|
418
|
+
result = {
|
419
|
+
"id": attrs.get("Id", "unknown")[7:19],
|
420
|
+
"name": attrs.get("Name", name),
|
421
|
+
"driver": attrs.get("Driver", driver),
|
422
|
+
"scope": attrs.get("Scope", "unknown"),
|
423
|
+
"created": created_str,
|
424
|
+
}
|
425
|
+
self.log_action("create_network", params, result)
|
426
|
+
return result
|
427
|
+
except Exception as e:
|
428
|
+
self.log_action("create_network", params, error=e)
|
429
|
+
raise RuntimeError(f"Failed to create network: {str(e)}")
|
430
|
+
|
431
|
+
def get_version(self) -> Dict:
|
432
|
+
params = {}
|
433
|
+
try:
|
434
|
+
version = self.client.version()
|
435
|
+
result = {
|
436
|
+
"version": version.get("Version", "unknown"),
|
437
|
+
"api_version": version.get("ApiVersion", "unknown"),
|
438
|
+
"os": version.get("Os", "unknown"),
|
439
|
+
"arch": version.get("Arch", "unknown"),
|
440
|
+
"build_time": version.get("BuildTime", "unknown"),
|
441
|
+
}
|
442
|
+
self.log_action("get_version", params, result)
|
443
|
+
return result
|
444
|
+
except Exception as e:
|
445
|
+
self.log_action("get_version", params, error=e)
|
446
|
+
raise RuntimeError(f"Failed to get version: {str(e)}")
|
447
|
+
|
448
|
+
def get_info(self) -> Dict:
|
449
|
+
params = {}
|
450
|
+
try:
|
451
|
+
info = self.client.info()
|
452
|
+
result = {
|
453
|
+
"containers_total": info.get("Containers", 0),
|
454
|
+
"containers_running": info.get("ContainersRunning", 0),
|
455
|
+
"images": info.get("Images", 0),
|
456
|
+
"driver": info.get("Driver", "unknown"),
|
457
|
+
"platform": f"{info.get('OperatingSystem', 'unknown')} {info.get('Architecture', 'unknown')}",
|
458
|
+
"memory_total": self._format_size(info.get("MemTotal", 0)),
|
459
|
+
"swap_total": self._format_size(info.get("SwapTotal", 0)),
|
460
|
+
}
|
461
|
+
self.log_action("get_info", params, result)
|
462
|
+
return result
|
463
|
+
except Exception as e:
|
464
|
+
self.log_action("get_info", params, error=e)
|
465
|
+
raise RuntimeError(f"Failed to get info: {str(e)}")
|
466
|
+
|
467
|
+
def remove_image(self, image: str, force: bool = False) -> Dict:
|
468
|
+
params = {"image": image, "force": force}
|
469
|
+
try:
|
470
|
+
self.client.images.remove(image, force=force)
|
471
|
+
result = {"removed": image}
|
472
|
+
self.log_action("remove_image", params, result)
|
473
|
+
return result
|
474
|
+
except Exception as e:
|
475
|
+
self.log_action("remove_image", params, error=e)
|
476
|
+
raise RuntimeError(f"Failed to remove image: {str(e)}")
|
477
|
+
|
433
478
|
def stop_container(self, container_id: str, timeout: int = 10) -> Dict:
|
434
479
|
params = {"container_id": container_id, "timeout": timeout}
|
435
480
|
try:
|
@@ -535,65 +580,6 @@ class DockerManager(ContainerManagerBase):
|
|
535
580
|
self.log_action("remove_volume", params, error=e)
|
536
581
|
raise RuntimeError(f"Failed to remove volume: {str(e)}")
|
537
582
|
|
538
|
-
def list_networks(self) -> List[Dict]:
|
539
|
-
params = {}
|
540
|
-
try:
|
541
|
-
networks = self.client.networks.list()
|
542
|
-
result = []
|
543
|
-
for net in networks:
|
544
|
-
attrs = net.attrs
|
545
|
-
containers = len(attrs.get("Containers", {}))
|
546
|
-
created = attrs.get("Created", "unknown")
|
547
|
-
if isinstance(created, str):
|
548
|
-
created_str = created
|
549
|
-
else:
|
550
|
-
created_str = (
|
551
|
-
datetime.fromtimestamp(created).strftime("%Y-%m-%dT%H:%M:%S")
|
552
|
-
if created
|
553
|
-
else "unknown"
|
554
|
-
)
|
555
|
-
simplified = {
|
556
|
-
"id": attrs.get("Id", "unknown")[7:19],
|
557
|
-
"name": attrs.get("Name", "unknown"),
|
558
|
-
"driver": attrs.get("Driver", "unknown"),
|
559
|
-
"scope": attrs.get("Scope", "unknown"),
|
560
|
-
"containers": containers,
|
561
|
-
"created": created_str,
|
562
|
-
}
|
563
|
-
result.append(simplified)
|
564
|
-
self.log_action("list_networks", params, result)
|
565
|
-
return result
|
566
|
-
except Exception as e:
|
567
|
-
self.log_action("list_networks", params, error=e)
|
568
|
-
raise RuntimeError(f"Failed to list networks: {str(e)}")
|
569
|
-
|
570
|
-
def create_network(self, name: str, driver: str = "bridge") -> Dict:
|
571
|
-
params = {"name": name, "driver": driver}
|
572
|
-
try:
|
573
|
-
network = self.client.networks.create(name, driver=driver)
|
574
|
-
attrs = network.attrs
|
575
|
-
created = attrs.get("Created", "unknown")
|
576
|
-
if isinstance(created, str):
|
577
|
-
created_str = created
|
578
|
-
else:
|
579
|
-
created_str = (
|
580
|
-
datetime.fromtimestamp(created).strftime("%Y-%m-%dT%H:%M:%S")
|
581
|
-
if created
|
582
|
-
else "unknown"
|
583
|
-
)
|
584
|
-
result = {
|
585
|
-
"id": attrs.get("Id", "unknown")[7:19],
|
586
|
-
"name": attrs.get("Name", name),
|
587
|
-
"driver": attrs.get("Driver", driver),
|
588
|
-
"scope": attrs.get("Scope", "unknown"),
|
589
|
-
"created": created_str,
|
590
|
-
}
|
591
|
-
self.log_action("create_network", params, result)
|
592
|
-
return result
|
593
|
-
except Exception as e:
|
594
|
-
self.log_action("create_network", params, error=e)
|
595
|
-
raise RuntimeError(f"Failed to create network: {str(e)}")
|
596
|
-
|
597
583
|
def remove_network(self, network_id: str) -> Dict:
|
598
584
|
params = {"network_id": network_id}
|
599
585
|
try:
|
@@ -839,43 +825,6 @@ class PodmanManager(ContainerManagerBase):
|
|
839
825
|
self.logger.error(f"Failed to connect to Podman daemon: {str(e)}")
|
840
826
|
raise RuntimeError(f"Failed to connect to Podman: {str(e)}")
|
841
827
|
|
842
|
-
def get_version(self) -> Dict:
|
843
|
-
params = {}
|
844
|
-
try:
|
845
|
-
version = self.client.version()
|
846
|
-
result = {
|
847
|
-
"version": version.get("Version", "unknown"),
|
848
|
-
"api_version": version.get("APIVersion", "unknown"),
|
849
|
-
"os": version.get("Os", "unknown"),
|
850
|
-
"arch": version.get("Arch", "unknown"),
|
851
|
-
"build_time": version.get("BuildTime", "unknown"),
|
852
|
-
}
|
853
|
-
self.log_action("get_version", params, result)
|
854
|
-
return result
|
855
|
-
except Exception as e:
|
856
|
-
self.log_action("get_version", params, error=e)
|
857
|
-
raise RuntimeError(f"Failed to get version: {str(e)}")
|
858
|
-
|
859
|
-
def get_info(self) -> Dict:
|
860
|
-
params = {}
|
861
|
-
try:
|
862
|
-
info = self.client.info()
|
863
|
-
host = info.get("host", {})
|
864
|
-
result = {
|
865
|
-
"containers_total": info.get("store", {}).get("containers", 0),
|
866
|
-
"containers_running": host.get("runningContainers", 0),
|
867
|
-
"images": info.get("store", {}).get("images", 0),
|
868
|
-
"driver": host.get("graphDriverName", "unknown"),
|
869
|
-
"platform": f"{host.get('os', 'unknown')} {host.get('arch', 'unknown')}",
|
870
|
-
"memory_total": self._format_size(host.get("memTotal", 0)),
|
871
|
-
"swap_total": self._format_size(host.get("swapTotal", 0)),
|
872
|
-
}
|
873
|
-
self.log_action("get_info", params, result)
|
874
|
-
return result
|
875
|
-
except Exception as e:
|
876
|
-
self.log_action("get_info", params, error=e)
|
877
|
-
raise RuntimeError(f"Failed to get info: {str(e)}")
|
878
|
-
|
879
828
|
def list_images(self) -> List[Dict]:
|
880
829
|
params = {}
|
881
830
|
try:
|
@@ -888,12 +837,8 @@ class PodmanManager(ContainerManagerBase):
|
|
888
837
|
repository, tag = (
|
889
838
|
repo_tag.rsplit(":", 1) if ":" in repo_tag else ("<none>", "<none>")
|
890
839
|
)
|
891
|
-
created = attrs.get("Created",
|
892
|
-
created_str = (
|
893
|
-
datetime.fromtimestamp(created).strftime("%Y-%m-%dT%H:%M:%S")
|
894
|
-
if created
|
895
|
-
else "unknown"
|
896
|
-
)
|
840
|
+
created = attrs.get("Created", None)
|
841
|
+
created_str = self._parse_timestamp(created)
|
897
842
|
size_bytes = attrs.get("Size", 0)
|
898
843
|
size_str = self._format_size(size_bytes) if size_bytes else "0B"
|
899
844
|
simplified = {
|
@@ -926,12 +871,8 @@ class PodmanManager(ContainerManagerBase):
|
|
926
871
|
repository, tag = (
|
927
872
|
repo_tag.rsplit(":", 1) if ":" in repo_tag else (image, tag)
|
928
873
|
)
|
929
|
-
created = attrs.get("Created",
|
930
|
-
created_str = (
|
931
|
-
datetime.fromtimestamp(created).strftime("%Y-%m-%dT%H:%M:%S")
|
932
|
-
if created
|
933
|
-
else "unknown"
|
934
|
-
)
|
874
|
+
created = attrs.get("Created", None)
|
875
|
+
created_str = self._parse_timestamp(created)
|
935
876
|
size_bytes = attrs.get("Size", 0)
|
936
877
|
size_str = self._format_size(size_bytes) if size_bytes else "0B"
|
937
878
|
result = {
|
@@ -949,17 +890,6 @@ class PodmanManager(ContainerManagerBase):
|
|
949
890
|
self.log_action("pull_image", params, error=e)
|
950
891
|
raise RuntimeError(f"Failed to pull image: {str(e)}")
|
951
892
|
|
952
|
-
def remove_image(self, image: str, force: bool = False) -> Dict:
|
953
|
-
params = {"image": image, "force": force}
|
954
|
-
try:
|
955
|
-
self.client.images.remove(image, force=force)
|
956
|
-
result = {"removed": image}
|
957
|
-
self.log_action("remove_image", params, result)
|
958
|
-
return result
|
959
|
-
except Exception as e:
|
960
|
-
self.log_action("remove_image", params, error=e)
|
961
|
-
raise RuntimeError(f"Failed to remove image: {str(e)}")
|
962
|
-
|
963
893
|
def list_containers(self, all: bool = False) -> List[Dict]:
|
964
894
|
params = {"all": all}
|
965
895
|
try:
|
@@ -973,12 +903,8 @@ class PodmanManager(ContainerManagerBase):
|
|
973
903
|
for p in ports
|
974
904
|
if p.get("host_port")
|
975
905
|
]
|
976
|
-
created = attrs.get("Created",
|
977
|
-
created_str = (
|
978
|
-
datetime.fromtimestamp(created).strftime("%Y-%m-%dT%H:%M:%S")
|
979
|
-
if created
|
980
|
-
else "unknown"
|
981
|
-
)
|
906
|
+
created = attrs.get("Created", None)
|
907
|
+
created_str = self._parse_timestamp(created)
|
982
908
|
simplified = {
|
983
909
|
"id": attrs.get("Id", "unknown")[7:19],
|
984
910
|
"image": attrs.get("Image", "unknown"),
|
@@ -1034,12 +960,8 @@ class PodmanManager(ContainerManagerBase):
|
|
1034
960
|
for p in ports
|
1035
961
|
if p.get("host_port")
|
1036
962
|
]
|
1037
|
-
created = attrs.get("Created",
|
1038
|
-
created_str = (
|
1039
|
-
datetime.fromtimestamp(created).strftime("%Y-%m-%dT%H:%M:%S")
|
1040
|
-
if created
|
1041
|
-
else "unknown"
|
1042
|
-
)
|
963
|
+
created = attrs.get("Created", None)
|
964
|
+
created_str = self._parse_timestamp(created)
|
1043
965
|
result = {
|
1044
966
|
"id": attrs.get("Id", "unknown")[7:19],
|
1045
967
|
"image": attrs.get("Image", image),
|
@@ -1054,6 +976,99 @@ class PodmanManager(ContainerManagerBase):
|
|
1054
976
|
self.log_action("run_container", params, error=e)
|
1055
977
|
raise RuntimeError(f"Failed to run container: {str(e)}")
|
1056
978
|
|
979
|
+
def list_networks(self) -> List[Dict]:
|
980
|
+
params = {}
|
981
|
+
try:
|
982
|
+
networks = self.client.networks.list()
|
983
|
+
result = []
|
984
|
+
for net in networks:
|
985
|
+
attrs = net.attrs
|
986
|
+
containers = len(attrs.get("Containers", {}))
|
987
|
+
created = attrs.get("Created", None)
|
988
|
+
created_str = self._parse_timestamp(created)
|
989
|
+
simplified = {
|
990
|
+
"id": attrs.get("Id", "unknown")[7:19],
|
991
|
+
"name": attrs.get("Name", "unknown"),
|
992
|
+
"driver": attrs.get("Driver", "unknown"),
|
993
|
+
"scope": attrs.get("Scope", "unknown"),
|
994
|
+
"containers": containers,
|
995
|
+
"created": created_str,
|
996
|
+
}
|
997
|
+
result.append(simplified)
|
998
|
+
self.log_action("list_networks", params, result)
|
999
|
+
return result
|
1000
|
+
except Exception as e:
|
1001
|
+
self.log_action("list_networks", params, error=e)
|
1002
|
+
raise RuntimeError(f"Failed to list networks: {str(e)}")
|
1003
|
+
|
1004
|
+
def create_network(self, name: str, driver: str = "bridge") -> Dict:
|
1005
|
+
params = {"name": name, "driver": driver}
|
1006
|
+
try:
|
1007
|
+
network = self.client.networks.create(name, driver=driver)
|
1008
|
+
attrs = network.attrs
|
1009
|
+
created = attrs.get("Created", None)
|
1010
|
+
created_str = self._parse_timestamp(created)
|
1011
|
+
result = {
|
1012
|
+
"id": attrs.get("Id", "unknown")[7:19],
|
1013
|
+
"name": attrs.get("Name", name),
|
1014
|
+
"driver": attrs.get("Driver", driver),
|
1015
|
+
"scope": attrs.get("Scope", "unknown"),
|
1016
|
+
"created": created_str,
|
1017
|
+
}
|
1018
|
+
self.log_action("create_network", params, result)
|
1019
|
+
return result
|
1020
|
+
except Exception as e:
|
1021
|
+
self.log_action("create_network", params, error=e)
|
1022
|
+
raise RuntimeError(f"Failed to create network: {str(e)}")
|
1023
|
+
|
1024
|
+
def get_version(self) -> Dict:
|
1025
|
+
params = {}
|
1026
|
+
try:
|
1027
|
+
version = self.client.version()
|
1028
|
+
result = {
|
1029
|
+
"version": version.get("Version", "unknown"),
|
1030
|
+
"api_version": version.get("APIVersion", "unknown"),
|
1031
|
+
"os": version.get("Os", "unknown"),
|
1032
|
+
"arch": version.get("Arch", "unknown"),
|
1033
|
+
"build_time": version.get("BuildTime", "unknown"),
|
1034
|
+
}
|
1035
|
+
self.log_action("get_version", params, result)
|
1036
|
+
return result
|
1037
|
+
except Exception as e:
|
1038
|
+
self.log_action("get_version", params, error=e)
|
1039
|
+
raise RuntimeError(f"Failed to get version: {str(e)}")
|
1040
|
+
|
1041
|
+
def get_info(self) -> Dict:
|
1042
|
+
params = {}
|
1043
|
+
try:
|
1044
|
+
info = self.client.info()
|
1045
|
+
host = info.get("host", {})
|
1046
|
+
result = {
|
1047
|
+
"containers_total": info.get("store", {}).get("containers", 0),
|
1048
|
+
"containers_running": host.get("runningContainers", 0),
|
1049
|
+
"images": info.get("store", {}).get("images", 0),
|
1050
|
+
"driver": host.get("graphDriverName", "unknown"),
|
1051
|
+
"platform": f"{host.get('os', 'unknown')} {host.get('arch', 'unknown')}",
|
1052
|
+
"memory_total": self._format_size(host.get("memTotal", 0)),
|
1053
|
+
"swap_total": self._format_size(host.get("swapTotal", 0)),
|
1054
|
+
}
|
1055
|
+
self.log_action("get_info", params, result)
|
1056
|
+
return result
|
1057
|
+
except Exception as e:
|
1058
|
+
self.log_action("get_info", params, error=e)
|
1059
|
+
raise RuntimeError(f"Failed to get info: {str(e)}")
|
1060
|
+
|
1061
|
+
def remove_image(self, image: str, force: bool = False) -> Dict:
|
1062
|
+
params = {"image": image, "force": force}
|
1063
|
+
try:
|
1064
|
+
self.client.images.remove(image, force=force)
|
1065
|
+
result = {"removed": image}
|
1066
|
+
self.log_action("remove_image", params, result)
|
1067
|
+
return result
|
1068
|
+
except Exception as e:
|
1069
|
+
self.log_action("remove_image", params, error=e)
|
1070
|
+
raise RuntimeError(f"Failed to remove image: {str(e)}")
|
1071
|
+
|
1057
1072
|
def stop_container(self, container_id: str, timeout: int = 10) -> Dict:
|
1058
1073
|
params = {"container_id": container_id, "timeout": timeout}
|
1059
1074
|
try:
|
@@ -1159,65 +1174,6 @@ class PodmanManager(ContainerManagerBase):
|
|
1159
1174
|
self.log_action("remove_volume", params, error=e)
|
1160
1175
|
raise RuntimeError(f"Failed to remove volume: {str(e)}")
|
1161
1176
|
|
1162
|
-
def list_networks(self) -> List[Dict]:
|
1163
|
-
params = {}
|
1164
|
-
try:
|
1165
|
-
networks = self.client.networks.list()
|
1166
|
-
result = []
|
1167
|
-
for net in networks:
|
1168
|
-
attrs = net.attrs
|
1169
|
-
containers = len(attrs.get("Containers", {}))
|
1170
|
-
created = attrs.get("Created", "unknown")
|
1171
|
-
if isinstance(created, str):
|
1172
|
-
created_str = created
|
1173
|
-
else:
|
1174
|
-
created_str = (
|
1175
|
-
datetime.fromtimestamp(created).strftime("%Y-%m-%dT%H:%M:%S")
|
1176
|
-
if created
|
1177
|
-
else "unknown"
|
1178
|
-
)
|
1179
|
-
simplified = {
|
1180
|
-
"id": attrs.get("Id", "unknown")[7:19],
|
1181
|
-
"name": attrs.get("Name", "unknown"),
|
1182
|
-
"driver": attrs.get("Driver", "unknown"),
|
1183
|
-
"scope": attrs.get("Scope", "unknown"),
|
1184
|
-
"containers": containers,
|
1185
|
-
"created": created_str,
|
1186
|
-
}
|
1187
|
-
result.append(simplified)
|
1188
|
-
self.log_action("list_networks", params, result)
|
1189
|
-
return result
|
1190
|
-
except Exception as e:
|
1191
|
-
self.log_action("list_networks", params, error=e)
|
1192
|
-
raise RuntimeError(f"Failed to list networks: {str(e)}")
|
1193
|
-
|
1194
|
-
def create_network(self, name: str, driver: str = "bridge") -> Dict:
|
1195
|
-
params = {"name": name, "driver": driver}
|
1196
|
-
try:
|
1197
|
-
network = self.client.networks.create(name, driver=driver)
|
1198
|
-
attrs = network.attrs
|
1199
|
-
created = attrs.get("Created", "unknown")
|
1200
|
-
if isinstance(created, str):
|
1201
|
-
created_str = created
|
1202
|
-
else:
|
1203
|
-
created_str = (
|
1204
|
-
datetime.fromtimestamp(created).strftime("%Y-%m-%dT%H:%M:%S")
|
1205
|
-
if created
|
1206
|
-
else "unknown"
|
1207
|
-
)
|
1208
|
-
result = {
|
1209
|
-
"id": attrs.get("Id", "unknown")[7:19],
|
1210
|
-
"name": attrs.get("Name", name),
|
1211
|
-
"driver": attrs.get("Driver", driver),
|
1212
|
-
"scope": attrs.get("Scope", "unknown"),
|
1213
|
-
"created": created_str,
|
1214
|
-
}
|
1215
|
-
self.log_action("create_network", params, result)
|
1216
|
-
return result
|
1217
|
-
except Exception as e:
|
1218
|
-
self.log_action("create_network", params, error=e)
|
1219
|
-
raise RuntimeError(f"Failed to create network: {str(e)}")
|
1220
|
-
|
1221
1177
|
def remove_network(self, network_id: str) -> Dict:
|
1222
1178
|
params = {"network_id": network_id}
|
1223
1179
|
try:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: container-manager-mcp
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.12
|
4
4
|
Summary: Container Manager manage Docker, Docker Swarm, and Podman containers as an MCP Server
|
5
5
|
Author-email: Audel Rouhi <knucklessg1@gmail.com>
|
6
6
|
License: MIT
|
@@ -48,7 +48,7 @@ Dynamic: license-file
|
|
48
48
|

|
49
49
|

|
50
50
|
|
51
|
-
*Version: 0.0.
|
51
|
+
*Version: 0.0.12*
|
52
52
|
|
53
53
|
Container Manager MCP Server provides a robust interface to manage Docker and Podman containers, networks, volumes, and Docker Swarm services through a FastMCP server, enabling programmatic and remote container management.
|
54
54
|
|
@@ -0,0 +1,10 @@
|
|
1
|
+
container_manager_mcp/__init__.py,sha256=N3bhKd_oh5YmBBl9N1omfZgaXhJyP0vOzH4VKxs68_g,506
|
2
|
+
container_manager_mcp/__main__.py,sha256=zic5tX336HG8LfdzQQ0sDVx-tMSOsgOZCtaxHWgJ4Go,134
|
3
|
+
container_manager_mcp/container_manager.py,sha256=hIdXyI7L2kFrJc_7bMOaQpKAPECxCVjaQoohqjmY-Iw,67859
|
4
|
+
container_manager_mcp/container_manager_mcp.py,sha256=cIAN8YGflQ9nZEHodkYdQKo2GECAuTH7WB-oMhnAd_0,37959
|
5
|
+
container_manager_mcp-0.0.12.dist-info/licenses/LICENSE,sha256=Z1xmcrPHBnGCETO_LLQJUeaSNBSnuptcDVTt4kaPUOE,1060
|
6
|
+
container_manager_mcp-0.0.12.dist-info/METADATA,sha256=ItKjQvWeRj9e8J26zMjGilRfuFPywXf6afihA_knBdA,8240
|
7
|
+
container_manager_mcp-0.0.12.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
8
|
+
container_manager_mcp-0.0.12.dist-info/entry_points.txt,sha256=I23pXcCgAShlfYbENzs3kbw3l1lU9Gy7lODPfRqeeiA,156
|
9
|
+
container_manager_mcp-0.0.12.dist-info/top_level.txt,sha256=B7QQLOd9mBdu0lsPKqyu4T8-zUtbqKzQJbMbtAzoozU,22
|
10
|
+
container_manager_mcp-0.0.12.dist-info/RECORD,,
|
@@ -1,10 +0,0 @@
|
|
1
|
-
container_manager_mcp/__init__.py,sha256=N3bhKd_oh5YmBBl9N1omfZgaXhJyP0vOzH4VKxs68_g,506
|
2
|
-
container_manager_mcp/__main__.py,sha256=zic5tX336HG8LfdzQQ0sDVx-tMSOsgOZCtaxHWgJ4Go,134
|
3
|
-
container_manager_mcp/container_manager.py,sha256=64pDF7gEadqh_rVqjfkznLSWm6fFnZJJ5hnkZlgX9kI,69338
|
4
|
-
container_manager_mcp/container_manager_mcp.py,sha256=cIAN8YGflQ9nZEHodkYdQKo2GECAuTH7WB-oMhnAd_0,37959
|
5
|
-
container_manager_mcp-0.0.11.dist-info/licenses/LICENSE,sha256=Z1xmcrPHBnGCETO_LLQJUeaSNBSnuptcDVTt4kaPUOE,1060
|
6
|
-
container_manager_mcp-0.0.11.dist-info/METADATA,sha256=bQQ_l-V1ZAh5ciFG00U0hLIqttSm45RjO_9Va7DKinU,8240
|
7
|
-
container_manager_mcp-0.0.11.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
8
|
-
container_manager_mcp-0.0.11.dist-info/entry_points.txt,sha256=I23pXcCgAShlfYbENzs3kbw3l1lU9Gy7lODPfRqeeiA,156
|
9
|
-
container_manager_mcp-0.0.11.dist-info/top_level.txt,sha256=B7QQLOd9mBdu0lsPKqyu4T8-zUtbqKzQJbMbtAzoozU,22
|
10
|
-
container_manager_mcp-0.0.11.dist-info/RECORD,,
|
File without changes
|
{container_manager_mcp-0.0.11.dist-info → container_manager_mcp-0.0.12.dist-info}/entry_points.txt
RENAMED
File without changes
|
{container_manager_mcp-0.0.11.dist-info → container_manager_mcp-0.0.12.dist-info}/licenses/LICENSE
RENAMED
File without changes
|
{container_manager_mcp-0.0.11.dist-info → container_manager_mcp-0.0.12.dist-info}/top_level.txt
RENAMED
File without changes
|