container-manager-mcp 1.0.2__py3-none-any.whl → 1.0.4__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 +592 -215
- container_manager_mcp/container_manager_mcp.py +223 -4
- {container_manager_mcp-1.0.2.dist-info → container_manager_mcp-1.0.4.dist-info}/METADATA +2 -2
- container_manager_mcp-1.0.4.dist-info/RECORD +10 -0
- container_manager_mcp-1.0.2.dist-info/RECORD +0 -10
- {container_manager_mcp-1.0.2.dist-info → container_manager_mcp-1.0.4.dist-info}/WHEEL +0 -0
- {container_manager_mcp-1.0.2.dist-info → container_manager_mcp-1.0.4.dist-info}/entry_points.txt +0 -0
- {container_manager_mcp-1.0.2.dist-info → container_manager_mcp-1.0.4.dist-info}/licenses/LICENSE +0 -0
- {container_manager_mcp-1.0.2.dist-info → container_manager_mcp-1.0.4.dist-info}/top_level.txt +0 -0
@@ -39,6 +39,29 @@ def to_boolean(string):
|
|
39
39
|
raise ValueError(f"Cannot convert '{string}' to boolean")
|
40
40
|
|
41
41
|
|
42
|
+
def parse_image_string(image: str, default_tag: str = "latest") -> tuple[str, str]:
|
43
|
+
"""
|
44
|
+
Parse a container image string into image and tag components.
|
45
|
+
|
46
|
+
Args:
|
47
|
+
image: Input image string (e.g., 'registry.arpa/ubuntu/ubuntu:latest' or 'nginx')
|
48
|
+
default_tag: Fallback tag if none is specified (default: 'latest')
|
49
|
+
|
50
|
+
Returns:
|
51
|
+
Tuple of (image, tag) where image includes registry/repository, tag is the tag or default_tag
|
52
|
+
"""
|
53
|
+
# Split on the last ':' to separate image and tag
|
54
|
+
if ":" in image:
|
55
|
+
parts = image.rsplit(":", 1)
|
56
|
+
image_name, tag = parts[0], parts[1]
|
57
|
+
# Ensure tag is valid (not a port or malformed)
|
58
|
+
if "/" in tag or not tag:
|
59
|
+
# If tag contains '/' or is empty, assume no tag was provided
|
60
|
+
return image, default_tag
|
61
|
+
return image_name, tag
|
62
|
+
return image, default_tag
|
63
|
+
|
64
|
+
|
42
65
|
environment_silent = os.environ.get("SILENT", False)
|
43
66
|
environment_log_file = os.environ.get("LOG_FILE", None)
|
44
67
|
environment_container_manager_type = os.environ.get("CONTAINER_MANAGER_TYPE", None)
|
@@ -171,8 +194,13 @@ async def list_images(
|
|
171
194
|
tags={"container_management"},
|
172
195
|
)
|
173
196
|
async def pull_image(
|
174
|
-
image: str = Field(
|
175
|
-
|
197
|
+
image: str = Field(
|
198
|
+
description="Image name to pull (e.g., nginx, registry.arpa/ubuntu/ubuntu:latest)."
|
199
|
+
),
|
200
|
+
tag: str = Field(
|
201
|
+
description="Image tag (overridden if tag is included in image string)",
|
202
|
+
default="latest",
|
203
|
+
),
|
176
204
|
platform: Optional[str] = Field(
|
177
205
|
description="Platform (e.g., linux/amd64)", default=None
|
178
206
|
),
|
@@ -191,12 +219,14 @@ async def pull_image(
|
|
191
219
|
),
|
192
220
|
) -> Dict:
|
193
221
|
logger = logging.getLogger("ContainerManager")
|
222
|
+
# Parse image string to separate image and tag
|
223
|
+
parsed_image, parsed_tag = parse_image_string(image, tag)
|
194
224
|
logger.debug(
|
195
|
-
f"Pulling image {
|
225
|
+
f"Pulling image {parsed_image}:{parsed_tag} for {manager_type}, silent: {silent}, log_file: {log_file}"
|
196
226
|
)
|
197
227
|
try:
|
198
228
|
manager = create_manager(manager_type, silent, log_file)
|
199
|
-
return manager.pull_image(
|
229
|
+
return manager.pull_image(parsed_image, parsed_tag, platform)
|
200
230
|
except Exception as e:
|
201
231
|
logger.error(f"Failed to pull image: {str(e)}")
|
202
232
|
raise RuntimeError(f"Failed to pull image: {str(e)}")
|
@@ -241,6 +271,44 @@ async def remove_image(
|
|
241
271
|
raise RuntimeError(f"Failed to remove image: {str(e)}")
|
242
272
|
|
243
273
|
|
274
|
+
@mcp.tool(
|
275
|
+
annotations={
|
276
|
+
"title": "Prune Images",
|
277
|
+
"readOnlyHint": False,
|
278
|
+
"destructiveHint": True,
|
279
|
+
"idempotentHint": True,
|
280
|
+
"openWorldHint": False,
|
281
|
+
},
|
282
|
+
tags={"container_management"},
|
283
|
+
)
|
284
|
+
async def prune_images(
|
285
|
+
all: bool = Field(description="Prune all unused images", default=False),
|
286
|
+
manager_type: Optional[str] = Field(
|
287
|
+
description="Container manager: docker, podman (default: auto-detect)",
|
288
|
+
default=environment_container_manager_type,
|
289
|
+
),
|
290
|
+
silent: Optional[bool] = Field(
|
291
|
+
description="Suppress output", default=environment_silent
|
292
|
+
),
|
293
|
+
log_file: Optional[str] = Field(
|
294
|
+
description="Path to log file", default=environment_log_file
|
295
|
+
),
|
296
|
+
ctx: Context = Field(
|
297
|
+
description="MCP context for progress reporting", default=None
|
298
|
+
),
|
299
|
+
) -> Dict:
|
300
|
+
logger = logging.getLogger("ContainerManager")
|
301
|
+
logger.debug(
|
302
|
+
f"Pruning images for {manager_type}, all: {all}, silent: {silent}, log_file: {log_file}"
|
303
|
+
)
|
304
|
+
try:
|
305
|
+
manager = create_manager(manager_type, silent, log_file)
|
306
|
+
return manager.prune_images(all=all)
|
307
|
+
except Exception as e:
|
308
|
+
logger.error(f"Failed to prune images: {str(e)}")
|
309
|
+
raise RuntimeError(f"Failed to prune images: {str(e)}")
|
310
|
+
|
311
|
+
|
244
312
|
@mcp.tool(
|
245
313
|
annotations={
|
246
314
|
"title": "List Containers",
|
@@ -414,6 +482,43 @@ async def remove_container(
|
|
414
482
|
raise RuntimeError(f"Failed to remove container: {str(e)}")
|
415
483
|
|
416
484
|
|
485
|
+
@mcp.tool(
|
486
|
+
annotations={
|
487
|
+
"title": "Prune Containers",
|
488
|
+
"readOnlyHint": False,
|
489
|
+
"destructiveHint": True,
|
490
|
+
"idempotentHint": True,
|
491
|
+
"openWorldHint": False,
|
492
|
+
},
|
493
|
+
tags={"container_management"},
|
494
|
+
)
|
495
|
+
async def prune_containers(
|
496
|
+
manager_type: Optional[str] = Field(
|
497
|
+
description="Container manager: docker, podman (default: auto-detect)",
|
498
|
+
default=environment_container_manager_type,
|
499
|
+
),
|
500
|
+
silent: Optional[bool] = Field(
|
501
|
+
description="Suppress output", default=environment_silent
|
502
|
+
),
|
503
|
+
log_file: Optional[str] = Field(
|
504
|
+
description="Path to log file", default=environment_log_file
|
505
|
+
),
|
506
|
+
ctx: Context = Field(
|
507
|
+
description="MCP context for progress reporting", default=None
|
508
|
+
),
|
509
|
+
) -> Dict:
|
510
|
+
logger = logging.getLogger("ContainerManager")
|
511
|
+
logger.debug(
|
512
|
+
f"Pruning containers for {manager_type}, silent: {silent}, log_file: {log_file}"
|
513
|
+
)
|
514
|
+
try:
|
515
|
+
manager = create_manager(manager_type, silent, log_file)
|
516
|
+
return manager.prune_containers()
|
517
|
+
except Exception as e:
|
518
|
+
logger.error(f"Failed to prune containers: {str(e)}")
|
519
|
+
raise RuntimeError(f"Failed to prune containers: {str(e)}")
|
520
|
+
|
521
|
+
|
417
522
|
@mcp.tool(
|
418
523
|
annotations={
|
419
524
|
"title": "Get Container Logs",
|
@@ -609,6 +714,44 @@ async def remove_volume(
|
|
609
714
|
raise RuntimeError(f"Failed to remove volume: {str(e)}")
|
610
715
|
|
611
716
|
|
717
|
+
@mcp.tool(
|
718
|
+
annotations={
|
719
|
+
"title": "Prune Volumes",
|
720
|
+
"readOnlyHint": False,
|
721
|
+
"destructiveHint": True,
|
722
|
+
"idempotentHint": True,
|
723
|
+
"openWorldHint": False,
|
724
|
+
},
|
725
|
+
tags={"container_management"},
|
726
|
+
)
|
727
|
+
async def prune_volumes(
|
728
|
+
all: bool = Field(description="Remove all volumes (dangerous)", default=False),
|
729
|
+
manager_type: Optional[str] = Field(
|
730
|
+
description="Container manager: docker, podman (default: auto-detect)",
|
731
|
+
default=environment_container_manager_type,
|
732
|
+
),
|
733
|
+
silent: Optional[bool] = Field(
|
734
|
+
description="Suppress output", default=environment_silent
|
735
|
+
),
|
736
|
+
log_file: Optional[str] = Field(
|
737
|
+
description="Path to log file", default=environment_log_file
|
738
|
+
),
|
739
|
+
ctx: Context = Field(
|
740
|
+
description="MCP context for progress reporting", default=None
|
741
|
+
),
|
742
|
+
) -> Dict:
|
743
|
+
logger = logging.getLogger("ContainerManager")
|
744
|
+
logger.debug(
|
745
|
+
f"Pruning volumes for {manager_type}, all: {all}, silent: {silent}, log_file: {log_file}"
|
746
|
+
)
|
747
|
+
try:
|
748
|
+
manager = create_manager(manager_type, silent, log_file)
|
749
|
+
return manager.prune_volumes(all=all)
|
750
|
+
except Exception as e:
|
751
|
+
logger.error(f"Failed to prune volumes: {str(e)}")
|
752
|
+
raise RuntimeError(f"Failed to prune volumes: {str(e)}")
|
753
|
+
|
754
|
+
|
612
755
|
@mcp.tool(
|
613
756
|
annotations={
|
614
757
|
"title": "List Networks",
|
@@ -723,6 +866,82 @@ async def remove_network(
|
|
723
866
|
raise RuntimeError(f"Failed to remove network: {str(e)}")
|
724
867
|
|
725
868
|
|
869
|
+
@mcp.tool(
|
870
|
+
annotations={
|
871
|
+
"title": "Prune Networks",
|
872
|
+
"readOnlyHint": False,
|
873
|
+
"destructiveHint": True,
|
874
|
+
"idempotentHint": True,
|
875
|
+
"openWorldHint": False,
|
876
|
+
},
|
877
|
+
tags={"container_management"},
|
878
|
+
)
|
879
|
+
async def prune_networks(
|
880
|
+
manager_type: Optional[str] = Field(
|
881
|
+
description="Container manager: docker, podman (default: auto-detect)",
|
882
|
+
default=environment_container_manager_type,
|
883
|
+
),
|
884
|
+
silent: Optional[bool] = Field(
|
885
|
+
description="Suppress output", default=environment_silent
|
886
|
+
),
|
887
|
+
log_file: Optional[str] = Field(
|
888
|
+
description="Path to log file", default=environment_log_file
|
889
|
+
),
|
890
|
+
ctx: Context = Field(
|
891
|
+
description="MCP context for progress reporting", default=None
|
892
|
+
),
|
893
|
+
) -> Dict:
|
894
|
+
logger = logging.getLogger("ContainerManager")
|
895
|
+
logger.debug(
|
896
|
+
f"Pruning networks for {manager_type}, silent: {silent}, log_file: {log_file}"
|
897
|
+
)
|
898
|
+
try:
|
899
|
+
manager = create_manager(manager_type, silent, log_file)
|
900
|
+
return manager.prune_networks()
|
901
|
+
except Exception as e:
|
902
|
+
logger.error(f"Failed to prune networks: {str(e)}")
|
903
|
+
raise RuntimeError(f"Failed to prune networks: {str(e)}")
|
904
|
+
|
905
|
+
|
906
|
+
@mcp.tool(
|
907
|
+
annotations={
|
908
|
+
"title": "Prune System",
|
909
|
+
"readOnlyHint": False,
|
910
|
+
"destructiveHint": True,
|
911
|
+
"idempotentHint": True,
|
912
|
+
"openWorldHint": False,
|
913
|
+
},
|
914
|
+
tags={"container_management"},
|
915
|
+
)
|
916
|
+
async def prune_system(
|
917
|
+
force: bool = Field(description="Force prune", default=False),
|
918
|
+
all: bool = Field(description="Prune all unused resources", default=False),
|
919
|
+
manager_type: Optional[str] = Field(
|
920
|
+
description="Container manager: docker, podman (default: auto-detect)",
|
921
|
+
default=environment_container_manager_type,
|
922
|
+
),
|
923
|
+
silent: Optional[bool] = Field(
|
924
|
+
description="Suppress output", default=environment_silent
|
925
|
+
),
|
926
|
+
log_file: Optional[str] = Field(
|
927
|
+
description="Path to log file", default=environment_log_file
|
928
|
+
),
|
929
|
+
ctx: Context = Field(
|
930
|
+
description="MCP context for progress reporting", default=None
|
931
|
+
),
|
932
|
+
) -> Dict:
|
933
|
+
logger = logging.getLogger("ContainerManager")
|
934
|
+
logger.debug(
|
935
|
+
f"Pruning system for {manager_type}, force: {force}, all: {all}, silent: {silent}, log_file: {log_file}"
|
936
|
+
)
|
937
|
+
try:
|
938
|
+
manager = create_manager(manager_type, silent, log_file)
|
939
|
+
return manager.prune_system(force, all)
|
940
|
+
except Exception as e:
|
941
|
+
logger.error(f"Failed to prune system: {str(e)}")
|
942
|
+
raise RuntimeError(f"Failed to prune system: {str(e)}")
|
943
|
+
|
944
|
+
|
726
945
|
# Swarm-specific tools
|
727
946
|
|
728
947
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: container-manager-mcp
|
3
|
-
Version: 1.0.
|
3
|
+
Version: 1.0.4
|
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: 1.0.
|
51
|
+
*Version: 1.0.4*
|
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=sHxM17dj7luzp1Ak2aQJ3JLqHd3POO3S2_W32HQwWog,89167
|
4
|
+
container_manager_mcp/container_manager_mcp.py,sha256=E3FOTFILyYYoJF3Erkpl5zapkiDi2pKlJlkZByc-8QE,47361
|
5
|
+
container_manager_mcp-1.0.4.dist-info/licenses/LICENSE,sha256=Z1xmcrPHBnGCETO_LLQJUeaSNBSnuptcDVTt4kaPUOE,1060
|
6
|
+
container_manager_mcp-1.0.4.dist-info/METADATA,sha256=XhTi2pEQeO1cfnrVgVViq_LiwGqLqUyhsogD-jXUFys,8238
|
7
|
+
container_manager_mcp-1.0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
8
|
+
container_manager_mcp-1.0.4.dist-info/entry_points.txt,sha256=I23pXcCgAShlfYbENzs3kbw3l1lU9Gy7lODPfRqeeiA,156
|
9
|
+
container_manager_mcp-1.0.4.dist-info/top_level.txt,sha256=B7QQLOd9mBdu0lsPKqyu4T8-zUtbqKzQJbMbtAzoozU,22
|
10
|
+
container_manager_mcp-1.0.4.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=9TPpZw2gB_rz4oLsu45jBeixkG9B-OYppsGM4ctqb5I,72495
|
4
|
-
container_manager_mcp/container_manager_mcp.py,sha256=PSKbw4NuoGWuZZJdTOstiIu_2mq2wZdNNM1gDQdZMNU,39788
|
5
|
-
container_manager_mcp-1.0.2.dist-info/licenses/LICENSE,sha256=Z1xmcrPHBnGCETO_LLQJUeaSNBSnuptcDVTt4kaPUOE,1060
|
6
|
-
container_manager_mcp-1.0.2.dist-info/METADATA,sha256=IyGwMPvMD3xjEeffWzuWO9wTOQTAIoNLQmhaJCTBxQw,8238
|
7
|
-
container_manager_mcp-1.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
8
|
-
container_manager_mcp-1.0.2.dist-info/entry_points.txt,sha256=I23pXcCgAShlfYbENzs3kbw3l1lU9Gy7lODPfRqeeiA,156
|
9
|
-
container_manager_mcp-1.0.2.dist-info/top_level.txt,sha256=B7QQLOd9mBdu0lsPKqyu4T8-zUtbqKzQJbMbtAzoozU,22
|
10
|
-
container_manager_mcp-1.0.2.dist-info/RECORD,,
|
File without changes
|
{container_manager_mcp-1.0.2.dist-info → container_manager_mcp-1.0.4.dist-info}/entry_points.txt
RENAMED
File without changes
|
{container_manager_mcp-1.0.2.dist-info → container_manager_mcp-1.0.4.dist-info}/licenses/LICENSE
RENAMED
File without changes
|
{container_manager_mcp-1.0.2.dist-info → container_manager_mcp-1.0.4.dist-info}/top_level.txt
RENAMED
File without changes
|