mcpdockery
# mcpdockery
An [MCP](https://modelcontextprotocol.io/) server that gives an LLM (Claude, etc.) direct, natural-language control over your local Docker daemon — containers, images, volumes, networks, and Compose stacks.
Built with [FastMCP](https://github.com/modelcontextprotocol/python-sdk) and the [Docker SDK for Python](https://docker-py.readthedocs.io/).
## Table of contents
- [Requirements](#requirements)
- [Installation](#installation)
- [Running the server](#running-the-server)
- [Connecting to an MCP client](#connecting-to-an-mcp-client)
- [Available tools](#available-tools)
- [Usage examples](#usage-examples)
- [Project structure](#project-structure)
- [Safety notes](#safety-notes)
- [License](#license)
## Requirements
| Requirement | Notes |
|---|---|
| [Python](https://www.python.org/) >= 3.14 | Interpreter version pinned in `.python-version` |
| [Docker](https://www.docker.com/) | Docker Desktop or Docker Engine, running locally |
| Docker Compose v2 CLI | `docker compose` must be available on `PATH` — required for the stack/compose tools |
| [Trivy](https://trivy.dev/) | `trivy` must be available on `PATH` — required for the `scan_image` and `scan_dockerfile` tools |
| [Hadolint](https://github.com/hadolint/hadolint) | `hadolint` must be available on `PATH` — required for the `lint_dockerfile` tool |
| [uv](https://docs.astral.sh/uv/) | Used for dependency management and running the server |
For pulling from or pushing to a private registry (Docker Hub, AWS ECR, GCR, etc.), authenticate with that registry beforehand using your normal `docker login` flow — this server never accepts or stores credentials itself.
## Installation
1. Clone the repository:
```bash
git clone <this-repo>
cd mcpdockery
```
2. Install dependencies:
```bash
uv sync
```
This creates a `.venv` and installs the exact dependency versions pinned in `uv.lock`.
3. Confirm Docker is running:
```bash
docker info
```
If this command fails, start Docker Desktop (or your Docker Engine) before continuing.
## Running the server
```bash
uv run src/main.py
```
The server communicates over stdio, so it's meant to be launched by an MCP client rather than run standalone in a terminal.
## Connecting to an MCP client
Add an entry to your MCP client's configuration (e.g. `claude_desktop_config.json` for Claude Desktop, or your project's `.mcp.json` for Claude Code):
```json
{
"mcpServers": {
"mcpdockery": {
"command": "uv",
"args": ["--directory", "/absolute/path/to/mcpdockery", "run", "src/main.py"]
}
}
}
```
Replace `/absolute/path/to/mcpdockery` with the actual path where you cloned the repository, then restart the client. The tools listed below will become available to the model.
## Available tools
### Containers (`containers.py`)
| Tool | Description |
|---|---|
| `run_container` | Runs a container from an image, mapping a container port to a host port |
| `stop_container` | Stops a running container |
| `container_start` | Starts a stopped container |
| `container_restart` | Restarts a container |
| `delete_container` | Force-removes a container (stops it first if needed). Destructive — requires `confirm=True`; the first call only previews what would be deleted |
| `list_containers` | Lists all containers and their status |
| `container_logs` | Fetches the last N log lines from a container. Secret-shaped values (passwords, tokens, API keys) are redacted |
| `container_stats` | Reports live CPU % and memory usage |
| `container_inspect` | Shows env vars, mounts, network IPs, and health status. Secret-shaped env values are redacted |
| `container_exec` | Executes a shell command inside a running container. Secret-shaped values in the output are redacted |
### Images (`images.py`)
| Tool | Description |
|---|---|
| `list_images` | Lists all local images, including untagged/intermediate ones, with size |
| `pull_image` | Pulls an image from a registry without running it; defaults to the `alpine` tag unless a different tag is requested |
| `build_image` | Builds an image from a Dockerfile already on disk |
| `push_image` | Tags and pushes a local image to a registry (requires prior `docker login`) |
| `delete_image` | Force-removes a local image. Destructive — requires `confirm=True`; the first call only previews what would be deleted |
### Volumes (`volumes.py`)
| Tool | Description |
|---|---|
| `list_volumes` | Lists volumes with driver and mountpoint |
| `create_volume` | Creates a new volume |
| `remove_volume` | Deletes a volume (fails if still in use). Destructive — requires `confirm=True`; the first call only previews what would be deleted |
### Networks (`networks.py`)
| Tool | Description |
|---|---|
| `list_networks` | Lists networks with driver and scope |
| `create_network` | Creates a new network |
### Optimization (`optimization.py`)
| Tool | Description |
|---|---|
| `analyze_multistage` | Detects whether a Dockerfile would benefit from a multi-stage build (build-tool commands in a single-stage image); returns reasoning + raw content for the model to draft the rewrite |
### Diagnostics (`diagnostics.py`)
| Tool | Description |
|---|---|
| `docker_doctor` | Scans all containers and reports only the ones needing attention: OOM kills, restart loops, unhealthy checks, crashes, high CPU/memory |
| `check_exposed_ports` | Flags running containers with sensitive ports (databases, admin panels, Docker daemon API) or any port bound to all network interfaces |
### Security (`security.py`)
| Tool | Description |
|---|---|
| `scan_image` | Scans an image for known vulnerabilities using Trivy; defaults to CRITICAL/HIGH severity only |
| `generate_sbom` | Generates a Software Bill of Materials (SBOM) for an image using Trivy, in CycloneDX or SPDX-JSON format |
| `scan_dockerfile` | Scans a Dockerfile for misconfigurations (root user, `latest` tag, hardcoded secrets, missing HEALTHCHECK, etc.) before it's even built |
| `lint_dockerfile` | Lints a Dockerfile with Hadolint for best-practice/style issues (unpinned versions, `ADD` vs `COPY`, missing `--no-install-recommends`, etc.) |
| `audit_dockerfile` | Combined report: `scan_dockerfile` + `lint_dockerfile` + raw file content, so the model can also draft a corrected Dockerfile — use for a general "check my Dockerfile" request |
### Compose stacks (`stacks.py`)
| Tool | Description |
|---|---|
| `deploy_stack` | Deploys a stack from an inline `docker-compose.yml` (`compose up -d`) |
| `stop_stack` | Stops a stack's containers without removing them |
| `remove_stack` | Stops and removes a stack, including its volumes (`compose down -v`). Destructive — requires `confirm=True`; the first call only previews what would be removed |
| `list_stacks` | Lists all compose projects, including stopped ones |
| `stack_status` | Shows the status of a stack's containers (`compose ps`) |
| `stack_logs` | Collects logs from every container in a stack |
## Usage examples
Once connected, you can drive the server with natural-language requests. A few examples of what to expect:
| You ask | Tool(s) the model will likely use |
|---|---|
| "Pull the alpine version of redis" | `pull_image` |
| "Run an nginx container on port 8080" | `run_container` |
| "Show me the logs for my-app from the last hour" | `container_logs` |
| "What's using all the CPU right now?" | `list_containers`, `container_stats` |
| "Is anything broken right now?" | `docker_doctor` |
| "Is anything exposed to the network that shouldn't be?" | `check_exposed_ports` |
| "Deploy this docker-compose file as 'staging'" | `deploy_stack` |
| "Push my-app:latest to my ECR repo" | `push_image` |
| "Clean up the my-app container and its image" | `delete_container`, `delete_image` |
| "Scan my-app:latest for vulnerabilities" | `scan_image` |
| "Generate an SBOM for my-app:latest" | `generate_sbom` |
| "Check my Dockerfile for security issues before I build it" | `scan_dockerfile` |
| "Lint my Dockerfile for best practices" | `lint_dockerfile` |
| "Check/review my Dockerfile" | `audit_dockerfile` |
| "Should this Dockerfile use multi-stage builds?" | `analyze_multistage` |
The model chooses which tool(s) to call based on your request — you don't need to name the tool yourself.
## Project structure
```
src/
main.py # Entrypoint: registers tool modules and starts the MCP server
server.py # Shared FastMCP server instance
docker_client.py # Lazy singleton Docker SDK client
compose_client.py # Thin wrapper around the `docker compose` CLI
helper.py # Shared helpers (path normalization, image tag parsing, Trivy wrapper)
containers.py # Container lifecycle & inspection tools
images.py # Image pull/build/push/list/delete tools
volumes.py # Volume tools
networks.py # Network tools
stacks.py # Compose stack tools
security.py # Image/Dockerfile vulnerability & misconfiguration scanning tools
diagnostics.py # Cross-container health triage tools
optimization.py # Dockerfile efficiency analysis tools
```
## Safety notes
This server gives the model real, unsandboxed control over your Docker daemon:
- `delete_container`, `delete_image`, `remove_volume`, and `remove_stack` are destructive and require an explicit `confirm=True` argument. The first call (confirm defaults to `False`) performs no action and only returns a preview of what would be deleted — the model is instructed to only pass `confirm=True` after you've explicitly agreed in the conversation. This is a safety net against a misread request, not a hard permission system: any client with tool access can still pass `confirm=True` directly.
- `remove_stack` deletes volumes (`-v`), which is destructive and irreversible for stateful data.
- `container_exec` runs arbitrary shell commands inside a container.
- `container_logs`, `container_exec`, and `container_inspect` redact values that look like secrets (keys matching PASSWORD/TOKEN/API_KEY/etc., in `KEY=value`, `KEY: value`, or `"key": "value"` form) before returning them. This is a best-effort heuristic, not a guarantee — anything that doesn't match the pattern (or that a container prints in an unusual format) is returned as-is, and remember that tool output is sent to the model provider as part of the conversation regardless of how "local" the Docker daemon is.
- `push_image` and `pull_image` use your existing local Docker credentials — the model can push to or pull from any registry you're currently authenticated with. Note that AWS ECR tokens expire after 12 hours; if a push/pull suddenly fails with an auth error, re-run your `docker login` / `aws ecr get-login-password` flow rather than assuming the tool is broken.
- The Docker socket grants root-equivalent access to the host. Giving a model tool access to this server is equivalent to giving it that level of access to your machine, whether or not the daemon is reachable over the network.
Only connect this server to clients/agents you trust, and be deliberate about which containers and stacks you let it touch.
## License
No license specified.
TDQS
Scored across 34 tools
Most tools have clear, distinct purposes (e.g., container_logs vs. container_stats vs. docker_doctor), but a few overlap in intent (scan_dockerfile vs. lint_dockerfile vs. audit_dockerfile) and could confuse an agent if descriptions are not read carefully.
Naming is inconsistent: some tools use object_verb (container_logs, container_restart), others use verb_object (stop_container, delete_container), and some have no clear pattern (docker_doctor, generate_sbom). This makes it harder to predict tool names.
With 34 tools, the server is heavy for a typical MCP, but it covers a broad Docker domain comprehensively. Each tool has a distinct function, so the count is justified, though it exceeds the typical 3-15 range.
The toolset covers all major Docker operations: container lifecycle, image management, Dockerfile analysis, volumes, networks, and health/security checks. No obvious gaps for common Docker workflows.