docker-mcp
by jhot21
README.md
# docker-mcp
A read-only MCP server for Docker. Exposes your Docker daemon over the [Model Context Protocol](https://modelcontextprotocol.io/) so AI assistants can inspect containers and logs without any write access.
Built with [FastAPI](https://fastapi.tiangolo.com/) and [FastMCP](https://github.com/jlowin/fastmcp).
## Tools
| Tool | Description |
|------|-------------|
| `list_containers` | List running containers (pass `all=true` to include stopped) |
| `get_container` | Full details for a container — ports, networks, env vars, mounts, resource limits |
| `get_logs` | Fetch container logs with filtering by tail/head, time range, and regex pattern |
### `get_logs` parameters
| Parameter | Default | Description |
|-----------|---------|-------------|
| `container_id` | — | Container ID or name |
| `tail` | `100` | Last N lines. Mutually exclusive with `head`. |
| `head` | — | First N lines. Mutually exclusive with `tail`. |
| `since` | — | UTC datetime — only return logs after this time |
| `until` | — | UTC datetime — only return logs before this time |
| `last_minutes` | — | Convenience shorthand: logs from the last N minutes |
| `pattern` | — | Regex (or substring) filter applied to log lines |
| `timestamps` | `true` | Prefix each line with an RFC3339 timestamp |
## Quick start
### Docker run
```bash
docker run -d \
--name docker-mcp \
-p 8000:8000 \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
-e MCP_API_KEY=mysecret \
codeberg.org/jhot/docker-mcp:latest
```
### Docker Compose
```yaml
services:
docker-mcp:
image: codeberg.org/jhot/docker-mcp:latest
ports:
- "8000:8000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
MCP_API_KEY: ${MCP_API_KEY:-}
restart: unless-stopped
```
```bash
MCP_API_KEY=mysecret docker compose up -d
```
## Configuration
| Variable | Default | Description |
|----------|---------|-------------|
| `MCP_API_KEY` | unset | Bearer token for `/mcp`. Unset = no auth. |
| `DOCKER_HOST` | unset | Docker daemon socket. Unset = `/var/run/docker.sock`. |
| `HOST` | `0.0.0.0` | Uvicorn bind address |
| `PORT` | `8000` | Host port (container always listens on 8000) |
| `LOG_LEVEL` | `info` | Uvicorn log level |
## MCP endpoint
The MCP Streamable HTTP endpoint is at `http://localhost:8000/mcp`.
Configure your client (e.g. Claude Desktop, Cursor) to connect to it:
```json
{
"mcpServers": {
"docker": {
"url": "http://localhost:8000/mcp",
"headers": {
"Authorization": "Bearer mysecret"
}
}
}
}
```
Omit `headers` if `MCP_API_KEY` is not set.
## Security
The Docker socket grants full control of the Docker daemon. Treat access to this server the same as access to the socket itself:
- Set `MCP_API_KEY` for any network-accessible deployment.
- Use a firewall or reverse proxy to restrict which clients can reach port 8000.
- The `:ro` socket mount prevents replacing the socket file but does not restrict Docker API calls — `MCP_API_KEY` is the primary access control.
## Remote Docker
To connect to a remote Docker daemon instead of the local socket, remove the `volumes` block and set `DOCKER_HOST`:
```bash
docker run -d \
--name docker-mcp \
-p 8000:8000 \
-e DOCKER_HOST=tcp://192.168.1.10:2376 \
-e MCP_API_KEY=mysecret \
codeberg.org/jhot/docker-mcp:latest
```
## Routes
| Path | Auth | Description |
|------|------|-------------|
| `/` | public | Landing page |
| `/health` | public | `{"status": "ok"}` |
| `/api/tools` | public | Tool metadata list |
| `/mcp` | gated | FastMCP Streamable HTTP endpoint |
## Development
Requirements: [mise](https://mise.jdx.dev/), Python 3.12, Docker
```bash
mise exec -- task install # uv sync
mise exec -- task fmt # ruff format
mise exec -- task lint # ruff check
mise exec -- task test # unit tests (no Docker required)
mise exec -- task test-integration # integration tests (requires live Docker)
mise exec -- task run # dev server at http://localhost:8000
mise exec -- task build # docker build
mise exec -- task up # docker compose up --build
```
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues