# Docker MCP Server (oci · ghcr.io/l337-org/docker-mcp-server:2.2.6)

Manage Docker (containers, images, Compose, Swarm, registries) via the Docker SDK and CLI.

- Trust score: 41/100 (low)
- Change this week: +5
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-09-21

## Components

- mcpb · `docker-mcp-server-2.2.6.mcpb`: 14/100, [markdown](https://verifymcp.io/servers/l337-org-docker-mcp-server/https-github-com-l337-org-docker-mcp-releases-download-v2-2-6-docker-mcp-server.md), [page](https://verifymcp.io/servers/l337-org-docker-mcp-server/https-github-com-l337-org-docker-mcp-releases-download-v2-2-6-docker-mcp-server)
- oci · `ghcr.io/l337-org/docker-mcp-server:2.2.6`: 41/100 (this document), [markdown](https://verifymcp.io/servers/l337-org-docker-mcp-server/ghcr-io-l337-org-docker-mcp-server-2-2-6.md), [page](https://verifymcp.io/servers/l337-org-docker-mcp-server/ghcr-io-l337-org-docker-mcp-server-2-2-6)
- pypi · `docker-mcp-server`: 37/100, [markdown](https://verifymcp.io/servers/l337-org-docker-mcp-server/docker-mcp-server.md), [page](https://verifymcp.io/servers/l337-org-docker-mcp-server/docker-mcp-server)

## Channel facts

- Registry: `oci`
- Package: `ghcr.io/l337-org/docker-mcp-server:2.2.6`
- Transport: `stdio`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-09-21.

- **Supply Chain Security**: 0/100
  - Malware scan not yet available for this package.
  - Known CVEs could not be checked: this artifact ships no SBOM, so there is no dependency list to read. Publishing one would let us assess it.
  - Install-script risk not yet assessed.
  - Dependency health could not be checked: this artifact ships no SBOM, so there is no dependency list to read. Publishing one would let us assess it.
- **Provenance & Transparency**: 35/100
  - Source repository is publicly reachable at the declared URL.
  - Provenance check failed: no build-provenance attestation is published.
  - License check failed: no license is declared.
  - Actively maintained (last published 10 days ago).
  - Publishes a security disclosure policy (SECURITY.md).
- **Schema Quality & AI Usability**: 75/100
  - 100% of prompts and resources have a non-trivial description (not blank, and not just the item's name).
  - AI-judged instruction clarity (good).
  - Context-footprint check failed: tool/resource definitions use about 40054 tokens (~238/item across 168 items; 165 tools + 3 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 37/100
  - Stability observed for 11 of 30 days with no destabilising changes; credit accrues until the full window elapses.
- **Tool Coverage**: 71/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 0% of tool parameters carry a description.
  - Structured output schemas are declared (23% of tools); any adoption earns full credit.
- **Tool Safety**: 100/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - All 17 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation.
  - An AI judge read all 167 captured unit(s) of tool text and found none that tries to manipulate the model reading it.
- **Capabilities**: 100/100
  - Implements a current MCP spec version (2026-07-28).

**Unverified: 1 category.** A category scored 0 because we could not verify it: a data source with nothing on this package, evidence we could not reach, or a check we could not run. We only credit what we can confirm.

## Install

### How do I install the Docker MCP Server server?

Docker MCP Server runs locally as a container image, launched with docker run --rm -i ghcr.io/l337-org/docker-mcp-server:2.2.6. Ready-made configuration for Claude, Cursor, VS Code, Codex and 3 more is on this page, copied from each client's own documentation.

### Claude

```bash
claude mcp add l337-org-docker-mcp-server -- docker run --rm -i ghcr.io/l337-org/docker-mcp-server:2.2.6
```

### Cursor

```json
{
  "mcpServers": {
    "l337-org-docker-mcp-server": {
      "command": "docker",
      "args": [
        "run",
        "--rm",
        "-i",
        "ghcr.io/l337-org/docker-mcp-server:2.2.6"
      ]
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "l337-org-docker-mcp-server": {
      "command": "docker",
      "args": [
        "run",
        "--rm",
        "-i",
        "ghcr.io/l337-org/docker-mcp-server:2.2.6"
      ]
    }
  }
}
```

### Codex

```bash
codex mcp add l337-org-docker-mcp-server -- docker run --rm -i ghcr.io/l337-org/docker-mcp-server:2.2.6
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "l337-org-docker-mcp-server": {
      "type": "local",
      "command": [
        "docker",
        "run",
        "--rm",
        "-i",
        "ghcr.io/l337-org/docker-mcp-server:2.2.6"
      ],
      "enabled": true
    }
  }
}
```

### Hermes

```yaml
mcp_servers:
  l337-org-docker-mcp-server:
    command: "docker"
    args: ["run", "--rm", "-i", "ghcr.io/l337-org/docker-mcp-server:2.2.6"]
```

### Netclaw

```json
{
  "McpServers": {
    "l337-org-docker-mcp-server": {
      "Transport": "stdio",
      "Command": "docker",
      "Arguments": [
        "run",
        "--rm",
        "-i",
        "ghcr.io/l337-org/docker-mcp-server:2.2.6"
      ]
    }
  }
}
```

### Other

```json
{
  "mcpServers": {
    "l337-org-docker-mcp-server": {
      "command": "docker",
      "args": [
        "run",
        "--rm",
        "-i",
        "ghcr.io/l337-org/docker-mcp-server:2.2.6"
      ]
    }
  }
}
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-09-20 (score 41, +1)

No change was recorded against any check on this day. Stability & Change Management went from 30 to 33. That category is still filling its 30-day observation window: 9 days of observed history at the previous scan, 10 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-19 (score 40, +4)

- [functional improvement] Stability: unverified → 0.30

### 2026-09-10 (score 36)

First indexed and scored.

## MCP tools (165)

### `buildx_build` (~1062 tokens)

Build an image with BuildKit via `docker buildx build`.

Replaces the legacy `image_build` tool when you need any of: multi-platform output
(`platforms`), modern cache export (`cache_from`/`cache_to`), SBOM or provenance
attestations, build secrets, or multi-stage builds with `target`. Always runs with
\`--progress=plain` so output is captured rather than redrawn on a TTY.
With no local buildx plugin and an `ssh://` target, the build runs on that host: a local `context`
directory is copied there honouring `.dockerignore`, as are `file`, `build_contexts` and `secret`
paths. Raises ToolInputError in that case for `output`/`cache_to` with a filesystem `dest=`,
\`cache_from` with a local `src=`, or any `ssh=` - each would resolve on the remote machine, losing
the output or silently changing the build.
Does not raise on a non-zero CLI exit (a missing buildx plugin or a timeout still raises) - inspect
\`returncode`/`stderr` in the result.

Args:
    context: Build context: a filesystem path or Git/HTTP URL (verbatim; no `~`/glob expansion). The `-`
        stdin-tarball form is NOT supported (stdin isn't forwarded - it'd block on the server's own stdin); serve a
        pre-packed tarball over HTTP instead. Copied to the target host when it names a local directory and there is
        no local plugin.
    tags: Image references to apply (`-t`, repeatable)
    platforms: Target platforms, e.g. ["linux/amd64", "linux/arm64"]
    file: Dockerfile path. A relative path resolves against this server's working directory (buildx's own rule), NOT
        against `context` - pass e.g. "ctx/Dockerfile" for a Dockerfile inside the context directory "ctx".
    build_args: Build-time variables (each becomes `--build-arg KEY=VALUE`)
    build_contexts: Additional named build contexts (e.g. {"deps": "./vendor"})
    labels: Labels to set on the resulting image (each becomes `--label KEY=VALUE`)
    annotations: OCI manifest annotations (passed verbatim, repeatable)
    target: Target buil…

Input parameters:

- `annotations` (array)
- `attest` (array)
- `build_args` (object)
- `build_contexts` (object)
- `builder` (string)
- `cache_from` (array)
- `cache_to` (array)
- `context` (string, required)
- `file` (string)
- `labels` (object)
- `load` (boolean)
- `no_cache` (boolean)
- `no_cache_filter` (array)
- `output` (array)
- `platforms` (array)
- `provenance` (string)
- `pull` (boolean)
- `push` (boolean)
- `sbom` (string)
- `secret` (array)
- `ssh` (array)
- `tags` (array)
- `target` (string)
- `timeout_seconds` (number)

### `buildx_bake` (~341 tokens)

Build multiple targets defined in a bake file (HCL, JSON, or compose).

Use it for multi-target builds declared in `docker-bake.hcl`/compose files; for a single
Dockerfile target use `buildx_build`.
Does not raise on a non-zero CLI exit (a missing buildx plugin or a timeout still raises) - inspect
\`returncode`/`stderr` in the result.

Args:
    targets: Bake targets to build (default: the `default` group)
    files: Bake file paths (`-f`, repeatable)
    set_overrides: Per-target overrides, e.g. ["app.platform=linux/amd64"]
    push: Push results to the registry
    load: Load results into the local image store
    no_cache: Do not use cache when building
    pull: Always pull a newer base image
    builder: Override the active builder
    cwd: Working directory containing the bake file (defaults to the server's cwd; copied to the target host if no
        local plugin)
    timeout_seconds: Subprocess timeout (default 1800s)

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}

Input parameters:

- `builder` (string)
- `cwd` (string)
- `files` (array)
- `load` (boolean)
- `no_cache` (boolean)
- `pull` (boolean)
- `push` (boolean)
- `set_overrides` (array)
- `targets` (array)
- `timeout_seconds` (number)

### `buildx_imagetools_inspect` (~306 tokens)

Inspect a manifest in a registry without pulling.

Replaces `docker manifest inspect`. The standalone `docker manifest` command is in
maintenance mode and lacks support for OCI image indexes, attestations, and
annotations - `buildx imagetools inspect` is the path forward and handles both
single-platform manifests and multi-platform manifest lists / OCI indexes. Uses the docker
CLI's credential store; `registry_manifest` answers the same question over direct HTTPS
with no daemon or plugin.
Does not raise on a non-zero CLI exit (a missing buildx plugin or a timeout still raises) - inspect
\`returncode`/`stderr` in the result.

Args:
    image: Image reference, e.g. "alpine:3.19" or "ghcr.io/org/repo@sha256:..."
    raw: Return the raw manifest bytes (a JSON document) instead of the human-rendered tree
    format: Go template format string (mutually exclusive with `raw`)
    builder: Override the active builder

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}. With `raw=True` or
        `format="{{json .}}"`, `stdout` is a JSON document the caller can parse.

Input parameters:

- `builder` (string)
- `format` (string)
- `image` (string, required)
- `raw` (boolean)

### `buildx_imagetools_create` (~330 tokens)

Create a manifest list / OCI image index from existing per-platform tags.

Replaces `docker manifest create` + `docker manifest push` - builds the index and pushes it in
one operation. Source tags must already be pushed; this only stitches them together. Verify
the result with `buildx_imagetools_inspect`.
Does not raise on a non-zero CLI exit (a missing buildx plugin or a timeout still raises) - inspect
\`returncode`/`stderr` in the result.

Args:
    target: Tag for the new manifest list (`-t`)
    sources: Source image references to combine
    append: Append to the existing manifest at `target` rather than replacing
    dry_run: Print the resulting manifest without pushing
    annotations: OCI annotations (repeatable; passed verbatim)
    platforms: Filter source platforms when combining
    descriptor_files: Files to read source descriptors from, instead of refs (copied to the target host if no local
        plugin)
    builder: Override the active builder
    timeout_seconds: Subprocess timeout (default 600s)

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}

Input parameters:

- `annotations` (array)
- `append` (boolean)
- `builder` (string)
- `descriptor_files` (array)
- `dry_run` (boolean)
- `platforms` (array)
- `sources` (array, required)
- `target` (string, required)
- `timeout_seconds` (number)

### `buildx_list` (~105 tokens)

List builder instances.

Machine-parsed view of every builder; use `buildx_inspect` for one builder's human-readable
detail and `buildx_use` to switch the default.
Raises RemoteFailureError if the CLI call fails.

Returns:
    list: One dict per builder (parsed from `--format '{{json .}}'`). If the captured stdout was truncated by
        MAX_CLI_OUTPUT_BYTES the last (likely partial) record is dropped before parsing.

### `buildx_history_list` (~162 tokens)

List recent build records (BuildKit build history), parsed from `--format '{{json .}}'`.

Each record is a past build with its ref, name, status, step counts, and timestamps - useful for
finding a build to drill into with `buildx_history_inspect`. Requires buildx >= v0.13 (older
versions have no `history` subcommand and this raises with the CLI's "unknown command" error).
Raises RemoteFailureError if the CLI call fails.

Args:
    builder: Builder instance to read history from (defaults to the active builder)

Returns:
    list: One dict per build record (ref, name, status, total/completed/cached steps, times)

Input parameters:

- `builder` (string)

### `buildx_history_inspect` (~250 tokens)

Inspect a single build record by ref, parsed from `--format json`.

Returns the full record for one build - duration, materials, attestations, error (if any) -
for debugging a failed or slow build found via `buildx_history_list`. Requires buildx >=
v0.13.
Raises RemoteFailureError if the CLI call fails.

Args:
    ref: Build record ref. Pass the `ref` field from `buildx_history_list` directly - it reports a qualified
        "<builder>/<node>/<id>", but `history inspect` only accepts the bare id, so this reduces it to the id and
        (unless `builder` is given) targets the builder named in the ref. Empty/omitted inspects the most recent
        build; the `^N` syntax (e.g. "^0" = latest) is also valid.
    builder: Builder instance the build ran on (defaults to the one in `ref`, else active)

Returns:
    dict: The parsed build record (or {"raw": <stdout>} if the output isn't a JSON object)

Input parameters:

- `builder` (string)
- `ref` (string)

### `buildx_inspect` (~167 tokens)

Inspect a builder instance.

Human-readable detail (driver, status, supported platforms) for one builder; `buildx_list`
returns machine-parsed JSON for all builders.
Does not raise on a non-zero CLI exit (a missing buildx plugin or a timeout still raises) - inspect
\`returncode`/`stderr` in the result.

Args:
    name: Builder name (defaults to the active builder)
    bootstrap: Boot the builder if it isn't already running

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}. stdout is human-readable; parse with
        the agent or call buildx_list for JSON.

Input parameters:

- `bootstrap` (boolean)
- `name` (string)

### `buildx_du` (~163 tokens)

Report BuildKit cache disk usage as a list of records.

A large cache can easily generate more output than MAX_CLI_OUTPUT_BYTES; if that happens the
captured stdout is truncated and this tool drops the final (partial) record before parsing.
For an exhaustive accounting on a busy builder, run `docker buildx du --format '{{json .}}'`
on the host directly. Reclaim the cache with `buildx_prune` (`system_df` covers daemon-side
disk, not builder cache).
Raises RemoteFailureError if the CLI call fails.

Args:
    builder: Override the active builder

Returns:
    list: One dict per cache record (parsed from `--format '{{json .}}'`)

Input parameters:

- `builder` (string)

### `buildx_prune` (~285 tokens)

Remove BuildKit cache entries.

Destructive: this tool always passes `--force` because no interactive prompt is
available under MCP. Pair with `buildx_du` first to inventory what would be removed.
Does not raise on a non-zero CLI exit (a missing buildx plugin or a timeout still raises) - inspect
\`returncode`/`stderr` in the result.

Args:
    all: Include internal/frontend images
    filters: Filter by attributes (e.g. {"until": "24h", "type": "exec.cachemount"})
    reserved_space: Amount of disk to always keep (e.g. "10GB")
    max_used_space: Maximum disk space the cache may use (e.g. "20GB")
    min_free_space: Target amount of free disk after pruning (e.g. "5GB")
    builder: Override the active builder
    timeout_seconds: Subprocess timeout (default 600s)

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}

Input parameters:

- `all` (boolean)
- `builder` (string)
- `filters` (object)
- `max_used_space` (string)
- `min_free_space` (string)
- `reserved_space` (string)
- `timeout_seconds` (number)

### `buildx_create` (~385 tokens)

Create a new BuildKit builder instance.

Needed when the default `docker` driver falls short: multi-platform builds and cache export
require a `docker-container` (or `kubernetes`/`remote`) builder. Pass `use=True` to make it
the default for later `buildx_build` calls (else switch with `buildx_use`); `bootstrap=True`
starts the builder now rather than on first build.
Does not raise on a non-zero CLI exit (a missing buildx plugin or a timeout still raises) - inspect
\`returncode`/`stderr` in the result.

Args:
    name: Name for the new builder (defaults to a generated name)
    driver: BuildKit driver (e.g. "docker-container", "kubernetes", "remote")
    driver_opts: Driver-specific options (each becomes `--driver-opt KEY=VALUE`)
    use: Set the new builder as the current one
    bootstrap: Boot the builder immediately
    platforms: Platforms the builder advertises
    config: Path to a buildkitd config file (copied to the target host if no local plugin); passed as
        `--buildkitd-config`, so this argument needs buildx >= 0.17
    node_name: Node name within the builder (for multi-node builders)
    append: Append a node to an existing builder named `name`

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}

Input parameters:

- `append` (boolean)
- `bootstrap` (boolean)
- `config` (string)
- `driver` (string)
- `driver_opts` (object)
- `name` (string)
- `node_name` (string)
- `platforms` (array)
- `use` (boolean)

### `buildx_use` (~235 tokens)

Select the active builder for subsequent buildx operations.

Without `default` or `global_default` the switch applies only to the current CLI
session. `default` persists the choice for the current Docker context; `global_default`
persists across all Docker contexts. Use `buildx_list` to see available builders and their
current status. To avoid switching the global default, pass a specific builder name
directly via `buildx_build`'s `builder` parameter instead.
Does not raise on a non-zero CLI exit (a missing buildx plugin or a timeout still raises) - inspect
\`returncode`/`stderr` in the result.

Args:
    name: Builder name to activate (from `buildx_list`)
    default: Persist as default builder for the current Docker context
    global_default: Persist as default builder across all Docker contexts

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}

Input parameters:

- `default` (boolean)
- `global_default` (boolean)
- `name` (string, required)

### `buildx_remove` (~218 tokens)

Remove a builder instance.

Deletes a builder made by `buildx_create`, including its build cache unless keep_state=True;
use `buildx_prune` to reclaim cache while keeping the builder.
Does not raise on a non-zero CLI exit (a missing buildx plugin or a timeout still raises) - inspect
\`returncode`/`stderr` in the result.

Args:
    name: Builder name to remove (mutually exclusive with `all_inactive`)
    all_inactive: Remove every inactive builder
    keep_state: Keep the BuildKit state volume
    keep_daemon: Keep the BuildKit daemon process running
    force: Force removal even if the builder is in use

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}

Input parameters:

- `all_inactive` (boolean)
- `force` (boolean)
- `keep_daemon` (boolean)
- `keep_state` (boolean)
- `name` (string)

### `system_ping` (~84 tokens)

Check that the Docker server is responsive.

The cheapest daemon health check. A failure here usually means connection config rather than
daemon load - `system_reconnect` rebuilds a wedged client, `host_list` shows the configured
endpoints. For daemon details use `system_version` / `system_info`.

Returns:
    bool: True if the daemon responded successfully

Output parameters:

- `result` (boolean)

### `system_version` (~82 tokens)

Return Docker server version information.

Engine version, API level, and per-component versions - the first thing to check for feature
availability. `system_info` reports runtime state (counts, drivers, swarm role) instead.

Returns:
    dict: {"Version", "ApiVersion", "MinAPIVersion", "Os", "Arch", "Components", ...}

### `system_info` (~86 tokens)

Return system-wide Docker information, like `docker info`.

Daemon runtime state: container/image counts, storage and logging drivers, swarm role, and
daemon warnings. Use `system_version` for version/API level and `system_df` for disk usage.

Returns:
    dict: {"Containers", "Images", "Driver", "ServerVersion", "Swarm", "Warnings", ...}

### `system_df` (~134 tokens)

Summarize Docker disk usage: layer storage plus per-object sizes for images, containers, volumes, build cache.

Equivalent to `docker system df`. Use it to find what to reclaim before `image_prune` /
\`container_prune` / `volume_prune` / `buildx_prune`; use `system_info` for daemon config and
counts rather than sizes. The reply enumerates every object on the daemon, so expect a large
payload on busy hosts.

Returns:
    dict: {"LayersSize", "Images", "Containers", "Volumes", "BuildCache"} with per-object size fields

### `host_list` (~174 tokens)

List the Docker hosts configured via DOCKER_MCP_SERVER_HOSTS.

With a single host (or the var unset) this is the one resolved daemon; with several it is
the set that the `host` argument selects from. The `default` entry is the one used when
\`host` is omitted; pass a `name` as the `host` argument of daemon-backed tools
(`system_ping(host=...)` checks one entry). The `docker-mcp://hosts` resource mirrors this
tool.

Returns:
    list[dict]: one per host: name; url (resolved daemon URL, null = docker-py platform default); read_only;
        non_destructive (blocks destructive calls only); tls (whether a per-host cert dir is configured); default
        (the omitted-host fallback)

Output parameters:

- `result` (array)

### `system_login` (~234 tokens)

Authenticate with a Docker registry.

Security: the password is sent as a tool argument, which many MCP clients log verbatim. Prefer
running `docker login` once on the host so the `docker` module reuses the credentials cached in
\`~/.docker/config.json`, and avoid calling this tool from an agent loop. Credentials let
\`image_pull` / `image_push` reach private repositories; `system_logout` clears them.

Args:
    username: Registry username
    password: Registry password or token
    email: Registry account email
    registry: URL to the registry (defaults to Docker Hub)
    reauth: Force re-authentication even if valid credentials exist
    dockercfg_path: Path to a custom dockercfg file

Returns:
    dict: The login response: {"Status"} always; "IdentityToken" only when the registry issues one

Input parameters:

- `dockercfg_path` (string)
- `email` (string)
- `password` (string, required)
- `reauth` (boolean)
- `registry` (string)
- `username` (string, required)

### `system_logout` (~210 tokens)

Clear cached registry credentials from this server's in-memory Docker client.

docker-py / the Engine have no true logout: `system_login` validates against the registry (the daemon's
\`/auth` is stateless) and caches credentials in-process. This drops that in-memory cache; it does
NOT contact the daemon or touch the host's `~/.docker/config.json`. With no `registry`, clears
every cached credential; pass one to clear just that entry (key must match `system_login`; Docker Hub
is cached under "docker.io"). `system_close`/`system_reconnect` also clear it by discarding the client.

Reaches into a private docker-py attribute (`api._auth_configs`); degrades to clearing nothing if
that internal shape changes.

Args:
    registry: Registry key to clear, or None to clear every cached credential

Returns:
    dict: {"cleared": [<registry keys removed>]}

Input parameters:

- `registry` (string)

### `system_events` (~352 tokens)

Stream real-time events from the Docker server, bounded by `limit` events or `timeout_seconds`.

Returns when `limit` events are collected or `timeout_seconds` elapses, whichever comes first
(`limit` caps memory; `timeout_seconds` caps how long the call blocks - without it a quiet daemon
would block indefinitely, since the stream only yields on an actual event).

Caveat for `ssh://` daemons: docker-py can't cancel an SSH stream, so the `timeout_seconds`
watchdog can't interrupt a fully idle stream - bound with `until`/`limit` (or a non-SSH endpoint).

"Wait for the next matching event" idiom: pass `limit=1` with `filters` narrowed to what you
care about (e.g. `{"type": "container", "event": "health_status"}`) and a generous
\`timeout_seconds`. This blocks until that one event arrives (or the timeout elapses, returning an
empty list) instead of re-polling a snapshot on a timer - there's no separate wait tool for this
since the filtering this call already does covers it.

Args:
    since: Show events created since this timestamp
    until: Show events created until this timestamp
    filters: Filters to apply to the event stream
    limit: Max events to return
    timeout_seconds: Max wall-clock seconds before returning what was collected

Returns:
    list: A list of decoded event dicts (length <= limit)

Input parameters:

- `filters` (object)
- `limit` (integer)
- `since` (string)
- `timeout_seconds` (number)
- `until` (string)

### `system_close` (~115 tokens)

Close and drop pooled Docker client connection(s); each is rebuilt lazily on next use.

Use this to force a stale or errored connection to be discarded. Prefer `system_reconnect` when you
want to immediately re-establish the connection rather than wait for the next tool call to
trigger a lazy rebuild. With `host` omitted every pooled client is closed (unlike other tools,
where omitting it means the default host). Closing clients does not affect running containers.

Returns:
    bool: True once closed

Output parameters:

- `result` (boolean)

### `system_reconnect` (~146 tokens)

Rebuild a pooled Docker client from its configured endpoint, to recover a wedged connection.

Validates the rebuilt client before swapping in (and only then closes the old one), so a failed
rebuild leaves the working client in place. Rebuilds the default host's client when `host` is
omitted. It CANNOT retarget to a different daemon - to add or change a daemon, edit
DOCKER_MCP_SERVER_HOSTS and restart. `system_close` closes pooled clients without rebuilding;
\`host_list` shows the configured endpoints.

Returns:
    dict: the rebuilt host's version info (same shape as `system_version`), confirming connectivity

### `compose_up` (~333 tokens)

Bring up a Docker Compose project, detached.

Always runs detached (`-d`) so it can't block the server. Use `compose_ps` to confirm
services are running, or `wait=True` to block until they're healthy.
Does not raise on a non-zero CLI exit (a missing compose plugin or a timeout still raises) - inspect
\`returncode`/`stderr` in the result.

Args:
    project_dir: Dir with the compose file (default: server cwd, copied to the target host if no local plugin; paths
        verbatim, no shell expansion)
    files: Explicit compose file paths (repeatable, `-f`)
    project_name: Compose project name override
    services: Specific services to bring up (default: all)
    build: Build images before starting
    pull: Pull strategy; omit to use each service's own `pull_policy`
    remove_orphans: Remove containers for services not in the compose file
    wait: Block until services are healthy (adds `--wait`)
    timeout_seconds: Subprocess timeout (default 600s)

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}

Input parameters:

- `build` (boolean)
- `files` (array)
- `profiles` (array)
- `project_dir` (string)
- `project_name` (string)
- `pull` (string)
- `remove_orphans` (boolean)
- `services` (array)
- `timeout_seconds` (number)
- `wait` (boolean)

### `compose_down` (~267 tokens)

Stop and remove containers, networks (and optionally volumes) for a compose project.

Inverse of `compose_up`. Images are kept; named volumes go only with volumes=True
(destructive). Use `compose_stop` to stop without removing anything.
Does not raise on a non-zero CLI exit (a missing compose plugin or a timeout still raises) - inspect
\`returncode`/`stderr` in the result.

Args:
    project_dir: Dir with the compose file (default: server cwd; copied to the target host if no local plugin)
    files: Explicit compose file paths (repeatable, `-f`)
    project_name: Compose project name override
    volumes: Also remove named volumes declared by the project (destructive)
    remove_orphans: Remove containers not declared in the compose file
    timeout_seconds: Subprocess timeout (default 300s)

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}

Input parameters:

- `files` (array)
- `profiles` (array)
- `project_dir` (string)
- `project_name` (string)
- `remove_orphans` (boolean)
- `timeout_seconds` (number)
- `volumes` (boolean)

### `compose_ps` (~237 tokens)

List containers in a compose project, parsed from `--format json`.

Container-level view of one project (state, health, publishers); `compose_list` enumerates
projects, and `container_list` covers non-compose containers.
Does not raise on a non-zero CLI exit (a missing compose plugin or a timeout still raises): `services`
comes back empty - inspect `raw.stderr`.

Args:
    project_dir: Dir with the compose file (default: server cwd; copied to the target host if no local plugin)
    files: Explicit compose file paths (repeatable, `-f`)
    project_name: Compose project name override
    services: Restrict output to these services
    all: Include stopped containers as well

Returns:
    dict: {"services": list[dict], "raw": <CliResult dict>}; on non-zero exit `services` is an empty list and the
        caller should inspect `raw.stderr`.

Input parameters:

- `all` (boolean)
- `files` (array)
- `project_dir` (string)
- `project_name` (string)
- `services` (array)

### `compose_logs` (~321 tokens)

Fetch a bounded slice of logs from a compose project (never follows).

Bounded and non-following by design, so it always returns promptly. For one container's logs
use `container_logs`; for a swarm service use `service_logs`. Log text arrives on `stdout`.
Does not raise on a non-zero CLI exit (a missing compose plugin or a timeout still raises) - inspect
\`returncode`/`stderr` in the result.

Args:
    project_dir: Dir with the compose file (default: server cwd; copied to the target host if no local plugin)
    files: Explicit compose file paths (repeatable, `-f`)
    project_name: Compose project name override
    services: Restrict to these services (default: all)
    tail: Lines per container, or the literal "all" (still capped at MAX_CLI_OUTPUT_BYTES)
    since: Show logs since this timestamp/duration (e.g. "10m", "2024-01-01T00:00:00")
    until: Show logs before this timestamp/duration
    timestamps: Include per-line timestamps

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}

Input parameters:

- `files` (array)
- `project_dir` (string)
- `project_name` (string)
- `services` (array)
- `since` (string)
- `tail`
- `timestamps` (boolean)
- `until` (string)

### `compose_config` (~256 tokens)

Render the canonical compose configuration after merges, profiles, and variable substitution.

Use it to validate compose files and see exactly what the CLI will run before `compose_up`.
Does not raise on a non-zero CLI exit (a missing compose plugin or a timeout still raises): on a failed
render `config` may be None - inspect `raw.stderr`.

Args:
    project_dir: Dir with the compose file (default: server cwd; copied to the target host if no local plugin)
    files: Explicit compose file paths (repeatable, `-f`)
    project_name: Compose project name override
    profiles: Profiles to activate before rendering
    services_only: List service names only (`--services`)
    format: Render as YAML (default) or JSON

Returns:
    dict: {"config": str|dict|None, "raw": <CliResult dict>}; `config` is a parsed dict when format="json" and
        parsing succeeds, otherwise the rendered text from stdout.

Input parameters:

- `files` (array)
- `format` (string)
- `profiles` (array)
- `project_dir` (string)
- `project_name` (string)
- `services_only` (boolean)

### `compose_build` (~260 tokens)

Build images for a compose project.

Builds the images declared by the project's `build:` sections without starting anything -
\`compose_up(build=True)` builds and starts in one step.
Does not raise on a non-zero CLI exit (a missing compose plugin or a timeout still raises) - inspect
\`returncode`/`stderr` in the result.

Args:
    project_dir: Dir with the compose file (default: server cwd; copied to the target host if no local plugin)
    files: Explicit compose file paths (repeatable, `-f`)
    project_name: Compose project name override
    services: Specific services to build (default: all)
    pull: Always attempt to pull a newer base image
    no_cache: Do not use cache when building
    timeout_seconds: Subprocess timeout (default 1800s)

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}

Input parameters:

- `files` (array)
- `no_cache` (boolean)
- `project_dir` (string)
- `project_name` (string)
- `pull` (boolean)
- `services` (array)
- `timeout_seconds` (number)

### `compose_pull` (~309 tokens)

Pre-fetch images for a compose project's services without starting them.

Use this to stage images before an outage window, to refresh cached images before
\`compose_up`, or to verify images are accessible without starting containers. For
registry-authenticated pulls ensure the daemon is logged in first with `system_login`.
\`compose_up --pull always` does the same as part of startup; use this tool when you
want to separate the pull step.
Does not raise on a non-zero CLI exit (a missing compose plugin or a timeout still raises) - inspect
\`returncode`/`stderr` in the result.

Args:
    project_dir: Dir with the compose file (default: server cwd; copied to the target host if no local plugin)
    files: Explicit compose file paths (repeatable, `-f`; overrides auto-discovery)
    project_name: Override the compose project name
    services: Pull only these services; omit to pull all
    ignore_pull_failures: Continue if an individual image pull fails
    timeout_seconds: Subprocess timeout (default 1800s for large image pulls)

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}

Input parameters:

- `files` (array)
- `ignore_pull_failures` (boolean)
- `project_dir` (string)
- `project_name` (string)
- `services` (array)
- `timeout_seconds` (number)

### `compose_restart` (~301 tokens)

Stop then start services without recreating containers or applying config changes.

Use this to bounce a service (e.g. to pick up a runtime file change or clear an
in-memory state). If the compose file has changed (new image, environment, volumes,
ports) use `compose_up` instead - it recreates affected containers to apply the diff.
\`stop_timeout_seconds` controls the SIGTERM grace period before Docker sends SIGKILL.
Does not raise on a non-zero CLI exit (a missing compose plugin or a timeout still raises) - inspect
\`returncode`/`stderr` in the result.

Args:
    project_dir: Dir with the compose file (default: server cwd; copied to the target host if no local plugin)
    files: Explicit compose file paths (repeatable, `-f`)
    project_name: Override the compose project name
    services: Restart only these services; omit to restart all
    stop_timeout_seconds: Seconds to wait for graceful stop before SIGKILL
    timeout_seconds: Subprocess timeout (default 300s)

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}

Input parameters:

- `files` (array)
- `project_dir` (string)
- `project_name` (string)
- `services` (array)
- `stop_timeout_seconds` (integer)
- `timeout_seconds` (number)

### `compose_stop` (~246 tokens)

Stop services in a compose project without removing their containers.

Unlike `compose_down`, containers/networks/volumes survive - use `compose_start` to bring them back.
Does not raise on a non-zero CLI exit (a missing compose plugin or a timeout still raises) - inspect
\`returncode`/`stderr` in the result.

Args:
    project_dir: Dir with the compose file (default: server cwd; copied to the target host if no local plugin)
    files: Explicit compose file paths (repeatable, `-f`)
    project_name: Compose project name override
    services: Specific services to stop (default: all)
    stop_timeout_seconds: Grace period before SIGKILL (passed as `--timeout`)
    timeout_seconds: Subprocess timeout (default 300s)

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}

Input parameters:

- `files` (array)
- `project_dir` (string)
- `project_name` (string)
- `services` (array)
- `stop_timeout_seconds` (integer)
- `timeout_seconds` (number)

### `compose_start` (~229 tokens)

Start existing (stopped) containers of a compose project.

Counterpart to `compose_stop`: starts existing containers without recreating them. Use
\`compose_up` to (re)create containers from the compose file.
Does not raise on a non-zero CLI exit (a missing compose plugin or a timeout still raises) - inspect
\`returncode`/`stderr` in the result.

Args:
    project_dir: Dir with the compose file (default: server cwd; copied to the target host if no local plugin)
    files: Explicit compose file paths (repeatable, `-f`)
    project_name: Compose project name override
    services: Specific services to start (default: all)
    timeout_seconds: Subprocess timeout (default 600s)

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}

Input parameters:

- `files` (array)
- `project_dir` (string)
- `project_name` (string)
- `services` (array)
- `timeout_seconds` (number)

### `compose_run` (~390 tokens)

Run a one-off command against a compose service.

Always passes `-T` (no TTY under MCP). Defaults to detached with `--rm` so the call returns
promptly. Unlike `compose_exec`, this starts a NEW container for the service rather than
running inside the existing one.
Does not raise on a non-zero CLI exit (a missing compose plugin or a timeout still raises) - inspect
\`returncode`/`stderr` in the result.

Args:
    service: Service name from the compose file
    command: Command + args to run (exec-form; no shell unless you invoke one)
    project_dir: Dir with the compose file (default: server cwd; copied to the target host if no local plugin)
    files: Explicit compose file paths (repeatable, `-f`)
    project_name: Compose project name override
    detach: Run detached
    rm: Remove the container after the run
    no_deps: Don't start linked services
    workdir: Working directory inside the container
    user: User to run as inside the container (uid or name)
    env: Environment variables to set inside the container
    name: Container name
    timeout_seconds: Subprocess timeout (default 600s)

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}

Input parameters:

- `command` (array)
- `detach` (boolean)
- `env` (object)
- `files` (array)
- `name` (string)
- `no_deps` (boolean)
- `project_dir` (string)
- `project_name` (string)
- `rm` (boolean)
- `service` (string, required)
- `timeout_seconds` (number)
- `user` (string)
- `workdir` (string)

### `compose_exec` (~351 tokens)

Run a command inside an already-running compose service container (see also `container_exec`).

Always passes `-T` (no TTY). Pass an exec-form argv (e.g. `["python", "-V"]`); a
\`["sh", "-c", "..."]` form interprets shell metacharacters in untrusted substrings.
Does not raise on a non-zero CLI exit (a missing compose plugin or a timeout still raises) - inspect
\`returncode`/`stderr` in the result.

Args:
    service: Service name from the compose file
    command: Argv to execute inside the container
    project_dir: Dir with the compose file (default: server cwd; copied to the target host if no local plugin)
    files: Explicit compose file paths (repeatable, `-f`)
    project_name: Compose project name override
    index: Container index when the service has multiple replicas
    workdir: Working directory inside the container
    user: User to run as inside the container (uid or name)
    env: Environment variables to set for the exec session
    timeout_seconds: Subprocess timeout (default 60s)

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}

Input parameters:

- `command` (array, required)
- `env` (object)
- `files` (array)
- `index` (integer)
- `project_dir` (string)
- `project_name` (string)
- `service` (string, required)
- `timeout_seconds` (number)
- `user` (string)
- `workdir` (string)

### `compose_images` (~197 tokens)

List the images used by a compose project's services, parsed from `--format json`.

Answers "what image and tag does each service container actually run?" - the containers must
exist (`compose_up` first). Use `compose_ps` for container state and
\`image_list` for daemon-wide images.
Raises RemoteFailureError if the CLI call fails.

Args:
    project_dir: Dir with the compose file (default: server cwd; copied to the target host if no local plugin)
    files: Explicit compose file paths (repeatable, `-f`)
    project_name: Compose project name override
    services: Restrict to these services (default: all)

Returns:
    list: One dict per container image (service, container, repository, tag, id, size)

Input parameters:

- `files` (array)
- `project_dir` (string)
- `project_name` (string)
- `services` (array)

### `compose_port` (~326 tokens)

Resolve the host binding for a service's container port.

The compose equivalent of `docker port`: which host address/port a service's private port is
published on. `published` is None when the port isn't published. For non-compose containers
read `container_inspect`'s NetworkSettings.Ports instead.
Raises RemoteFailureError if the CLI call fails.

Args:
    service: Service name from the compose file
    private_port: The container-internal port to look up
    protocol: "tcp" (default) or "udp"
    index: Container index when the service has multiple replicas
    project_dir: Dir with the compose file (default: server cwd; copied to the target host if no local plugin)
    files: Explicit compose file paths (repeatable, `-f`)
    project_name: Compose project name override

Returns:
    dict: {"service", "private_port", "protocol", "published": "host:port"|None, "host": str|None, "port": int|None,
        "bindings": list[str]}. `published`/`host`/`port` describe the first binding; `bindings` lists every line (a
        port can be published on more than one address, e.g. IPv4 and IPv6).

Input parameters:

- `files` (array)
- `index` (integer)
- `private_port` (integer, required)
- `project_dir` (string)
- `project_name` (string)
- `protocol` (string)
- `service` (string, required)

### `compose_wait` (~264 tokens)

Block until the named service containers stop, then return their exit codes.

For one-shot / batch services. A long-running service that never exits blocks until
\`timeout_seconds`, then the subprocess is killed (TimeoutExpired) - bound it sensibly.
Exit codes are on stdout. For a single container use `container_wait`; for swarm services
use `service_wait`.
Does not raise on a non-zero CLI exit (a missing compose plugin or a timeout still raises) - inspect
\`returncode`/`stderr` in the result.

Args:
    services: One or more services to wait on. At least one is required.
    project_dir: Dir with the compose file (default: server cwd; copied to the target host if no local plugin)
    files: Explicit compose file paths (repeatable, `-f`)
    project_name: Compose project name override
    timeout_seconds: Subprocess timeout (default 300s)

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}

Input parameters:

- `files` (array)
- `project_dir` (string)
- `project_name` (string)
- `services` (array, required)
- `timeout_seconds` (number)

### `compose_top` (~207 tokens)

Show the running processes of a compose project's containers.

Output is the `ps`-style process table per service (not JSON); read it from `stdout`. The
per-container equivalent is `container_top`.
Does not raise on a non-zero CLI exit (a missing compose plugin or a timeout still raises) - inspect
\`returncode`/`stderr` in the result.

Args:
    services: Restrict to these services (default: all)
    project_dir: Dir with the compose file (default: server cwd; copied to the target host if no local plugin)
    files: Explicit compose file paths (repeatable, `-f`)
    project_name: Compose project name override

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}

Input parameters:

- `files` (array)
- `project_dir` (string)
- `project_name` (string)
- `services` (array)

### `compose_cp` (~530 tokens)

Copy files/folders between a service container and the server host's filesystem.

Exactly one of `source`/`dest` is `SERVICE:PATH`; the other is a path on the host running this
MCP server, read/written as the server's user (same host exposure as the file-path archive
tools - see SECURITY.md). Copying to stdout (`dest="-"`) is unsupported; use
\`container_archive_get`.
Does not raise on a non-zero CLI exit (a missing compose plugin or a timeout still raises) - inspect
\`returncode`/`stderr` in the result. With no local compose plugin and an `ssh://` target, runs the real
\`docker compose cp` on that host instead and relays whichever side of the copy is local over the same SSH
connection - every parameter above behaves the same either way, since the actual copy always runs through
the real CLI. The one difference: a container->host copy is refused if the local destination already
exists, since only this host (not the remote one) knows that. `unix://`/`tcp://`+TLS hosts with no local
plugin are not covered by this fallback (no shell to run the CLI on) and still raise `CapabilityError` -
use `container_archive_put` (host to container) or `container_archive_get_to_file` (container to host)
there instead; both talk to the daemon directly and need no local CLI (`compose_ps` gives you the
container name).

Args:
    source: `SERVICE:SRC_PATH` or a host path
    dest: `SERVICE:DEST_PATH` or a host path (not "-")
    index: Container index when the service has multiple replicas
    all_containers: Copy to/from all containers of the service (`--all`)
    project_dir: Dir with the compose file (default: server cwd; copied to the target host if no local plugin)
    files: Explicit compose file paths (repeatable, `-f`)
    project_name: Compose project name override
    timeout_seconds: Subprocess timeout (default 300s)

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}

Input parameters:

- `all_containers` (boolean)
- `dest` (string, required)
- `files` (array)
- `index` (integer)
- `project_dir` (string)
- `project_name` (string)
- `source` (string, required)
- `timeout_seconds` (number)

### `compose_kill` (~263 tokens)

Send a signal to a compose project's containers (default SIGKILL).

Immediate, with no grace period - prefer `compose_stop` for a clean shutdown (stop signal,
then kill after a timeout).
Does not raise on a non-zero CLI exit (a missing compose plugin or a timeout still raises) - inspect
\`returncode`/`stderr` in the result.

Args:
    services: Restrict to these services (default: all)
    signal: Signal to send (default "SIGKILL"; e.g. "SIGTERM", "SIGHUP")
    remove_orphans: Also remove containers for services not in the compose file
    project_dir: Dir with the compose file (default: server cwd; copied to the target host if no local plugin)
    files: Explicit compose file paths (repeatable, `-f`)
    project_name: Compose project name override

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}

Input parameters:

- `files` (array)
- `project_dir` (string)
- `project_name` (string)
- `remove_orphans` (boolean)
- `services` (array)
- `signal` (string)

### `compose_pause` (~233 tokens)

Pause the containers of a compose project (freezes their processes in place).

Paused containers stop consuming CPU but keep memory, network endpoints, and state; resume
with `compose_unpause`. To actually stop containers (each one's configured stop signal,
freeing resources) use `compose_stop`; to stop and delete them use `compose_down`.
Does not raise on a non-zero CLI exit (a missing compose plugin or a timeout still raises) - inspect
\`returncode`/`stderr` in the result.

Args:
    services: Restrict to these services (default: all)
    project_dir: Dir with the compose file (default: server cwd; copied to the target host if no local plugin)
    files: Explicit compose file paths (repeatable, `-f`)
    project_name: Compose project name override

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}

Input parameters:

- `files` (array)
- `project_dir` (string)
- `project_name` (string)
- `services` (array)

### `compose_unpause` (~208 tokens)

Unpause the containers of a compose project (resumes paused processes).

Reverse of `compose_pause`: processes continue from where they were frozen (no restart).
\`compose_start` is the counterpart for stopped containers.
Does not raise on a non-zero CLI exit (a missing compose plugin or a timeout still raises) - inspect
\`returncode`/`stderr` in the result.

Args:
    services: Restrict to these services (default: all)
    project_dir: Dir with the compose file (default: server cwd; copied to the target host if no local plugin)
    files: Explicit compose file paths (repeatable, `-f`)
    project_name: Compose project name override

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}

Input parameters:

- `files` (array)
- `project_dir` (string)
- `project_name` (string)
- `services` (array)

### `compose_list` (~88 tokens)

List compose projects known to the daemon (across all directories).

Project-level view (one entry per project); `compose_ps` lists the containers of a single
project.
Raises RemoteFailureError if the CLI call fails.

Args:
    all: Include stopped projects

Returns:
    list: One dict per project (parsed from `--format json`)

Input parameters:

- `all` (boolean)

### `config_create` (~245 tokens)

Create an immutable Swarm config object; requires a swarm manager.

Configs store non-sensitive configuration files (nginx.conf, app.yaml, etc.) and mount
them into service containers at a specified path. Unlike secrets, config data is not
encrypted at rest - use `secret_create` for credentials or keys. `data` is raw bytes;
encode strings first (e.g. `"my config".encode()`). Once created, a config is immutable:
to update it, create a new config with a new name and update the service to reference it,
then remove the old config with `config_remove`.

Args:
    name: Unique config name within the swarm
    data: Raw bytes content of the config file
    labels: Labels to set on the config
    templating: Templating driver config (e.g. {"Name": "golang"} for Go template syntax)

Returns:
    dict: The created config's full document ({"ID", "Version", "CreatedAt", "Spec", ...})

Input parameters:

- `data` (string, required)
- `labels` (object)
- `name` (string, required)
- `templating` (object)

### `config_inspect` (~114 tokens)

Get a swarm config's full inspect payload by id or name.

Requires a swarm manager. Unlike a secret, a config's payload IS readable after creation:
\`Spec.Data` in the result holds the base64-encoded contents. Use `config_list` to enumerate
configs; use this to read one config's contents and metadata.

Returns:
    dict: The config's full document (ID, CreatedAt, UpdatedAt, Spec{Name, Labels, Data base64})

Input parameters:

- `id_or_name` (string, required)

### `config_list` (~123 tokens)

List swarm configs; requires a swarm manager.

Unlike secrets, config attrs include the actual config data (`Spec.Data`, base64-encoded)
since configs are not treated as sensitive. Valid filter keys: `id`, `name`, `names`,
\`label` (key or key=value). Fetch a single config by id/name with `config_inspect`.

Args:
    filters: Narrow the list; omit to return every config

Returns:
    list: One full config document ({"ID", "Spec", ...}) per config

Input parameters:

- `filters` (object)

### `config_remove` (~68 tokens)

Remove a swarm config.

Requires a swarm manager, and fails while any service still references the config - update or
remove those services first. The last step of the rotation flow described in `config_create`.

Returns:
    bool: True after removal

Input parameters:

- `id_or_name` (string, required)

Output parameters:

- `result` (boolean)

### `container_run` (~595 tokens)

Run a container from an image (create and start in one call, like `docker run`).

Use `container_create` to prepare a container without starting it, or `container_exec` to run
a command in a container that already exists. With detach=False the call blocks until the
container exits and returns its output, so long-running images need detach=True. Created
containers are stamped with provenance labels.

Args:
    command: The command to run in the container
    name: Name to assign to the container
    detach: Run in the background and return container info
    environment: Environment variables, as `{"KEY": "value"}` or a list of "KEY=value" strings
    ports: Port mappings, e.g. {'2222/tcp': 3333}
    volumes: Volumes to mount, as `{"/host/path": {"bind": "/in/container", "mode": "rw"}}`
        or a list of "host:container:mode" strings
    network: Name of the network to attach
    hostname: Hostname for the container
    user: Username or UID to run as
    working_dir: Working directory inside the container
    entrypoint: Entrypoint to override the image default
    restart_policy: Restart policy, e.g. {'Name': 'on-failure', 'MaximumRetryCount': 3}
    labels: Labels to set on the container
    remove: Remove the container when it exits (only with detach=False)
    auto_remove: Enable auto-removal of the container on daemon side
    privileged: Give extended privileges to the container
    tty: Allocate a pseudo-TTY
    mem_limit: Memory limit: bytes as an int, or a units string ("100000b", "1000k", "128m", "1g")
    cpu_count: Number of CPUs
    extra_kwargs: Additional keyword arguments forwarded to ContainerCollection.run (call
        `docs_lookup(section="containers")` for the full accepted set)

Returns:
    dict | str: The container's full inspect payload when detach=True, else stdout/stderr as a string

Input parameters:

- `auto_remove` (boolean)
- `command`
- `cpu_count` (integer)
- `detach` (boolean)
- `entrypoint`
- `environment`
- `extra_kwargs` (object)
- `hostname` (string)
- `image` (string, required)
- `labels`
- `mem_limit`
- `name` (string)
- `network` (string)
- `ports` (object)
- `privileged` (boolean)
- `remove` (boolean)
- `restart_policy`
- `stdin_open` (boolean)
- `tty` (boolean)
- `user` (string)
- `volumes`
- `working_dir` (string)

Output parameters:

- `result`

### `container_create` (~285 tokens)

Create a container from an image without starting it.

Use this when you need to configure a container (with `extra_kwargs`) before its first
start, or want creation and start as separate observable steps. For the common case of
create-then-start-immediately use `container_run` instead - it does both in one call.
Start the created container with `container_start`. Common `extra_kwargs` keys: `name`
(str), `environment` (list of "KEY=VAL" or dict), `ports` (dict, e.g. `{"80/tcp": 8080}`),
\`volumes` (dict, e.g. `{"/host/path": {"bind": "/container/path", "mode": "rw"}}`), `labels`
(dict). For anything else docker-py's `ContainerCollection.create` accepts, call
\`docs_lookup(section="containers")` rather than guessing a key name.

Args:
    image: Image to create the container from, e.g. "nginx:alpine"
    command: Override the image's default command; string or list of strings
    extra_kwargs: Additional docker-py ContainerCollection.create keyword arguments

Returns:
    dict: The created container's full inspect payload (not yet running)

Input parameters:

- `command`
- `extra_kwargs` (object)
- `image` (string, required)

### `container_inspect` (~133 tokens)

Return the full inspect detail for a single container.

Use this when you need complete information about one container - config, state,
network settings, mounts, environment variables, and resource limits. To enumerate many
containers use `container_list` instead (same payload per container by default; abridged
with sparse=True). For just logs or stats use `container_logs` / `container_stats`.

Args:
    id_or_name: Container id (full or short) or name

Returns:
    dict: Full container inspect attrs (equivalent to `docker inspect`)

Input parameters:

- `id_or_name` (string, required)

### `container_list` (~309 tokens)

List containers on the daemon (running only by default).

Pass all=True to include stopped containers. For a compose project `compose_ps` groups
containers by service; for swarm services use `service_ps` (tasks may live on other nodes).

Args:
    all: Show all containers, including stopped ones (default False: running only)
    since: Only show containers created after this id or name
    before: Only show containers created before this id or name
    limit: Maximum number of results
    filters: Filter by attributes (e.g. status, label)
    sparse: Skip inspect calls and return less detail
    ignore_removed: Ignore containers removed during listing; inert when `sparse=True`, which
        skips the inspect calls that would fail
    managed_only: Only return containers created by this MCP server (filters on the docker-mcp-server.managed
        label); combines with any `filters` given

Returns:
    list: One dict per container: full inspect payloads by default (each match is inspected, like
        `container_inspect`); sparse=True skips the per-container inspect calls and returns the daemon's abridged
        list entries instead

Input parameters:

- `all` (boolean)
- `before` (string)
- `filters` (object)
- `ignore_removed` (boolean)
- `limit` (integer)
- `managed_only` (boolean)
- `since` (string)
- `sparse` (boolean)

### `container_prune` (~158 tokens)

Remove all stopped containers to reclaim disk space.

Only removes containers that are not running - running containers are never affected.
Use `container_list(all=True)` to preview what would be removed before calling this.
Valid filter keys: `until` (RFC3339 timestamp or duration like "24h" - removes containers
stopped before that point), `label` (key or key=value). For a broader cleanup of
containers plus unused images, networks, and volumes see the `prune_managed` prompt.

Args:
    filters: Narrow which stopped containers to remove; omit to remove all stopped

Returns:
    dict: {"ContainersDeleted": [...], "SpaceReclaimed": <bytes>}

Input parameters:

- `filters` (object)

### `container_start` (~123 tokens)

Start an existing stopped container.

Use this to restart a container that was previously created or stopped without removing it.
To create and start a new container in one step use `container_run` instead. Calling on
an already-running container has no effect (the daemon returns 304 and no error is
raised). To stop then start a running container use `container_restart`.

Args:
    id_or_name: Container id (full or short) or name

Returns:
    dict: The container's full inspect payload after starting

Input parameters:

- `id_or_name` (string, required)

### `container_stop` (~174 tokens)

Gracefully stop a running container (its configured stop signal, then SIGKILL after a timeout).

Prefer this over `container_kill` for a clean shutdown: the main process receives the
container's stop signal (`STOPSIGNAL`, default SIGTERM) and has stop_timeout_seconds to exit
before the daemon force-kills it. Use `container_restart` to stop and start again in one call,
or `container_pause` to freeze processes without stopping. When the server runs containerized
it refuses to stop its own container.

Args:
    stop_timeout_seconds: Seconds between the stop signal and SIGKILL

Returns:
    dict: The container's full inspect payload after the stop (exit code under State.ExitCode)

Input parameters:

- `id_or_name` (string, required)
- `stop_timeout_seconds` (integer)

### `container_restart` (~133 tokens)

Restart a container: stop then start again in one call.

The container receives its configured stop signal (`STOPSIGNAL`, default SIGTERM), SIGKILL
after stop_timeout_seconds, and is then started. Use `container_stop`/`container_start` to do
the halves separately. When the server runs containerized it refuses to restart its own
container.

Args:
    stop_timeout_seconds: Seconds between the stop signal and SIGKILL

Returns:
    dict: The container's full inspect payload after the restart

Input parameters:

- `id_or_name` (string, required)
- `stop_timeout_seconds` (integer)

### `container_kill` (~178 tokens)

Send a signal to a running container (default SIGKILL - immediate, no graceful shutdown).

Use it to force-kill a container that ignores `container_stop`, or with `signal` to poke a
process without stopping it (e.g. SIGHUP for a config reload). For a normal shutdown prefer
\`container_stop`, which sends the container's configured stop signal first. Fails with a
conflict error if the container is not running. When the server runs containerized it refuses
to signal its own container.

Args:
    signal: Signal name or number as a string (e.g. "SIGHUP", "9"); default SIGKILL

Returns:
    dict: The container's full inspect payload after the signal

Input parameters:

- `id_or_name` (string, required)
- `signal` (string)

### `container_pause` (~116 tokens)

Suspend all processes in a container using the kernel freezer cgroup.

Unlike sending SIGSTOP, the freezer cgroup suspends processes without their being able
to observe or intercept the suspension. A paused container keeps its resources (memory,
open file descriptors) but consumes no CPU. Resume with `container_unpause` -
\`container_exec` fails against a paused container until it is unpaused.

Returns:
    dict: The container's full inspect payload after pause (State.Paused true)

Input parameters:

- `id_or_name` (string, required)

### `container_unpause` (~91 tokens)

Resume all processes in a paused container (the reverse of `container_pause`).

Only valid on a paused container - it fails if the container is merely stopped; use
\`container_start` for stopped containers. Processes continue from where they were frozen.

Returns:
    dict: The container's full inspect payload after unpause (State.Paused becomes false)

Input parameters:

- `id_or_name` (string, required)

### `container_remove` (~162 tokens)

Remove a container, deleting its writable layer.

The image is untouched (`image_remove` deletes images); named volumes are never removed -
volumes=True only covers anonymous ones. A running container is refused unless force=True,
which kills it first. When the server runs containerized it refuses to remove its own
container.

Args:
    volumes: Also remove anonymous volumes (the CLI's `--volumes`); named volumes persist
    link: Remove the specified link
    force: Kill a running container before removing it (default False: running is an error)

Returns:
    bool: True after removal completes

Input parameters:

- `force` (boolean)
- `id_or_name` (string, required)
- `link` (boolean)
- `volumes` (boolean)

Output parameters:

- `result` (boolean)

### `container_logs` (~454 tokens)

Get the logs of a container: a one-shot snapshot by default, or a bounded live tail with `follow=True`.

Follow mode returns when `limit_lines` lines are collected, `timeout_seconds` elapses, or the
container exits, whichever comes first - so the agent can watch live output without blocking
forever. `limit_lines`/`timeout_seconds` apply only in follow mode; `until` only in snapshot mode.

Snapshot mode is capped at 32 MiB and raises ToolInputError past it, so a noisy container can't
exhaust the server's memory; `service_logs` caps the same way and lets the caller raise it.
Prefer an integer `tail`, or `since`, over `tail="all"` on a long-running container: "all" is
safe but will abort on the cap rather than returning a partial answer, and a large result can
still exceed the agent's context.

Caveat for `ssh://` daemons: docker-py can't cancel an SSH stream, so in follow mode the
\`timeout_seconds` watchdog can't interrupt a fully silent container - use the snapshot mode
there if you need a hard time bound.

Args:
    tail: Number of lines from the end, or the literal "all" for everything
    since: Only return logs created after this unix timestamp
    until: Only return logs created before this unix timestamp (snapshot mode only)
    follow: Follow the live log stream instead of returning a snapshot
    limit_lines: Follow mode: max lines to collect before returning
    timeout_seconds: Follow mode: max wall-clock seconds before returning what was collected

Returns:
    str: Decoded log output (up to `limit_lines` lines in follow mode). Raises ToolInputError in snapshot mode if
        the logs exceed 32 MiB.

Input parameters:

- `follow` (boolean)
- `id_or_name` (string, required)
- `limit_lines` (integer)
- `since` (number)
- `stderr` (boolean)
- `stdout` (boolean)
- `tail`
- `timeout_seconds` (number)
- `timestamps` (boolean)
- `until` (number)

Output parameters:

- `result` (string)

### `container_stats` (~249 tokens)

Get one point-in-time resource-usage snapshot for a container (non-streaming).

Returns the raw engine stats payload; CPU percent must be computed from the delta between
\`cpu_stats` and `precpu_stats`. By default the daemon collects two cycles before answering,
which is what fills `precpu_stats` - so the call takes about a second. `one_shot=True` returns
after a single collection instead, leaving `precpu_stats` zeroed and CPU percent uncomputable;
use it when only `memory_stats`/`pids_stats` matter. For a pre-computed human-readable summary
prefer the `docker-stats://{id_or_name}` resource; for a process listing use `container_top`.

Args:
    one_shot: Skip the second collection cycle for a faster answer, at the cost of an empty
        `precpu_stats` (so no CPU percent); needs daemon API v1.41+

Returns:
    dict: Engine stats payload (read, cpu_stats, precpu_stats, memory_stats, networks, pids_stats, ...)

Input parameters:

- `id_or_name` (string, required)
- `one_shot` (boolean)

### `container_top` (~151 tokens)

List the processes running inside a container (the daemon runs `ps` on the host).

Works on any running container without executing anything in it, so it needs no shell or `ps`
binary in the image - unlike `container_exec` with `ps`. Use `container_stats` for resource
usage rather than process lists. Fails if the container is not running.

Args:
    ps_args: Extra ps arguments (e.g. "aux"); default is the daemon's standard ps invocation

Returns:
    dict: {"Titles": [ps column names], "Processes": [[one row of values per process]]}

Input parameters:

- `id_or_name` (string, required)
- `ps_args` (string)

### `container_exec` (~328 tokens)

Run a command inside a running container (for a compose service, prefer `compose_exec`).

Security: when any element of `cmd` is agent-controlled, use an exec-form argv list that does not
invoke a shell (e.g. `["python", "-V"]`, `["ls", path]`). A string `cmd`, or a shell form like
\`["sh", "-c", template]`, interprets shell metacharacters in the untrusted parts.

Args:
    cmd: Command to execute (prefer exec-form argv, no shell, when any element is agent-controlled)
    tty: Allocate a pseudo-TTY
    privileged: Run with extended privileges
    user: User to run the command as
    detach: Detach from the exec
    environment: Environment variables, as `{"KEY": "value"}` or a list of "KEY=value" strings
    workdir: Working directory inside the container
    demux: Return stdout and stderr separately

Returns:
    dict: {"exit_code", "output"}; output is combined stdout+stderr, or a [stdout, stderr] pair with demux=True

Input parameters:

- `cmd` (required)
- `demux` (boolean)
- `detach` (boolean)
- `environment`
- `id_or_name` (string, required)
- `privileged` (boolean)
- `stderr` (boolean)
- `stdin` (boolean)
- `stdout` (boolean)
- `tty` (boolean)
- `user` (string)
- `workdir` (string)

### `container_commit` (~321 tokens)

Snapshot a container's current filesystem state as a new image.

Useful for capturing a debugging state or saving manual changes made inside a container.
For repeatable builds use `image_build` with a Dockerfile instead; publish the result with
\`image_tag` + `image_push`. The container is paused by default during
the snapshot to ensure filesystem consistency - set `pause=False` only if the container
cannot be paused. `changes` accepts Dockerfile instructions to apply on top of the
snapshot, e.g. `["CMD ["python", "app.py"]", "ENV FOO=bar"]`.

Args:
    id_or_name: Container id or name to snapshot
    repository: Repository name for the new image, e.g. "myorg/myimage"
    tag: Tag for the new image (default: "latest")
    message: Commit message stored in the image metadata
    author: Author string stored in the image metadata
    pause: Pause the container during commit for consistency
    changes: Dockerfile instructions (CMD, ENV, EXPOSE, etc.) to apply to the image
    conf: Additional image configuration overrides as a dict

Returns:
    dict: The new image's full inspect payload (Id is the new image id)

Input parameters:

- `author` (string)
- `changes`
- `conf` (object)
- `id_or_name` (string, required)
- `message` (string)
- `pause` (boolean)
- `repository` (string)
- `tag` (string)

### `container_diff` (~106 tokens)

List filesystem changes a container has made relative to its image.

Use it to audit what a container wrote before `container_commit` or `container_archive_get`,
or to debug unexpected writes. Only the writable container layer is compared - files in
volumes and bind mounts never show up.

Returns:
    list: Dicts of {"Path", "Kind"}; Kind 0=modified, 1=added, 2=deleted

Input parameters:

- `id_or_name` (string, required)

### `container_rename` (~133 tokens)

Rename a container in place; its id, state, and configuration are unchanged.

Use it to free up or claim a container name (names are unique per daemon) - e.g. before
starting a replacement under the old name. Fails with a conflict error if the new name is
already taken. Not related to `image_tag`, which names images.

Args:
    name: The new name; must not be in use by any other container

Returns:
    dict: The container's full inspect payload after the rename

Input parameters:

- `id_or_name` (string, required)
- `name` (string, required)

### `container_update` (~247 tokens)

Update resource limits on a container without recreating it.

Changes take effect immediately on Linux (cgroups); not all fields are updatable on
every platform. Common `updates` keys: `mem_limit` (bytes, e.g. 134217728 for 128 MB),
\`memswap_limit` (memory+swap in bytes; -1 = unlimited), `cpu_shares` (relative weight,
default 1024), `cpu_period` / `cpu_quota` (microseconds for CFS throttling),
\`cpuset_cpus` (e.g. "0-1"), `restart_policy` (dict with `Name` such as
"on-failure"/"always"/"unless-stopped" and optional `MaximumRetryCount`). To change
image, env, or volumes the container must be recreated (`container_remove` +
\`container_run`).

Args:
    id_or_name: Container id or name to update
    updates: Resource fields to update; see description for valid keys

Returns:
    dict: The container's full inspect payload after the update

Input parameters:

- `id_or_name` (string, required)
- `updates` (object, required)

### `container_wait` (~682 tokens)

Block until a container reaches a condition: stopped, "healthy", or its logs contain a pattern.

One contract for every mode: never raises on timeout - the result always carries `met` (condition
reached) and `timed_out`. The stop conditions ("not-running"/"next-exit"/"removed") use the
daemon's blocking wait and fill `status_code`/`error` (the container's exit info); "healthy" polls
the container's HEALTHCHECK every `poll_interval`s and fills `health`/`status`; "log-match" polls
recent logs every `poll_interval`s for `pattern` and fills `matched_line`. For a compose
project use `compose_wait`; for swarm services use `service_wait`.

Health semantics: with no HEALTHCHECK defined, once the container is `running` the tool returns
promptly with `health: null` and `met: false` (false = "not confirmed healthy", not "unhealthy" -
check `health` to tell them apart). A container that exits before becoming healthy returns its
terminal `status` and `met: false`.

Log-match semantics: `pattern` is matched as a **plain substring** by default - safe against any
input, including adversarial ones. Pass `regex=True` to match `pattern` as a regular expression
(via `re.search`) instead; only do this with patterns you trust, since a regex with catastrophic
backtracking run against attacker-influenced log content can exhaust CPU (ReDoS). Checks stdout
and stderr, most recent lines first within each poll. If the container exits/dies before the
pattern ever appears, returns promptly with `met=false` (not `timed_out`) - no further logs can
arrive, so there's nothing to keep polling for.

Args:
    until: Condition to wait for: "not-running" (default), "next-exit", "removed", "healthy", or "log-match"
        (requires `pattern`)
    timeout_seconds: Max seconds to wait before returning with timed_out=true
    poll_interval: "healthy"/"log-match" only: seconds between re-checks (default 2, > 0); capped by the time left
        so a large value can't push the total wait past the timeout…

Input parameters:

- `id_or_name` (string, required)
- `pattern` (string)
- `poll_interval` (number)
- `regex` (boolean)
- `timeout_seconds` (number)
- `until` (string)

### `container_export` (~304 tokens)

Export a container's filesystem as a tar archive: to a file on the server host, or in band.

The tar is a flat filesystem snapshot with no image metadata or layers - use `image_save` for
an archive that `image_load` can restore, and `container_archive_get` for a single file or
directory. With `dest_path` the archive streams straight to disk (no byte cap), so it handles large
containers - the file is written by the server's user, `~` is expanded, and an existing file is
refused unless `overwrite=True`. Without `dest_path` the tar bytes are returned in band, capped
at `max_bytes` (default 32 MiB) because MCP base64-encodes them - a fallback for when no
writable host path exists (e.g. a containerized server without a bind mount).

Args:
    dest_path: Destination path on the server host; omit to return the bytes in band
    overwrite: Replace dest_path if it already exists
    max_bytes: In-band mode: abort with ToolInputError beyond this many bytes (default 32 MiB)

Returns:
    bytes | dict: the tar bytes (in band), or {"path": <resolved path>, "bytes_written": int}

Input parameters:

- `dest_path` (string)
- `id_or_name` (string, required)
- `max_bytes` (integer)
- `overwrite` (boolean)

Output parameters:

- `result`

### `container_archive_get` (~138 tokens)

Retrieve a file or directory from a container as a tar archive, returned in band.

In-band bytes are capped (default 32 MiB) because MCP base64-encodes them;
\`container_archive_get_to_file` streams to a host path instead.

Args:
    path: Path inside the container
    max_bytes: Abort with ToolInputError if the archive exceeds this many bytes (defaults to 32 MiB)

Returns:
    dict: Mapping with archive (bytes) and stat (dict) keys

Input parameters:

- `id_or_name` (string, required)
- `max_bytes` (integer)
- `path` (string, required)

### `container_archive_get_to_file` (~200 tokens)

Retrieve a file or directory from a container as a tar archive written to a file on the server host.

File-writing variant of `container_archive_get` - prefer it for anything large, since in-band
bytes are base64-encoded by MCP. For the whole filesystem use `container_export`. Streams
straight to disk (no in-band byte cap). The file is written by the server's user; `~` is
expanded and an existing file is refused unless `overwrite=True`.

Args:
    path: Path inside the container
    dest_path: Destination path on the server host for the tarball
    overwrite: Replace dest_path if it already exists

Returns:
    dict: {"path": <resolved path>, "bytes_written": int, "stat": dict}

Input parameters:

- `dest_path` (string, required)
- `id_or_name` (string, required)
- `overwrite` (boolean)
- `path` (string, required)

### `container_archive_put` (~216 tokens)

Upload a tar archive to a path inside a container, from in-band bytes or a file on the server host.

Inverse of `container_archive_get`: the archive is extracted at `path` inside the container.
Pass exactly one of `data` (tar bytes in band) or `from_file` (a path on the server host, streamed
straight to the daemon - preferred for large archives, since in-band bytes are base64-encoded by
MCP). `from_file` is read by the server's user; `~` is expanded.

Args:
    path: Destination path inside the container (must already exist)
    data: Tar archive bytes; exactly one of data/from_file
    from_file: Path on the server host to the tar archive to upload; exactly one of data/from_file

Returns:
    bool: True if the upload succeeded

Input parameters:

- `data` (string)
- `from_file` (string)
- `id_or_name` (string, required)
- `path` (string, required)

Output parameters:

- `result` (boolean)

### `context_list` (~156 tokens)

List Docker CLI contexts known to the host running this MCP server.

Contexts are a CLI concept (stored in the docker config dir) letting one CLI target multiple
daemons. This server uses whatever DOCKER_HOST / current-context resolved to at startup, so
changing contexts only affects future subprocess-based tools, not the docker-py SDK client.
Use `context_inspect` for one context's full config and `context_use` to switch.
Raises RemoteFailureError if the CLI call fails.

Returns:
    list: One dict per context, keyed as the CLI emits them - `Name`, `Description`,
        `DockerEndpoint` and `Current`, the last being true for the context the CLI
        would use by default

### `context_inspect` (~106 tokens)

Return the full configuration for a single Docker context.

Full endpoint/TLS detail for one context; `context_list` gives the one-line summary of all.
Raises RemoteFailureError if the CLI call fails.

Args:
    name: Context name (use the `Name` field from `context_list`)

Returns:
    dict: The parsed `docker context inspect` entry (keys include "Name" and "Endpoints" with the daemon URL)

Input parameters:

- `name` (string, required)

### `context_create` (~377 tokens)

Create a new Docker CLI context pointing at a daemon endpoint.

Registers a named endpoint for the CLI; switch with `context_use`, enumerate with
\`context_list`. It does not retarget this server's docker-py client (pinned at startup).
Does not raise on a non-zero CLI exit (a missing `docker` binary or a timeout still raises) - inspect
\`returncode`/`stderr` in the result. It does raise ToolInputError before running anything if `docker_host`
or a TLS path contains a comma, which would inject extra keys (including `skip-tls-verify`) into the
endpoint spec.

Args:
    name: Name for the new context (must not already exist)
    docker_host: Daemon URL, e.g. "tcp://10.0.0.5:2376" or "unix:///var/run/docker.sock"; no commas
    description: Human description shown in `context ls`
    tls_ca: Path on the local host to the CA cert (for TLS daemons); no commas
    tls_cert: Path on the local host to the client cert; no commas
    tls_key: Path on the local host to the client key; no commas
    skip_tls_verify: Disable TLS verification (insecure; for testing only). The only way to set it: it cannot be
        smuggled through `docker_host`

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}

Input parameters:

- `description` (string)
- `docker_host` (string, required)
- `name` (string, required)
- `skip_tls_verify` (boolean)
- `tls_ca` (string)
- `tls_cert` (string)
- `tls_key` (string)

### `context_use` (~173 tokens)

Set the active Docker context for the CLI on the host running this MCP server.

Note: this does not retarget the long-lived docker-py client - SDK-backed tools keep using the
endpoint they connected to at startup. To retarget those, restart the server with a different
DOCKER_HOST / DOCKER_CONTEXT. Create contexts with `context_create`; list them with
\`context_list`.
Does not raise on a non-zero CLI exit (a missing `docker` binary or a timeout still raises) - inspect
\`returncode`/`stderr` in the result.

Args:
    name: Existing context name to set as default

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}

Input parameters:

- `name` (string, required)

### `context_remove` (~149 tokens)

Remove a Docker CLI context.

Deletes only the CLI's connection metadata - the daemon it pointed at is untouched. The
current context needs force=True (or `context_use` another first).
Does not raise on a non-zero CLI exit (a missing `docker` binary or a timeout still raises) - inspect
\`returncode`/`stderr` in the result.

Args:
    name: Context name to remove
    force: Force removal even if the context is the current one

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}

Input parameters:

- `force` (boolean)
- `name` (string, required)

### `image_build` (~786 tokens)

Build an image from a Dockerfile using the daemon's classic builder.

Use this for simple single-platform builds from a local context. For multi-platform
builds, BuildKit cache export/import, or advanced build features prefer `buildx_build`.
\`path` must be a directory accessible on the host running this server (it is the build
context sent to the daemon). `dockerfile` is normally relative to `path`; omit to use the
default `Dockerfile`.

\`dockerfile` is not confined to the context, despite the usual relative form: docker-py detects
an absolute path, or a relative one escaping via `..`, reads that file from the **server host's**
filesystem and injects its contents into the build. So it can read any file the server user can,
like the other host-filesystem parameters (`dest_path`, `from_file`), and unlike them it is easy
to mistake for a context-relative name. `buildx_build`'s `--file` resolves differently again
(against the CLI's working directory) - see its docstring.

Args:
    path: Build context directory path on the server host
    tag: Name and optional tag in "name:tag" format to apply to the built image
    quiet: Suppress verbose build output (final image id still returned)
    nocache: Ignore the layer cache and rebuild all layers
    rm: Remove intermediate containers on success
    pull: Always pull a newer version of each FROM base image before building
    forcerm: Remove intermediate containers even on build failure
    dockerfile: Dockerfile filename relative to path (default: "Dockerfile"); an absolute path or one containing
        ".." reads that file from the server host instead of the context
    buildargs: Build-time variables passed as `--build-arg`; dict of str to str
    container_limits: Resource limits for the build container, e.g. {"memory": 134217728}
    shmsize: Size of /dev/shm in bytes for build steps that need shared memory
    labels: Labels to set on the resulting image (dict of str to str)
    cache_from: List of image references…

Input parameters:

- `buildargs` (object)
- `cache_from` (array)
- `container_limits` (object)
- `dockerfile` (string)
- `extra_hosts` (object)
- `forcerm` (boolean)
- `isolation` (string)
- `labels` (object)
- `network_mode` (string)
- `nocache` (boolean)
- `path` (string)
- `platform` (string)
- `pull` (boolean)
- `quiet` (boolean)
- `rm` (boolean)
- `shmsize` (integer)
- `squash` (boolean)
- `tag` (string)
- `target` (string)
- `use_config_proxy` (boolean)

### `image_inspect` (~175 tokens)

Return the full inspect detail for a single local image.

Includes config (env, entrypoint, exposed ports), size, layer digests (`RootFS.Layers`),
and all tags/digests referencing it (`RepoTags`/`RepoDigests`). For a quick overview of
many images use `image_list` instead. For the per-layer build history (which command
produced each layer) use `image_history`. Only inspects images already present locally -
for a remote image's manifest without pulling it use `image_registry_data` or
\`registry_manifest`.

Args:
    id_or_name: Image name (with optional tag/digest) or id

Returns:
    dict: Full image inspect attrs (equivalent to `docker inspect` on an image)

Input parameters:

- `id_or_name` (string, required)

### `image_registry_data` (~172 tokens)

Get registry data for an image without pulling it, via the daemon's distribution endpoint.

Uses the daemon (and its cached credentials) to resolve the remote descriptor and platform
list. For direct registry access without a daemon use `registry_manifest`.

Security: `auth_config` carries registry credentials, which many MCP clients log verbatim. Prefer
\`docker login` on the host so the `docker` module reuses credentials cached in
\`~/.docker/config.json`, and leave `auth_config` unset.

Args:
    repository: Image reference
    auth_config: Registry authentication config

Returns:
    dict: The registry data document {"Descriptor", "Platforms"} - the OCI descriptor and
        the platforms available for the reference

Input parameters:

- `auth_config` (object)
- `repository` (string, required)

### `image_list` (~174 tokens)

List images in the daemon's local store.

Local only - for a registry's contents use `registry_tags` / `hub_tags`, and `image_search`
to find images on Docker Hub. Dangling (untagged) build leftovers show with
filters={"dangling": True}.

Args:
    repository: Only show images of this repository
    all: Show intermediate image layers
    filters: Filter by attributes (label, dangling, before, since, etc.)

Returns:
    list: One summary dict per image ({"Id", "RepoTags", "RepoDigests", "Created", "Size", "Labels", ...}); use
        `image_inspect` for a full inspect payload

Input parameters:

- `all` (boolean)
- `filters` (object)
- `repository` (string)

### `image_pull` (~263 tokens)

Pull an image from a registry to the daemon's local store.

Private repositories need credentials - `system_login` (or `docker login` on the host) first,
or `auth_config` to authenticate this call alone. Use `image_load` for tarballs, and
\`registry_manifest` / `image_registry_data` to inspect a remote image without pulling it.

Security: `auth_config` carries registry credentials, which many MCP clients log verbatim. Prefer
\`docker login` on the host so the `docker` module reuses credentials cached in
\`~/.docker/config.json`, and leave `auth_config` unset.

Args:
    tag: The image tag (ignored when all_tags=True)
    all_tags: Pull all tags from the repository
    platform: Platform in os/arch format
    auth_config: Per-call registry credentials under the keys `username` and `password`;
        overrides the cached credential for this pull only

Returns:
    dict | list: The pulled image's full inspect payload, or one per image if all_tags=True

Input parameters:

- `all_tags` (boolean)
- `auth_config` (object)
- `platform` (string)
- `repository` (string, required)
- `tag` (string)

Output parameters:

- `result`

### `image_push` (~170 tokens)

Push an image or repository to a registry.

The local image must already bear the target name - `image_tag` it with the
registry-qualified `repository[:tag]` first; a bare name pushes to Docker Hub. Private
registries need credentials (`system_login`, or `docker login` on the host).

Security: `auth_config` carries registry credentials, which many MCP clients log verbatim. Prefer
\`docker login` on the host so the `docker` module reuses credentials cached in
\`~/.docker/config.json`, and leave `auth_config` unset.

Args:
    auth_config: Registry authentication config

Returns:
    str: Push output as a string

Input parameters:

- `auth_config` (object)
- `repository` (string, required)
- `tag` (string)

Output parameters:

- `result` (string)

### `image_remove` (~182 tokens)

Remove a local image by name or id.

Fails without `force` if the image is tagged by multiple names (untag first with
\`image_tag`) or if stopped containers reference it. Running containers always block
removal regardless of `force`. `noprune` keeps untagged parent layers that would
otherwise be removed as a side-effect; leave False unless you need to preserve
the parent layers for another purpose.

Args:
    id_or_name: Image name (with optional tag/digest) or id to remove
    force: Remove even if referenced by stopped containers or multiple tags
    noprune: Do not delete untagged intermediate parent layers

Returns:
    bool: True after removal completes

Input parameters:

- `force` (boolean)
- `id_or_name` (string, required)
- `noprune` (boolean)

Output parameters:

- `result` (boolean)

### `image_search` (~171 tokens)

Search Docker Hub for public images matching a term.

Searches Docker Hub only - not GHCR, ECR, or other registries. For listing tags on a
specific image from any OCI registry use `registry_tags` instead.

Args:
    term: Search keyword, e.g. "nginx" or "python"
    limit: Maximum number of results to return (Docker Hub default is 25)

Returns:
    list: Result dicts: {"name", "description", "star_count", "is_official", "is_automated"} - `is_automated` is
        deprecated in the Engine API and is always false, so rank on `star_count`/`is_official` instead

Input parameters:

- `limit` (integer)
- `term` (string, required)

### `image_prune` (~168 tokens)

Remove unused local images to reclaim disk space.

Without filters removes only "dangling" images - untagged layers not referenced by any
tag or container. To remove all images not used by any container (including tagged ones)
pass `filters={"dangling": False}`. Valid filter keys: `dangling` (bool as string
"true"/"false"), `until` (RFC3339 timestamp or duration like "24h"), `label`
(key or key=value). Use `system_df` first to see how much space is reclaimable.

Args:
    filters: Narrow which images to remove; omit to remove dangling images only

Returns:
    dict: {"ImagesDeleted": [...], "SpaceReclaimed": <bytes>}

Input parameters:

- `filters` (object)

### `image_prune_builds` (~355 tokens)

Delete the daemon's build cache to reclaim disk space.

Prunes the *build cache* - a separate Engine resource from the images `image_prune` removes,
so run both to reclaim everything a build leaves behind. Prefer `buildx_prune` when the build
ran on a non-default buildx builder (that builder keeps its own cache, invisible here) or when
you need any disk ceiling at all - `reserved_space`, `max_used_space` or `min_free_space`, none of
which the docker-py path can send; this tool needs no CLI plugin and works over any
transport, including a daemon with no local `docker` binary. Inventory first with `system_df`
(its `BuildCache` entry) or `buildx_du`. Destructive and immediate: later builds must re-run
the steps whose cache was removed. Needs Docker API v1.31+; passing either `filters` or `all`
needs v1.39+ and raises `InvalidVersion` on an older daemon - omit both to prune with the
daemon's own defaults.

Args:
    filters: Narrow which cache records to remove, e.g. {"until": "24h"} (a duration or timestamp relative to the
        daemon's clock); also accepts `id`, `parent`, `type`, `description`, `inuse`, `shared`, `private`; omit to
        let the daemon prune unused cache
    all: Remove all types of build cache, not just the unused records

Returns:
    dict: {"CachesDeleted": [...], "SpaceReclaimed": <bytes>}

Input parameters:

- `all` (boolean)
- `filters` (object)

### `image_load` (~223 tokens)

Load an image from a tarball produced by `image_save`, from in-band bytes or a file on the server host.

Counterpart of `image_save`; when the image lives in a registry, `image_pull` is the normal
route, and for a flat rootfs archive that is not a `docker save` bundle use `image_import`.
Pass exactly one of `data` (tarball bytes in band) or `from_file` (a path on the server host,
streamed straight to the daemon - preferred for anything but small images, since in-band bytes are
base64-encoded by MCP). `from_file` is read by the server's user; `~` is expanded.

Args:
    data: Tarball contents; exactly one of data/from_file
    from_file: Path to a tarball produced by `docker save` / `image_save`; exactly one of data/from_file

Returns:
    list: One full inspect payload per loaded image

Input parameters:

- `data` (string)
- `from_file` (string)

### `image_import` (~813 tokens)

Create an image from a flat root-filesystem tarball, like `docker import`.

Imports a *filesystem* archive as a new single-layer image with no build history - not the same
thing as `image_load`, which restores a `docker save` archive complete with its layers, tags and
history, so prefer `image_load` for anything `image_save` produced. Use this for a rootfs that
came from somewhere else: a `container_export` archive, a distro base tarball, a VM image dump.
The result has an empty config - no `CMD`/`ENTRYPOINT`/`ENV` - unless you supply `changes`, so an
imported image is usually not runnable until you set at least a command. Pass exactly one source
(`from_file`, `data`, `from_url` or `from_image`); ToolInputError otherwise. `from_url` and
\`from_image` are fetched by the *daemon*, `from_file`/`data` are read here and uploaded; a
\`from_file` path that is not a readable file raises rather than being retried as a URL. Unlike
the other image-creating tools this stamps no provenance labels: the Engine's import call accepts
no labels field, and `changes` does not cover `LABEL`.

Args:
    repository: Repository name to give the new image, e.g. "myorg/rootfs"; may include a tag (`myorg/rootfs:v1`),
        and defaults to `:latest` when it does not. Omit to import untagged, addressable only by the id in the
        returned progress (omit it entirely -- a blank string is a ToolInputError, not a shorthand for untagged). A
        digest reference is refused by the daemon. Required if `tag` is given
    tag: Tag to apply, e.g. "v1". **Overrides** a tag already in `repository` rather than being ignored, so passing
        `repository="myorg/rootfs:v1"` with `tag="v2"` yields `:v2`. Requires `repository` (ToolInputError without
        it - the daemon would otherwise silently drop the tag and import untagged). Blank is also a ToolInputError,
        not a shorthand for the default: the daemon would substitute `latest` without saying so
    from_file: Path to a rootfs tarball on…

Input parameters:

- `changes` (array)
- `data` (string)
- `from_file` (string)
- `from_image` (string)
- `from_url` (string)
- `repository` (string)
- `tag` (string)

Output parameters:

- `result` (string)

### `image_save` (~310 tokens)

Save an image as a tar archive: to a file on the server host, or in band.

The archive keeps layers, tags, and metadata so `image_load` can restore it - different from
\`container_export`, which flattens one container's filesystem. With `dest_path` the archive
streams straight to disk (no byte cap), so it handles large images - the file is written by
the server's user, `~` is expanded, and an existing file is refused unless
\`overwrite=True`. Without `dest_path` the tar bytes are returned in band, capped at `max_bytes`
(default 32 MiB) because MCP base64-encodes them - a fallback for when no writable host path
exists (e.g. a containerized server without a bind mount).

Args:
    dest_path: Destination path on the server host; omit to return the bytes in band
    named: Whether to retain repository/tag names in the saved archive
    overwrite: Replace dest_path if it already exists
    max_bytes: In-band mode: abort with ToolInputError beyond this many bytes (default 32 MiB)

Returns:
    bytes | dict: the tarball bytes (in band), or {"path": <resolved path>, "bytes_written": int}

Input parameters:

- `dest_path` (string)
- `id_or_name` (string, required)
- `max_bytes` (integer)
- `named` (boolean)
- `overwrite` (boolean)

Output parameters:

- `result`

### `image_tag` (~174 tokens)

Tag an image into a repository (add a name to an existing local image).

The image id stays the same and no data is copied - a tag is an alias. Typical flow: tag with
the registry-qualified name, then `image_push`. `image_remove` on a tag merely untags while
other names remain. Tagging over a name that already exists repoints it, without asking.

Args:
    id_or_name: The source image name or id
    repository: Target repository name (registry-qualified for pushing, e.g. "ghcr.io/o/r")
    tag: Tag for the new image (default "latest")

Returns:
    bool: True if the image was tagged

Input parameters:

- `id_or_name` (string, required)
- `repository` (string, required)
- `tag` (string)

Output parameters:

- `result` (boolean)

### `image_history` (~146 tokens)

Return the layer history of an image.

Useful for auditing what commands built each layer and diagnosing image size. Each entry
includes `Id` (layer digest or "<missing>" for imported layers), `Created` (unix
timestamp), `CreatedBy` (the Dockerfile command that produced the layer, e.g. a RUN or
COPY), `Size` (bytes added by that layer), and `Comment`. For full image metadata use
\`image_inspect` instead.

Args:
    id_or_name: Image name (with optional tag/digest) or id

Returns:
    list: Layer history entries, newest first

Input parameters:

- `id_or_name` (string, required)

### `network_create` (~312 tokens)

Create a network.

The daemon default driver is `bridge` (single-host); use `overlay` for swarm-wide networks.
Creating a network attaches nothing - connect containers afterwards with `network_connect` or
at start via `container_run(network=...)`. Created networks carry provenance labels.
A duplicate `name` is always rejected,
so creating is not idempotent - check `network_list` first when the network may already exist.

Args:
    driver: Driver name (daemon default `bridge`; `overlay` for swarm scope)
    options: Driver-specific options dict
    ipam: IPAM configuration as a dict (engine shape: {"Driver", "Config": [{"Subnet", "Gateway", ...}]})
    internal: Restrict external access
    labels: Labels to set on the network
    enable_ipv6: Enable IPv6 networking
    attachable: Allow standalone containers to attach (swarm overlay networks)
    scope: Network scope; the driver picks a sensible default when omitted
    ingress: Make this an ingress network for swarm routing-mesh

Returns:
    dict: The created network's full inspect payload

Input parameters:

- `attachable` (boolean)
- `driver` (string)
- `enable_ipv6` (boolean)
- `ingress` (boolean)
- `internal` (boolean)
- `ipam` (object)
- `labels` (object)
- `name` (string, required)
- `options` (object)
- `scope` (string)

### `network_inspect` (~115 tokens)

Return the full inspect detail for a single network.

Includes the connected containers (`Containers`, keyed by container id, with each
entry's assigned IP), IPAM config, and driver options. For a quick overview of many
networks use `network_list` instead - its default (non-`greedy`) response omits the
per-network `Containers` detail for speed.

Returns:
    dict: Full network inspect attrs (equivalent to `docker network inspect`)

Input parameters:

- `id_or_name` (string, required)

### `network_list` (~280 tokens)

List networks.

Valid filter keys: `driver` (driver name), `label` (key or key=value), `type`
("custom" or "builtin"). `names`/`ids` are a separate shorthand for filtering by exact
name/id, applied in addition to `filters`. Set `greedy` to fetch each network's attrs
individually (adds the connected-containers detail that `network_inspect` returns, at
the cost of one extra daemon call per network) - leave it False for a fast summary list.

Args:
    names: Filter by exact network names
    ids: Filter by exact network ids
    filters: Additional server-side filters; see description for valid keys
    greedy: Fetch extended per-network details (including connected containers)
    managed_only: Only return networks created by this MCP server (filters on the docker-mcp-server.managed label);
        combines with any `filters` given

Returns:
    list: One dict ({"Id", "Name", "Driver", "Scope", ...}) per network: summary attrs by default, full inspect
        attrs (adding "Containers") when greedy=True

Input parameters:

- `filters` (object)
- `greedy` (boolean)
- `ids` (array)
- `managed_only` (boolean)
- `names` (array)

### `network_prune` (~125 tokens)

Remove networks that have no active container endpoints.

Built-in networks (bridge, host, none) are never removed. Only networks with zero
connected containers are eligible. Valid filter keys: `until` (RFC3339 timestamp or
duration - removes networks created before that point), `label` (key or key=value). Use
\`network_remove` to delete one specific network instead.

Args:
    filters: Narrow which networks to remove; omit to remove all unused custom networks

Returns:
    dict: {"NetworksDeleted": [...]}

Input parameters:

- `filters` (object)

### `network_remove` (~103 tokens)

Remove a single custom network by id or name.

Fails if any container is still attached (disconnect with `network_disconnect` or stop
the containers first). Built-in networks (`bridge`, `host`, `none`) can never be removed
and return an error regardless of attachment state. For bulk cleanup of every unused
custom network at once use `network_prune` instead.

Returns:
    bool: True after removal

Input parameters:

- `id_or_name` (string, required)

Output parameters:

- `result` (boolean)

### `network_connect` (~348 tokens)

Attach a running container to an additional network without restarting it.

Use this to give a container access to services on a network it was not started with.
\`aliases` sets extra DNS names for this container within the network (other containers
can reach it by those names in addition to its container name). `ipv4_address` /
\`ipv6_address` assign a specific IP on the network; omit to let the driver assign one.
\`links` is a legacy feature (deprecated; prefer DNS aliases). Use `network_disconnect`
to undo.

Args:
    id_or_name: Network id or name to connect the container to
    container: Container id or name to attach
    aliases: Additional DNS names for this container within the network
    links: Legacy container links (deprecated)
    ipv4_address: Static IPv4 address to assign on this network
    ipv6_address: Static IPv6 address to assign on this network
    link_local_ips: Link-local IP addresses to assign
    driver_opt: Driver-specific endpoint options
    mac_address: Static MAC address for this endpoint, e.g. "02:42:ac:11:00:04"; per-network
        rather than per-container, and a driver is free to ignore it

Returns:
    bool: True after the container is connected

Input parameters:

- `aliases` (array)
- `container` (string, required)
- `driver_opt` (object)
- `id_or_name` (string, required)
- `ipv4_address` (string)
- `ipv6_address` (string)
- `link_local_ips` (array)
- `links` (array)
- `mac_address` (string)

Output parameters:

- `result` (boolean)

### `network_disconnect` (~127 tokens)

Disconnect a container from a network.

The container keeps running with its other network attachments; only this endpoint is removed
(the reverse of `network_connect`). A network with connected containers cannot be deleted, so
disconnect them before `network_remove`.

Args:
    container: The container id or name to disconnect
    force: Force the disconnect; use to clear a stale endpoint (e.g. from a deleted container)

Returns:
    bool: True after the container is disconnected

Input parameters:

- `container` (string, required)
- `force` (boolean)
- `id_or_name` (string, required)

Output parameters:

- `result` (boolean)

### `node_inspect` (~126 tokens)

Get a swarm node's full inspect payload by id or name.

Must run against a swarm manager. Shows role, availability, status, and manager reachability -
use `node_list` to enumerate nodes first; `service_ps(filters={"node": ...})` shows what a
service runs on one node.

Args:
    id_or_name: The node id or hostname (as shown by `node_list`)

Returns:
    dict: The node's full document (Spec{Role, Availability}, Status, ManagerStatus for managers)

Input parameters:

- `id_or_name` (string, required)

### `node_list` (~87 tokens)

List swarm nodes.

Must run against a swarm manager. The fleet view of membership, role, and state; drill into
one node with `node_inspect`.

Args:
    filters: Filter by attributes (id, name, membership, role)

Returns:
    list: One full node document per node (Spec, Status, ManagerStatus for managers)

Input parameters:

- `filters` (object)

### `node_update` (~140 tokens)

Replace a node's spec (availability, name, role, labels).

Replacement, not a merge: `spec` becomes the node's entire spec, and omitted keys are cleared.
Fetch the current spec via `node_inspect` (its `Spec` key), modify it, and resubmit the whole
dict - e.g. sending just {"Availability": "drain"} would also wipe the node's role and labels.

Args:
    spec: The complete new node spec (see description - omitted keys are cleared)

Returns:
    bool: True after the update

Input parameters:

- `id_or_name` (string, required)
- `spec` (object, required)

Output parameters:

- `result` (boolean)

### `node_remove` (~115 tokens)

Remove a node from the swarm.

A node should normally be drained (`node_update` with Availability "drain") and have left the
swarm first, so its tasks reschedule cleanly. Removing an active/reachable node requires `force=True`.

Args:
    id_or_name: The node id or name to remove
    force: Force removal of an active/reachable node

Returns:
    bool: True after the node is removed

Input parameters:

- `force` (boolean)
- `id_or_name` (string, required)

Output parameters:

- `result` (boolean)

### `node_wait` (~309 tokens)

Block until a swarm node's Status.State reaches a target value.

Never raises on timeout - the result always carries `met` and `timed_out`. Polls
\`Status.State` (one of "unknown"/"down"/"ready"/"disconnected") every `poll_interval`s.
Common uses: `until="ready"` after a newly joined node, or `until="down"` while draining a
node before removal. Does not track task placement - for "has this drained node's workload
fully moved off", inspect the relevant services' tasks directly; no single cheap call spans
every service in the swarm, so that check isn't built into this tool. `service_wait` covers
service convergence; `node_list` shows every node's state at once.

Args:
    until: Target Status.State to wait for: "ready" (default), "down", "disconnected", "unknown"
    timeout_seconds: Max seconds to wait before returning with timed_out=true
    poll_interval: Seconds between re-inspections (default 2, > 0); capped by the time left so a large value can't
        push the total wait past the timeout

Returns:
    dict: {"node", "until", "met", "timed_out", "state", "availability", "waited_seconds"}

Input parameters:

- `id_or_name` (string, required)
- `poll_interval` (number)
- `timeout_seconds` (number)
- `until` (string)

### `plugin_create` (~349 tokens)

Build a plugin from a local plugin data directory and install it under `name`.

The counterpart to `plugin_install`, which pulls an already-published plugin from a registry:
use this only for a plugin rootfs you built yourself, and `plugin_install` for anything on a
registry. `plugin_data_dir` is read on the machine running this server (not on the daemon
host), must already contain a `config.json` manifest and a `rootfs` directory, and is tarred
client-side and posted to the daemon - in a container it must be a bind mount or the path
resolves to nothing. The new plugin is created **disabled**: call `plugin_configure` for any
settings it declares, then `plugin_enable` to activate it. Raises if the directory is missing
or lacks `config.json`/`rootfs`, or if `name` is already installed (remove it first with
\`plugin_remove`). Unlike the other create tools, this stamps no provenance labels - the Engine
API's plugin-create call accepts none.

Args:
    name: Local name for the plugin, `author/name:tag`; the `:latest` tag is optional and is the default if omitted
    plugin_data_dir: Path on this server's filesystem to the plugin data directory (containing `config.json` and
        `rootfs`)
    gzip: Compress the uploaded directory with gzip

Returns:
    dict: The created plugin's full document ({"Id", "Name", "Enabled", "Settings", "Config"})

Input parameters:

- `gzip` (boolean)
- `name` (string, required)
- `plugin_data_dir` (string, required)

### `plugin_inspect` (~123 tokens)

Return the full attrs for a single installed plugin.

Use this to check a plugin's `Enabled` state before calling `plugin_enable` /
\`plugin_disable`, or to read the config keys it exposes under `Settings.Env` before
calling `plugin_configure`. For the set of all installed plugins use `plugin_list`.

Args:
    name: Plugin name, e.g. "vieux/sshfs:latest"

Returns:
    dict: The plugin's full document (Id, Name, Enabled, Settings, Config)

Input parameters:

- `name` (string, required)

### `plugin_install` (~217 tokens)

Install a plugin from Docker Hub.

\`remote` is a Docker Hub reference in `author/name:tag` form, e.g.
\`vieux/sshfs:latest`. The daemon handles permission grants non-interactively - call
\`plugin_privileges` first to see what host access the plugin is asking for.
After installation use `plugin_inspect` to confirm the plugin's enabled state, then call
\`plugin_enable` to activate it if needed, and optionally `plugin_configure` first if
it requires settings. Use `plugin_list` to list all plugins, or `plugin_remove` to
uninstall.

Args:
    remote: Docker Hub plugin reference, e.g. "vieux/sshfs:latest"
    local_name: Alias to refer to the plugin locally; defaults to remote

Returns:
    dict: The installed plugin's full document ({"Id", "Name", "Enabled", "Settings", "Config"})

Input parameters:

- `local_name` (string)
- `remote` (string, required)

### `plugin_privileges` (~264 tokens)

Ask the registry which host privileges a not-yet-installed plugin demands.

The review step before `plugin_install`, which grants these privileges non-interactively (the
daemon never prompts) - so this is the only chance to see what a plugin wants before it has it.
Worth checking for anything not already trusted: plugins routinely request host mounts, devices,
and elevated capabilities, and a granted privilege is host-level access, not container-scoped.
Reads the *remote* plugin from its registry and installs nothing; for the privileges of a plugin
already installed, read `Config` from `plugin_inspect` instead. Credentials come from
\`system_login`, or from `~/.docker/config.json` if the host ran `docker login`. Raises if the
reference cannot be resolved in the registry.

Args:
    remote: Registry plugin reference, `author/name:tag`; the `:latest` tag is optional and is the default if
        omitted

Returns:
    list: One dict per requested privilege ({"Name", "Description", "Value"}), e.g. Name "mount" with Value
        ["/data"], or "capabilities" with Value ["CAP_SYS_ADMIN"]; empty if the plugin requests none

Input parameters:

- `remote` (string, required)

### `plugin_push` (~553 tokens)

Push an installed plugin to its registry.

The write-side counterpart to `plugin_install` (which pulls) and the publish step after
\`plugin_create` builds a plugin locally: `name` must already be the registry-qualified name the
plugin is installed under, since - unlike `image_push` - there is no plugin equivalent of
\`image_tag` to rename it first, so create it under the target name. The plugin does not need to
be enabled. Credentials come from `system_login`, or from `~/.docker/config.json` if the host
ran `docker login`. Does NOT raise when the registry rejects the push: an authentication or
quota failure arrives as a final progress record and is surfaced as the `error` key, so check
that key rather than assuming success. Raises `CapabilityError` if the installed docker-py is too
old to expose the internals below, and reports the daemon's own error if the plugin isn't installed.

Bypasses docker-py's `Plugin.push()`/`APIClient.push_plugin()`, which cannot work: both POST to
\`/plugins/{name}/pull`, a route the Engine does not define (push is `/plugins/{name}/push`), so
they 404 against any daemon. Bug present since the method was written in 2017 and still in
docker-py `main`; it survives because upstream has no test covering it. This calls the correct
endpoint through docker-py's private request helpers, in the manner of `system_logout`'s
\`api._auth_configs` reach-in, and fails loudly if those internals change shape.

Caveat for `ssh://` daemons: docker-py can't cancel an SSH stream, so the `timeout_seconds`
watchdog can't interrupt a push that stalls with the connection still open - the same limitation
\`container_logs` carries in follow mode. The call still returns normally once the registry
answers or the stream ends.

Args:
    name: Installed plugin name to push, `[registry/]author/name:tag`; `:latest` if the tag is omitted. A bare
        `author/name` pushes to Docker Hub
    timeout_seconds: Max wall-clock seconds to wait on the push stream before returning…

Input parameters:

- `name` (string, required)
- `timeout_seconds` (number)

### `plugin_list` (~113 tokens)

List installed engine plugins with their full attrs.

Covers managed engine plugins (volume/network/logging drivers installed via `plugin_install`)
\- not docker CLI plugins such as compose, buildx, or scout. Use it to find exact plugin names
for `plugin_inspect`/`plugin_enable`/`plugin_disable`/`plugin_remove`; the `Enabled` key shows
each plugin's state.

Returns:
    list: One full document per installed plugin (Id, Name, Enabled, Settings, Config)

### `plugin_configure` (~165 tokens)

Set runtime configuration options on an installed plugin.

Use `plugin_inspect` first to see which keys the plugin exposes under `Settings.Env`; pass
those same keys as a plain dict, e.g. `{"DEBUG": "1", "SOCKET": "/run/x.sock"}`. The
plugin must be disabled before reconfiguring - call `plugin_disable` first if it is
currently active, then `plugin_enable` afterwards to apply the new settings.

Args:
    name: Plugin name, e.g. "vieux/sshfs:latest"
    options: Key/value settings to apply, matching the plugin's declared env keys

Returns:
    bool: True after configuration

Input parameters:

- `name` (string, required)
- `options` (object, required)

Output parameters:

- `result` (boolean)

### `plugin_disable` (~138 tokens)

Disable a plugin so it stops intercepting Docker API calls; the plugin remains installed.

A disabled plugin cannot be used by new containers but existing containers that already
have it attached are unaffected. Use `force=True` to disable even if active containers
are still using it - this may cause those containers to lose access to plugin-provided
resources (e.g. a volume driver). Re-enable with `plugin_enable`.

Args:
    force: Disable even if active containers are using the plugin (may disrupt them)

Returns:
    bool: True after the plugin is disabled

Input parameters:

- `force` (boolean)
- `name` (string, required)

Output parameters:

- `result` (boolean)

### `plugin_enable` (~152 tokens)

Activate an installed plugin so Docker routes relevant API calls through it.

Activates a plugin that is currently disabled - either freshly installed or previously
disabled via `plugin_disable`. If the plugin exposes configuration (check via
\`plugin_inspect`), call `plugin_configure` while it is still disabled before enabling it.
\`timeout_seconds` controls how long Docker waits for the plugin process to become healthy;
0 means wait indefinitely.

Args:
    name: The plugin name to enable
    timeout_seconds: Seconds to wait for the plugin to become healthy (0 = no timeout)

Returns:
    bool: True after the plugin is enabled

Input parameters:

- `name` (string, required)
- `timeout_seconds` (integer)

Output parameters:

- `result` (boolean)

### `plugin_remove` (~112 tokens)

Uninstall an engine plugin from the daemon.

Permanent removal - to deactivate but keep a plugin installed use `plugin_disable` instead. An
enabled plugin must be disabled first unless `force=True`. Plugin names come from
\`plugin_list`.

Args:
    name: The plugin name (e.g. "vieux/sshfs:latest")
    force: Remove even if the plugin is enabled

Returns:
    bool: True after removal

Input parameters:

- `force` (boolean)
- `name` (string, required)

Output parameters:

- `result` (boolean)

### `plugin_upgrade` (~160 tokens)

Upgrade an installed plugin to a newer version.

The plugin must be disabled first - call `plugin_disable` before this, then
\`plugin_enable` afterwards to bring it back up. `remote` lets you upgrade to a
different reference (e.g. a newer tag) than the plugin's current name; omit it to
re-pull the same reference. Existing settings and volumes created by the plugin
persist across the upgrade.

Args:
    name: The plugin name to upgrade
    remote: Reference to upgrade to, e.g. "vieux/sshfs:next" (default: same as name)

Returns:
    bool: True after the upgrade completes

Input parameters:

- `name` (string, required)
- `remote` (string)

Output parameters:

- `result` (boolean)

### `registry_tags` (~291 tokens)

List tags for a repository in an OCI v2 registry without pulling.

Works against Docker Hub, GHCR, ECR, GAR, and any OCI-compliant registry; anonymous if no
credentials are passed. Talks directly to the registry over HTTPS and does NOT read
\`~/.docker/config.json` - for private registries prefer the DOCKER_MCP_SERVER_REGISTRY_USERNAME /
DOCKER_MCP_SERVER_REGISTRY_PASSWORD env vars (keeps secrets out of tool args, which clients
often log). Fetch one tag's manifest with `registry_manifest`; `hub_tags` adds Hub-specific
tag metadata (sizes, push dates).

Args:
    repository: Image/repository ref, e.g. "alpine", "ghcr.io/org/repo"; any `:tag`/`@digest` is stripped
    username: Registry username (overrides DOCKER_MCP_SERVER_REGISTRY_USERNAME)
    password: Registry password/token (overrides DOCKER_MCP_SERVER_REGISTRY_PASSWORD)
    limit: Max tags to return (default 1000, >= 1); pagination capped at 50 pages

Returns:
    dict: {"name": <repo>, "registry": <host>, "tags": [..], "truncated": bool}

Input parameters:

- `limit` (integer)
- `password` (string)
- `repository` (string, required)
- `username` (string)

### `registry_tag_wait` (~423 tokens)

Block until a specific tag appears in a repository (e.g. waiting for a CI push to land).

Never raises on timeout - the result always carries `met` and `timed_out`. Polls `registry_tags`
every `poll_interval`s and checks whether `tag` is in its result. Works against Docker Hub too
(`registry_tags`' own scope covers it), so there is no separate Hub variant. Unlike every other
wait tool, this has no `host` argument - registry tools talk HTTPS directly to the registry, not
a Docker daemon.

Caveat: `registry_tags` paginates up to 50 pages (or `limit` tags, whichever comes first); if
\`tag` would only appear beyond that window it is never found, even once it exists. Raise `limit`
if you expect a very large tag list.

Args:
    repository: Image/repository ref, e.g. "alpine", "ghcr.io/org/repo"; any `:tag`/`@digest` is stripped
    tag: The exact tag name to wait for
    username: Registry username (overrides DOCKER_MCP_SERVER_REGISTRY_USERNAME)
    password: Registry password/token (overrides DOCKER_MCP_SERVER_REGISTRY_PASSWORD)
    limit: Max tags to scan per poll (default 1000, >= 1); forwarded to `registry_tags`
    timeout_seconds: Max seconds to wait before returning with timed_out=true
    poll_interval: Seconds between re-checks (default 5, > 0); capped by the time left so a large value can't push
        the total wait past the timeout

Returns:
    dict: {"repository", "tag", "met", "timed_out", "waited_seconds"}

Input parameters:

- `limit` (integer)
- `password` (string)
- `poll_interval` (number)
- `repository` (string, required)
- `tag` (string, required)
- `timeout_seconds` (number)
- `username` (string)

### `registry_manifest` (~235 tokens)

Fetch a repository's manifest without pulling.

May return a single-platform image manifest or a multi-platform manifest list / OCI image
index, depending on what the registry serves for that tag. Talks HTTPS directly - no daemon
or CLI needed. Alternatives for the same question: `buildx_imagetools_inspect` (uses the
docker CLI and its credential store) and `image_registry_data` (asks the daemon).

Args:
    repository: Image/repository ref, e.g. "ghcr.io/org/repo"; `:tag`/`@digest` is stripped - pass via `reference`
    reference: Tag or digest
    username: Registry username (overrides DOCKER_MCP_SERVER_REGISTRY_USERNAME; no config.json)
    password: Registry password/token (overrides DOCKER_MCP_SERVER_REGISTRY_PASSWORD)

Returns:
    dict: {"name", "registry", "reference", "media_type", "digest", "manifest": <JSON body>}

Input parameters:

- `password` (string)
- `reference` (string)
- `repository` (string, required)
- `username` (string)

### `registry_image_config` (~313 tokens)

Fetch and parse an image's config blob from a registry without pulling.

Answers "what's inside this image?" - env vars, entrypoint/cmd, workdir, exposed ports, user,
labels, layer history (what `registry_manifest` only points at via `config.digest`).
Resolves in up to three hops: manifest -> (if multi-platform) the `platform` entry's manifest
\-> the config blob. Fails where that resolves to no config descriptor, which means the
reference is a manifest list rather than an image - `registry_manifest` reads those.

Args:
    repository: Image/repository ref, e.g. "ghcr.io/org/repo"; `:tag`/`@digest` is stripped - pass via `reference`
    reference: Tag or digest
    platform: Platform to select from a multi-platform image, "os/arch[/variant]"; ignored
        for single-platform images
    username: Registry username (overrides DOCKER_MCP_SERVER_REGISTRY_USERNAME)
    password: Registry password/token (overrides DOCKER_MCP_SERVER_REGISTRY_PASSWORD)

Returns:
    dict: {"name", "registry", "reference", "platform", "config_digest", "config": <parsed>}; `platform` is the
        selected platform (None if single-platform)

Input parameters:

- `password` (string)
- `platform` (string)
- `reference` (string)
- `repository` (string, required)
- `username` (string)

### `hub_tags` (~191 tokens)

List tags on a Docker Hub repository with Hub-specific metadata.

Hits the Hub UI API (hub.docker.com) for richer per-tag data than `registry_tags` -
last pushed date, per-platform sizes, digest. Public repos only: sends no auth and does NOT
read `~/.docker/config.json`; private repos return 404/401 (use `registry_tags` against
registry-1.docker.io with credentials).

Args:
    repository: Hub repository, e.g. "library/alpine" or "myorg/myimage"
    limit: Max tags to return (default 100, >= 1); pagination capped at 50 pages

Returns:
    dict: {"name": <repo>, "tags": [{name, full_size, last_updated, digest, images}, ...], "truncated": bool}

Input parameters:

- `limit` (integer)
- `repository` (string, required)

### `hub_repo_info` (~141 tokens)

Fetch Docker Hub metadata for a repository.

Public repos only: sends no auth and does NOT read the local Docker credential store;
private repos return 404/401. Hub-only metadata (stars, pulls, description) - use
\`registry_tags` for tag lists on any OCI registry and `hub_tags` for Hub tag details.

Args:
    repository: Hub repository, e.g. "library/alpine" or "myorg/myimage"

Returns:
    dict: The Hub /v2/repositories/<repo>/ response (description, star_count, pull_count, last_updated, is_private,
        etc.)

Input parameters:

- `repository` (string, required)

### `hub_rate_limit` (~229 tokens)

Report the caller's remaining Docker Hub pull-rate-limit budget.

Sends a HEAD to the `ratelimitpreview/test` manifest (a HEAD isn't metered as a pull, so the
check costs no budget) and reads the RateLimit-Limit / RateLimit-Remaining headers. Call it
before a large `compose_pull` / `image_pull` to avoid hitting the cap mid-deploy. Credentials
raise the limit and switch metering from per-IP to per-account; falls back to
DOCKER_MCP_SERVER_REGISTRY_USERNAME / DOCKER_MCP_SERVER_REGISTRY_PASSWORD, does NOT read `~/.docker/config.json`.
Plans with no limit return no headers - reported as `"unlimited": true`.

Args:
    username: Hub username (overrides DOCKER_MCP_SERVER_REGISTRY_USERNAME)
    password: Hub password/token (overrides DOCKER_MCP_SERVER_REGISTRY_PASSWORD)

Returns:
    dict: {"authenticated", "limit", "remaining", "window_seconds", "unlimited"}

Input parameters:

- `password` (string)
- `username` (string)

### `service_create` (~308 tokens)

Create a Swarm service; requires a swarm manager node.

Use this instead of `container_run` when you need replicated or global scheduling,
rolling updates, or automatic restart across the swarm. Common `extra_kwargs` keys:
\`name` (str), `env` (list of "KEY=VAL"), `mode` ({"Replicated": {"Replicas": N}} or
{"Global": {}}), `networks` (list of network names/ids), `endpoint_spec`
({"Ports": [{"PublishedPort": 80, "TargetPort": 8080}]}), `labels` (dict),
\`restart_policy` ({"Condition": "on-failure", "MaxAttempts": 3}),
\`resources` ({"Limits": {"NanoCPUs": 500000000, "MemoryBytes": 134217728}}). For anything else
docker-py's `ServiceCollection.create` accepts, call `docs_lookup(section="services")` rather
than guessing a key name.

Args:
    image: Image to run service tasks from (e.g. "nginx:alpine")
    command: Override the image's default command; string or list of strings
    extra_kwargs: Additional docker-py ServiceCollection.create keyword arguments

Returns:
    dict: The created service's full document ({"ID", "Version", "Spec", ...})

Input parameters:

- `command`
- `extra_kwargs` (object)
- `image` (string, required)

### `service_inspect` (~131 tokens)

Get a swarm service by id or name.

Must run against a swarm manager. Returns the desired-state spec and rollout status - for the
actually-running tasks use `service_ps`, or the `service-tasks://{id_or_name}` resource for a
computed rollout summary.

Args:
    insert_defaults: Merge default values into the output

Returns:
    dict: The full service document ({"ID", "Version", "Spec", "Endpoint", ...}; "UpdateStatus" during a rolling
        update)

Input parameters:

- `id_or_name` (string, required)
- `insert_defaults` (boolean)

### `service_list` (~135 tokens)

List swarm services.

Must run against a swarm manager. One entry per service (the desired state); `service_ps`
lists a service's tasks, and `stack_services` groups services by stack.

Args:
    filters: Filter by attributes (id, name, label, mode)
    managed_only: Only return services created by this MCP server (filters on the docker-mcp-server.managed label);
        combines with any `filters` given

Returns:
    list: One full service document ({"ID", "Spec", ...}) per service

Input parameters:

- `filters` (object)
- `managed_only` (boolean)

### `service_update` (~170 tokens)

Update a swarm service's configuration, or force a redeploy with no spec change.

Pass exactly one of `updates` (fields to change, same parameters as `service_create`) or
\`force=True` (the `docker service update --force` equivalent: bumps the ForceUpdate counter so
the service's tasks redeploy with an unchanged spec - e.g. to reschedule after a node change or
re-pull a mutable tag).

Args:
    updates: Fields to update on the service; exactly one of updates/force
    force: Redeploy the service without changing its spec; exactly one of updates/force

Returns:
    bool: True after the update

Input parameters:

- `force` (boolean)
- `id_or_name` (string, required)
- `updates` (object)

Output parameters:

- `result` (boolean)

### `service_remove` (~76 tokens)

Stop and remove a swarm service.

Requires a swarm manager. Deletes the service definition and shuts down its tasks - no
confirmation, no undo. To stop work but keep the definition, `service_scale` to 0 replicas.

Returns:
    bool: True after the service is removed

Input parameters:

- `id_or_name` (string, required)

Output parameters:

- `result` (boolean)

### `service_ps` (~182 tokens)

List a swarm service's tasks (per-replica scheduling units), like `docker service ps`.

Shows where replicas run and why they fail: each task carries `Status`
(State/Message/ContainerStatus), `DesiredState`, `NodeID`, and `Slot`. Prefer this over
\`container_list` for services (tasks may run on other nodes), `stack_ps` for a whole stack,
and the `service-tasks://{id_or_name}` resource for a computed rollout summary. Requires a
swarm manager.

Args:
    filters: Filter dict; keys: id, name, node, label, desired-state (running|shutdown|accepted)

Returns:
    list: Task dicts (ID, Slot, NodeID, Status, DesiredState, Spec)

Input parameters:

- `filters` (object)
- `id_or_name` (string, required)

### `service_logs` (~308 tokens)

Get a bounded snapshot of a swarm service's logs (never follows).

\`follow` is intentionally not exposed: the stream is joined into one string before returning, so
following would block forever and grow unbounded. Collection is capped at `max_bytes` (ToolInputError
if exceeded) so a noisy service can't OOM the server. The default is a bounded `tail=200`;
\`tail="all"` returns the whole buffer, which can be huge on long-running services and exceed
the agent's context - prefer an integer, or `since`, to constrain output. Logs aggregate across
all the service's tasks: use `swarm_task_logs` for one task, `container_logs` for one container,
and the `service-logs://{id_or_name}` resource for the resource-flavored equivalent of this
tool.

Args:
    details: Show extra details
    since: Show logs since this Unix timestamp
    tail: Number of lines from the end, or the literal "all" for everything
    max_bytes: Abort with ToolInputError if the buffered logs exceed this many bytes (default 32 MiB)

Returns:
    str: Decoded log output

Input parameters:

- `details` (boolean)
- `id_or_name` (string, required)
- `max_bytes` (integer)
- `since` (integer)
- `stderr` (boolean)
- `stdout` (boolean)
- `tail`
- `timestamps` (boolean)

Output parameters:

- `result` (string)

### `service_scale` (~162 tokens)

Set the desired replica count for a Replicated-mode swarm service.

Only applies to services in `Replicated` mode; a `Global` service runs one task per
eligible node and has no replica count to set. The swarm scheduler places or removes
tasks asynchronously to converge on the new count - this call returns once the update
is accepted, not once every task is running. Check progress with `service_ps` or
\`service_inspect`. For any other spec change (image, env, resources) use
\`service_update` instead.

Args:
    replicas: The desired number of running task replicas

Returns:
    bool: True once the scale request is accepted

Input parameters:

- `id_or_name` (string, required)
- `replicas` (integer, required)

Output parameters:

- `result` (boolean)

### `service_rollback` (~144 tokens)

Roll a swarm service back to its previous spec (the docker `service rollback` equivalent).

Re-applies the service's `PreviousSpec` - the spec from before the most recent `service_update` /
\`service_scale`. Raises ToolInputError if the service has no PreviousSpec
(it has never been updated, or was already rolled back). The high-level SDK exposes no rollback,
so this reads the current version and previous spec via the low-level APIClient and submits them
with the low-level `update_service` API call.

Returns:
    dict: The daemon response (a dict with a "Warnings" key)

Input parameters:

- `id_or_name` (string, required)

### `service_wait` (~381 tokens)

Block until a swarm service's tasks converge, or a rolling update finishes.

One contract for both modes: never raises on timeout - the result always carries `met` and
\`timed_out`. "running" polls task state via the same task-counting logic as
\`service-tasks://{id_or_name}` (not the unconfirmed daemon `ServiceStatus` field) until running
tasks reach the desired count (Replicated mode) or every returned task is running (Global mode,
which has no fixed target). "update-converged" polls `UpdateStatus.State` until it reaches a
terminal value (`completed` or `rollback_completed`); if the service has never been updated (no
\`UpdateStatus` at all), returns promptly with `met=false` - there's nothing to converge to, same
as `container_wait`'s no-healthcheck case.

Args:
    until: Condition to wait for: "running" (default) or "update-converged"
    replicas: "running" mode only: override the desired replica count (e.g. right after a same-turn `service_scale`
        call, before polling reflects the new target)
    timeout_seconds: Max seconds to wait before returning with timed_out=true
    poll_interval: Seconds between re-checks (default 2, > 0); capped by the time left so a large value can't push
        the total wait past the timeout

Returns:
    dict: {"service", "until", "met", "timed_out", "running_tasks", "desired_tasks", "failed_tasks", "update_state",
        "waited_seconds"}

Input parameters:

- `id_or_name` (string, required)
- `poll_interval` (number)
- `replicas` (integer)
- `timeout_seconds` (number)
- `until` (string)

### `docs_lookup` (~285 tokens)

Look up Docker SDK/CLI/registry reference documentation by section.

A tool-callable mirror of the docker-docs:// resources, for clients that can't read MCP
resources (e.g. Claude Desktop, Cursor). Always registered regardless of
DOCKER_MCP_SERVER_DISABLE - looking something up costs nothing and isn't tied to any single
Docker feature area - but an individual section still refuses if the domain it documents is
disabled, matching the equivalent `docker-docs://{section}` resource exactly.

Omit `section` to list every available section with its source URL (same as
\`docker-docs://contents`); pass a `section` name to fetch that page's content (same as
\`docker-docs://{section}`). Most useful before constructing an `extra_kwargs`-style passthrough
dict for a tool like `container_run`/`container_create`/`service_create` (their docstrings only
list common keys, not every key docker-py accepts), or before writing Compose/Dockerfile/buildx
bake-file syntax, which no tool generates.

Args:
    section: Section name (from a no-argument call's index); omit to list all sections instead

Returns:
    str: JSON section index (no `section`) or that section's raw HTML/Markdown content

Input parameters:

- `section` (string)

Output parameters:

- `result` (string)

### `tool_list` (~427 tokens)

List this server's registered tools as compact rows, filtered by domain, category or keyword.

A tool-callable mirror of `docker-mcp://tool-catalog` for clients that can't read MCP resources
(e.g. Claude Desktop, Cursor), and the only way to ask what no per-tool description search can
express: which tools are destructive, which accept a `host`, what this server actually
registered. Use it to brief on an unfamiliar area (`domain="buildx"` returns one line per tool
rather than ~13 full definitions), to check blast radius (`category="destructive"`), or to
establish that nothing matches - `matched: 0` is a definitive negative, which a client's fuzzy
search cannot give. Covers this server's own surface; `docs_lookup` covers external Docker
reference documentation. Rows are summaries, not definitions - fetch a tool's own definition for
its parameters. Read-only, never raises on a query matching nothing, and always registered even
when DOCKER_MCP_SERVER_DISABLE drops every domain. A tool dropped by a switch or a disabled
domain is absent rather than flagged; `hidden_by_configuration` reports how many each domain
hides.

Args:
    domain: Exact domain name (see any result's `domains` key); omit for every domain
    category: Exact category; omit for all three
    keyword: Case-insensitive substring over tool names, summaries and parameter names

Returns:
    dict: {"matched": int, "tools": [{"name", "domain", "category", "summary"}], "domains": {domain: count},
        "no_domain": int, "hidden_by_configuration": {domain: count}, "switches", "filters"}. Every `domains` key is
        a value `domain` accepts; `no_domain` counts the domain-less tools, whose rows carry `domain: null` and
        which no `domain` value selects.

Input parameters:

- `category` (string)
- `domain` (string)
- `keyword` (string)

### `scout_cves` (~355 tokens)

List vulnerabilities (CVEs) in an image via Docker Scout.

Anonymous scans work for public images; Hub policy enforcement and richer recommendations need
\`docker login` on the host that runs the CLI - this server's host, or the target `ssh://` host
itself when no local scout plugin is installed. Start with `scout_quickview` for a
per-severity summary; `scout_sbom` inventories packages without vulnerability matching.
Does not raise on a non-zero CLI exit (a missing scout plugin or a timeout still raises) - inspect
\`raw.stderr`.

Args:
    image: Image reference (a tag or a digest)
    only_fixed: Only report CVEs with a fixed version available
    only_severity: Filter to these severities (omit for all)
    ignore_base: Exclude CVEs introduced by the base image
    format: Parsed into `result` as JSON: "sarif" (default, the standard vulnerability-report schema), "spdx",
        "gitlab", "sbom". Returned verbatim as text: "packages" (Scout's own default, grouped by package),
        "markdown". There is no plain "json" for this subcommand
    platform: Platform of the image to analyze, e.g. "linux/amd64"

Returns:
    dict: {"format": <format>, "result": <parsed-json-or-raw-text>, "raw": <CliResult dict>}

Input parameters:

- `format` (string)
- `ignore_base` (boolean)
- `image` (string, required)
- `only_fixed` (boolean)
- `only_severity` (array)
- `platform` (string)

### `scout_quickview` (~190 tokens)

Render a compact summary of an image's CVE posture.

The fastest triage step - counts per severity plus base-image status. Drill into individual
findings with `scout_cves`, which unlike this tool can emit machine-readable JSON; get upgrade
suggestions with `scout_recommendations`.
Output is plain text only: `docker scout quickview` has no output-format option, so `result`
is always the rendered text rather than a parsed document.
Does not raise on a non-zero CLI exit (a missing scout plugin or a timeout still raises) - inspect
\`raw.stderr`.

Args:
    platform: Platform of the image to analyze, e.g. "linux/amd64"

Returns:
    dict: {"result": <rendered text>, "raw": <CliResult dict>}

Input parameters:

- `image` (string, required)
- `platform` (string)

### `scout_recommendations` (~268 tokens)

Suggest base-image upgrades for an image.

Computed against Docker Scout's catalog; generally needs `docker login` on the host that runs the
CLI (the target `ssh://` host itself when no local scout plugin is installed) to return useful
results for private or rarely-scanned base images. The natural follow-up to `scout_cves` when the
fix is a newer base image.
Output is plain text only: `docker scout recommendations` has no output-format option, so
\`result` is always the rendered text rather than a parsed document.
Does not raise on a non-zero CLI exit (a missing scout plugin or a timeout still raises) - inspect
\`raw.stderr`.

Args:
    only_refresh: Only show "refresh" recommendations (same major/minor)
    only_update: Only show "update" recommendations (newer minor/major)
    tag: Restrict to suggestions matching this tag pattern
    platform: Platform of the image to analyze

Returns:
    dict: {"result": <rendered text>, "raw": <CliResult dict>}

Input parameters:

- `image` (string, required)
- `only_refresh` (boolean)
- `only_update` (boolean)
- `platform` (string)
- `tag` (string)

### `scout_compare` (~374 tokens)

Compare two image references and report the CVE delta.

Exactly one of `to`, `to_env`, or `to_latest=True` must be supplied to identify the comparison
target. Use it after a rebuild to check the new image against the old (`scout_cves` scans a
single image).
Does not raise on a non-zero CLI exit (a missing scout plugin or a timeout still raises) - inspect
\`raw.stderr`. Raises ToolInputError if `to` names a local directory/archive while the call has to run
on a remote `ssh://` host (no local scout plugin): the file is not staged, so it would resolve
against that host's filesystem instead.

Args:
    image: The new / candidate image reference
    to: Compare against this image reference, directory, or archive (a local directory/archive only when the CLI
        runs on this host - see above)
    to_env: Compare against an image associated with this Scout environment
    to_latest: Compare against the latest scan of `image`
    only_severity: Filter to these severities (omit for all)
    ignore_unchanged: Exclude unchanged packages from the diff
    format: Output format; only "json" (the default) is parsed into `result`
    platform: Platform of the image to analyze

Returns:
    dict: {"format": <format>, "result": <parsed-json-or-raw-text>, "raw": <CliResult dict>}

Input parameters:

- `format` (string)
- `ignore_unchanged` (boolean)
- `image` (string, required)
- `only_severity` (array)
- `platform` (string)
- `to` (string)
- `to_env` (string)
- `to_latest` (boolean)

### `scout_sbom` (~265 tokens)

Generate a Software Bill of Materials (SBOM) for an image.

Package inventory only - `scout_cves` adds vulnerability matching on top. SBOMs can be large;
captured stdout is subject to MAX_CLI_OUTPUT_BYTES and may be truncated for big images. If
that's a concern, run `docker scout sbom -o file.json ...` on the host and load the file
separately.
Does not raise on a non-zero CLI exit (a missing scout plugin or a timeout still raises) - inspect
\`raw.stderr`.

Args:
    format: "spdx" (default, SPDX JSON), "cyclonedx" (CycloneDX JSON), "json" (Scout's native JSON), or "list"
        (plain-text package list)
    platform: Platform of the image to analyze

Returns:
    dict: {"format", "result", "raw": <CliResult dict>}. `result` is a parsed dict when `format` is
        "spdx"/"cyclonedx"/"json" and stdout parses cleanly; for "list" or a parse failure it's the raw text.

Input parameters:

- `format` (string)
- `image` (string, required)
- `platform` (string)

### `secret_create` (~206 tokens)

Create a swarm secret; requires a swarm manager.

Write-once: the payload can never be read back through the API (`secret_inspect` returns
metadata only) and cannot be changed later - to rotate, create a new secret and update the
consuming services, keeping your own copy of the value. For non-sensitive data that should
stay readable, use `config_create` instead. Created secrets are stamped with provenance
labels.

Args:
    name: Name for the secret (unique within the swarm)
    data: The secret payload (max 500 KB; must be empty when driver is set)
    labels: Labels to set on the secret
    driver: Secret-driver config for values held in an external store

Returns:
    dict: The created secret's full document (ID and Spec metadata; never the payload)

Input parameters:

- `data` (string, required)
- `driver` (object)
- `labels` (object)
- `name` (string, required)

### `secret_inspect` (~149 tokens)

Get a swarm secret's metadata by id or name; requires a swarm manager.

The returned attrs never include the secret's actual data (`Spec.Data` is write-only -
the daemon accepts it on `secret_create` but never returns it back, by design). Use this
to check a secret's `CreatedAt`, `Labels`, or which driver created it, not to read its
contents. To see which services reference it, inspect each service's spec via
\`service_inspect` (there is no server-side filter for "services using this secret").

Returns:
    dict: The secret's full document, excluding the actual secret data

Input parameters:

- `id_or_name` (string, required)

### `secret_list` (~103 tokens)

List swarm secrets' metadata; requires a swarm manager.

Like `secret_inspect`, results never include secret data, only metadata (name, id,
labels, timestamps). Valid filter keys: `id`, `name`, `names`, `label` (key or
key=value).

Args:
    filters: Narrow the list; omit to return every secret

Returns:
    list: One full secret document per secret (data-free)

Input parameters:

- `filters` (object)

### `secret_remove` (~125 tokens)

Remove a Swarm secret; requires a swarm manager.

Removing a secret does not immediately affect running service tasks - tasks that already
have the secret mounted retain access until they are restarted or the service is updated.
Use `service_list` and inspect each service's spec via `service_inspect` to identify
services that mount the secret before removing it (service filters do not support
filtering by secret reference).

Args:
    id_or_name: The secret id or name to remove

Returns:
    bool: True after removal

Input parameters:

- `id_or_name` (string, required)

Output parameters:

- `result` (boolean)

### `stack_deploy` (~388 tokens)

Deploy (or update) a stack to the swarm from one or more Compose files.

Requires the target daemon to be a swarm manager. Re-running with the same `name` updates the
stack in place. Defaults to `detach=True` (returns once specs are submitted, not on
convergence); set `detach=False` to wait for the rollout (give it a generous
\`timeout_seconds`). The swarm analogue of `compose_up`; watch the rollout with
\`stack_services` / `stack_ps`.
Does not raise on a non-zero CLI exit (a missing `docker` binary or a timeout still raises) - inspect
\`returncode`/`stderr` in the result.

Args:
    name: Name of the stack to create or update
    compose_files: One or more Compose file paths (repeated `-c`; later override earlier). At least one required.
    with_registry_auth: Send registry credentials to swarm agents (needed for private images)
    prune: Remove services no longer defined in the Compose file
    resolve_image: Image-digest resolution; omit for the CLI default ("always")
    detach: Return immediately after submitting specs (True) vs wait for convergence (False)
    cwd: Working directory for resolving relative Compose paths (defaults to the server's cwd; copied to the target
        host if no local docker CLI)
    timeout_seconds: Subprocess timeout (default 1800s)

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}

Input parameters:

- `compose_files` (array, required)
- `cwd` (string)
- `detach` (boolean)
- `name` (string, required)
- `prune` (boolean)
- `resolve_image` (string)
- `timeout_seconds` (number)
- `with_registry_auth` (boolean)

### `stack_list` (~87 tokens)

List the stacks deployed to the swarm, parsed from `--format '{{json .}}'`.

Requires the target daemon to be a swarm manager. `compose_list` is the non-swarm equivalent;
drill into one stack with `stack_services`.
Raises RemoteFailureError if the CLI call fails.

Returns:
    list: One dict per stack (name, services count, orchestrator)

### `stack_ps` (~169 tokens)

List the tasks of a stack, parsed from `--format '{{json .}}'`.

Task-level view across every service in the stack (`service_ps` covers one service): where
each task runs and why it failed. Requires a swarm manager.
Raises RemoteFailureError if the CLI call fails.

Args:
    name: The stack to list tasks for
    no_trunc: Do not truncate task IDs / errors in the output
    filters: Filter by attributes, e.g. {"desired-state": "running"}; a list value repeats the filter

Returns:
    list: One dict per task (id, name, node, image, desired/current state, error)

Input parameters:

- `filters` (object)
- `name` (string, required)
- `no_trunc` (boolean)

### `stack_services` (~147 tokens)

List the services of a stack, parsed from `--format '{{json .}}'`.

Service-level rollup (replicas ready per service); use `stack_ps` for individual tasks and
\`service_inspect` for one service's full spec. Requires a swarm manager.
Raises RemoteFailureError if the CLI call fails.

Args:
    name: The stack to list services for
    filters: Filter by attributes, e.g. {"name": "web"}; a list value repeats the filter

Returns:
    list: One dict per service (id, name, mode, replicas, image, ports)

Input parameters:

- `filters` (object)
- `name` (string, required)

### `stack_remove` (~224 tokens)

Remove one or more stacks from the swarm (tears down their services, networks, and secrets).

Destructive: this stops and deletes every service in the named stack(s) - the reverse of
\`stack_deploy` and the swarm analogue of `compose_down`. Defaults to `detach=True` so the call
returns once removal is requested rather than waiting for teardown.
Does not raise on a non-zero CLI exit (a missing `docker` binary or a timeout still raises) - inspect
\`returncode`/`stderr` in the result.

Args:
    names: One or more stack names to remove. At least one is required.
    detach: Return immediately (True) vs wait for the stack(s) to be fully removed (False)
    timeout_seconds: Subprocess timeout (default 300s)

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool}

Input parameters:

- `detach` (boolean)
- `names` (array, required)
- `timeout_seconds` (number)

### `swarm_init` (~428 tokens)

Initialize a new swarm, making this Engine its first manager node.

Fails if the Engine is already part of a swarm - call `swarm_leave` first to reset it.
\`advertise_addr` only needs setting when the host has multiple network interfaces or is
behind NAT (otherwise it is auto-detected); it must be reachable by every other node
that will join. To add more nodes afterwards, retrieve join tokens with
\`swarm_join_tokens` and call `swarm_join` on each one. Set `autolock_managers=True` to
require the unlock key (`swarm_unlock_key`) on every manager restart - store that key
securely immediately, since it is only shown once autolock is enabled.

Args:
    name: Name for the swarm cluster itself, not for the node running init
    advertise_addr: Externally reachable address advertised to other nodes
    listen_addr: Listen address used for inter-manager communication
    force_new_cluster: Force a new single-node cluster from this node's current state (disaster recovery when a
        majority of managers is lost)
    default_addr_pool: IP address pools for swarm overlay networks
    subnet_size: Subnet size for the IP pool
    data_path_addr: Address to use for data path traffic
    data_path_port: Port number for data path traffic
    labels: Labels to set on the swarm
    autolock_managers: Require the unlock key after every manager restart
    log_driver: Default log driver configuration

Returns:
    str: The node id of the newly created swarm manager

Input parameters:

- `advertise_addr` (string)
- `autolock_managers` (boolean)
- `data_path_addr` (string)
- `data_path_port` (integer)
- `default_addr_pool` (array)
- `force_new_cluster` (boolean)
- `labels` (object)
- `listen_addr` (string)
- `log_driver` (object)
- `name` (string)
- `subnet_size` (integer)

Output parameters:

- `result` (string)

### `swarm_join` (~268 tokens)

Join this Engine to an existing swarm as a worker or manager.

Fails if the Engine is already part of a swarm. Whether this node joins as a worker or
a manager is determined entirely by which token is passed - `join_token` must be one of
the two tokens from `swarm_join_tokens`, called against any existing manager. `advertise_addr`
only needs setting when this host has multiple network interfaces or is behind NAT
(otherwise it is auto-detected from the interface used to reach `remote_addrs`); it must
be reachable by every other node in the swarm.

Args:
    remote_addrs: Address(es) of existing swarm managers to connect to
    join_token: The worker or manager join token (from `swarm_join_tokens`) - determines the role this node joins as
    listen_addr: Listen address for inter-manager communication
    advertise_addr: Externally reachable address advertised to other nodes
    data_path_addr: Address to use for data path traffic

Returns:
    bool: True after the engine joins the swarm

Input parameters:

- `advertise_addr` (string)
- `data_path_addr` (string)
- `join_token` (string, required)
- `listen_addr` (string)
- `remote_addrs` (array, required)

Output parameters:

- `result` (boolean)

### `swarm_leave` (~99 tokens)

Leave the current swarm.

The daemon's service tasks are rescheduled to the remaining nodes. A manager refuses to leave
without force=True, since leaving can break raft quorum. The departed node lingers as "down"
in `node_list` until a manager runs `node_remove`.

Args:
    force: Force leave even if the node is a manager

Returns:
    bool: True after leaving the swarm

Input parameters:

- `force` (boolean)

Output parameters:

- `result` (boolean)

### `swarm_update` (~417 tokens)

Update swarm-wide settings: the single home for join-token rotation and cluster spec changes.

Must be called on a swarm manager node. Token rotation invalidates the old join token
immediately - nodes that have not yet joined using the old token must use the new one.
Existing joined nodes are unaffected. Use `swarm_join_tokens` to retrieve the new
tokens after rotation. Rotating the unlock key requires all managers to be re-unlocked
on restart with the new key; retrieve it immediately via `swarm_unlock_key`. Rotation and
\`updates` are independent and may be combined in one call; `swarm_init` sets these same
fields when the swarm is first created, and `swarm_inspect` reads the current values back.

The Engine replaces the whole cluster spec on every update, so this reads the current spec
first and resubmits it, merging `updates` over it - omitting `updates` therefore changes
nothing but the requested rotation.

Args:
    rotate_worker_token: Issue a new worker join token, invalidating the current one
    rotate_manager_token: Issue a new manager join token, invalidating the current one
    rotate_manager_unlock_key: Issue a new autolock unlock key for manager restart
    updates: Engine SwarmSpec fields to change, merged over the current spec one top-level key at a time, so a named
        block is replaced whole rather than field by field: keys are "Name", "Labels", "Orchestration", "Raft",
        "Dispatcher", "CAConfig", "EncryptionConfig" and "TaskDefaults", e.g. {"EncryptionConfig":
        {"AutoLockManagers": True}} to turn manager autolock on. Read the current blocks from `swarm_inspect`

Returns:
    bool: True after the update completes

Input parameters:

- `rotate_manager_token` (boolean)
- `rotate_manager_unlock_key` (boolean)
- `rotate_worker_token` (boolean)
- `updates` (object)

Output parameters:

- `result` (boolean)

### `swarm_inspect` (~83 tokens)

Inspect the swarm this daemon belongs to (id, spec, join-token config, CA info).

Works on a manager node only. Cluster-level configuration - for per-node state use
\`node_list`; for the tokens new nodes need, `swarm_join_tokens`.

Returns:
    dict: The swarm's attrs, as returned by the daemon's swarm inspect endpoint

### `swarm_unlock` (~180 tokens)

Unlock a manager node that is locked after restart due to autolock being enabled.

When autolock is enabled (via `swarm_init` or `swarm_update`), manager nodes require
the unlock key after every restart before they can rejoin the swarm and resume
scheduling. Must be called on the locked manager node directly. Retrieve the current
unlock key with `swarm_unlock_key` from any unlocked manager - store it securely
when enabling autolock. A locked node cannot serve API requests and cannot return its
own key while locked; other unlocked managers in the swarm can still serve the key.
Once unlocked the manager resumes automatically.

Args:
    key: The swarm unlock key (from `swarm_unlock_key`)

Returns:
    bool: True after the swarm is unlocked

Input parameters:

- `key` (string, required)

Output parameters:

- `result` (boolean)

### `swarm_unlock_key` (~125 tokens)

Return the swarm's current unlock key.

The key only serves a purpose when autolock is enabled (see `swarm_init`'s /
\`swarm_update`'s `autolock_managers` / `rotate_manager_unlock_key`). Must be called
against an unlocked manager - a locked manager cannot serve API requests, including
this one. Feed the result's key to `swarm_unlock` to unlock a manager after restart.
Treat the key as a sensitive credential.

Returns:
    dict: {"UnlockKey": <the current unlock key>}

### `swarm_join_tokens` (~132 tokens)

Return the swarm's worker and manager join tokens.

These are the tokens a new node passes to `swarm_join` - without one, `swarm_join` cannot be
called, so this closes the init -> join loop. The tokens are secret bearer credentials (anyone
holding the manager token can join as a manager); treat the result as sensitive and avoid logging
it. Reads `swarm.attrs["JoinTokens"]` after a reload, so it always reflects the current tokens.

Returns:
    dict: {"Worker": <worker join token>, "Manager": <manager join token>}

### `swarm_task_list` (~317 tokens)

List tasks across the whole swarm, like `docker service ps` with no service to scope it.

The cluster-wide view of what is actually scheduled. `service_ps` covers one service and
\`stack_ps` one stack, so answering "what is failing anywhere" or "what is running on this node"
through those means looping over every service; this is one call, and `swarm_task_logs` reads
what a failing one printed. Filter by `node` for a node's workload (the CLI's `docker node ps`),
\`desired-state` to separate what should be running from what is shutting down, or `service` for
a single service -- for which `service_ps` is the simpler call. Each task carries its full
\`Spec`, including the `ContainerSpec` (image, command, env), so this returns much more per task
than the `service-tasks://{id_or_name}` resource's computed rollout summary. Read-only. Requires
a swarm manager: on any other node the daemon refuses, and its refusal is what comes back.

Args:
    filters: Filter dict; keys: id, name, service, node, label, desired-state (running|shutdown|accepted); omit for
        every task in the cluster

Returns:
    list: One full task document per task (ID, ServiceID, NodeID, Slot, Spec, Status, DesiredState), the same shape
        `service_ps` returns

Input parameters:

- `filters` (object)

### `swarm_task_inspect` (~377 tokens)

Inspect a single swarm task, like `docker inspect --type task`.

For when you already hold a task reference -- from a `swarm_task_list` or `service_ps` row, a
service event, or an error message -- and want just that task. `swarm_task_list` returns the
same document for every task, so prefer it when scanning; this is the single-object fetch.
To reach the container behind a running task, read `Status.ContainerStatus.ContainerID` and pass
it to `container_inspect` / `container_logs` -- but note the container may be on another node,
where those tools cannot see it, and `service_logs` aggregates across tasks instead;
\`swarm_task_logs` reads that one task's output wherever it landed. Read-only.
Requires a swarm manager; reports the daemon's own error if the task does not exist, if a
prefix matches more than one task, or if this node is not a manager.

Args:
    id_or_name: The task id, an unambiguous id prefix, or the task's full name -- which is the container-name form
        `<service>.<slot>.<taskid>` (`<service>.<nodeid>.<taskid>` for a global service), NOT the shorter
        `<service>.<slot>` that `docker service ps` prints in its NAME column, which does not resolve. The daemon
        tries full id, then full name, then prefix, and rejects an ambiguous prefix rather than picking a match

Returns:
    dict: Full task inspect payload, as `docker inspect --type task`. Carries no name field of its own; compose one
        from `ServiceID`/`Slot` if you need it

Input parameters:

- `id_or_name` (string, required)

### `swarm_task_logs` (~412 tokens)

Get a bounded snapshot of one swarm task's logs (never follows).

The per-replica counterpart to `service_logs`, which interleaves every task in the service: use
this to read the replica that actually failed, found with `swarm_task_list` or `service_ps`.
\`container_logs` is no substitute on a multi-node swarm - the task's container lives on
whichever node the scheduler placed it on, and this server talks to one daemon.

As with `service_logs`, `follow` is not exposed (the stream is joined into one string before
returning, so following would never finish) and collection is capped at `max_bytes`. The Engine
offers no `until` bound here, unlike `container_logs`, so narrow with `since` or an integer
\`tail`.

docker-py has no task collection and no `APIClient.task_logs`, so this drives its private
request helpers against the published `GET /tasks/{id}/logs`, raising `CapabilityError` if those
internals move. Drop the reach-in if docker-py grows a public method.

Args:
    id_or_name: The task id, an unambiguous id prefix, or its full `<service>.<slot>.<taskid>` name; see
        `swarm_task_inspect` for how the daemon resolves these and which name forms do not work
    since: Show logs since this Unix timestamp
    tail: Number of lines from the end, or the literal "all" for everything
    max_bytes: Abort with ToolInputError if the buffered logs exceed this many bytes (default 32 MiB)

Returns:
    str: Decoded log output

Input parameters:

- `details` (boolean)
- `id_or_name` (string, required)
- `max_bytes` (integer)
- `since` (integer)
- `stderr` (boolean)
- `stdout` (boolean)
- `tail`
- `timestamps` (boolean)

Output parameters:

- `result` (string)

### `volume_create` (~324 tokens)

Create a volume managed by Docker.

Named volumes persist after their containers stop or are removed; use them for
databases, uploads, or any data that must outlive a container. Anonymous volumes
(no `name`) are only removed automatically when the container was started with `--rm`
or removed with `docker rm -v`; otherwise they accumulate and must be pruned manually.
Common `driver_opts` for the default `local` driver: bind-mount an existing host path
with `{"type": "none", "device": "/host/path", "o": "bind"}`, or mount an NFS share
with `{"type": "nfs", "device": "server:/export", "o": "addr=server,rw"}`. Third-party
drivers (e.g. `rexray`, `convoy`) accept their own option keys. List existing volumes with
\`volume_list`; reclaim unused ones with `volume_prune`. Created volumes are stamped with provenance
labels.

Args:
    name: Volume name; auto-generated if omitted (creates an anonymous volume)
    driver: Volume driver to use (default: "local")
    driver_opts: Driver-specific options dict
    labels: Labels to set on the volume

Returns:
    dict: The created volume's full document (keys include Name, Driver, Mountpoint, CreatedAt,
        Labels, Options, Scope)

Input parameters:

- `driver` (string)
- `driver_opts` (object)
- `labels` (object)
- `name` (string)

### `volume_inspect` (~121 tokens)

Get a volume's full inspect payload by name.

Use it after `volume_list` to see a volume's on-disk location, driver, and labels - e.g.
before a backup or `volume_remove`. Volumes are addressed purely by name; they have no
separate id.

Args:
    name: The volume name (volumes have no ids)

Returns:
    dict: The volume's full document (keys include Name, Driver, Mountpoint, CreatedAt, Labels,
        Options, Scope)

Input parameters:

- `name` (string, required)

### `volume_list` (~151 tokens)

List volumes.

Volumes are addressed by name only - feed a Name to `volume_inspect` for detail or
\`volume_remove` / `volume_prune` to clean up. filters={"dangling": True} finds volumes that no
container references.

Args:
    filters: Filter by attributes (e.g. dangling, name, label)
    managed_only: Only return volumes created by this MCP server (filters on the docker-mcp-server.managed label);
        combines with any `filters` given

Returns:
    list: One volume document ({"Name", "Driver", "Mountpoint", ...}) per volume

Input parameters:

- `filters` (object)
- `managed_only` (boolean)

### `volume_prune` (~162 tokens)

Remove volumes not referenced by any container, running or stopped.

A volume used by even one stopped container is not "unused" and survives the prune -
remove the container first (or use `container_prune`, then this) to reclaim its
volumes. Valid filter keys: `label` (key or key=value), `all` ("true" as a string -
without it only anonymous volumes are eligible, matching `docker volume prune`'s
default). Use `volume_list` first to see what currently exists.

Args:
    filters: Narrow which unused volumes to remove; omit to remove all anonymous ones

Returns:
    dict: {"VolumesDeleted": [...], "SpaceReclaimed": <bytes>}

Input parameters:

- `filters` (object)

### `volume_remove` (~130 tokens)

Remove a single volume by name.

Fails if any container, running or stopped, still references the volume - remove or
recreate those containers first, or pass `force=True` to remove it anyway (the
containers keep their reference but lose the underlying data). For bulk cleanup of
volumes with no container references at all, use `volume_prune` instead.

Args:
    name: Volume name to remove
    force: Remove even if a container still references the volume

Returns:
    bool: True after removal

Input parameters:

- `force` (boolean)
- `name` (string, required)

Output parameters:

- `result` (boolean)

## Diagnostics

Captured diagnostic sections: Provenance. The full working is on the page: https://verifymcp.io/servers/l337-org-docker-mcp-server/ghcr-io-l337-org-docker-mcp-server-2-2-6#diagnostics

## Score history

- 2026-09-21: 41
- 2026-09-20: 41
- 2026-09-19: 40
- 2026-09-18: 36
- 2026-09-17: 36
- 2026-09-16: 36
- 2026-09-15: 36
- 2026-09-14: 36
- 2026-09-13: 36
- 2026-09-12: 36
- 2026-09-11: 36
- 2026-09-10: 36

## Common questions

### What is the Docker MCP Server server?

Docker MCP Server is listed in the public MCP registry as io.github.L337-org/docker-mcp-server. Manage Docker (containers, images, Compose, Swarm, registries) via the Docker SDK and CLI. This page covers its container image (ghcr.io/l337-org/docker-mcp-server:2.2.6).

### Is the Docker MCP Server server safe to use?

Docker MCP Server scores 41 out of 100 on VerifyMCP. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

### What tools does the Docker MCP Server server expose?

Docker MCP Server exposes 165 tools: buildx_build, buildx_bake, buildx_imagetools_inspect, buildx_imagetools_create, buildx_list, and 160 more. Their descriptions and schemas cost roughly 39,062 tokens of context every time the server is loaded.

### Is the Docker MCP Server server still maintained?

Docker MCP Server is still listed as active in the MCP registry. We last reached this channel on 21 September 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

## Links

- Repository: https://github.com/L337-org/docker-mcp
- Changelog RSS feed: https://verifymcp.io/servers/l337-org-docker-mcp-server/ghcr-io-l337-org-docker-mcp-server-2-2-6.xml
- Changelog JSON feed: https://verifymcp.io/servers/l337-org-docker-mcp-server/ghcr-io-l337-org-docker-mcp-server-2-2-6.json
- HTML version of this page: https://verifymcp.io/servers/l337-org-docker-mcp-server/ghcr-io-l337-org-docker-mcp-server-2-2-6
