Full Code of QuantGeekDev/docker-mcp for AI

main 6dada7feb355 cached
9 files
23.8 KB
5.6k tokens
36 symbols
1 requests
Download .txt
Repository: QuantGeekDev/docker-mcp
Branch: main
Commit: 6dada7feb355
Files: 9
Total size: 23.8 KB

Directory structure:
gitextract_dbxbtxq3/

├── .gitignore
├── .python-version
├── LICENSE
├── README.md
├── pyproject.toml
└── src/
    └── docker_mcp/
        ├── __init__.py
        ├── docker_executor.py
        ├── handlers.py
        └── server.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# Python-generated files
__pycache__/
*.py[oc]
build/
dist/
wheels/
*.egg-info

# Virtual environments
.venv
conf
work
README_PRIVATE.md
docker_compose_files

================================================
FILE: .python-version
================================================
3.12


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) [year] [fullname]

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: README.md
================================================
# 🐳 docker-mcp

[![Python 3.12](https://img.shields.io/badge/python-3.12-blue.svg)](https://www.python.org/downloads/release/python-3120/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![smithery badge](https://smithery.ai/badge/docker-mcp)](https://smithery.ai/protocol/docker-mcp)

A powerful Model Context Protocol (MCP) server for Docker operations, enabling seamless container and compose stack management through Claude AI.

## ✨ Features

- 🚀 Container creation and instantiation
- 📦 Docker Compose stack deployment
- 🔍 Container logs retrieval
- 📊 Container listing and status monitoring

### 🎬 Demos
#### Deploying a Docker Compose Stack


https://github.com/user-attachments/assets/b5f6e40a-542b-4a39-ba12-7fdf803ee278



#### Analyzing Container Logs



https://github.com/user-attachments/assets/da386eea-2fab-4835-82ae-896de955d934



## 🚀 Quickstart

To try this in Claude Desktop app, add this to your claude config files:
```json
{
  "mcpServers": {
    "docker-mcp": {
      "command": "uvx",
      "args": [
        "docker-mcp"
      ]
    }
  }
}
```

### Installing via Smithery

To install Docker MCP for Claude Desktop automatically via [Smithery](https://smithery.ai/protocol/docker-mcp):

```bash
npx @smithery/cli install docker-mcp --client claude
```

### Prerequisites

- UV (package manager)
- Python 3.12+
- Docker Desktop or Docker Engine
- Claude Desktop

### Installation

#### Claude Desktop Configuration

Add the server configuration to your Claude Desktop config file:

**MacOS**: `~/Library/Application\ Support/Claude/claude_desktop_config.json`  
**Windows**: `%APPDATA%/Claude/claude_desktop_config.json`

<details>
  <summary>💻 Development Configuration</summary>

```json
{
  "mcpServers": {
    "docker-mcp": {
      "command": "uv",
      "args": [
        "--directory",
        "<path-to-docker-mcp>",
        "run",
        "docker-mcp"
      ]
    }
  }
}
```
</details>

<details>
  <summary>🚀 Production Configuration</summary>

```json
{
  "mcpServers": {
    "docker-mcp": {
      "command": "uvx",
      "args": [
        "docker-mcp"
      ]
    }
  }
}
```
</details>

## 🛠️ Development

### Local Setup

1. Clone the repository:
```bash
git clone https://github.com/QuantGeekDev/docker-mcp.git
cd docker-mcp
```

2. Create and activate a virtual environment:
```bash
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate
```

3. Install dependencies:
```bash
uv sync
```

### 🔍 Debugging

Launch the MCP Inspector for debugging:

```bash
npx @modelcontextprotocol/inspector uv --directory <path-to-docker-mcp> run docker-mcp
```

The Inspector will provide a URL to access the debugging interface.

## 📝 Available Tools

The server provides the following tools:

### create-container
Creates a standalone Docker container
```json
{
    "image": "image-name",
    "name": "container-name",
    "ports": {"80": "80"},
    "environment": {"ENV_VAR": "value"}
}
```

### deploy-compose
Deploys a Docker Compose stack
```json
{
    "project_name": "example-stack",
    "compose_yaml": "version: '3.8'\nservices:\n  service1:\n    image: image1:latest\n    ports:\n      - '8080:80'"
}
```

### get-logs
Retrieves logs from a specific container
```json
{
    "container_name": "my-container"
}
```

### list-containers
Lists all Docker containers
```json
{}
```

## 🚧 Current Limitations

- No built-in environment variable support for containers
- No volume management
- No network management
- No container health checks
- No container restart policies
- No container resource limits

## 🤝 Contributing

1. Fork the repository from [docker-mcp](https://github.com/QuantGeekDev/docker-mcp)
2. Create your feature branch
3. Commit your changes
4. Push to the branch
5. Open a Pull Request

## 📜 License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## ✨ Authors

- **Alex Andru** - *Initial work | Core contributor* - [@QuantGeekDev](https://github.com/QuantGeekDev)
- **Ali Sadykov** - *Initial work  | Core contributor* - [@md-archive](https://github.com/md-archive)

---
Made with ❤️


================================================
FILE: pyproject.toml
================================================
[project]
name = "docker-mcp"
version = "0.1.0"
description = "A docker MCP server"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
    "httpx>=0.28.0",
    "mcp>=1.0.0",
    "python-dotenv>=1.0.1",
    "python-on-whales>=0.67.0",
    "pyyaml>=6.0.1"
]

[[project.authors]]
name = "Alex Andru"
email = "alex007d@gmail.com"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project.scripts]
docker-mcp = "docker_mcp:main"

================================================
FILE: src/docker_mcp/__init__.py
================================================
from . import server
import asyncio

def main():
    """Main entry point for the package."""
    asyncio.run(server.main())

# Optionally expose other important items at package level
__all__ = ['main', 'server']

================================================
FILE: src/docker_mcp/docker_executor.py
================================================
from typing import Tuple, Protocol, List
import asyncio
import os
import platform
import shutil
from abc import ABC, abstractmethod


class CommandExecutor(Protocol):
    async def execute(self, cmd: str | List[str]) -> Tuple[int, str, str]:
        pass


class WindowsExecutor:
    async def execute(self, cmd: str) -> Tuple[int, str, str]:
        process = await asyncio.create_subprocess_shell(
            cmd,
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.PIPE,
            shell=True
        )
        stdout, stderr = await process.communicate()
        return process.returncode, stdout.decode(), stderr.decode()


class UnixExecutor:
    async def execute(self, cmd: List[str]) -> Tuple[int, str, str]:
        process = await asyncio.create_subprocess_exec(
            *cmd,
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.PIPE
        )
        stdout, stderr = await process.communicate()
        return process.returncode, stdout.decode(), stderr.decode()


class DockerExecutorBase(ABC):
    def __init__(self):
        self.docker_cmd = self._initialize_docker_cmd()
        self.executor = WindowsExecutor() if platform.system() == 'Windows' else UnixExecutor()

    @abstractmethod
    async def run_command(self, command: str, *args) -> Tuple[int, str, str]:
        pass

    def _initialize_docker_cmd(self) -> str:
        if platform.system() == 'Windows':
            docker_dir = r"C:\Program Files\Docker\Docker\resources\bin"
            docker_paths = [
                os.path.join(docker_dir, "docker-compose.exe"),
                os.path.join(docker_dir, "docker.exe")
            ]
            for path in docker_paths:
                if os.path.exists(path):
                    return path

        docker_cmd = shutil.which('docker')
        if not docker_cmd:
            raise RuntimeError("Docker executable not found")
        return docker_cmd


class DockerComposeExecutor(DockerExecutorBase):
    def __init__(self, compose_file: str, project_name: str):
        super().__init__()
        self.compose_file = os.path.abspath(compose_file)
        self.project_name = project_name

    async def run_command(self, command: str, *args) -> Tuple[int, str, str]:
        if platform.system() == 'Windows':
            cmd = self._build_windows_command(command, *args)
        else:
            cmd = self._build_unix_command(command, *args)
        return await self.executor.execute(cmd)

    def _build_windows_command(self, command: str, *args) -> str:
        compose_file = self.compose_file.replace('\\', '/')
        return (f'cd "{os.path.dirname(compose_file)}" && docker compose '
                f'-f "{os.path.basename(compose_file)}" '
                f'-p {self.project_name} {command} {" ".join(args)}')

    def _build_unix_command(self, command: str, *args) -> list[str]:
        return [
            self.docker_cmd,
            "compose",
            "-f", self.compose_file,
            "-p", self.project_name,
            command,
            *args
        ]

    async def down(self) -> Tuple[int, str, str]:
        return await self.run_command("down", "--volumes")

    async def pull(self) -> Tuple[int, str, str]:
        return await self.run_command("pull")

    async def up(self) -> Tuple[int, str, str]:
        return await self.run_command("up", "-d")

    async def ps(self) -> Tuple[int, str, str]:
        return await self.run_command("ps")


================================================
FILE: src/docker_mcp/handlers.py
================================================
from typing import List, Dict, Any
import asyncio
import os
import yaml
import platform
from python_on_whales import DockerClient
from mcp.types import TextContent, Tool, Prompt, PromptArgument, GetPromptResult, PromptMessage
from .docker_executor import DockerComposeExecutor
docker_client = DockerClient()


async def parse_port_mapping(host_key: str, container_port: str | int) -> tuple[str, str] | tuple[str, str, str]:
    if '/' in str(host_key):
        host_port, protocol = host_key.split('/')
        if protocol.lower() == 'udp':
            return (str(host_port), str(container_port), 'udp')
        return (str(host_port), str(container_port))

    if isinstance(container_port, str) and '/' in container_port:
        port, protocol = container_port.split('/')
        if protocol.lower() == 'udp':
            return (str(host_key), port, 'udp')
        return (str(host_key), port)

    return (str(host_key), str(container_port))


class DockerHandlers:
    TIMEOUT_AMOUNT = 200

    @staticmethod
    async def handle_create_container(arguments: Dict[str, Any]) -> List[TextContent]:
        try:
            image = arguments["image"]
            container_name = arguments.get("name")
            ports = arguments.get("ports", {})
            environment = arguments.get("environment", {})

            if not image:
                raise ValueError("Image name cannot be empty")

            port_mappings = []
            for host_key, container_port in ports.items():
                mapping = await parse_port_mapping(host_key, container_port)
                port_mappings.append(mapping)

            async def pull_and_run():
                if not docker_client.image.exists(image):
                    await asyncio.to_thread(docker_client.image.pull, image)

                container = await asyncio.to_thread(
                    docker_client.container.run,
                    image,
                    name=container_name,
                    publish=port_mappings,
                    envs=environment,
                    detach=True
                )
                return container

            container = await asyncio.wait_for(pull_and_run(), timeout=DockerHandlers.TIMEOUT_AMOUNT)
            return [TextContent(type="text", text=f"Created container '{container.name}' (ID: {container.id})")]
        except asyncio.TimeoutError:
            return [TextContent(type="text", text=f"Operation timed out after {DockerHandlers.TIMEOUT_AMOUNT} seconds")]
        except Exception as e:
            return [TextContent(type="text", text=f"Error creating container: {str(e)} | Arguments: {arguments}")]

    @staticmethod
    async def handle_deploy_compose(arguments: Dict[str, Any]) -> List[TextContent]:
        debug_info = []
        try:
            compose_yaml = arguments.get("compose_yaml")
            project_name = arguments.get("project_name")

            if not compose_yaml or not project_name:
                raise ValueError(
                    "Missing required compose_yaml or project_name")

            yaml_content = DockerHandlers._process_yaml(
                compose_yaml, debug_info)
            compose_path = DockerHandlers._save_compose_file(
                yaml_content, project_name)

            try:
                result = await DockerHandlers._deploy_stack(compose_path, project_name, debug_info)
                return [TextContent(type="text", text=result)]
            finally:
                DockerHandlers._cleanup_files(compose_path)

        except Exception as e:
            debug_output = "\n".join(debug_info)
            return [TextContent(type="text", text=f"Error deploying compose stack: {str(e)}\n\nDebug Information:\n{debug_output}")]

    @staticmethod
    def _process_yaml(compose_yaml: str, debug_info: List[str]) -> dict:
        debug_info.append("=== Original YAML ===")
        debug_info.append(compose_yaml)

        try:
            yaml_content = yaml.safe_load(compose_yaml)
            debug_info.append("\n=== Loaded YAML Structure ===")
            debug_info.append(str(yaml_content))
            return yaml_content
        except yaml.YAMLError as e:
            raise ValueError(f"Invalid YAML format: {str(e)}")

    @staticmethod
    def _save_compose_file(yaml_content: dict, project_name: str) -> str:
        compose_dir = os.path.join(os.getcwd(), "docker_compose_files")
        os.makedirs(compose_dir, exist_ok=True)

        compose_yaml = yaml.safe_dump(
            yaml_content, default_flow_style=False, sort_keys=False)
        compose_path = os.path.join(
            compose_dir, f"{project_name}-docker-compose.yml")

        with open(compose_path, 'w', encoding='utf-8') as f:
            f.write(compose_yaml)
            f.flush()
            if platform.system() != 'Windows':
                os.fsync(f.fileno())

        return compose_path

    @staticmethod
    async def _deploy_stack(compose_path: str, project_name: str, debug_info: List[str]) -> str:
        compose = DockerComposeExecutor(compose_path, project_name)

        for command in [compose.down, compose.up]:
            try:
                code, out, err = await command()
                debug_info.extend([
                    f"\n=== {command.__name__.capitalize()} Command ===",
                    f"Return Code: {code}",
                    f"Stdout: {out}",
                    f"Stderr: {err}"
                ])

                if code != 0 and command == compose.up:
                    raise Exception(f"Deploy failed with code {code}: {err}")
            except Exception as e:
                if command != compose.down:
                    raise e
                debug_info.append(f"Warning during {
                                  command.__name__}: {str(e)}")

        code, out, err = await compose.ps()
        service_info = out if code == 0 else "Unable to list services"

        return (f"Successfully deployed compose stack '{project_name}'\n"
                f"Running services:\n{service_info}\n\n"
                f"Debug Info:\n{chr(10).join(debug_info)}")

    @staticmethod
    def _cleanup_files(compose_path: str) -> None:
        try:
            if os.path.exists(compose_path):
                os.remove(compose_path)
            compose_dir = os.path.dirname(compose_path)
            if os.path.exists(compose_dir) and not os.listdir(compose_dir):
                os.rmdir(compose_dir)
        except Exception as e:
            print(f"Warning during cleanup: {str(e)}")

    @staticmethod
    async def handle_get_logs(arguments: Dict[str, Any]) -> List[TextContent]:
        debug_info = []
        try:
            container_name = arguments.get("container_name")
            if not container_name:
                raise ValueError("Missing required container_name")

            debug_info.append(f"Fetching logs for container '{
                              container_name}'")
            logs = await asyncio.to_thread(docker_client.container.logs, container_name, tail=100)

            return [TextContent(type="text", text=f"Logs for container '{container_name}':\n{logs}\n\nDebug Info:\n{chr(10).join(debug_info)}")]
        except Exception as e:
            debug_output = "\n".join(debug_info)
            return [TextContent(type="text", text=f"Error retrieving logs: {str(e)}\n\nDebug Information:\n{debug_output}")]

    @staticmethod
    async def handle_list_containers(arguments: Dict[str, Any]) -> List[TextContent]:
        debug_info = []
        try:
            debug_info.append("Listing all Docker containers")
            containers = await asyncio.to_thread(docker_client.container.list, all=True)
            container_list = "\n".join(
                [f"{c.id[:12]} - {c.name} - {c.state.status}" for c in containers])

            return [TextContent(type="text", text=f"All Docker Containers:\n{container_list}\n\nDebug Info:\n{chr(10).join(debug_info)}")]
        except Exception as e:
            debug_output = "\n".join(debug_info)
            return [TextContent(type="text", text=f"Error listing containers: {str(e)}\n\nDebug Information:\n{debug_output}")]


================================================
FILE: src/docker_mcp/server.py
================================================
import asyncio
import signal
import sys
from typing import List, Dict, Any
import mcp.types as types
from mcp.server import NotificationOptions, Server
from mcp.server.models import InitializationOptions
import mcp.server.stdio
from .handlers import DockerHandlers

server = Server("docker-mcp")


@server.list_prompts()
async def handle_list_prompts() -> List[types.Prompt]:
    return [
        types.Prompt(
            name="deploy-stack",
            description="Generate and deploy a Docker stack based on requirements",
            arguments=[
                types.PromptArgument(
                    name="requirements",
                    description="Description of the desired Docker stack",
                    required=True
                ),
                types.PromptArgument(
                    name="project_name",
                    description="Name for the Docker Compose project",
                    required=True
                )
            ]
        )
    ]


@server.get_prompt()
async def handle_get_prompt(name: str, arguments: Dict[str, str] | None) -> types.GetPromptResult:
    if name != "deploy-stack":
        raise ValueError(f"Unknown prompt: {name}")

    if not arguments or "requirements" not in arguments or "project_name" not in arguments:
        raise ValueError("Missing required arguments")

    system_message = (
        "You are a Docker deployment specialist. Generate appropriate Docker Compose YAML or "
        "container configurations based on user requirements. For simple single-container "
        "deployments, use the create-container tool. For multi-container deployments, generate "
        "a docker-compose.yml and use the deploy-compose tool. To access logs, first use the "
        "list-containers tool to discover running containers, then use the get-logs tool to "
        "retrieve logs for a specific container."
    )

    user_message = f"""Please help me deploy the following stack:
Requirements: {arguments['requirements']}
Project name: {arguments['project_name']}

Analyze if this needs a single container or multiple containers. Then:
1. For single container: Use the create-container tool with format:
{{
    "image": "image-name",
    "name": "container-name",
    "ports": {{"80": "80"}},
    "environment": {{"ENV_VAR": "value"}}
}}

2. For multiple containers: Use the deploy-compose tool with format:
{{
    "project_name": "example-stack",
    "compose_yaml": "version: '3.8'\\nservices:\\n  service1:\\n    image: image1:latest\\n    ports:\\n      - '8080:80'"
}}"""

    return types.GetPromptResult(
        description="Generate and deploy a Docker stack",
        messages=[
            types.PromptMessage(
                role="system",
                content=types.TextContent(
                    type="text",
                    text=system_message
                )
            ),
            types.PromptMessage(
                role="user",
                content=types.TextContent(
                    type="text",
                    text=user_message
                )
            )
        ]
    )


@server.list_tools()
async def handle_list_tools() -> List[types.Tool]:
    return [
        types.Tool(
            name="create-container",
            description="Create a new standalone Docker container",
            inputSchema={
                "type": "object",
                "properties": {
                    "image": {"type": "string"},
                    "name": {"type": "string"},
                    "ports": {
                        "type": "object",
                        "additionalProperties": {"type": "string"}
                    },
                    "environment": {
                        "type": "object",
                        "additionalProperties": {"type": "string"}
                    }
                },
                "required": ["image"]
            }
        ),
        types.Tool(
            name="deploy-compose",
            description="Deploy a Docker Compose stack",
            inputSchema={
                "type": "object",
                "properties": {
                    "compose_yaml": {"type": "string"},
                    "project_name": {"type": "string"}
                },
                "required": ["compose_yaml", "project_name"]
            }
        ),
        types.Tool(
            name="get-logs",
            description="Retrieve the latest logs for a specified Docker container",
            inputSchema={
                "type": "object",
                "properties": {
                    "container_name": {"type": "string"}
                },
                "required": ["container_name"]
            }
        ),
        types.Tool(
            name="list-containers",
            description="List all Docker containers",
            inputSchema={
                "type": "object",
                "properties": {}
            }
        )
    ]


@server.call_tool()
async def handle_call_tool(name: str, arguments: Dict[str, Any] | None) -> List[types.TextContent]:
    if not arguments and name != "list-containers":
        raise ValueError("Missing arguments")

    try:
        if name == "create-container":
            return await DockerHandlers.handle_create_container(arguments)
        elif name == "deploy-compose":
            return await DockerHandlers.handle_deploy_compose(arguments)
        elif name == "get-logs":
            return await DockerHandlers.handle_get_logs(arguments)
        elif name == "list-containers":
            return await DockerHandlers.handle_list_containers(arguments)
        else:
            raise ValueError(f"Unknown tool: {name}")
    except Exception as e:
        return [types.TextContent(type="text", text=f"Error: {str(e)} | Arguments: {arguments}")]


async def main():
    signal.signal(signal.SIGINT, handle_shutdown)
    signal.signal(signal.SIGTERM, handle_shutdown)

    async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
        await server.run(
            read_stream,
            write_stream,
            InitializationOptions(
                server_name="docker-mcp",
                server_version="0.1.0",
                capabilities=server.get_capabilities(
                    notification_options=NotificationOptions(),
                    experimental_capabilities={},
                ),
            ),
        )


def handle_shutdown(signum, frame):
    print("Shutting down gracefully...")
    sys.exit(0)


if __name__ == "__main__":
    asyncio.run(main())
Download .txt
gitextract_dbxbtxq3/

├── .gitignore
├── .python-version
├── LICENSE
├── README.md
├── pyproject.toml
└── src/
    └── docker_mcp/
        ├── __init__.py
        ├── docker_executor.py
        ├── handlers.py
        └── server.py
Download .txt
SYMBOL INDEX (36 symbols across 4 files)

FILE: src/docker_mcp/__init__.py
  function main (line 4) | def main():

FILE: src/docker_mcp/docker_executor.py
  class CommandExecutor (line 9) | class CommandExecutor(Protocol):
    method execute (line 10) | async def execute(self, cmd: str | List[str]) -> Tuple[int, str, str]:
  class WindowsExecutor (line 14) | class WindowsExecutor:
    method execute (line 15) | async def execute(self, cmd: str) -> Tuple[int, str, str]:
  class UnixExecutor (line 26) | class UnixExecutor:
    method execute (line 27) | async def execute(self, cmd: List[str]) -> Tuple[int, str, str]:
  class DockerExecutorBase (line 37) | class DockerExecutorBase(ABC):
    method __init__ (line 38) | def __init__(self):
    method run_command (line 43) | async def run_command(self, command: str, *args) -> Tuple[int, str, str]:
    method _initialize_docker_cmd (line 46) | def _initialize_docker_cmd(self) -> str:
  class DockerComposeExecutor (line 63) | class DockerComposeExecutor(DockerExecutorBase):
    method __init__ (line 64) | def __init__(self, compose_file: str, project_name: str):
    method run_command (line 69) | async def run_command(self, command: str, *args) -> Tuple[int, str, str]:
    method _build_windows_command (line 76) | def _build_windows_command(self, command: str, *args) -> str:
    method _build_unix_command (line 82) | def _build_unix_command(self, command: str, *args) -> list[str]:
    method down (line 92) | async def down(self) -> Tuple[int, str, str]:
    method pull (line 95) | async def pull(self) -> Tuple[int, str, str]:
    method up (line 98) | async def up(self) -> Tuple[int, str, str]:
    method ps (line 101) | async def ps(self) -> Tuple[int, str, str]:

FILE: src/docker_mcp/handlers.py
  function parse_port_mapping (line 12) | async def parse_port_mapping(host_key: str, container_port: str | int) -...
  class DockerHandlers (line 28) | class DockerHandlers:
    method handle_create_container (line 32) | async def handle_create_container(arguments: Dict[str, Any]) -> List[T...
    method handle_deploy_compose (line 69) | async def handle_deploy_compose(arguments: Dict[str, Any]) -> List[Tex...
    method _process_yaml (line 95) | def _process_yaml(compose_yaml: str, debug_info: List[str]) -> dict:
    method _save_compose_file (line 108) | def _save_compose_file(yaml_content: dict, project_name: str) -> str:
    method _deploy_stack (line 126) | async def _deploy_stack(compose_path: str, project_name: str, debug_in...
    method _cleanup_files (line 155) | def _cleanup_files(compose_path: str) -> None:
    method handle_get_logs (line 166) | async def handle_get_logs(arguments: Dict[str, Any]) -> List[TextConte...
    method handle_list_containers (line 183) | async def handle_list_containers(arguments: Dict[str, Any]) -> List[Te...

FILE: src/docker_mcp/server.py
  function handle_list_prompts (line 15) | async def handle_list_prompts() -> List[types.Prompt]:
  function handle_get_prompt (line 37) | async def handle_get_prompt(name: str, arguments: Dict[str, str] | None)...
  function handle_list_tools (line 94) | async def handle_list_tools() -> List[types.Tool]:
  function handle_call_tool (line 151) | async def handle_call_tool(name: str, arguments: Dict[str, Any] | None) ...
  function main (line 170) | async def main():
  function handle_shutdown (line 189) | def handle_shutdown(signum, frame):
Condensed preview — 9 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (26K chars).
[
  {
    "path": ".gitignore",
    "chars": 157,
    "preview": "# Python-generated files\n__pycache__/\n*.py[oc]\nbuild/\ndist/\nwheels/\n*.egg-info\n\n# Virtual environments\n.venv\nconf\nwork\nR"
  },
  {
    "path": ".python-version",
    "chars": 5,
    "preview": "3.12\n"
  },
  {
    "path": "LICENSE",
    "chars": 1069,
    "preview": "MIT License\n\nCopyright (c) [year] [fullname]\n\nPermission is hereby granted, free of charge, to any person obtaining a co"
  },
  {
    "path": "README.md",
    "chars": 4293,
    "preview": "# 🐳 docker-mcp\n\n[![Python 3.12](https://img.shields.io/badge/python-3.12-blue.svg)](https://www.python.org/downloads/rel"
  },
  {
    "path": "pyproject.toml",
    "chars": 464,
    "preview": "[project]\nname = \"docker-mcp\"\nversion = \"0.1.0\"\ndescription = \"A docker MCP server\"\nreadme = \"README.md\"\nrequires-python"
  },
  {
    "path": "src/docker_mcp/__init__.py",
    "chars": 212,
    "preview": "from . import server\nimport asyncio\n\ndef main():\n    \"\"\"Main entry point for the package.\"\"\"\n    asyncio.run(server.main"
  },
  {
    "path": "src/docker_mcp/docker_executor.py",
    "chars": 3493,
    "preview": "from typing import Tuple, Protocol, List\nimport asyncio\nimport os\nimport platform\nimport shutil\nfrom abc import ABC, abs"
  },
  {
    "path": "src/docker_mcp/handlers.py",
    "chars": 8159,
    "preview": "from typing import List, Dict, Any\nimport asyncio\nimport os\nimport yaml\nimport platform\nfrom python_on_whales import Doc"
  },
  {
    "path": "src/docker_mcp/server.py",
    "chars": 6569,
    "preview": "import asyncio\nimport signal\nimport sys\nfrom typing import List, Dict, Any\nimport mcp.types as types\nfrom mcp.server imp"
  }
]

About this extraction

This page contains the full source code of the QuantGeekDev/docker-mcp GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 9 files (23.8 KB), approximately 5.6k tokens, and a symbol index with 36 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!