container-manager-mcp 0.0.12__tar.gz → 1.0.1__tar.gz
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-0.0.12/container_manager_mcp.egg-info → container_manager_mcp-1.0.1}/PKG-INFO +2 -2
- {container_manager_mcp-0.0.12 → container_manager_mcp-1.0.1}/README.md +1 -1
- {container_manager_mcp-0.0.12 → container_manager_mcp-1.0.1}/container_manager_mcp/container_manager.py +294 -266
- {container_manager_mcp-0.0.12 → container_manager_mcp-1.0.1}/container_manager_mcp/container_manager_mcp.py +113 -87
- {container_manager_mcp-0.0.12 → container_manager_mcp-1.0.1/container_manager_mcp.egg-info}/PKG-INFO +2 -2
- {container_manager_mcp-0.0.12 → container_manager_mcp-1.0.1}/pyproject.toml +1 -1
- {container_manager_mcp-0.0.12 → container_manager_mcp-1.0.1}/LICENSE +0 -0
- {container_manager_mcp-0.0.12 → container_manager_mcp-1.0.1}/MANIFEST.in +0 -0
- {container_manager_mcp-0.0.12 → container_manager_mcp-1.0.1}/container_manager_mcp/__init__.py +0 -0
- {container_manager_mcp-0.0.12 → container_manager_mcp-1.0.1}/container_manager_mcp/__main__.py +0 -0
- {container_manager_mcp-0.0.12 → container_manager_mcp-1.0.1}/container_manager_mcp.egg-info/SOURCES.txt +0 -0
- {container_manager_mcp-0.0.12 → container_manager_mcp-1.0.1}/container_manager_mcp.egg-info/dependency_links.txt +0 -0
- {container_manager_mcp-0.0.12 → container_manager_mcp-1.0.1}/container_manager_mcp.egg-info/entry_points.txt +0 -0
- {container_manager_mcp-0.0.12 → container_manager_mcp-1.0.1}/container_manager_mcp.egg-info/requires.txt +0 -0
- {container_manager_mcp-0.0.12 → container_manager_mcp-1.0.1}/container_manager_mcp.egg-info/top_level.txt +0 -0
- {container_manager_mcp-0.0.12 → container_manager_mcp-1.0.1}/requirements.txt +0 -0
- {container_manager_mcp-0.0.12 → container_manager_mcp-1.0.1}/setup.cfg +0 -0
{container_manager_mcp-0.0.12/container_manager_mcp.egg-info → container_manager_mcp-1.0.1}/PKG-INFO
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: container-manager-mcp
|
3
|
-
Version:
|
3
|
+
Version: 1.0.1
|
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:
|
51
|
+
*Version: 1.0.1*
|
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
|
|
@@ -20,7 +20,7 @@
|
|
20
20
|

|
21
21
|

|
22
22
|
|
23
|
-
*Version:
|
23
|
+
*Version: 1.0.1*
|
24
24
|
|
25
25
|
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.
|
26
26
|
|
@@ -6,11 +6,12 @@ import logging
|
|
6
6
|
import os
|
7
7
|
from abc import ABC, abstractmethod
|
8
8
|
from typing import List, Dict, Optional, Any
|
9
|
-
import
|
9
|
+
import argparse
|
10
10
|
import json
|
11
11
|
import subprocess
|
12
12
|
from datetime import datetime
|
13
13
|
import dateutil.parser
|
14
|
+
import platform
|
14
15
|
|
15
16
|
try:
|
16
17
|
import docker
|
@@ -815,15 +816,107 @@ class DockerManager(ContainerManagerBase):
|
|
815
816
|
|
816
817
|
|
817
818
|
class PodmanManager(ContainerManagerBase):
|
818
|
-
def __init__(self, silent: bool = False, log_file: str = None):
|
819
|
+
def __init__(self, silent: bool = False, log_file: Optional[str] = None):
|
819
820
|
super().__init__(silent, log_file)
|
821
|
+
|
820
822
|
if PodmanClient is None:
|
821
823
|
raise ImportError("Please install podman-py: pip install podman")
|
824
|
+
|
825
|
+
base_url = self._autodetect_podman_url()
|
826
|
+
if base_url is None:
|
827
|
+
self.logger.error(
|
828
|
+
"No valid Podman socket found after trying all known locations"
|
829
|
+
)
|
830
|
+
raise RuntimeError("Failed to connect to Podman: No valid socket found")
|
831
|
+
|
822
832
|
try:
|
823
|
-
self.client = PodmanClient()
|
833
|
+
self.client = PodmanClient(base_url=base_url)
|
834
|
+
self.logger.info(f"Connected to Podman with base_url: {base_url}")
|
824
835
|
except PodmanError as e:
|
825
|
-
self.logger.error(
|
826
|
-
|
836
|
+
self.logger.error(
|
837
|
+
f"Failed to connect to Podman daemon with {base_url}: {str(e)}"
|
838
|
+
)
|
839
|
+
raise RuntimeError(f"Failed to connect to Podman with {base_url}: {str(e)}")
|
840
|
+
|
841
|
+
def _is_wsl(self) -> bool:
|
842
|
+
"""Check if running inside WSL2."""
|
843
|
+
try:
|
844
|
+
with open("/proc/version", "r") as f:
|
845
|
+
return "WSL" in f.read()
|
846
|
+
except FileNotFoundError:
|
847
|
+
return "WSL_DISTRO_NAME" in os.environ
|
848
|
+
|
849
|
+
def _is_podman_machine_running(self) -> bool:
|
850
|
+
"""Check if Podman machine is running (for Windows/WSL2)."""
|
851
|
+
try:
|
852
|
+
result = subprocess.run(
|
853
|
+
["podman", "machine", "list", "--format", "{{.Running}}"],
|
854
|
+
capture_output=True,
|
855
|
+
text=True,
|
856
|
+
check=False,
|
857
|
+
)
|
858
|
+
return "true" in result.stdout.lower()
|
859
|
+
except (subprocess.SubprocessError, FileNotFoundError):
|
860
|
+
return False
|
861
|
+
|
862
|
+
def _try_connect(self, base_url: str) -> Optional[PodmanClient]:
|
863
|
+
"""Attempt to connect to Podman with the given base_url."""
|
864
|
+
try:
|
865
|
+
client = PodmanClient(base_url=base_url)
|
866
|
+
# Test connection
|
867
|
+
client.version()
|
868
|
+
return client
|
869
|
+
except PodmanError as e:
|
870
|
+
self.logger.debug(f"Connection failed for {base_url}: {str(e)}")
|
871
|
+
return None
|
872
|
+
|
873
|
+
def _autodetect_podman_url(self) -> Optional[str]:
|
874
|
+
"""Autodetect the appropriate Podman socket URL based on platform."""
|
875
|
+
# Check for environment variable override
|
876
|
+
base_url = os.environ.get("PODMAN_BASE_URL")
|
877
|
+
if base_url:
|
878
|
+
self.logger.info(f"Using PODMAN_BASE_URL from environment: {base_url}")
|
879
|
+
return base_url
|
880
|
+
|
881
|
+
system = platform.system()
|
882
|
+
is_wsl = self._is_wsl()
|
883
|
+
|
884
|
+
# Define socket candidates based on platform
|
885
|
+
socket_candidates = []
|
886
|
+
if system == "Windows" and not is_wsl:
|
887
|
+
# Windows with Podman machine
|
888
|
+
if self._is_podman_machine_running():
|
889
|
+
socket_candidates.append("npipe:////./pipe/docker_engine")
|
890
|
+
# Fallback to WSL2 distro sockets if running in a mixed setup
|
891
|
+
socket_candidates.extend(
|
892
|
+
[
|
893
|
+
"unix:///mnt/wsl/podman-sockets/podman-machine-default/podman-user.sock", # Rootless
|
894
|
+
"unix:///mnt/wsl/podman-sockets/podman-machine-default/podman-root.sock", # Rootful
|
895
|
+
]
|
896
|
+
)
|
897
|
+
elif system == "Linux" or is_wsl:
|
898
|
+
# Linux or WSL2 distro: prioritize rootless, then rootful
|
899
|
+
uid = os.getuid()
|
900
|
+
socket_candidates.extend(
|
901
|
+
[
|
902
|
+
f"unix:///run/user/{uid}/podman/podman.sock", # Rootless
|
903
|
+
"unix:///run/podman/podman.sock", # Rootful
|
904
|
+
]
|
905
|
+
)
|
906
|
+
|
907
|
+
# Try each socket candidate
|
908
|
+
for url in socket_candidates:
|
909
|
+
# For Unix sockets, check if the file exists (on Linux/WSL2)
|
910
|
+
if url.startswith("unix://") and (system == "Linux" or is_wsl):
|
911
|
+
socket_path = url.replace("unix://", "")
|
912
|
+
if not os.path.exists(socket_path):
|
913
|
+
self.logger.debug(f"Socket {socket_path} does not exist")
|
914
|
+
continue
|
915
|
+
client = self._try_connect(url)
|
916
|
+
if client:
|
917
|
+
return url
|
918
|
+
|
919
|
+
return None
|
827
920
|
|
828
921
|
def list_images(self) -> List[Dict]:
|
829
922
|
params = {}
|
@@ -1272,13 +1365,32 @@ class PodmanManager(ContainerManagerBase):
|
|
1272
1365
|
raise NotImplementedError("Swarm not supported in Podman")
|
1273
1366
|
|
1274
1367
|
|
1275
|
-
# The rest of the file (create_manager, usage, container_manager) remains unchanged
|
1276
|
-
|
1277
|
-
|
1278
1368
|
def create_manager(
|
1279
|
-
manager_type: str, silent: bool = False, log_file: str = None
|
1369
|
+
manager_type: Optional[str] = None, silent: bool = False, log_file: str = None
|
1280
1370
|
) -> ContainerManagerBase:
|
1281
|
-
if manager_type
|
1371
|
+
if manager_type is None:
|
1372
|
+
manager_type = os.environ.get("CONTAINER_MANAGER_TYPE")
|
1373
|
+
if manager_type is None:
|
1374
|
+
# Autodetect
|
1375
|
+
if PodmanClient is not None:
|
1376
|
+
try:
|
1377
|
+
test_client = PodmanClient()
|
1378
|
+
test_client.close()
|
1379
|
+
manager_type = "podman"
|
1380
|
+
except Exception:
|
1381
|
+
pass
|
1382
|
+
if manager_type is None and docker is not None:
|
1383
|
+
try:
|
1384
|
+
test_client = docker.from_env()
|
1385
|
+
test_client.close()
|
1386
|
+
manager_type = "docker"
|
1387
|
+
except Exception:
|
1388
|
+
pass
|
1389
|
+
if manager_type is None:
|
1390
|
+
raise ValueError(
|
1391
|
+
"No supported container manager detected. Set CONTAINER_MANAGER_TYPE or install Docker/Podman."
|
1392
|
+
)
|
1393
|
+
if manager_type.lower() in ["docker", "swarm"]:
|
1282
1394
|
return DockerManager(silent=silent, log_file=log_file)
|
1283
1395
|
elif manager_type.lower() == "podman":
|
1284
1396
|
return PodmanManager(silent=silent, log_file=log_file)
|
@@ -1294,7 +1406,7 @@ Container Manager: A tool to manage containers with Docker, Podman, and Docker S
|
|
1294
1406
|
Usage:
|
1295
1407
|
-h | --help [ See usage for script ]
|
1296
1408
|
-s | --silent [ Suppress output ]
|
1297
|
-
-m | --manager <type> [ docker, podman, swarm; default:
|
1409
|
+
-m | --manager <type> [ docker, podman, swarm; default: auto-detect ]
|
1298
1410
|
--log-file <path> [ Log to specified file (default: container_manager.log in script dir) ]
|
1299
1411
|
|
1300
1412
|
Actions:
|
@@ -1359,262 +1471,178 @@ container_manager.py --manager docker --pull-image nginx --tag latest --list-con
|
|
1359
1471
|
|
1360
1472
|
|
1361
1473
|
def container_manager(argv):
|
1362
|
-
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
1366
|
-
|
1367
|
-
|
1368
|
-
|
1369
|
-
|
1370
|
-
|
1371
|
-
|
1372
|
-
|
1373
|
-
|
1374
|
-
|
1375
|
-
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1379
|
-
|
1380
|
-
|
1381
|
-
|
1382
|
-
|
1383
|
-
|
1384
|
-
|
1385
|
-
|
1386
|
-
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1390
|
-
|
1391
|
-
|
1392
|
-
|
1393
|
-
|
1394
|
-
|
1395
|
-
|
1396
|
-
|
1397
|
-
|
1398
|
-
|
1399
|
-
|
1400
|
-
|
1401
|
-
|
1402
|
-
|
1403
|
-
|
1404
|
-
|
1405
|
-
|
1406
|
-
|
1407
|
-
|
1408
|
-
|
1409
|
-
|
1410
|
-
|
1411
|
-
|
1412
|
-
|
1413
|
-
|
1414
|
-
|
1415
|
-
|
1416
|
-
|
1417
|
-
|
1418
|
-
|
1419
|
-
|
1420
|
-
|
1421
|
-
|
1422
|
-
|
1423
|
-
|
1424
|
-
|
1425
|
-
|
1426
|
-
|
1427
|
-
|
1428
|
-
|
1429
|
-
|
1430
|
-
|
1431
|
-
|
1432
|
-
|
1433
|
-
|
1434
|
-
|
1435
|
-
|
1436
|
-
|
1437
|
-
|
1438
|
-
|
1439
|
-
|
1440
|
-
|
1441
|
-
|
1442
|
-
|
1443
|
-
|
1444
|
-
|
1445
|
-
|
1446
|
-
|
1447
|
-
|
1448
|
-
|
1449
|
-
|
1450
|
-
|
1451
|
-
|
1452
|
-
|
1453
|
-
|
1454
|
-
|
1455
|
-
|
1456
|
-
|
1457
|
-
|
1458
|
-
|
1459
|
-
|
1460
|
-
"remove-container=",
|
1461
|
-
"get-container-logs=",
|
1462
|
-
"tail=",
|
1463
|
-
"exec-in-container=",
|
1464
|
-
"exec-command=",
|
1465
|
-
"exec-detach",
|
1466
|
-
"list-volumes",
|
1467
|
-
"create-volume=",
|
1468
|
-
"remove-volume=",
|
1469
|
-
"list-networks",
|
1470
|
-
"create-network=",
|
1471
|
-
"driver=",
|
1472
|
-
"remove-network=",
|
1473
|
-
"compose-up=",
|
1474
|
-
"build",
|
1475
|
-
"compose-down=",
|
1476
|
-
"compose-ps=",
|
1477
|
-
"compose-logs=",
|
1478
|
-
"service=",
|
1479
|
-
"init-swarm",
|
1480
|
-
"advertise-addr=",
|
1481
|
-
"leave-swarm",
|
1482
|
-
"list-nodes",
|
1483
|
-
"list-services",
|
1484
|
-
"create-service=",
|
1485
|
-
"image=",
|
1486
|
-
"replicas=",
|
1487
|
-
"mounts=",
|
1488
|
-
"remove-service=",
|
1489
|
-
],
|
1490
|
-
)
|
1491
|
-
except getopt.GetoptError:
|
1492
|
-
usage()
|
1493
|
-
sys.exit(2)
|
1474
|
+
parser = argparse.ArgumentParser(
|
1475
|
+
description="Container Manager: A tool to manage containers with Docker, Podman, and Docker Swarm!"
|
1476
|
+
)
|
1477
|
+
parser.add_argument("-s", "--silent", action="store_true", help="Suppress output")
|
1478
|
+
parser.add_argument(
|
1479
|
+
"-m",
|
1480
|
+
"--manager",
|
1481
|
+
type=str,
|
1482
|
+
default=None,
|
1483
|
+
help="Container manager type: docker, podman, swarm (default: auto-detect)",
|
1484
|
+
)
|
1485
|
+
parser.add_argument("--log-file", type=str, default=None, help="Path to log file")
|
1486
|
+
parser.add_argument("--get-version", action="store_true", help="Get version info")
|
1487
|
+
parser.add_argument("--get-info", action="store_true", help="Get system info")
|
1488
|
+
parser.add_argument("--list-images", action="store_true", help="List images")
|
1489
|
+
parser.add_argument("--pull-image", type=str, default=None, help="Image to pull")
|
1490
|
+
parser.add_argument("--tag", type=str, default="latest", help="Image tag")
|
1491
|
+
parser.add_argument("--platform", type=str, default=None, help="Platform")
|
1492
|
+
parser.add_argument(
|
1493
|
+
"--remove-image", type=str, default=None, help="Image to remove"
|
1494
|
+
)
|
1495
|
+
parser.add_argument("--force", action="store_true", help="Force removal")
|
1496
|
+
parser.add_argument(
|
1497
|
+
"--list-containers", action="store_true", help="List containers"
|
1498
|
+
)
|
1499
|
+
parser.add_argument("--all", action="store_true", help="Show all containers")
|
1500
|
+
parser.add_argument("--run-container", type=str, default=None, help="Image to run")
|
1501
|
+
parser.add_argument("--name", type=str, default=None, help="Container name")
|
1502
|
+
parser.add_argument("--command", type=str, default=None, help="Command to run")
|
1503
|
+
parser.add_argument("--detach", action="store_true", help="Detach mode")
|
1504
|
+
parser.add_argument("--ports", type=str, default=None, help="Port mappings")
|
1505
|
+
parser.add_argument("--volumes", type=str, default=None, help="Volume mappings")
|
1506
|
+
parser.add_argument(
|
1507
|
+
"--environment", type=str, default=None, help="Environment vars"
|
1508
|
+
)
|
1509
|
+
parser.add_argument(
|
1510
|
+
"--stop-container", type=str, default=None, help="Container to stop"
|
1511
|
+
)
|
1512
|
+
parser.add_argument("--timeout", type=int, default=10, help="Timeout in seconds")
|
1513
|
+
parser.add_argument(
|
1514
|
+
"--remove-container", type=str, default=None, help="Container to remove"
|
1515
|
+
)
|
1516
|
+
parser.add_argument(
|
1517
|
+
"--get-container-logs", type=str, default=None, help="Container logs"
|
1518
|
+
)
|
1519
|
+
parser.add_argument("--tail", type=str, default="all", help="Tail lines")
|
1520
|
+
parser.add_argument(
|
1521
|
+
"--exec-in-container", type=str, default=None, help="Container to exec"
|
1522
|
+
)
|
1523
|
+
parser.add_argument("--exec-command", type=str, default=None, help="Exec command")
|
1524
|
+
parser.add_argument("--exec-detach", action="store_true", help="Detach exec")
|
1525
|
+
parser.add_argument("--list-volumes", action="store_true", help="List volumes")
|
1526
|
+
parser.add_argument(
|
1527
|
+
"--create-volume", type=str, default=None, help="Volume to create"
|
1528
|
+
)
|
1529
|
+
parser.add_argument(
|
1530
|
+
"--remove-volume", type=str, default=None, help="Volume to remove"
|
1531
|
+
)
|
1532
|
+
parser.add_argument("--list-networks", action="store_true", help="List networks")
|
1533
|
+
parser.add_argument(
|
1534
|
+
"--create-network", type=str, default=None, help="Network to create"
|
1535
|
+
)
|
1536
|
+
parser.add_argument("--driver", type=str, default="bridge", help="Network driver")
|
1537
|
+
parser.add_argument(
|
1538
|
+
"--remove-network", type=str, default=None, help="Network to remove"
|
1539
|
+
)
|
1540
|
+
parser.add_argument("--compose-up", type=str, default=None, help="Compose file up")
|
1541
|
+
parser.add_argument("--build", action="store_true", help="Build images")
|
1542
|
+
parser.add_argument(
|
1543
|
+
"--compose-detach", action="store_true", default=True, help="Detach compose"
|
1544
|
+
)
|
1545
|
+
parser.add_argument(
|
1546
|
+
"--compose-down", type=str, default=None, help="Compose file down"
|
1547
|
+
)
|
1548
|
+
parser.add_argument("--compose-ps", type=str, default=None, help="Compose ps")
|
1549
|
+
parser.add_argument("--compose-logs", type=str, default=None, help="Compose logs")
|
1550
|
+
parser.add_argument("--service", type=str, default=None, help="Specific service")
|
1551
|
+
parser.add_argument("--init-swarm", action="store_true", help="Init swarm")
|
1552
|
+
parser.add_argument(
|
1553
|
+
"--advertise-addr", type=str, default=None, help="Advertise address"
|
1554
|
+
)
|
1555
|
+
parser.add_argument("--leave-swarm", action="store_true", help="Leave swarm")
|
1556
|
+
parser.add_argument("--list-nodes", action="store_true", help="List swarm nodes")
|
1557
|
+
parser.add_argument(
|
1558
|
+
"--list-services", action="store_true", help="List swarm services"
|
1559
|
+
)
|
1560
|
+
parser.add_argument(
|
1561
|
+
"--create-service", type=str, default=None, help="Service to create"
|
1562
|
+
)
|
1563
|
+
parser.add_argument("--image", type=str, default=None, help="Service image")
|
1564
|
+
parser.add_argument("--replicas", type=int, default=1, help="Replicas")
|
1565
|
+
parser.add_argument("--mounts", type=str, default=None, help="Mounts")
|
1566
|
+
parser.add_argument(
|
1567
|
+
"--remove-service", type=str, default=None, help="Service to remove"
|
1568
|
+
)
|
1569
|
+
parser.add_argument("-h", "--help", action="store_true", help="Show help")
|
1570
|
+
|
1571
|
+
args = parser.parse_args(argv)
|
1494
1572
|
|
1495
|
-
|
1496
|
-
|
1497
|
-
|
1498
|
-
|
1499
|
-
|
1500
|
-
|
1501
|
-
|
1502
|
-
|
1503
|
-
|
1504
|
-
|
1505
|
-
|
1506
|
-
|
1507
|
-
|
1508
|
-
|
1509
|
-
|
1510
|
-
|
1511
|
-
|
1512
|
-
|
1513
|
-
|
1514
|
-
|
1515
|
-
|
1516
|
-
|
1517
|
-
|
1518
|
-
|
1519
|
-
|
1520
|
-
|
1521
|
-
|
1522
|
-
|
1523
|
-
|
1524
|
-
|
1525
|
-
|
1526
|
-
|
1527
|
-
|
1528
|
-
|
1529
|
-
|
1530
|
-
|
1531
|
-
|
1532
|
-
|
1533
|
-
|
1534
|
-
|
1535
|
-
|
1536
|
-
|
1537
|
-
|
1538
|
-
|
1539
|
-
|
1540
|
-
|
1541
|
-
|
1542
|
-
|
1543
|
-
|
1544
|
-
|
1545
|
-
|
1546
|
-
|
1547
|
-
|
1548
|
-
|
1549
|
-
|
1550
|
-
|
1551
|
-
|
1552
|
-
|
1553
|
-
|
1554
|
-
|
1555
|
-
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
1559
|
-
|
1560
|
-
|
1561
|
-
|
1562
|
-
|
1563
|
-
|
1564
|
-
|
1565
|
-
|
1566
|
-
|
1567
|
-
|
1568
|
-
remove_volume = True
|
1569
|
-
remove_volume_name = arg
|
1570
|
-
elif opt == "--list-networks":
|
1571
|
-
list_networks = True
|
1572
|
-
elif opt == "--create-network":
|
1573
|
-
create_network = True
|
1574
|
-
create_network_name = arg
|
1575
|
-
elif opt == "--driver":
|
1576
|
-
driver = arg
|
1577
|
-
elif opt == "--remove-network":
|
1578
|
-
remove_network = True
|
1579
|
-
remove_network_id = arg
|
1580
|
-
elif opt == "--compose-up":
|
1581
|
-
compose_up = True
|
1582
|
-
compose_up_file = arg
|
1583
|
-
elif opt == "--build":
|
1584
|
-
compose_build = True
|
1585
|
-
elif opt == "--compose-down":
|
1586
|
-
compose_down = True
|
1587
|
-
compose_down_file = arg
|
1588
|
-
elif opt == "--compose-ps":
|
1589
|
-
compose_ps = True
|
1590
|
-
compose_ps_file = arg
|
1591
|
-
elif opt == "--compose-logs":
|
1592
|
-
compose_logs = True
|
1593
|
-
compose_logs_file = arg
|
1594
|
-
elif opt == "--service":
|
1595
|
-
compose_service = arg
|
1596
|
-
elif opt == "--init-swarm":
|
1597
|
-
init_swarm = True
|
1598
|
-
elif opt == "--advertise-addr":
|
1599
|
-
advertise_addr = arg
|
1600
|
-
elif opt == "--leave-swarm":
|
1601
|
-
leave_swarm = True
|
1602
|
-
elif opt == "--list-nodes":
|
1603
|
-
list_nodes = True
|
1604
|
-
elif opt == "--list-services":
|
1605
|
-
list_services = True
|
1606
|
-
elif opt == "--create-service":
|
1607
|
-
create_service = True
|
1608
|
-
create_service_name = arg
|
1609
|
-
elif opt == "--image":
|
1610
|
-
service_image = arg
|
1611
|
-
elif opt == "--replicas":
|
1612
|
-
replicas = int(arg)
|
1613
|
-
elif opt == "--mounts":
|
1614
|
-
mounts_str = arg
|
1615
|
-
elif opt == "--remove-service":
|
1616
|
-
remove_service = True
|
1617
|
-
remove_service_id = arg
|
1573
|
+
if args.help:
|
1574
|
+
usage()
|
1575
|
+
sys.exit(0)
|
1576
|
+
|
1577
|
+
get_version = args.get_version
|
1578
|
+
get_info = args.get_info
|
1579
|
+
list_images = args.list_images
|
1580
|
+
pull_image = args.pull_image is not None
|
1581
|
+
pull_image_str = args.pull_image
|
1582
|
+
tag = args.tag
|
1583
|
+
platform = args.platform
|
1584
|
+
remove_image = args.remove_image is not None
|
1585
|
+
remove_image_str = args.remove_image
|
1586
|
+
force = args.force
|
1587
|
+
list_containers = args.list_containers
|
1588
|
+
all_containers = args.all
|
1589
|
+
run_container = args.run_container is not None
|
1590
|
+
run_image = args.run_container
|
1591
|
+
name = args.name
|
1592
|
+
command = args.command
|
1593
|
+
detach = args.detach
|
1594
|
+
ports_str = args.ports
|
1595
|
+
volumes_str = args.volumes
|
1596
|
+
environment_str = args.environment
|
1597
|
+
stop_container = args.stop_container is not None
|
1598
|
+
stop_container_id = args.stop_container
|
1599
|
+
timeout = args.timeout
|
1600
|
+
remove_container = args.remove_container is not None
|
1601
|
+
remove_container_id = args.remove_container
|
1602
|
+
get_container_logs = args.get_container_logs is not None
|
1603
|
+
container_logs_id = args.get_container_logs
|
1604
|
+
tail = args.tail
|
1605
|
+
exec_in_container = args.exec_in_container is not None
|
1606
|
+
exec_container_id = args.exec_in_container
|
1607
|
+
exec_command = args.exec_command
|
1608
|
+
exec_detach = args.exec_detach
|
1609
|
+
list_volumes = args.list_volumes
|
1610
|
+
create_volume = args.create_volume is not None
|
1611
|
+
create_volume_name = args.create_volume
|
1612
|
+
remove_volume = args.remove_volume is not None
|
1613
|
+
remove_volume_name = args.remove_volume
|
1614
|
+
list_networks = args.list_networks
|
1615
|
+
create_network = args.create_network is not None
|
1616
|
+
create_network_name = args.create_network
|
1617
|
+
driver = args.driver
|
1618
|
+
remove_network = args.remove_network is not None
|
1619
|
+
remove_network_id = args.remove_network
|
1620
|
+
compose_up = args.compose_up is not None
|
1621
|
+
compose_up_file = args.compose_up
|
1622
|
+
compose_build = args.build
|
1623
|
+
compose_detach = args.compose_detach
|
1624
|
+
compose_down = args.compose_down is not None
|
1625
|
+
compose_down_file = args.compose_down
|
1626
|
+
compose_ps = args.compose_ps is not None
|
1627
|
+
compose_ps_file = args.compose_ps
|
1628
|
+
compose_logs = args.compose_logs is not None
|
1629
|
+
compose_logs_file = args.compose_logs
|
1630
|
+
compose_service = args.service
|
1631
|
+
init_swarm = args.init_swarm
|
1632
|
+
advertise_addr = args.advertise_addr
|
1633
|
+
leave_swarm = args.leave_swarm
|
1634
|
+
list_nodes = args.list_nodes
|
1635
|
+
list_services = args.list_services
|
1636
|
+
create_service = args.create_service is not None
|
1637
|
+
create_service_name = args.create_service
|
1638
|
+
service_image = args.image
|
1639
|
+
replicas = args.replicas
|
1640
|
+
mounts_str = args.mounts
|
1641
|
+
remove_service = args.remove_service is not None
|
1642
|
+
remove_service_id = args.remove_service
|
1643
|
+
manager_type = args.manager
|
1644
|
+
silent = args.silent
|
1645
|
+
log_file = args.log_file
|
1618
1646
|
|
1619
1647
|
manager = create_manager(manager_type, silent, log_file)
|
1620
1648
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env python
|
2
2
|
# coding: utf-8
|
3
3
|
|
4
|
-
import
|
4
|
+
import argparse
|
5
5
|
import os
|
6
6
|
import sys
|
7
7
|
import logging
|
@@ -41,6 +41,7 @@ def to_boolean(string):
|
|
41
41
|
|
42
42
|
environment_silent = os.environ.get("SILENT", False)
|
43
43
|
environment_log_file = os.environ.get("LOG_FILE", None)
|
44
|
+
environment_container_manager_type = os.environ.get("CONTAINER_MANAGER_TYPE", None)
|
44
45
|
|
45
46
|
if environment_silent:
|
46
47
|
environment_silent = to_boolean(environment_silent)
|
@@ -59,8 +60,9 @@ if environment_silent:
|
|
59
60
|
tags={"container_management"},
|
60
61
|
)
|
61
62
|
async def get_version(
|
62
|
-
manager_type: str = Field(
|
63
|
-
description="Container manager: docker, podman
|
63
|
+
manager_type: Optional[str] = Field(
|
64
|
+
description="Container manager: docker, podman (default: auto-detect)",
|
65
|
+
default=environment_container_manager_type,
|
64
66
|
),
|
65
67
|
silent: Optional[bool] = Field(
|
66
68
|
description="Suppress output", default=environment_silent
|
@@ -95,8 +97,9 @@ async def get_version(
|
|
95
97
|
tags={"container_management"},
|
96
98
|
)
|
97
99
|
async def get_info(
|
98
|
-
manager_type: str = Field(
|
99
|
-
description="Container manager: docker, podman
|
100
|
+
manager_type: Optional[str] = Field(
|
101
|
+
description="Container manager: docker, podman (default: auto-detect)",
|
102
|
+
default=environment_container_manager_type,
|
100
103
|
),
|
101
104
|
silent: Optional[bool] = Field(
|
102
105
|
description="Suppress output", default=environment_silent
|
@@ -131,8 +134,9 @@ async def get_info(
|
|
131
134
|
tags={"container_management"},
|
132
135
|
)
|
133
136
|
async def list_images(
|
134
|
-
manager_type: str = Field(
|
135
|
-
description="Container manager: docker, podman
|
137
|
+
manager_type: Optional[str] = Field(
|
138
|
+
description="Container manager: docker, podman (default: auto-detect)",
|
139
|
+
default=environment_container_manager_type,
|
136
140
|
),
|
137
141
|
silent: Optional[bool] = Field(
|
138
142
|
description="Suppress output", default=environment_silent
|
@@ -172,8 +176,9 @@ async def pull_image(
|
|
172
176
|
platform: Optional[str] = Field(
|
173
177
|
description="Platform (e.g., linux/amd64)", default=None
|
174
178
|
),
|
175
|
-
manager_type: str = Field(
|
176
|
-
description="Container manager: docker, podman
|
179
|
+
manager_type: Optional[str] = Field(
|
180
|
+
description="Container manager: docker, podman (default: auto-detect)",
|
181
|
+
default=environment_container_manager_type,
|
177
182
|
),
|
178
183
|
silent: Optional[bool] = Field(
|
179
184
|
description="Suppress output", default=environment_silent
|
@@ -210,8 +215,9 @@ async def pull_image(
|
|
210
215
|
async def remove_image(
|
211
216
|
image: str = Field(description="Image name or ID to remove"),
|
212
217
|
force: bool = Field(description="Force removal", default=False),
|
213
|
-
manager_type: str = Field(
|
214
|
-
description="Container manager: docker, podman
|
218
|
+
manager_type: Optional[str] = Field(
|
219
|
+
description="Container manager: docker, podman (default: auto-detect)",
|
220
|
+
default=environment_container_manager_type,
|
215
221
|
),
|
216
222
|
silent: Optional[bool] = Field(
|
217
223
|
description="Suppress output", default=environment_silent
|
@@ -249,8 +255,9 @@ async def list_containers(
|
|
249
255
|
all: bool = Field(
|
250
256
|
description="Show all containers (default running only)", default=False
|
251
257
|
),
|
252
|
-
manager_type: str = Field(
|
253
|
-
description="Container manager: docker, podman
|
258
|
+
manager_type: Optional[str] = Field(
|
259
|
+
description="Container manager: docker, podman (default: auto-detect)",
|
260
|
+
default=environment_container_manager_type,
|
254
261
|
),
|
255
262
|
silent: Optional[bool] = Field(
|
256
263
|
description="Suppress output", default=environment_silent
|
@@ -301,8 +308,9 @@ async def run_container(
|
|
301
308
|
environment: Optional[Dict[str, str]] = Field(
|
302
309
|
description="Environment variables", default=None
|
303
310
|
),
|
304
|
-
manager_type: str = Field(
|
305
|
-
description="Container manager: docker, podman
|
311
|
+
manager_type: Optional[str] = Field(
|
312
|
+
description="Container manager: docker, podman (default: auto-detect)",
|
313
|
+
default=environment_container_manager_type,
|
306
314
|
),
|
307
315
|
silent: Optional[bool] = Field(
|
308
316
|
description="Suppress output", default=environment_silent
|
@@ -341,8 +349,9 @@ async def run_container(
|
|
341
349
|
async def stop_container(
|
342
350
|
container_id: str = Field(description="Container ID or name"),
|
343
351
|
timeout: int = Field(description="Timeout in seconds", default=10),
|
344
|
-
manager_type: str = Field(
|
345
|
-
description="Container manager: docker, podman
|
352
|
+
manager_type: Optional[str] = Field(
|
353
|
+
description="Container manager: docker, podman (default: auto-detect)",
|
354
|
+
default=environment_container_manager_type,
|
346
355
|
),
|
347
356
|
silent: Optional[bool] = Field(
|
348
357
|
description="Suppress output", default=environment_silent
|
@@ -379,8 +388,9 @@ async def stop_container(
|
|
379
388
|
async def remove_container(
|
380
389
|
container_id: str = Field(description="Container ID or name"),
|
381
390
|
force: bool = Field(description="Force removal", default=False),
|
382
|
-
manager_type: str = Field(
|
383
|
-
description="Container manager: docker, podman
|
391
|
+
manager_type: Optional[str] = Field(
|
392
|
+
description="Container manager: docker, podman (default: auto-detect)",
|
393
|
+
default=environment_container_manager_type,
|
384
394
|
),
|
385
395
|
silent: Optional[bool] = Field(
|
386
396
|
description="Suppress output", default=environment_silent
|
@@ -419,8 +429,9 @@ async def get_container_logs(
|
|
419
429
|
tail: str = Field(
|
420
430
|
description="Number of lines to show from the end (or 'all')", default="all"
|
421
431
|
),
|
422
|
-
manager_type: str = Field(
|
423
|
-
description="Container manager: docker, podman
|
432
|
+
manager_type: Optional[str] = Field(
|
433
|
+
description="Container manager: docker, podman (default: auto-detect)",
|
434
|
+
default=environment_container_manager_type,
|
424
435
|
),
|
425
436
|
silent: Optional[bool] = Field(
|
426
437
|
description="Suppress output", default=environment_silent
|
@@ -458,8 +469,9 @@ async def exec_in_container(
|
|
458
469
|
container_id: str = Field(description="Container ID or name"),
|
459
470
|
command: List[str] = Field(description="Command to execute"),
|
460
471
|
detach: bool = Field(description="Detach execution", default=False),
|
461
|
-
manager_type: str = Field(
|
462
|
-
description="Container manager: docker, podman
|
472
|
+
manager_type: Optional[str] = Field(
|
473
|
+
description="Container manager: docker, podman (default: auto-detect)",
|
474
|
+
default=environment_container_manager_type,
|
463
475
|
),
|
464
476
|
silent: Optional[bool] = Field(
|
465
477
|
description="Suppress output", default=environment_silent
|
@@ -494,8 +506,9 @@ async def exec_in_container(
|
|
494
506
|
tags={"container_management"},
|
495
507
|
)
|
496
508
|
async def list_volumes(
|
497
|
-
manager_type: str = Field(
|
498
|
-
description="Container manager: docker, podman
|
509
|
+
manager_type: Optional[str] = Field(
|
510
|
+
description="Container manager: docker, podman (default: auto-detect)",
|
511
|
+
default=environment_container_manager_type,
|
499
512
|
),
|
500
513
|
silent: Optional[bool] = Field(
|
501
514
|
description="Suppress output", default=environment_silent
|
@@ -531,8 +544,9 @@ async def list_volumes(
|
|
531
544
|
)
|
532
545
|
async def create_volume(
|
533
546
|
name: str = Field(description="Volume name"),
|
534
|
-
manager_type: str = Field(
|
535
|
-
description="Container manager: docker, podman
|
547
|
+
manager_type: Optional[str] = Field(
|
548
|
+
description="Container manager: docker, podman (default: auto-detect)",
|
549
|
+
default=environment_container_manager_type,
|
536
550
|
),
|
537
551
|
silent: Optional[bool] = Field(
|
538
552
|
description="Suppress output", default=environment_silent
|
@@ -569,8 +583,9 @@ async def create_volume(
|
|
569
583
|
async def remove_volume(
|
570
584
|
name: str = Field(description="Volume name"),
|
571
585
|
force: bool = Field(description="Force removal", default=False),
|
572
|
-
manager_type: str = Field(
|
573
|
-
description="Container manager: docker, podman
|
586
|
+
manager_type: Optional[str] = Field(
|
587
|
+
description="Container manager: docker, podman (default: auto-detect)",
|
588
|
+
default=environment_container_manager_type,
|
574
589
|
),
|
575
590
|
silent: Optional[bool] = Field(
|
576
591
|
description="Suppress output", default=environment_silent
|
@@ -605,8 +620,9 @@ async def remove_volume(
|
|
605
620
|
tags={"container_management"},
|
606
621
|
)
|
607
622
|
async def list_networks(
|
608
|
-
manager_type: str = Field(
|
609
|
-
description="Container manager: docker, podman
|
623
|
+
manager_type: Optional[str] = Field(
|
624
|
+
description="Container manager: docker, podman (default: auto-detect)",
|
625
|
+
default=environment_container_manager_type,
|
610
626
|
),
|
611
627
|
silent: Optional[bool] = Field(
|
612
628
|
description="Suppress output", default=environment_silent
|
@@ -643,8 +659,9 @@ async def list_networks(
|
|
643
659
|
async def create_network(
|
644
660
|
name: str = Field(description="Network name"),
|
645
661
|
driver: str = Field(description="Network driver (e.g., bridge)", default="bridge"),
|
646
|
-
manager_type: str = Field(
|
647
|
-
description="Container manager: docker, podman
|
662
|
+
manager_type: Optional[str] = Field(
|
663
|
+
description="Container manager: docker, podman (default: auto-detect)",
|
664
|
+
default=environment_container_manager_type,
|
648
665
|
),
|
649
666
|
silent: Optional[bool] = Field(
|
650
667
|
description="Suppress output", default=environment_silent
|
@@ -680,8 +697,9 @@ async def create_network(
|
|
680
697
|
)
|
681
698
|
async def remove_network(
|
682
699
|
network_id: str = Field(description="Network ID or name"),
|
683
|
-
manager_type: str = Field(
|
684
|
-
description="Container manager: docker, podman
|
700
|
+
manager_type: Optional[str] = Field(
|
701
|
+
description="Container manager: docker, podman (default: auto-detect)",
|
702
|
+
default=environment_container_manager_type,
|
685
703
|
),
|
686
704
|
silent: Optional[bool] = Field(
|
687
705
|
description="Suppress output", default=environment_silent
|
@@ -722,7 +740,10 @@ async def init_swarm(
|
|
722
740
|
advertise_addr: Optional[str] = Field(
|
723
741
|
description="Advertise address", default=None
|
724
742
|
),
|
725
|
-
manager_type: str = Field(
|
743
|
+
manager_type: Optional[str] = Field(
|
744
|
+
description="Container manager: must be docker for swarm (default: auto-detect)",
|
745
|
+
default=environment_container_manager_type,
|
746
|
+
),
|
726
747
|
silent: Optional[bool] = Field(
|
727
748
|
description="Suppress output", default=environment_silent
|
728
749
|
),
|
@@ -733,7 +754,7 @@ async def init_swarm(
|
|
733
754
|
description="MCP context for progress reporting", default=None
|
734
755
|
),
|
735
756
|
) -> Dict:
|
736
|
-
if manager_type != "docker":
|
757
|
+
if manager_type and manager_type != "docker":
|
737
758
|
raise ValueError("Swarm operations are only supported on Docker")
|
738
759
|
logger = logging.getLogger("ContainerManager")
|
739
760
|
logger.debug(
|
@@ -759,7 +780,10 @@ async def init_swarm(
|
|
759
780
|
)
|
760
781
|
async def leave_swarm(
|
761
782
|
force: bool = Field(description="Force leave", default=False),
|
762
|
-
manager_type: str = Field(
|
783
|
+
manager_type: Optional[str] = Field(
|
784
|
+
description="Container manager: must be docker for swarm (default: auto-detect)",
|
785
|
+
default=environment_container_manager_type,
|
786
|
+
),
|
763
787
|
silent: Optional[bool] = Field(
|
764
788
|
description="Suppress output", default=environment_silent
|
765
789
|
),
|
@@ -770,7 +794,7 @@ async def leave_swarm(
|
|
770
794
|
description="MCP context for progress reporting", default=None
|
771
795
|
),
|
772
796
|
) -> Dict:
|
773
|
-
if manager_type != "docker":
|
797
|
+
if manager_type and manager_type != "docker":
|
774
798
|
raise ValueError("Swarm operations are only supported on Docker")
|
775
799
|
logger = logging.getLogger("ContainerManager")
|
776
800
|
logger.debug(
|
@@ -795,7 +819,10 @@ async def leave_swarm(
|
|
795
819
|
tags={"container_management", "swarm"},
|
796
820
|
)
|
797
821
|
async def list_nodes(
|
798
|
-
manager_type: str = Field(
|
822
|
+
manager_type: Optional[str] = Field(
|
823
|
+
description="Container manager: must be docker for swarm (default: auto-detect)",
|
824
|
+
default=environment_container_manager_type,
|
825
|
+
),
|
799
826
|
silent: Optional[bool] = Field(
|
800
827
|
description="Suppress output", default=environment_silent
|
801
828
|
),
|
@@ -806,7 +833,7 @@ async def list_nodes(
|
|
806
833
|
description="MCP context for progress reporting", default=None
|
807
834
|
),
|
808
835
|
) -> List[Dict]:
|
809
|
-
if manager_type != "docker":
|
836
|
+
if manager_type and manager_type != "docker":
|
810
837
|
raise ValueError("Swarm operations are only supported on Docker")
|
811
838
|
logger = logging.getLogger("ContainerManager")
|
812
839
|
logger.debug(
|
@@ -831,7 +858,10 @@ async def list_nodes(
|
|
831
858
|
tags={"container_management", "swarm"},
|
832
859
|
)
|
833
860
|
async def list_services(
|
834
|
-
manager_type: str = Field(
|
861
|
+
manager_type: Optional[str] = Field(
|
862
|
+
description="Container manager: must be docker for swarm (default: auto-detect)",
|
863
|
+
default=environment_container_manager_type,
|
864
|
+
),
|
835
865
|
silent: Optional[bool] = Field(
|
836
866
|
description="Suppress output", default=environment_silent
|
837
867
|
),
|
@@ -842,7 +872,7 @@ async def list_services(
|
|
842
872
|
description="MCP context for progress reporting", default=None
|
843
873
|
),
|
844
874
|
) -> List[Dict]:
|
845
|
-
if manager_type != "docker":
|
875
|
+
if manager_type and manager_type != "docker":
|
846
876
|
raise ValueError("Swarm operations are only supported on Docker")
|
847
877
|
logger = logging.getLogger("ContainerManager")
|
848
878
|
logger.debug(
|
@@ -876,7 +906,10 @@ async def create_service(
|
|
876
906
|
mounts: Optional[List[str]] = Field(
|
877
907
|
description="Mounts [source:target:mode]", default=None
|
878
908
|
),
|
879
|
-
manager_type: str = Field(
|
909
|
+
manager_type: Optional[str] = Field(
|
910
|
+
description="Container manager: must be docker for swarm (default: auto-detect)",
|
911
|
+
default=environment_container_manager_type,
|
912
|
+
),
|
880
913
|
silent: Optional[bool] = Field(
|
881
914
|
description="Suppress output", default=environment_silent
|
882
915
|
),
|
@@ -887,7 +920,7 @@ async def create_service(
|
|
887
920
|
description="MCP context for progress reporting", default=None
|
888
921
|
),
|
889
922
|
) -> Dict:
|
890
|
-
if manager_type != "docker":
|
923
|
+
if manager_type and manager_type != "docker":
|
891
924
|
raise ValueError("Swarm operations are only supported on Docker")
|
892
925
|
logger = logging.getLogger("ContainerManager")
|
893
926
|
logger.debug(
|
@@ -913,7 +946,10 @@ async def create_service(
|
|
913
946
|
)
|
914
947
|
async def remove_service(
|
915
948
|
service_id: str = Field(description="Service ID or name"),
|
916
|
-
manager_type: str = Field(
|
949
|
+
manager_type: Optional[str] = Field(
|
950
|
+
description="Container manager: must be docker for swarm (default: auto-detect)",
|
951
|
+
default=environment_container_manager_type,
|
952
|
+
),
|
917
953
|
silent: Optional[bool] = Field(
|
918
954
|
description="Suppress output", default=environment_silent
|
919
955
|
),
|
@@ -924,7 +960,7 @@ async def remove_service(
|
|
924
960
|
description="MCP context for progress reporting", default=None
|
925
961
|
),
|
926
962
|
) -> Dict:
|
927
|
-
if manager_type != "docker":
|
963
|
+
if manager_type and manager_type != "docker":
|
928
964
|
raise ValueError("Swarm operations are only supported on Docker")
|
929
965
|
logger = logging.getLogger("ContainerManager")
|
930
966
|
logger.debug(
|
@@ -952,8 +988,9 @@ async def compose_up(
|
|
952
988
|
compose_file: str = Field(description="Path to compose file"),
|
953
989
|
detach: bool = Field(description="Detach mode", default=True),
|
954
990
|
build: bool = Field(description="Build images", default=False),
|
955
|
-
manager_type: str = Field(
|
956
|
-
description="Container manager: docker, podman
|
991
|
+
manager_type: Optional[str] = Field(
|
992
|
+
description="Container manager: docker, podman (default: auto-detect)",
|
993
|
+
default=environment_container_manager_type,
|
957
994
|
),
|
958
995
|
silent: Optional[bool] = Field(
|
959
996
|
description="Suppress output", default=environment_silent
|
@@ -989,8 +1026,9 @@ async def compose_up(
|
|
989
1026
|
)
|
990
1027
|
async def compose_down(
|
991
1028
|
compose_file: str = Field(description="Path to compose file"),
|
992
|
-
manager_type: str = Field(
|
993
|
-
description="Container manager: docker, podman
|
1029
|
+
manager_type: Optional[str] = Field(
|
1030
|
+
description="Container manager: docker, podman (default: auto-detect)",
|
1031
|
+
default=environment_container_manager_type,
|
994
1032
|
),
|
995
1033
|
silent: Optional[bool] = Field(
|
996
1034
|
description="Suppress output", default=environment_silent
|
@@ -1026,8 +1064,9 @@ async def compose_down(
|
|
1026
1064
|
)
|
1027
1065
|
async def compose_ps(
|
1028
1066
|
compose_file: str = Field(description="Path to compose file"),
|
1029
|
-
manager_type: str = Field(
|
1030
|
-
description="Container manager: docker, podman
|
1067
|
+
manager_type: Optional[str] = Field(
|
1068
|
+
description="Container manager: docker, podman (default: auto-detect)",
|
1069
|
+
default=environment_container_manager_type,
|
1031
1070
|
),
|
1032
1071
|
silent: Optional[bool] = Field(
|
1033
1072
|
description="Suppress output", default=environment_silent
|
@@ -1064,8 +1103,9 @@ async def compose_ps(
|
|
1064
1103
|
async def compose_logs(
|
1065
1104
|
compose_file: str = Field(description="Path to compose file"),
|
1066
1105
|
service: Optional[str] = Field(description="Specific service", default=None),
|
1067
|
-
manager_type: str = Field(
|
1068
|
-
description="Container manager: docker, podman
|
1106
|
+
manager_type: Optional[str] = Field(
|
1107
|
+
description="Container manager: docker, podman (default: auto-detect)",
|
1108
|
+
default=environment_container_manager_type,
|
1069
1109
|
),
|
1070
1110
|
silent: Optional[bool] = Field(
|
1071
1111
|
description="Suppress output", default=environment_silent
|
@@ -1089,36 +1129,22 @@ async def compose_logs(
|
|
1089
1129
|
raise RuntimeError(f"Failed to compose logs: {str(e)}")
|
1090
1130
|
|
1091
1131
|
|
1092
|
-
def container_manager_mcp(
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
sys.exit(2)
|
1109
|
-
elif opt in ("-t", "--transport"):
|
1110
|
-
transport = arg
|
1111
|
-
elif opt in ("-h", "--host"):
|
1112
|
-
host = arg
|
1113
|
-
elif opt in ("-p", "--port"):
|
1114
|
-
try:
|
1115
|
-
port = int(arg)
|
1116
|
-
if not (0 <= port <= 65535):
|
1117
|
-
print(f"Error: Port {arg} is out of valid range (0-65535).")
|
1118
|
-
sys.exit(1)
|
1119
|
-
except ValueError:
|
1120
|
-
print(f"Error: Port {arg} is not a valid integer.")
|
1121
|
-
sys.exit(1)
|
1132
|
+
def container_manager_mcp():
|
1133
|
+
parser = argparse.ArgumentParser(description="Container Manager MCP Server")
|
1134
|
+
parser.add_argument(
|
1135
|
+
"-t", "--transport", type=str, default="stdio", help="Transport (stdio/http)"
|
1136
|
+
)
|
1137
|
+
parser.add_argument("-h", "--host", type=str, default="0.0.0.0", help="Host")
|
1138
|
+
parser.add_argument("-p", "--port", type=int, default=8000, help="Port")
|
1139
|
+
args = parser.parse_args()
|
1140
|
+
|
1141
|
+
transport = args.transport
|
1142
|
+
host = args.host
|
1143
|
+
port = args.port
|
1144
|
+
if not (0 <= port <= 65535):
|
1145
|
+
print(f"Error: Port {port} is out of valid range (0-65535).")
|
1146
|
+
sys.exit(1)
|
1147
|
+
|
1122
1148
|
setup_logging(is_mcp_server=True, log_file="container_manager_mcp.log")
|
1123
1149
|
if transport == "stdio":
|
1124
1150
|
mcp.run(transport="stdio")
|
@@ -1131,8 +1157,8 @@ def container_manager_mcp(argv):
|
|
1131
1157
|
|
1132
1158
|
|
1133
1159
|
def main():
|
1134
|
-
container_manager_mcp(
|
1160
|
+
container_manager_mcp()
|
1135
1161
|
|
1136
1162
|
|
1137
1163
|
if __name__ == "__main__":
|
1138
|
-
container_manager_mcp(
|
1164
|
+
container_manager_mcp()
|
{container_manager_mcp-0.0.12 → container_manager_mcp-1.0.1/container_manager_mcp.egg-info}/PKG-INFO
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: container-manager-mcp
|
3
|
-
Version:
|
3
|
+
Version: 1.0.1
|
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:
|
51
|
+
*Version: 1.0.1*
|
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
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "container-manager-mcp"
|
7
|
-
version = "
|
7
|
+
version = "1.0.1"
|
8
8
|
description = "Container Manager manage Docker, Docker Swarm, and Podman containers as an MCP Server"
|
9
9
|
readme = "README.md"
|
10
10
|
authors = [{ name = "Audel Rouhi", email = "knucklessg1@gmail.com" }]
|
File without changes
|
File without changes
|
{container_manager_mcp-0.0.12 → container_manager_mcp-1.0.1}/container_manager_mcp/__init__.py
RENAMED
File without changes
|
{container_manager_mcp-0.0.12 → container_manager_mcp-1.0.1}/container_manager_mcp/__main__.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|