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.
@@ -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(description="Image name to pull"),
175
- tag: str = Field(description="Image tag", default="latest"),
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 {image}:{tag} for {manager_type}, silent: {silent}, log_file: {log_file}"
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(image, tag, platform)
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.2
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
  ![PyPI - Wheel](https://img.shields.io/pypi/wheel/container-manager-mcp)
49
49
  ![PyPI - Implementation](https://img.shields.io/pypi/implementation/container-manager-mcp)
50
50
 
51
- *Version: 1.0.2*
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,,