container-manager-mcp 1.0.6__py3-none-any.whl → 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of container-manager-mcp might be problematic. Click here for more details.
- container_manager_mcp/__init__.py +2 -2
- container_manager_mcp/__main__.py +2 -2
- container_manager_mcp/container_manager.py +24 -15
- container_manager_mcp/container_manager_mcp.py +288 -110
- {container_manager_mcp-1.0.6.dist-info → container_manager_mcp-1.1.0.dist-info}/METADATA +6 -4
- container_manager_mcp-1.1.0.dist-info/RECORD +10 -0
- container_manager_mcp-1.1.0.dist-info/entry_points.txt +3 -0
- container_manager_mcp-1.0.6.dist-info/RECORD +0 -10
- container_manager_mcp-1.0.6.dist-info/entry_points.txt +0 -3
- {container_manager_mcp-1.0.6.dist-info → container_manager_mcp-1.1.0.dist-info}/WHEEL +0 -0
- {container_manager_mcp-1.0.6.dist-info → container_manager_mcp-1.1.0.dist-info}/licenses/LICENSE +0 -0
- {container_manager_mcp-1.0.6.dist-info → container_manager_mcp-1.1.0.dist-info}/top_level.txt +0 -0
|
@@ -8,7 +8,7 @@ from container_manager_mcp.container_manager import (
|
|
|
8
8
|
DockerManager,
|
|
9
9
|
PodmanManager,
|
|
10
10
|
)
|
|
11
|
-
from container_manager_mcp.container_manager_mcp import
|
|
11
|
+
from container_manager_mcp.container_manager_mcp import container_manager_mcp
|
|
12
12
|
|
|
13
13
|
"""
|
|
14
14
|
container-manager
|
|
@@ -17,7 +17,7 @@ Manage your containers using docker, podman, compose, or docker swarm!
|
|
|
17
17
|
"""
|
|
18
18
|
|
|
19
19
|
__all__ = [
|
|
20
|
-
"
|
|
20
|
+
"container_manager_mcp",
|
|
21
21
|
"create_manager",
|
|
22
22
|
"container_manager",
|
|
23
23
|
"ContainerManagerBase",
|
|
@@ -510,14 +510,17 @@ class DockerManager(ContainerManagerBase):
|
|
|
510
510
|
self.log_action("run_container", params, result)
|
|
511
511
|
return result
|
|
512
512
|
attrs = container.attrs
|
|
513
|
-
ports = attrs.get("NetworkSettings", {}).get("Ports", {})
|
|
514
513
|
port_mappings = []
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
514
|
+
if ports: # Check if ports is not None
|
|
515
|
+
network_settings = attrs.get("NetworkSettings", {})
|
|
516
|
+
container_ports = network_settings.get("Ports", {})
|
|
517
|
+
if container_ports: # Check if Ports dictionary is not empty
|
|
518
|
+
for container_port, host_ports in container_ports.items():
|
|
519
|
+
if host_ports: # Check if host_ports is not None or empty
|
|
520
|
+
for hp in host_ports:
|
|
521
|
+
port_mappings.append(
|
|
522
|
+
f"{hp.get('HostIp', '0.0.0.0')}:{hp.get('HostPort')}->{container_port}"
|
|
523
|
+
)
|
|
521
524
|
created = attrs.get("Created", None)
|
|
522
525
|
created_str = self._parse_timestamp(created)
|
|
523
526
|
result = {
|
|
@@ -1057,9 +1060,11 @@ class PodmanManager(ContainerManagerBase):
|
|
|
1057
1060
|
|
|
1058
1061
|
def _autodetect_podman_url(self) -> Optional[str]:
|
|
1059
1062
|
"""Autodetect the appropriate Podman socket URL based on platform."""
|
|
1060
|
-
base_url = os.environ.get("
|
|
1063
|
+
base_url = os.environ.get("CONTAINER_MANAGER_PODMAN_BASE_URL")
|
|
1061
1064
|
if base_url:
|
|
1062
|
-
self.logger.info(
|
|
1065
|
+
self.logger.info(
|
|
1066
|
+
f"Using CONTAINER_MANAGER_PODMAN_BASE_URL from environment: {base_url}"
|
|
1067
|
+
)
|
|
1063
1068
|
return base_url
|
|
1064
1069
|
system = platform.system()
|
|
1065
1070
|
is_wsl = self._is_wsl()
|
|
@@ -1069,6 +1074,7 @@ class PodmanManager(ContainerManagerBase):
|
|
|
1069
1074
|
raise RuntimeError("Podman Machine is not running on Windows system")
|
|
1070
1075
|
socket_candidates.extend(
|
|
1071
1076
|
[
|
|
1077
|
+
"tcp://127.0.0.1:8080",
|
|
1072
1078
|
"unix:///run/podman/podman.sock",
|
|
1073
1079
|
"npipe:////./pipe/docker_engine",
|
|
1074
1080
|
"unix:///mnt/wsl/podman-sockets/podman-machine-default/podman-user.sock",
|
|
@@ -1430,12 +1436,15 @@ class PodmanManager(ContainerManagerBase):
|
|
|
1430
1436
|
self.log_action("run_container", params, result)
|
|
1431
1437
|
return result
|
|
1432
1438
|
attrs = container.attrs
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
+
port_mappings = []
|
|
1440
|
+
if ports: # Check if ports is not None
|
|
1441
|
+
container_ports = attrs.get("Ports", [])
|
|
1442
|
+
if container_ports: # Check if Ports list is not empty
|
|
1443
|
+
port_mappings = [
|
|
1444
|
+
f"{p.get('host_ip', '0.0.0.0')}:{p.get('host_port')}->{p.get('container_port')}/{p.get('protocol', 'tcp')}"
|
|
1445
|
+
for p in container_ports
|
|
1446
|
+
if p.get("host_port")
|
|
1447
|
+
]
|
|
1439
1448
|
created = attrs.get("Created", None)
|
|
1440
1449
|
created_str = self._parse_timestamp(created)
|
|
1441
1450
|
result = {
|
|
@@ -62,16 +62,6 @@ def parse_image_string(image: str, default_tag: str = "latest") -> tuple[str, st
|
|
|
62
62
|
return image, default_tag
|
|
63
63
|
|
|
64
64
|
|
|
65
|
-
environment_silent = os.environ.get("SILENT", False)
|
|
66
|
-
environment_log_file = os.environ.get("LOG_FILE", None)
|
|
67
|
-
environment_container_manager_type = os.environ.get("CONTAINER_MANAGER_TYPE", None)
|
|
68
|
-
|
|
69
|
-
if environment_silent:
|
|
70
|
-
environment_silent = to_boolean(environment_silent)
|
|
71
|
-
|
|
72
|
-
# Common tools
|
|
73
|
-
|
|
74
|
-
|
|
75
65
|
@mcp.tool(
|
|
76
66
|
annotations={
|
|
77
67
|
"title": "Get Version",
|
|
@@ -85,18 +75,24 @@ if environment_silent:
|
|
|
85
75
|
async def get_version(
|
|
86
76
|
manager_type: Optional[str] = Field(
|
|
87
77
|
description="Container manager: docker, podman (default: auto-detect)",
|
|
88
|
-
default=
|
|
78
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
89
79
|
),
|
|
90
80
|
silent: Optional[bool] = Field(
|
|
91
|
-
description="Suppress output",
|
|
81
|
+
description="Suppress output",
|
|
82
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
92
83
|
),
|
|
93
84
|
log_file: Optional[str] = Field(
|
|
94
|
-
description="Path to log file",
|
|
85
|
+
description="Path to log file",
|
|
86
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
95
87
|
),
|
|
96
88
|
ctx: Context = Field(
|
|
97
89
|
description="MCP context for progress reporting", default=None
|
|
98
90
|
),
|
|
99
91
|
) -> Dict:
|
|
92
|
+
"""
|
|
93
|
+
Retrieves the version information of the container manager (Docker or Podman).
|
|
94
|
+
Returns: A dictionary with keys like 'version', 'api_version', etc., detailing the manager's version.
|
|
95
|
+
"""
|
|
100
96
|
logger = logging.getLogger("ContainerManager")
|
|
101
97
|
logger.debug(
|
|
102
98
|
f"Getting version for {manager_type}, silent: {silent}, log_file: {log_file}"
|
|
@@ -122,18 +118,24 @@ async def get_version(
|
|
|
122
118
|
async def get_info(
|
|
123
119
|
manager_type: Optional[str] = Field(
|
|
124
120
|
description="Container manager: docker, podman (default: auto-detect)",
|
|
125
|
-
default=
|
|
121
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
126
122
|
),
|
|
127
123
|
silent: Optional[bool] = Field(
|
|
128
|
-
description="Suppress output",
|
|
124
|
+
description="Suppress output",
|
|
125
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
129
126
|
),
|
|
130
127
|
log_file: Optional[str] = Field(
|
|
131
|
-
description="Path to log file",
|
|
128
|
+
description="Path to log file",
|
|
129
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
132
130
|
),
|
|
133
131
|
ctx: Context = Field(
|
|
134
132
|
description="MCP context for progress reporting", default=None
|
|
135
133
|
),
|
|
136
134
|
) -> Dict:
|
|
135
|
+
"""
|
|
136
|
+
Retrieves detailed information about the container manager system.
|
|
137
|
+
Returns: A dictionary containing system info such as OS, architecture, storage driver, and more.
|
|
138
|
+
"""
|
|
137
139
|
logger = logging.getLogger("ContainerManager")
|
|
138
140
|
logger.debug(
|
|
139
141
|
f"Getting info for {manager_type}, silent: {silent}, log_file: {log_file}"
|
|
@@ -159,18 +161,24 @@ async def get_info(
|
|
|
159
161
|
async def list_images(
|
|
160
162
|
manager_type: Optional[str] = Field(
|
|
161
163
|
description="Container manager: docker, podman (default: auto-detect)",
|
|
162
|
-
default=
|
|
164
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
163
165
|
),
|
|
164
166
|
silent: Optional[bool] = Field(
|
|
165
|
-
description="Suppress output",
|
|
167
|
+
description="Suppress output",
|
|
168
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
166
169
|
),
|
|
167
170
|
log_file: Optional[str] = Field(
|
|
168
|
-
description="Path to log file",
|
|
171
|
+
description="Path to log file",
|
|
172
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
169
173
|
),
|
|
170
174
|
ctx: Context = Field(
|
|
171
175
|
description="MCP context for progress reporting", default=None
|
|
172
176
|
),
|
|
173
177
|
) -> List[Dict]:
|
|
178
|
+
"""
|
|
179
|
+
Lists all container images available on the system.
|
|
180
|
+
Returns: A list of dictionaries, each with image details like 'id', 'tags', 'created', 'size'.
|
|
181
|
+
"""
|
|
174
182
|
logger = logging.getLogger("ContainerManager")
|
|
175
183
|
logger.debug(
|
|
176
184
|
f"Listing images for {manager_type}, silent: {silent}, log_file: {log_file}"
|
|
@@ -206,18 +214,24 @@ async def pull_image(
|
|
|
206
214
|
),
|
|
207
215
|
manager_type: Optional[str] = Field(
|
|
208
216
|
description="Container manager: docker, podman (default: auto-detect)",
|
|
209
|
-
default=
|
|
217
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
210
218
|
),
|
|
211
219
|
silent: Optional[bool] = Field(
|
|
212
|
-
description="Suppress output",
|
|
220
|
+
description="Suppress output",
|
|
221
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
213
222
|
),
|
|
214
223
|
log_file: Optional[str] = Field(
|
|
215
|
-
description="Path to log file",
|
|
224
|
+
description="Path to log file",
|
|
225
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
216
226
|
),
|
|
217
227
|
ctx: Context = Field(
|
|
218
228
|
description="MCP context for progress reporting", default=None
|
|
219
229
|
),
|
|
220
230
|
) -> Dict:
|
|
231
|
+
"""
|
|
232
|
+
Pulls a container image from a registry.
|
|
233
|
+
Returns: A dictionary with the pull status, including 'id' of the pulled image and any error messages.
|
|
234
|
+
"""
|
|
221
235
|
logger = logging.getLogger("ContainerManager")
|
|
222
236
|
# Parse image string to separate image and tag
|
|
223
237
|
parsed_image, parsed_tag = parse_image_string(image, tag)
|
|
@@ -247,18 +261,24 @@ async def remove_image(
|
|
|
247
261
|
force: bool = Field(description="Force removal", default=False),
|
|
248
262
|
manager_type: Optional[str] = Field(
|
|
249
263
|
description="Container manager: docker, podman (default: auto-detect)",
|
|
250
|
-
default=
|
|
264
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
251
265
|
),
|
|
252
266
|
silent: Optional[bool] = Field(
|
|
253
|
-
description="Suppress output",
|
|
267
|
+
description="Suppress output",
|
|
268
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
254
269
|
),
|
|
255
270
|
log_file: Optional[str] = Field(
|
|
256
|
-
description="Path to log file",
|
|
271
|
+
description="Path to log file",
|
|
272
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
257
273
|
),
|
|
258
274
|
ctx: Context = Field(
|
|
259
275
|
description="MCP context for progress reporting", default=None
|
|
260
276
|
),
|
|
261
277
|
) -> Dict:
|
|
278
|
+
"""
|
|
279
|
+
Removes a specified container image.
|
|
280
|
+
Returns: A dictionary indicating success or failure, with details like removed image ID.
|
|
281
|
+
"""
|
|
262
282
|
logger = logging.getLogger("ContainerManager")
|
|
263
283
|
logger.debug(
|
|
264
284
|
f"Removing image {image} for {manager_type}, silent: {silent}, log_file: {log_file}"
|
|
@@ -285,18 +305,24 @@ async def prune_images(
|
|
|
285
305
|
all: bool = Field(description="Prune all unused images", default=False),
|
|
286
306
|
manager_type: Optional[str] = Field(
|
|
287
307
|
description="Container manager: docker, podman (default: auto-detect)",
|
|
288
|
-
default=
|
|
308
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
289
309
|
),
|
|
290
310
|
silent: Optional[bool] = Field(
|
|
291
|
-
description="Suppress output",
|
|
311
|
+
description="Suppress output",
|
|
312
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
292
313
|
),
|
|
293
314
|
log_file: Optional[str] = Field(
|
|
294
|
-
description="Path to log file",
|
|
315
|
+
description="Path to log file",
|
|
316
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
295
317
|
),
|
|
296
318
|
ctx: Context = Field(
|
|
297
319
|
description="MCP context for progress reporting", default=None
|
|
298
320
|
),
|
|
299
321
|
) -> Dict:
|
|
322
|
+
"""
|
|
323
|
+
Prunes unused container images.
|
|
324
|
+
Returns: A dictionary with prune results, including space reclaimed and list of deleted images.
|
|
325
|
+
"""
|
|
300
326
|
logger = logging.getLogger("ContainerManager")
|
|
301
327
|
logger.debug(
|
|
302
328
|
f"Pruning images for {manager_type}, all: {all}, silent: {silent}, log_file: {log_file}"
|
|
@@ -325,18 +351,24 @@ async def list_containers(
|
|
|
325
351
|
),
|
|
326
352
|
manager_type: Optional[str] = Field(
|
|
327
353
|
description="Container manager: docker, podman (default: auto-detect)",
|
|
328
|
-
default=
|
|
354
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
329
355
|
),
|
|
330
356
|
silent: Optional[bool] = Field(
|
|
331
|
-
description="Suppress output",
|
|
357
|
+
description="Suppress output",
|
|
358
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
332
359
|
),
|
|
333
360
|
log_file: Optional[str] = Field(
|
|
334
|
-
description="Path to log file",
|
|
361
|
+
description="Path to log file",
|
|
362
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
335
363
|
),
|
|
336
364
|
ctx: Context = Field(
|
|
337
365
|
description="MCP context for progress reporting", default=None
|
|
338
366
|
),
|
|
339
367
|
) -> List[Dict]:
|
|
368
|
+
"""
|
|
369
|
+
Lists containers on the system.
|
|
370
|
+
Returns: A list of dictionaries, each with container details like 'id', 'name', 'status', 'image'.
|
|
371
|
+
"""
|
|
340
372
|
logger = logging.getLogger("ContainerManager")
|
|
341
373
|
logger.debug(
|
|
342
374
|
f"Listing containers for {manager_type}, all: {all}, silent: {silent}, log_file: {log_file}"
|
|
@@ -378,18 +410,24 @@ async def run_container(
|
|
|
378
410
|
),
|
|
379
411
|
manager_type: Optional[str] = Field(
|
|
380
412
|
description="Container manager: docker, podman (default: auto-detect)",
|
|
381
|
-
default=
|
|
413
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
382
414
|
),
|
|
383
415
|
silent: Optional[bool] = Field(
|
|
384
|
-
description="Suppress output",
|
|
416
|
+
description="Suppress output",
|
|
417
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
385
418
|
),
|
|
386
419
|
log_file: Optional[str] = Field(
|
|
387
|
-
description="Path to log file",
|
|
420
|
+
description="Path to log file",
|
|
421
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
388
422
|
),
|
|
389
423
|
ctx: Context = Field(
|
|
390
424
|
description="MCP context for progress reporting", default=None
|
|
391
425
|
),
|
|
392
426
|
) -> Dict:
|
|
427
|
+
"""
|
|
428
|
+
Runs a new container from the specified image.
|
|
429
|
+
Returns: A dictionary with the container's ID and status after starting.
|
|
430
|
+
"""
|
|
393
431
|
logger = logging.getLogger("ContainerManager")
|
|
394
432
|
logger.debug(
|
|
395
433
|
f"Running container from {image} for {manager_type}, silent: {silent}, log_file: {log_file}"
|
|
@@ -419,18 +457,24 @@ async def stop_container(
|
|
|
419
457
|
timeout: int = Field(description="Timeout in seconds", default=10),
|
|
420
458
|
manager_type: Optional[str] = Field(
|
|
421
459
|
description="Container manager: docker, podman (default: auto-detect)",
|
|
422
|
-
default=
|
|
460
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
423
461
|
),
|
|
424
462
|
silent: Optional[bool] = Field(
|
|
425
|
-
description="Suppress output",
|
|
463
|
+
description="Suppress output",
|
|
464
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
426
465
|
),
|
|
427
466
|
log_file: Optional[str] = Field(
|
|
428
|
-
description="Path to log file",
|
|
467
|
+
description="Path to log file",
|
|
468
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
429
469
|
),
|
|
430
470
|
ctx: Context = Field(
|
|
431
471
|
description="MCP context for progress reporting", default=None
|
|
432
472
|
),
|
|
433
473
|
) -> Dict:
|
|
474
|
+
"""
|
|
475
|
+
Stops a running container.
|
|
476
|
+
Returns: A dictionary confirming the stop action, with container ID and any errors.
|
|
477
|
+
"""
|
|
434
478
|
logger = logging.getLogger("ContainerManager")
|
|
435
479
|
logger.debug(
|
|
436
480
|
f"Stopping container {container_id} for {manager_type}, silent: {silent}, log_file: {log_file}"
|
|
@@ -458,18 +502,24 @@ async def remove_container(
|
|
|
458
502
|
force: bool = Field(description="Force removal", default=False),
|
|
459
503
|
manager_type: Optional[str] = Field(
|
|
460
504
|
description="Container manager: docker, podman (default: auto-detect)",
|
|
461
|
-
default=
|
|
505
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
462
506
|
),
|
|
463
507
|
silent: Optional[bool] = Field(
|
|
464
|
-
description="Suppress output",
|
|
508
|
+
description="Suppress output",
|
|
509
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
465
510
|
),
|
|
466
511
|
log_file: Optional[str] = Field(
|
|
467
|
-
description="Path to log file",
|
|
512
|
+
description="Path to log file",
|
|
513
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
468
514
|
),
|
|
469
515
|
ctx: Context = Field(
|
|
470
516
|
description="MCP context for progress reporting", default=None
|
|
471
517
|
),
|
|
472
518
|
) -> Dict:
|
|
519
|
+
"""
|
|
520
|
+
Removes a container.
|
|
521
|
+
Returns: A dictionary with removal status, including deleted container ID.
|
|
522
|
+
"""
|
|
473
523
|
logger = logging.getLogger("ContainerManager")
|
|
474
524
|
logger.debug(
|
|
475
525
|
f"Removing container {container_id} for {manager_type}, silent: {silent}, log_file: {log_file}"
|
|
@@ -495,18 +545,24 @@ async def remove_container(
|
|
|
495
545
|
async def prune_containers(
|
|
496
546
|
manager_type: Optional[str] = Field(
|
|
497
547
|
description="Container manager: docker, podman (default: auto-detect)",
|
|
498
|
-
default=
|
|
548
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
499
549
|
),
|
|
500
550
|
silent: Optional[bool] = Field(
|
|
501
|
-
description="Suppress output",
|
|
551
|
+
description="Suppress output",
|
|
552
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
502
553
|
),
|
|
503
554
|
log_file: Optional[str] = Field(
|
|
504
|
-
description="Path to log file",
|
|
555
|
+
description="Path to log file",
|
|
556
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
505
557
|
),
|
|
506
558
|
ctx: Context = Field(
|
|
507
559
|
description="MCP context for progress reporting", default=None
|
|
508
560
|
),
|
|
509
561
|
) -> Dict:
|
|
562
|
+
"""
|
|
563
|
+
Prunes stopped containers.
|
|
564
|
+
Returns: A dictionary with prune results, including space reclaimed and deleted containers.
|
|
565
|
+
"""
|
|
510
566
|
logger = logging.getLogger("ContainerManager")
|
|
511
567
|
logger.debug(
|
|
512
568
|
f"Pruning containers for {manager_type}, silent: {silent}, log_file: {log_file}"
|
|
@@ -536,18 +592,24 @@ async def get_container_logs(
|
|
|
536
592
|
),
|
|
537
593
|
manager_type: Optional[str] = Field(
|
|
538
594
|
description="Container manager: docker, podman (default: auto-detect)",
|
|
539
|
-
default=
|
|
595
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
540
596
|
),
|
|
541
597
|
silent: Optional[bool] = Field(
|
|
542
|
-
description="Suppress output",
|
|
598
|
+
description="Suppress output",
|
|
599
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
543
600
|
),
|
|
544
601
|
log_file: Optional[str] = Field(
|
|
545
|
-
description="Path to log file",
|
|
602
|
+
description="Path to log file",
|
|
603
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
546
604
|
),
|
|
547
605
|
ctx: Context = Field(
|
|
548
606
|
description="MCP context for progress reporting", default=None
|
|
549
607
|
),
|
|
550
608
|
) -> str:
|
|
609
|
+
"""
|
|
610
|
+
Retrieves logs from a container.
|
|
611
|
+
Returns: A string containing the log output, parse as plain text lines.
|
|
612
|
+
"""
|
|
551
613
|
logger = logging.getLogger("ContainerManager")
|
|
552
614
|
logger.debug(
|
|
553
615
|
f"Getting logs for container {container_id} for {manager_type}, silent: {silent}, log_file: {log_file}"
|
|
@@ -576,18 +638,24 @@ async def exec_in_container(
|
|
|
576
638
|
detach: bool = Field(description="Detach execution", default=False),
|
|
577
639
|
manager_type: Optional[str] = Field(
|
|
578
640
|
description="Container manager: docker, podman (default: auto-detect)",
|
|
579
|
-
default=
|
|
641
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
580
642
|
),
|
|
581
643
|
silent: Optional[bool] = Field(
|
|
582
|
-
description="Suppress output",
|
|
644
|
+
description="Suppress output",
|
|
645
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
583
646
|
),
|
|
584
647
|
log_file: Optional[str] = Field(
|
|
585
|
-
description="Path to log file",
|
|
648
|
+
description="Path to log file",
|
|
649
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
586
650
|
),
|
|
587
651
|
ctx: Context = Field(
|
|
588
652
|
description="MCP context for progress reporting", default=None
|
|
589
653
|
),
|
|
590
654
|
) -> Dict:
|
|
655
|
+
"""
|
|
656
|
+
Executes a command inside a running container.
|
|
657
|
+
Returns: A dictionary with execution results, including 'exit_code' and 'output' as string.
|
|
658
|
+
"""
|
|
591
659
|
logger = logging.getLogger("ContainerManager")
|
|
592
660
|
logger.debug(
|
|
593
661
|
f"Executing {command} in container {container_id} for {manager_type}, silent: {silent}, log_file: {log_file}"
|
|
@@ -613,18 +681,24 @@ async def exec_in_container(
|
|
|
613
681
|
async def list_volumes(
|
|
614
682
|
manager_type: Optional[str] = Field(
|
|
615
683
|
description="Container manager: docker, podman (default: auto-detect)",
|
|
616
|
-
default=
|
|
684
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
617
685
|
),
|
|
618
686
|
silent: Optional[bool] = Field(
|
|
619
|
-
description="Suppress output",
|
|
687
|
+
description="Suppress output",
|
|
688
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
620
689
|
),
|
|
621
690
|
log_file: Optional[str] = Field(
|
|
622
|
-
description="Path to log file",
|
|
691
|
+
description="Path to log file",
|
|
692
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
623
693
|
),
|
|
624
694
|
ctx: Context = Field(
|
|
625
695
|
description="MCP context for progress reporting", default=None
|
|
626
696
|
),
|
|
627
697
|
) -> Dict:
|
|
698
|
+
"""
|
|
699
|
+
Lists all volumes.
|
|
700
|
+
Returns: A dictionary with 'volumes' as a list of dicts containing name, driver, mountpoint, etc.
|
|
701
|
+
"""
|
|
628
702
|
logger = logging.getLogger("ContainerManager")
|
|
629
703
|
logger.debug(
|
|
630
704
|
f"Listing volumes for {manager_type}, silent: {silent}, log_file: {log_file}"
|
|
@@ -651,18 +725,24 @@ async def create_volume(
|
|
|
651
725
|
name: str = Field(description="Volume name"),
|
|
652
726
|
manager_type: Optional[str] = Field(
|
|
653
727
|
description="Container manager: docker, podman (default: auto-detect)",
|
|
654
|
-
default=
|
|
728
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
655
729
|
),
|
|
656
730
|
silent: Optional[bool] = Field(
|
|
657
|
-
description="Suppress output",
|
|
731
|
+
description="Suppress output",
|
|
732
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
658
733
|
),
|
|
659
734
|
log_file: Optional[str] = Field(
|
|
660
|
-
description="Path to log file",
|
|
735
|
+
description="Path to log file",
|
|
736
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
661
737
|
),
|
|
662
738
|
ctx: Context = Field(
|
|
663
739
|
description="MCP context for progress reporting", default=None
|
|
664
740
|
),
|
|
665
741
|
) -> Dict:
|
|
742
|
+
"""
|
|
743
|
+
Creates a new volume.
|
|
744
|
+
Returns: A dictionary with details of the created volume, like 'name' and 'mountpoint'.
|
|
745
|
+
"""
|
|
666
746
|
logger = logging.getLogger("ContainerManager")
|
|
667
747
|
logger.debug(
|
|
668
748
|
f"Creating volume {name} for {manager_type}, silent: {silent}, log_file: {log_file}"
|
|
@@ -690,18 +770,24 @@ async def remove_volume(
|
|
|
690
770
|
force: bool = Field(description="Force removal", default=False),
|
|
691
771
|
manager_type: Optional[str] = Field(
|
|
692
772
|
description="Container manager: docker, podman (default: auto-detect)",
|
|
693
|
-
default=
|
|
773
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
694
774
|
),
|
|
695
775
|
silent: Optional[bool] = Field(
|
|
696
|
-
description="Suppress output",
|
|
776
|
+
description="Suppress output",
|
|
777
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
697
778
|
),
|
|
698
779
|
log_file: Optional[str] = Field(
|
|
699
|
-
description="Path to log file",
|
|
780
|
+
description="Path to log file",
|
|
781
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
700
782
|
),
|
|
701
783
|
ctx: Context = Field(
|
|
702
784
|
description="MCP context for progress reporting", default=None
|
|
703
785
|
),
|
|
704
786
|
) -> Dict:
|
|
787
|
+
"""
|
|
788
|
+
Removes a volume.
|
|
789
|
+
Returns: A dictionary confirming removal, with deleted volume name.
|
|
790
|
+
"""
|
|
705
791
|
logger = logging.getLogger("ContainerManager")
|
|
706
792
|
logger.debug(
|
|
707
793
|
f"Removing volume {name} for {manager_type}, silent: {silent}, log_file: {log_file}"
|
|
@@ -728,18 +814,24 @@ async def prune_volumes(
|
|
|
728
814
|
all: bool = Field(description="Remove all volumes (dangerous)", default=False),
|
|
729
815
|
manager_type: Optional[str] = Field(
|
|
730
816
|
description="Container manager: docker, podman (default: auto-detect)",
|
|
731
|
-
default=
|
|
817
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
732
818
|
),
|
|
733
819
|
silent: Optional[bool] = Field(
|
|
734
|
-
description="Suppress output",
|
|
820
|
+
description="Suppress output",
|
|
821
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
735
822
|
),
|
|
736
823
|
log_file: Optional[str] = Field(
|
|
737
|
-
description="Path to log file",
|
|
824
|
+
description="Path to log file",
|
|
825
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
738
826
|
),
|
|
739
827
|
ctx: Context = Field(
|
|
740
828
|
description="MCP context for progress reporting", default=None
|
|
741
829
|
),
|
|
742
830
|
) -> Dict:
|
|
831
|
+
"""
|
|
832
|
+
Prunes unused volumes.
|
|
833
|
+
Returns: A dictionary with prune results, including space reclaimed and deleted volumes.
|
|
834
|
+
"""
|
|
743
835
|
logger = logging.getLogger("ContainerManager")
|
|
744
836
|
logger.debug(
|
|
745
837
|
f"Pruning volumes for {manager_type}, all: {all}, silent: {silent}, log_file: {log_file}"
|
|
@@ -765,18 +857,24 @@ async def prune_volumes(
|
|
|
765
857
|
async def list_networks(
|
|
766
858
|
manager_type: Optional[str] = Field(
|
|
767
859
|
description="Container manager: docker, podman (default: auto-detect)",
|
|
768
|
-
default=
|
|
860
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
769
861
|
),
|
|
770
862
|
silent: Optional[bool] = Field(
|
|
771
|
-
description="Suppress output",
|
|
863
|
+
description="Suppress output",
|
|
864
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
772
865
|
),
|
|
773
866
|
log_file: Optional[str] = Field(
|
|
774
|
-
description="Path to log file",
|
|
867
|
+
description="Path to log file",
|
|
868
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
775
869
|
),
|
|
776
870
|
ctx: Context = Field(
|
|
777
871
|
description="MCP context for progress reporting", default=None
|
|
778
872
|
),
|
|
779
873
|
) -> List[Dict]:
|
|
874
|
+
"""
|
|
875
|
+
Lists all networks.
|
|
876
|
+
Returns: A list of dictionaries, each with network details like 'id', 'name', 'driver', 'scope'.
|
|
877
|
+
"""
|
|
780
878
|
logger = logging.getLogger("ContainerManager")
|
|
781
879
|
logger.debug(
|
|
782
880
|
f"Listing networks for {manager_type}, silent: {silent}, log_file: {log_file}"
|
|
@@ -804,18 +902,24 @@ async def create_network(
|
|
|
804
902
|
driver: str = Field(description="Network driver (e.g., bridge)", default="bridge"),
|
|
805
903
|
manager_type: Optional[str] = Field(
|
|
806
904
|
description="Container manager: docker, podman (default: auto-detect)",
|
|
807
|
-
default=
|
|
905
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
808
906
|
),
|
|
809
907
|
silent: Optional[bool] = Field(
|
|
810
|
-
description="Suppress output",
|
|
908
|
+
description="Suppress output",
|
|
909
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
811
910
|
),
|
|
812
911
|
log_file: Optional[str] = Field(
|
|
813
|
-
description="Path to log file",
|
|
912
|
+
description="Path to log file",
|
|
913
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
814
914
|
),
|
|
815
915
|
ctx: Context = Field(
|
|
816
916
|
description="MCP context for progress reporting", default=None
|
|
817
917
|
),
|
|
818
918
|
) -> Dict:
|
|
919
|
+
"""
|
|
920
|
+
Creates a new network.
|
|
921
|
+
Returns: A dictionary with the created network's ID and details.
|
|
922
|
+
"""
|
|
819
923
|
logger = logging.getLogger("ContainerManager")
|
|
820
924
|
logger.debug(
|
|
821
925
|
f"Creating network {name} for {manager_type}, silent: {silent}, log_file: {log_file}"
|
|
@@ -842,18 +946,24 @@ async def remove_network(
|
|
|
842
946
|
network_id: str = Field(description="Network ID or name"),
|
|
843
947
|
manager_type: Optional[str] = Field(
|
|
844
948
|
description="Container manager: docker, podman (default: auto-detect)",
|
|
845
|
-
default=
|
|
949
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
846
950
|
),
|
|
847
951
|
silent: Optional[bool] = Field(
|
|
848
|
-
description="Suppress output",
|
|
952
|
+
description="Suppress output",
|
|
953
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
849
954
|
),
|
|
850
955
|
log_file: Optional[str] = Field(
|
|
851
|
-
description="Path to log file",
|
|
956
|
+
description="Path to log file",
|
|
957
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
852
958
|
),
|
|
853
959
|
ctx: Context = Field(
|
|
854
960
|
description="MCP context for progress reporting", default=None
|
|
855
961
|
),
|
|
856
962
|
) -> Dict:
|
|
963
|
+
"""
|
|
964
|
+
Removes a network.
|
|
965
|
+
Returns: A dictionary confirming removal, with deleted network ID.
|
|
966
|
+
"""
|
|
857
967
|
logger = logging.getLogger("ContainerManager")
|
|
858
968
|
logger.debug(
|
|
859
969
|
f"Removing network {network_id} for {manager_type}, silent: {silent}, log_file: {log_file}"
|
|
@@ -879,18 +989,24 @@ async def remove_network(
|
|
|
879
989
|
async def prune_networks(
|
|
880
990
|
manager_type: Optional[str] = Field(
|
|
881
991
|
description="Container manager: docker, podman (default: auto-detect)",
|
|
882
|
-
default=
|
|
992
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
883
993
|
),
|
|
884
994
|
silent: Optional[bool] = Field(
|
|
885
|
-
description="Suppress output",
|
|
995
|
+
description="Suppress output",
|
|
996
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
886
997
|
),
|
|
887
998
|
log_file: Optional[str] = Field(
|
|
888
|
-
description="Path to log file",
|
|
999
|
+
description="Path to log file",
|
|
1000
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
889
1001
|
),
|
|
890
1002
|
ctx: Context = Field(
|
|
891
1003
|
description="MCP context for progress reporting", default=None
|
|
892
1004
|
),
|
|
893
1005
|
) -> Dict:
|
|
1006
|
+
"""
|
|
1007
|
+
Prunes unused networks.
|
|
1008
|
+
Returns: A dictionary with prune results, including deleted networks.
|
|
1009
|
+
"""
|
|
894
1010
|
logger = logging.getLogger("ContainerManager")
|
|
895
1011
|
logger.debug(
|
|
896
1012
|
f"Pruning networks for {manager_type}, silent: {silent}, log_file: {log_file}"
|
|
@@ -918,18 +1034,24 @@ async def prune_system(
|
|
|
918
1034
|
all: bool = Field(description="Prune all unused resources", default=False),
|
|
919
1035
|
manager_type: Optional[str] = Field(
|
|
920
1036
|
description="Container manager: docker, podman (default: auto-detect)",
|
|
921
|
-
default=
|
|
1037
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
922
1038
|
),
|
|
923
1039
|
silent: Optional[bool] = Field(
|
|
924
|
-
description="Suppress output",
|
|
1040
|
+
description="Suppress output",
|
|
1041
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
925
1042
|
),
|
|
926
1043
|
log_file: Optional[str] = Field(
|
|
927
|
-
description="Path to log file",
|
|
1044
|
+
description="Path to log file",
|
|
1045
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
928
1046
|
),
|
|
929
1047
|
ctx: Context = Field(
|
|
930
1048
|
description="MCP context for progress reporting", default=None
|
|
931
1049
|
),
|
|
932
1050
|
) -> Dict:
|
|
1051
|
+
"""
|
|
1052
|
+
Prunes all unused system resources (containers, images, volumes, networks).
|
|
1053
|
+
Returns: A dictionary summarizing the prune operation across resources.
|
|
1054
|
+
"""
|
|
933
1055
|
logger = logging.getLogger("ContainerManager")
|
|
934
1056
|
logger.debug(
|
|
935
1057
|
f"Pruning system for {manager_type}, force: {force}, all: {all}, silent: {silent}, log_file: {log_file}"
|
|
@@ -961,18 +1083,24 @@ async def init_swarm(
|
|
|
961
1083
|
),
|
|
962
1084
|
manager_type: Optional[str] = Field(
|
|
963
1085
|
description="Container manager: must be docker for swarm (default: auto-detect)",
|
|
964
|
-
default=
|
|
1086
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
965
1087
|
),
|
|
966
1088
|
silent: Optional[bool] = Field(
|
|
967
|
-
description="Suppress output",
|
|
1089
|
+
description="Suppress output",
|
|
1090
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
968
1091
|
),
|
|
969
1092
|
log_file: Optional[str] = Field(
|
|
970
|
-
description="Path to log file",
|
|
1093
|
+
description="Path to log file",
|
|
1094
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
971
1095
|
),
|
|
972
1096
|
ctx: Context = Field(
|
|
973
1097
|
description="MCP context for progress reporting", default=None
|
|
974
1098
|
),
|
|
975
1099
|
) -> Dict:
|
|
1100
|
+
"""
|
|
1101
|
+
Initializes a Docker Swarm cluster.
|
|
1102
|
+
Returns: A dictionary with swarm info, including join tokens for manager and worker.
|
|
1103
|
+
"""
|
|
976
1104
|
if manager_type and manager_type != "docker":
|
|
977
1105
|
raise ValueError("Swarm operations are only supported on Docker")
|
|
978
1106
|
logger = logging.getLogger("ContainerManager")
|
|
@@ -1001,18 +1129,24 @@ async def leave_swarm(
|
|
|
1001
1129
|
force: bool = Field(description="Force leave", default=False),
|
|
1002
1130
|
manager_type: Optional[str] = Field(
|
|
1003
1131
|
description="Container manager: must be docker for swarm (default: auto-detect)",
|
|
1004
|
-
default=
|
|
1132
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
1005
1133
|
),
|
|
1006
1134
|
silent: Optional[bool] = Field(
|
|
1007
|
-
description="Suppress output",
|
|
1135
|
+
description="Suppress output",
|
|
1136
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
1008
1137
|
),
|
|
1009
1138
|
log_file: Optional[str] = Field(
|
|
1010
|
-
description="Path to log file",
|
|
1139
|
+
description="Path to log file",
|
|
1140
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
1011
1141
|
),
|
|
1012
1142
|
ctx: Context = Field(
|
|
1013
1143
|
description="MCP context for progress reporting", default=None
|
|
1014
1144
|
),
|
|
1015
1145
|
) -> Dict:
|
|
1146
|
+
"""
|
|
1147
|
+
Leaves the Docker Swarm cluster.
|
|
1148
|
+
Returns: A dictionary confirming the leave action.
|
|
1149
|
+
"""
|
|
1016
1150
|
if manager_type and manager_type != "docker":
|
|
1017
1151
|
raise ValueError("Swarm operations are only supported on Docker")
|
|
1018
1152
|
logger = logging.getLogger("ContainerManager")
|
|
@@ -1040,18 +1174,24 @@ async def leave_swarm(
|
|
|
1040
1174
|
async def list_nodes(
|
|
1041
1175
|
manager_type: Optional[str] = Field(
|
|
1042
1176
|
description="Container manager: must be docker for swarm (default: auto-detect)",
|
|
1043
|
-
default=
|
|
1177
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
1044
1178
|
),
|
|
1045
1179
|
silent: Optional[bool] = Field(
|
|
1046
|
-
description="Suppress output",
|
|
1180
|
+
description="Suppress output",
|
|
1181
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
1047
1182
|
),
|
|
1048
1183
|
log_file: Optional[str] = Field(
|
|
1049
|
-
description="Path to log file",
|
|
1184
|
+
description="Path to log file",
|
|
1185
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
1050
1186
|
),
|
|
1051
1187
|
ctx: Context = Field(
|
|
1052
1188
|
description="MCP context for progress reporting", default=None
|
|
1053
1189
|
),
|
|
1054
1190
|
) -> List[Dict]:
|
|
1191
|
+
"""
|
|
1192
|
+
Lists nodes in the Docker Swarm cluster.
|
|
1193
|
+
Returns: A list of dictionaries, each with node details like 'id', 'hostname', 'status', 'role'.
|
|
1194
|
+
"""
|
|
1055
1195
|
if manager_type and manager_type != "docker":
|
|
1056
1196
|
raise ValueError("Swarm operations are only supported on Docker")
|
|
1057
1197
|
logger = logging.getLogger("ContainerManager")
|
|
@@ -1079,18 +1219,24 @@ async def list_nodes(
|
|
|
1079
1219
|
async def list_services(
|
|
1080
1220
|
manager_type: Optional[str] = Field(
|
|
1081
1221
|
description="Container manager: must be docker for swarm (default: auto-detect)",
|
|
1082
|
-
default=
|
|
1222
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
1083
1223
|
),
|
|
1084
1224
|
silent: Optional[bool] = Field(
|
|
1085
|
-
description="Suppress output",
|
|
1225
|
+
description="Suppress output",
|
|
1226
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
1086
1227
|
),
|
|
1087
1228
|
log_file: Optional[str] = Field(
|
|
1088
|
-
description="Path to log file",
|
|
1229
|
+
description="Path to log file",
|
|
1230
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
1089
1231
|
),
|
|
1090
1232
|
ctx: Context = Field(
|
|
1091
1233
|
description="MCP context for progress reporting", default=None
|
|
1092
1234
|
),
|
|
1093
1235
|
) -> List[Dict]:
|
|
1236
|
+
"""
|
|
1237
|
+
Lists services in the Docker Swarm.
|
|
1238
|
+
Returns: A list of dictionaries, each with service details like 'id', 'name', 'replicas', 'image'.
|
|
1239
|
+
"""
|
|
1094
1240
|
if manager_type and manager_type != "docker":
|
|
1095
1241
|
raise ValueError("Swarm operations are only supported on Docker")
|
|
1096
1242
|
logger = logging.getLogger("ContainerManager")
|
|
@@ -1127,18 +1273,24 @@ async def create_service(
|
|
|
1127
1273
|
),
|
|
1128
1274
|
manager_type: Optional[str] = Field(
|
|
1129
1275
|
description="Container manager: must be docker for swarm (default: auto-detect)",
|
|
1130
|
-
default=
|
|
1276
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
1131
1277
|
),
|
|
1132
1278
|
silent: Optional[bool] = Field(
|
|
1133
|
-
description="Suppress output",
|
|
1279
|
+
description="Suppress output",
|
|
1280
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
1134
1281
|
),
|
|
1135
1282
|
log_file: Optional[str] = Field(
|
|
1136
|
-
description="Path to log file",
|
|
1283
|
+
description="Path to log file",
|
|
1284
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
1137
1285
|
),
|
|
1138
1286
|
ctx: Context = Field(
|
|
1139
1287
|
description="MCP context for progress reporting", default=None
|
|
1140
1288
|
),
|
|
1141
1289
|
) -> Dict:
|
|
1290
|
+
"""
|
|
1291
|
+
Creates a new service in Docker Swarm.
|
|
1292
|
+
Returns: A dictionary with the created service's ID and details.
|
|
1293
|
+
"""
|
|
1142
1294
|
if manager_type and manager_type != "docker":
|
|
1143
1295
|
raise ValueError("Swarm operations are only supported on Docker")
|
|
1144
1296
|
logger = logging.getLogger("ContainerManager")
|
|
@@ -1167,18 +1319,24 @@ async def remove_service(
|
|
|
1167
1319
|
service_id: str = Field(description="Service ID or name"),
|
|
1168
1320
|
manager_type: Optional[str] = Field(
|
|
1169
1321
|
description="Container manager: must be docker for swarm (default: auto-detect)",
|
|
1170
|
-
default=
|
|
1322
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
1171
1323
|
),
|
|
1172
1324
|
silent: Optional[bool] = Field(
|
|
1173
|
-
description="Suppress output",
|
|
1325
|
+
description="Suppress output",
|
|
1326
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
1174
1327
|
),
|
|
1175
1328
|
log_file: Optional[str] = Field(
|
|
1176
|
-
description="Path to log file",
|
|
1329
|
+
description="Path to log file",
|
|
1330
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
1177
1331
|
),
|
|
1178
1332
|
ctx: Context = Field(
|
|
1179
1333
|
description="MCP context for progress reporting", default=None
|
|
1180
1334
|
),
|
|
1181
1335
|
) -> Dict:
|
|
1336
|
+
"""
|
|
1337
|
+
Removes a service from Docker Swarm.
|
|
1338
|
+
Returns: A dictionary confirming the removal.
|
|
1339
|
+
"""
|
|
1182
1340
|
if manager_type and manager_type != "docker":
|
|
1183
1341
|
raise ValueError("Swarm operations are only supported on Docker")
|
|
1184
1342
|
logger = logging.getLogger("ContainerManager")
|
|
@@ -1209,18 +1367,24 @@ async def compose_up(
|
|
|
1209
1367
|
build: bool = Field(description="Build images", default=False),
|
|
1210
1368
|
manager_type: Optional[str] = Field(
|
|
1211
1369
|
description="Container manager: docker, podman (default: auto-detect)",
|
|
1212
|
-
default=
|
|
1370
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
1213
1371
|
),
|
|
1214
1372
|
silent: Optional[bool] = Field(
|
|
1215
|
-
description="Suppress output",
|
|
1373
|
+
description="Suppress output",
|
|
1374
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
1216
1375
|
),
|
|
1217
1376
|
log_file: Optional[str] = Field(
|
|
1218
|
-
description="Path to log file",
|
|
1377
|
+
description="Path to log file",
|
|
1378
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
1219
1379
|
),
|
|
1220
1380
|
ctx: Context = Field(
|
|
1221
1381
|
description="MCP context for progress reporting", default=None
|
|
1222
1382
|
),
|
|
1223
1383
|
) -> str:
|
|
1384
|
+
"""
|
|
1385
|
+
Starts services defined in a Docker Compose file.
|
|
1386
|
+
Returns: A string with the output of the compose up command, parse for status messages.
|
|
1387
|
+
"""
|
|
1224
1388
|
logger = logging.getLogger("ContainerManager")
|
|
1225
1389
|
logger.debug(
|
|
1226
1390
|
f"Compose up {compose_file} for {manager_type}, silent: {silent}, log_file: {log_file}"
|
|
@@ -1247,18 +1411,24 @@ async def compose_down(
|
|
|
1247
1411
|
compose_file: str = Field(description="Path to compose file"),
|
|
1248
1412
|
manager_type: Optional[str] = Field(
|
|
1249
1413
|
description="Container manager: docker, podman (default: auto-detect)",
|
|
1250
|
-
default=
|
|
1414
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
1251
1415
|
),
|
|
1252
1416
|
silent: Optional[bool] = Field(
|
|
1253
|
-
description="Suppress output",
|
|
1417
|
+
description="Suppress output",
|
|
1418
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
1254
1419
|
),
|
|
1255
1420
|
log_file: Optional[str] = Field(
|
|
1256
|
-
description="Path to log file",
|
|
1421
|
+
description="Path to log file",
|
|
1422
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
1257
1423
|
),
|
|
1258
1424
|
ctx: Context = Field(
|
|
1259
1425
|
description="MCP context for progress reporting", default=None
|
|
1260
1426
|
),
|
|
1261
1427
|
) -> str:
|
|
1428
|
+
"""
|
|
1429
|
+
Stops and removes services from a Docker Compose file.
|
|
1430
|
+
Returns: A string with the output of the compose down command, parse for status messages.
|
|
1431
|
+
"""
|
|
1262
1432
|
logger = logging.getLogger("ContainerManager")
|
|
1263
1433
|
logger.debug(
|
|
1264
1434
|
f"Compose down {compose_file} for {manager_type}, silent: {silent}, log_file: {log_file}"
|
|
@@ -1285,18 +1455,24 @@ async def compose_ps(
|
|
|
1285
1455
|
compose_file: str = Field(description="Path to compose file"),
|
|
1286
1456
|
manager_type: Optional[str] = Field(
|
|
1287
1457
|
description="Container manager: docker, podman (default: auto-detect)",
|
|
1288
|
-
default=
|
|
1458
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
1289
1459
|
),
|
|
1290
1460
|
silent: Optional[bool] = Field(
|
|
1291
|
-
description="Suppress output",
|
|
1461
|
+
description="Suppress output",
|
|
1462
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
1292
1463
|
),
|
|
1293
1464
|
log_file: Optional[str] = Field(
|
|
1294
|
-
description="Path to log file",
|
|
1465
|
+
description="Path to log file",
|
|
1466
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
1295
1467
|
),
|
|
1296
1468
|
ctx: Context = Field(
|
|
1297
1469
|
description="MCP context for progress reporting", default=None
|
|
1298
1470
|
),
|
|
1299
1471
|
) -> str:
|
|
1472
|
+
"""
|
|
1473
|
+
Lists containers for a Docker Compose project.
|
|
1474
|
+
Returns: A string in table format listing name, command, state, ports; parse as text table.
|
|
1475
|
+
"""
|
|
1300
1476
|
logger = logging.getLogger("ContainerManager")
|
|
1301
1477
|
logger.debug(
|
|
1302
1478
|
f"Compose ps {compose_file} for {manager_type}, silent: {silent}, log_file: {log_file}"
|
|
@@ -1324,18 +1500,24 @@ async def compose_logs(
|
|
|
1324
1500
|
service: Optional[str] = Field(description="Specific service", default=None),
|
|
1325
1501
|
manager_type: Optional[str] = Field(
|
|
1326
1502
|
description="Container manager: docker, podman (default: auto-detect)",
|
|
1327
|
-
default=
|
|
1503
|
+
default=os.environ.get("CONTAINER_MANAGER_TYPE", None),
|
|
1328
1504
|
),
|
|
1329
1505
|
silent: Optional[bool] = Field(
|
|
1330
|
-
description="Suppress output",
|
|
1506
|
+
description="Suppress output",
|
|
1507
|
+
default=to_boolean(os.environ.get("CONTAINER_MANAGER_SILENT", False)),
|
|
1331
1508
|
),
|
|
1332
1509
|
log_file: Optional[str] = Field(
|
|
1333
|
-
description="Path to log file",
|
|
1510
|
+
description="Path to log file",
|
|
1511
|
+
default=os.environ.get("CONTAINER_MANAGER_LOG_FILE", None),
|
|
1334
1512
|
),
|
|
1335
1513
|
ctx: Context = Field(
|
|
1336
1514
|
description="MCP context for progress reporting", default=None
|
|
1337
1515
|
),
|
|
1338
1516
|
) -> str:
|
|
1517
|
+
"""
|
|
1518
|
+
Retrieves logs for services in a Docker Compose project.
|
|
1519
|
+
Returns: A string containing combined log output, prefixed by service names; parse as text lines.
|
|
1520
|
+
"""
|
|
1339
1521
|
logger = logging.getLogger("ContainerManager")
|
|
1340
1522
|
logger.debug(
|
|
1341
1523
|
f"Compose logs {compose_file} for {manager_type}, silent: {silent}, log_file: {log_file}"
|
|
@@ -1375,9 +1557,5 @@ def container_manager_mcp():
|
|
|
1375
1557
|
sys.exit(1)
|
|
1376
1558
|
|
|
1377
1559
|
|
|
1378
|
-
def main():
|
|
1379
|
-
container_manager_mcp()
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
1560
|
if __name__ == "__main__":
|
|
1383
1561
|
container_manager_mcp()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: container-manager-mcp
|
|
3
|
-
Version: 1.0
|
|
3
|
+
Version: 1.1.0
|
|
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.1.0*
|
|
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
|
|
|
@@ -123,8 +123,10 @@ Configure `mcp.json`
|
|
|
123
123
|
"container-manager-mcp"
|
|
124
124
|
],
|
|
125
125
|
"env": {
|
|
126
|
-
"
|
|
127
|
-
"
|
|
126
|
+
"CONTAINER_MANAGER_SILENT": "False", //Optional
|
|
127
|
+
"CONTAINER_MANAGER_LOG_FILE": "~/Documents/container_manager_mcp.log" //Optional
|
|
128
|
+
"CONTAINER_MANAGER_TYPE": "podman", //Optional
|
|
129
|
+
"CONTAINER_MANAGER_PODMAN_BASE_URL": "tcp://127.0.0.1:8080" //Optional
|
|
128
130
|
},
|
|
129
131
|
"timeout": 200000
|
|
130
132
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
container_manager_mcp/__init__.py,sha256=2jGc7NMMlaj8-VhO0Z3KpQM50bB_fweiex-hDBfVTWo,540
|
|
2
|
+
container_manager_mcp/__main__.py,sha256=4bpregRyaWJBXOg9_h_F4xcHcX6bPVLL48V2EuwNGBs,168
|
|
3
|
+
container_manager_mcp/container_manager.py,sha256=Rtqrb4H05thq_i7z7v7M33m5rrNZ1V5rYWwV2NC8Zwo,89504
|
|
4
|
+
container_manager_mcp/container_manager_mcp.py,sha256=qeyOZAv7rgOVwjHZuIxb6gBeHvoz8gG9p7dSRDXTvYA,55003
|
|
5
|
+
container_manager_mcp-1.1.0.dist-info/licenses/LICENSE,sha256=Z1xmcrPHBnGCETO_LLQJUeaSNBSnuptcDVTt4kaPUOE,1060
|
|
6
|
+
container_manager_mcp-1.1.0.dist-info/METADATA,sha256=Wgr36FGeQrKP9tWSDnpHMbGgJDsbestwVH506_2SNsY,8452
|
|
7
|
+
container_manager_mcp-1.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
8
|
+
container_manager_mcp-1.1.0.dist-info/entry_points.txt,sha256=n7fE9uXdRktmjvZwyaiQkY0Dd7tMN6AKiHav0ZQdE2I,186
|
|
9
|
+
container_manager_mcp-1.1.0.dist-info/top_level.txt,sha256=B7QQLOd9mBdu0lsPKqyu4T8-zUtbqKzQJbMbtAzoozU,22
|
|
10
|
+
container_manager_mcp-1.1.0.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=O-Dk6q-QSQJbzHVHC3KeljJuGoDdkjlTcaCwC_4hdgo,88877
|
|
4
|
-
container_manager_mcp/container_manager_mcp.py,sha256=E3FOTFILyYYoJF3Erkpl5zapkiDi2pKlJlkZByc-8QE,47361
|
|
5
|
-
container_manager_mcp-1.0.6.dist-info/licenses/LICENSE,sha256=Z1xmcrPHBnGCETO_LLQJUeaSNBSnuptcDVTt4kaPUOE,1060
|
|
6
|
-
container_manager_mcp-1.0.6.dist-info/METADATA,sha256=WygJ6UaeqBGVthdIZvSYbl40n2M40F2aL-toAm2zq8o,8238
|
|
7
|
-
container_manager_mcp-1.0.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
8
|
-
container_manager_mcp-1.0.6.dist-info/entry_points.txt,sha256=I23pXcCgAShlfYbENzs3kbw3l1lU9Gy7lODPfRqeeiA,156
|
|
9
|
-
container_manager_mcp-1.0.6.dist-info/top_level.txt,sha256=B7QQLOd9mBdu0lsPKqyu4T8-zUtbqKzQJbMbtAzoozU,22
|
|
10
|
-
container_manager_mcp-1.0.6.dist-info/RECORD,,
|
|
File without changes
|
{container_manager_mcp-1.0.6.dist-info → container_manager_mcp-1.1.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
{container_manager_mcp-1.0.6.dist-info → container_manager_mcp-1.1.0.dist-info}/top_level.txt
RENAMED
|
File without changes
|