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

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

- Trust score: 39/100 (low)
- Change this week: +3
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-10-23

## Components

- mcpb · `docker-mcp-server-2.2.8.mcpb`: 13/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`: 39/100 (this document), [markdown](https://verifymcp.io/servers/l337-org-docker-mcp-server/ghcr-io-l337-org-docker-mcp-server.md), [page](https://verifymcp.io/servers/l337-org-docker-mcp-server/ghcr-io-l337-org-docker-mcp-server)
- pypi · `docker-mcp-server`: 35/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`
- Version: `2.2.8`
- 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-10-23.

- **Supply Chain Security**: 0/100
  - No malware scan is available for this kind of package: the supply-chain vendors we use do not cover it. This is a permanent gap in our coverage, not a finding about the package.
  - Known CVEs could not be checked: this artifact ships no SBOM or dependency manifest, so there is no dependency list to read.
  - Install-script risk not yet assessed.
  - Dependency health could not be checked: this artifact ships no SBOM or dependency manifest, so there is no dependency list to read.
- **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 0 days ago).
  - Publishes a security disclosure policy (SECURITY.md).
- **Schema Quality & AI Usability**: 74/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 36263 tokens (~230/item across 157 items; 154 tools + 3 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 22/100
  - Stability check failed: the tool surface changed between 2.2.7 and 2.2.8: 12 tool removals, 2 breaking changes, 1 additions.
- **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 156 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.8. 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.8
```

### Cursor

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

### VS Code

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

### Codex

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

### 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.8"
      ],
      "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.8"]
```

### 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.8"
      ]
    }
  }
}
```

### Other

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

## 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-10-22 (score 39, 0)

- [security regression] Stability: 0.23 → unverified
- [security regression] Tool safety: pass → unverified
- [security regression] Stability: 0.23 → fail
- [functional regression] Capabilities: pass → unverified
- [functional regression] Tool coverage: 100 → unverified
- [functional regression] Schema quality: 100 → unverified
- [functional improvement] Schema quality: 40351 → 36263
- [functional] Package version: 2.2.7 → 2.2.8

### 2026-10-19 (score 39, +1)

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

### 2026-10-17 (score 38, +2)

- [functional improvement] Stability: unverified → 0.10

### 2026-10-14 (score 36, +29)

- [security improvement] Injection markers: unverified → pass
- [security] First check of Judged manipulation: pass
- [security] Stability: Stability not yet verified: not enough scan history yet (needs a 30-day window).
- [functional improvement] Tool coverage: unverified → 100
- [functional improvement] Schema quality: unverified → 100
- [functional improvement] MCP protocol: unverified → pass
- [functional] First check of Destructive annotations: 100
- [functional] First check of Tool coverage: 0
- [functional] First check of Schema quality: fail
- [functional] First check of Schema quality: good
- [functional] First check of Tool coverage: 23
- [functional] First check of Schema quality: fail

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

First indexed and scored.

## MCP tools (154)

### `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` (~225 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:
    password: Registry password or token
    registry: URL to the registry (defaults to Docker Hub)
    reauth: Force re-authentication even if valid credentials exist
    dockercfg_path: A dockercfg file on the server host to use instead of `~/.docker/config.json`

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

Input parameters:

- `dockercfg_path` (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` (~376 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. To wait for a state rather than an event
(a container exited or healthy, a service converged), use `system_wait`, which checks the state itself
and so also sees one reached before the call.

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_wait` (~603 tokens)

Wait until a container/compose project exits or is healthy, a service/stack converges, a node/registry tag is ready.

The one wait tool: action tools return once their work is started (bar `container_exec` and a
timed `container_run`), and this waits for the result. For
a status read without waiting use `container_inspect`, `service_ps` or `compose_ps`; to follow changes
as they happen, `system_events`. Never raises on timeout. `met: false` without `timed_out` means the
condition can no longer be met (the target stopped, has nothing to wait for, or is gone): `detail`
says why.

\`kind`: `target` - `until` values:
\- container: id or name - not-running (exit code in `detail.status_code`), next-exit, removed,
  healthy (no HEALTHCHECK: not met), log-match (finds `pattern` in recent logs)
\- service: swarm manager only; id or name - running (all desired tasks, or `replicas`), update-converged
\- node: swarm manager only; id or hostname - ready, down, disconnected, unknown
\- stack: swarm manager only; stack name - converged (every service's tasks running, updates finished), removed
\- compose: project name from `compose_list` - running, healthy (no HEALTHCHECK: running is enough;
  a job others wait on may have exited 0), exited (exit codes in `detail`); `services` narrows it
\- registry_tag: "repository:tag" - present (polls the registry over HTTPS every 5 s by default; no
  daemon, no `host`)

Args:
    poll_interval: Seconds between checks (default 2, unless the kind says otherwise), capped by the time left
    pattern: log-match: a plain substring, or with `regex=True` a regular expression (trusted patterns
        only: a hostile one run over attacker-influenced logs can exhaust CPU)
    username: registry_tag: overrides DOCKER_MCP_SERVER_REGISTRY_USERNAME
    password: registry_tag: overrides DOCKER_MCP_SERVER_REGISTRY_PASSWORD
    limit: registry_tag: most tags read per poll; a tag beyond it is never found

Returns:
    dict: {"kind", "target", "until", "met", "timed…

Input parameters:

- `kind` (string, required)
- `limit` (integer)
- `password` (string)
- `pattern` (string)
- `poll_interval` (number)
- `regex` (boolean)
- `replicas` (integer)
- `services` (array)
- `target` (string, required)
- `timeout_seconds` (number)
- `until` (string, required)
- `username` (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

### `buildx_build` (~929 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; options that would resolve on that host instead (a filesystem `dest=`, a local cache `src=`,
\`ssh=`) are refused.
Does not raise on a non-zero CLI exit - inspect `returncode`/`stderr`.

Args:
    context: Build context: a filesystem path or Git/HTTP URL (a leading `~` is expanded; `-` is refused).
        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 leading `~` is expanded. 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"}); a leading `~` is expanded
    labels: Labels to set on the resulting image (each becomes `--label KEY=VALUE`)
    annotations: OCI manifest annotations (passed verbatim, repeatable)
    target: Target build stage to stop at
    push: Push the result to the registry (mutually exclusive with `load`)
    load: Load the result into the local image store (single-platform builds only)
    output: Custom `--output` specs (e.g. ["type=tar,dest=out.tar"]); `dest=-` is stdout
    no_cache…

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` (~293 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 - inspect `returncode`/`stderr`.

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"]
    load: Load results into the local image store
    no_cache: Do not use cache when building
    pull: Always pull a newer base image
    cwd: Working directory containing the bake file (defaults to the server's cwd; copied to the target host if no
        local plugin)

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` (~282 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 - inspect `returncode`/`stderr`.

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`)

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` (~292 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 - inspect `returncode`/`stderr`.

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)

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` (~150 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 - inspect `returncode`/`stderr`.

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` (~184 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`'s `BuildCache` reports
the same records for a `docker`-driver builder without needing buildx; a builder on another
driver keeps its own cache, which only this tool reports.
Raises RemoteFailureError if the CLI call fails.

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

Input parameters:

- `builder` (string)

### `buildx_prune` (~383 tokens)

Remove BuildKit build cache entries to reclaim disk space.

Destructive and immediate: later builds re-run the steps whose cache was removed. Always passes
\`--force`, since no interactive prompt is available under MCP. Inventory first with `buildx_du`
or `system_df` (its `BuildCache` entry); build cache is separate from the images `image_prune`
removes, so run both to reclaim everything a build leaves behind. With no buildx plugin here and
a host not reached over ssh://, this prunes the daemon's default build cache through the Engine
API instead: `builder` and the three space limits cannot be sent that way, so passing any of them
there raises rather than pruning without the limit. Does not raise on a non-zero CLI exit - inspect
\`returncode`/`stderr`.

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")

Returns:
    dict: {"returncode": int, "stdout": str, "stderr": str, "truncated": bool} through the CLI; through the Engine
        API, {"CachesDeleted": [...], "SpaceReclaimed": <bytes>}

Input parameters:

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

### `buildx_create` (~368 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 - inspect `returncode`/`stderr`.

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` (~241 tokens)

Select the active builder for subsequent buildx operations.

The choice always persists: buildx saves it in the Docker CLI config of the machine the command
runs on - this server's host, or the remote host under the `ssh://` fallback - so every later buildx
command there uses it, including ones run outside this server, until it is changed. Use `buildx_list`
to see available builders and their current status. To build with a builder without changing the
selection, pass `builder` to `buildx_build` instead.
Does not raise on a non-zero CLI exit - inspect `returncode`/`stderr`.

Args:
    name: Builder name to activate (from `buildx_list`)
    default: Also set it as the default builder for the current Docker context (`--default`)
    global_default: Keep it selected when the Docker context changes (`--global`)

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

Input parameters:

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

### `buildx_remove` (~201 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 - inspect `returncode`/`stderr`.

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)

### `compose_up` (~296 tokens)

Bring up a Docker Compose project, detached.

Always runs detached (`-d`), so it returns once the containers are started. To wait for them, call
\`system_wait(kind="compose", target=<project name>, until="healthy")` (or `until="running"`);
\`compose_ps` shows their state without waiting.
Does not raise on a non-zero CLI exit - inspect `returncode`/`stderr`.

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`)
    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

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)

### `compose_down` (~229 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 - inspect `returncode`/`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`)
    volumes: Also remove named volumes declared by the project (destructive)
    remove_orphans: Remove containers not declared in the compose file

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` (~218 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: `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`)
    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` (~296 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 - inspect `returncode`/`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`)
    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` (~236 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: 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`)
    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` (~221 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 - inspect `returncode`/`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`)
    services: Specific services to build (default: all)
    pull: Always attempt to pull a newer base image
    no_cache: Do not use cache when building

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` (~283 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 - inspect `returncode`/`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`; overrides auto-discovery)
    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` (~262 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 - inspect `returncode`/`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`)
    services: Restart only these services; omit to restart all
    stop_timeout_seconds: Seconds to wait for graceful stop before SIGKILL

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` (~209 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 - inspect `returncode`/`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`)
    services: Specific services to stop (default: all)
    stop_timeout_seconds: Grace period before SIGKILL (passed as `--timeout`)

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` (~191 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 - inspect `returncode`/`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`)
    services: Specific services to start (default: all)

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` (~388 tokens)

Run a one-off command against a compose service.

Always detached (`-d`) and `-T` (no TTY under MCP): it returns once the container starts, with the
container's id as `stdout`, newline-terminated (strip it). For its result,
\`system_wait(kind="container", target=<that id>, until="not-running")` gives the exit code,
\`container_logs` the output, and `container_remove` the cleanup; the container is kept until then,
because removing it on exit would delete its logs. 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 - inspect `returncode`/`stderr`.

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`)
    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

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

Input parameters:

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

### `compose_exec` (~313 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 - inspect `returncode`/`stderr`.

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`)
    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

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` (~188 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`)
    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` (~318 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`)

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_top` (~183 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 - inspect `returncode`/`stderr`.

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`)

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

Input parameters:

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

### `compose_cp` (~497 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 - inspect `returncode`/`stderr`. 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: Also include one-off containers created by `compose_run` (`--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`)

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` (~239 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 - inspect `returncode`/`stderr`.

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`)

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` (~209 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 - inspect `returncode`/`stderr`.

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`)

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

Input parameters:

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

### `compose_unpause` (~184 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 - inspect `returncode`/`stderr`.

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`)

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` (~685 tokens)

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

Returns once the container has started. For a one-shot command set `timeout_seconds` (with `auto_remove`,
like `docker run --rm`) to wait for it to exit and get its exit code and output; one still running then
is reported `timed_out` and left, never auto-removed: remove it with `container_remove(force=True)`. For
a long job, wait with `system_wait` instead. `container_create` prepares one without starting it;
\`container_exec` runs a command in an existing one. Created containers get provenance labels.

Args:
    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
    user: Username or UID to run as
    restart_policy: Restart policy, e.g. {'Name': 'on-failure', 'MaximumRetryCount': 3}
    labels: Labels to set on the container
    auto_remove: Delete the container on exit, logs and exit code included; with `timeout_seconds`, after
        they are read. Not with a restart policy
    privileged: Give extended privileges to the container
    mem_limit: Memory limit: bytes as an int, or a units string ("100000b", "1000k", "128m", "1g")
    cpu_count: Usable CPUs, on a Windows daemon only
    timeout_seconds: Seconds to wait for exit before returning with timed_out=true
    extra_kwargs: Additional keyword arguments forwarded to ContainerCollection.run (call
        `docs_lookup(section="containers")` for the full accepted set)

Returns:
    dict: Without `timeout_seconds`, the started container's full inspect payload; an `auto_remove` container
        already gone returns the payload from before the start, with `removed_on_exit: true`. With it,
        {"id", "name", "exit_code", "stdout", "stderr", "timed_out", "truncated", "removed", "error"}: each…

Input parameters:

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

### `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` (~116 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`.

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` (~131 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`.

Returns:
    dict: The container's full inspect payload after starting; for an `auto_remove` container already
        gone, the one from before, with `removed_on_exit: true`

Input parameters:

- `id_or_name` (string, required)

### `container_stop` (~202 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); for an
        `auto_remove` container, which the stop deletes, the one from before, with `removed_on_exit: true`

Input parameters:

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

### `container_restart` (~158 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; for an `auto_remove` container already
        gone, the one from before, with `removed_on_exit: true`

Input parameters:

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

### `container_kill` (~203 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; for an `auto_remove` container already
        gone, the one from before, with `removed_on_exit: true`

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` (~171 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 legacy link named `id_or_name`, not the container
    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` (~427 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, 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)

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` (~547 tokens)

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

Waits for the command, bounded by `timeout_seconds` (default 120). A command still running then is
reported with `timed_out: true` and whatever output arrived. Each stream past `max_bytes` (default
32 MiB) is cut off with `truncated: true`; once every stream being kept is cut, the call returns at once
rather than waiting for the command. A command still running then has a null `exit_code` and keeps
running in the container, because the
Engine has no way to stop an exec: `container_top` shows it, and another `container_exec` can kill it. For a
long-running or background job, start a container with `container_run` and wait with `system_wait`.

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)
    privileged: Run with extended privileges
    environment: Environment variables, as `{"KEY": "value"}` or a list of "KEY=value" strings
    workdir: Working directory inside the container
    demux: Return `stdout` and `stderr` as separate keys instead of one combined `output`
    timeout_seconds: Wall-clock seconds to wait for the command before returning with timed_out=true
    max_bytes: Most bytes to keep of each stream (stdout and stderr each, with `demux`)

Returns:
    dict: {"exit_code", "output", "timed_out", "truncated", "exec_id"}, with "stdout" and "stderr" in place of
        "output" when demux=True; exit_code is null when the command had not finished

Input parameters:

- `cmd` (required)
- `demux` (boolean)
- `environment`
- `id_or_name` (string, required)
- `max_bytes` (integer)
- `privileged` (boolean)
- `stderr` (boolean)
- `stdin` (boolean)
- `stdout` (boolean)
- `timeout_seconds` (number)
- `tty` (boolean)
- `user` (string)
- `workdir` (string)

### `container_commit` (~298 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:
    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
    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` (~235 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:
    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_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` (~317 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 - inspect `returncode`/`stderr`.

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` (~142 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 - inspect `returncode`/`stderr`.

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

Input parameters:

- `name` (string, required)

### `context_remove` (~123 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 - inspect `returncode`/`stderr`.

Args:
    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` (~639 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` must be inside `path`; one outside it is refused.

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 path inside `path`, relative to it or absolute (default: "Dockerfile")
    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 to use as layer cache sources
    target: Stop at this named build stage (multi-stage Dockerfiles)
    network_mode: Network mode for RUN instructions during build (e.g. "host", "none")
    squash: Squash all new layers into one (experimental; requires daemon flag)
    extra_hosts: Additional /etc/hosts entries during build; dict of hostname to IP
    platform: Target platform, e.g. "linux/amd64" (single platform only; use buildx for multi)
    isolation: Isolation technology, passed to the daemon as given; platform-dependent, so not validated here
        (Windows documents "default", "process", "hyperv")
    use_config_proxy: Forward prox…

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` (~198 tokens)

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

Uses the daemon, with the credentials this server's docker client has cached, 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: Per-call registry credentials under the keys `username` and `password`;
        overrides the cached credential for this call only

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` (~190 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: Per-call registry credentials under the keys `username` and `password`;
        overrides the cached credential for this call only

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` (~303 tokens)

Remove unused local images to reclaim disk space, and with `build_cache=True` the daemon's build cache too.

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. The build cache
is a separate resource, often the larger one on a machine that builds images; `build_cache=True`
prunes its unused records through the Engine API, with `filters` applying to images only. For a
non-default builder's cache, cache filters, or a space floor, use `buildx_prune` instead.

Args:
    filters: Narrow which images to remove; omit to remove dangling images only
    build_cache: Also prune the daemon's unused build cache

Returns:
    dict: {"ImagesDeleted": [...], "SpaceReclaimed": <bytes>}, plus with `build_cache` a "BuildCache" key holding
        {"CachesDeleted": [...], "SpaceReclaimed": <bytes>}

Input parameters:

- `build_cache` (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` (~641 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`). `from_url` and `from_image` are fetched by the
\*daemon*, `from_file`/`data` are read here and uploaded. Like every image-creating tool it stamps
no provenance labels, and here a caller cannot add one either: 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
    tag: Tag to apply, e.g. "v1". Needs `repository`, and must match any tag already in it
    from_file: Path to a rootfs tarball on the server host (`~` expanded), read by the server's user
    data: Rootfs tarball contents in band (base64-encoded by MCP, so prefer `from_file` for anything but small
        archives)
    from_url: URL the daemon fetches the tarball from
    from_image: Name of an existing image to import from, like a Dockerfile `FROM`
    changes: Dockerfile instructions applied to the new image, e.g. ['CMD ["/bin/sh"]']; only CMD, ENTRYPOINT, ENV,
        EXPOSE, ONBUILD, USER, VOLUME and WORKDIR are supported. Parsed as real Dockerfile syntax, so shell form is
        wrapped exactly as a Dockerfile would w…

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` (~302 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
    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` (~272 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. `verbose` is the swarm-overlay troubleshooting
switch: it adds the cluster-wide `Services` breakdown that a plain inspect never carries,
which is what shows whether the tasks on other nodes have actually joined the network.

Args:
    verbose: Add the cluster-wide service/peer detail for an overlay network (needs daemon API v1.28+); omit on a
        single-host network, where it adds nothing
    scope: Disambiguate when the same name exists at more than one scope; omit to let the daemon choose (needs
        daemon API v1.31+)

Returns:
    dict: Full network inspect attrs (equivalent to `docker network inspect`); with `verbose` also a `Services` map
        of service name to its tasks and peers on this network

Input parameters:

- `id_or_name` (string, required)
- `scope` (string)
- `verbose` (boolean)

### `network_list` (~315 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 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: Network names, each an unanchored regular expression, so `web` also matches `webapp`;
        use `^web$` for one name
    ids: Network ids, matched the same way, so an id prefix finds its network
    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` (~204 tokens)

Change a node's availability, role, name or labels, leaving every field not named as it was.

Must run against a swarm manager. Send only what changes: {"Availability": "drain"} drains the
node and keeps its role and labels. "Labels" adds or changes the labels named and keeps the rest;
"RemoveLabels" removes the keys listed; a key in both is refused. Version-guarded, so a node changed
by someone else since it was read fails the call rather than being overwritten. `node_inspect` shows
the current `Spec`; to take a node out of the swarm use `node_remove`.

Args:
    updates: What to change: "Availability", "Role", "Name", "Labels" as {"key": "value"}, and
        "RemoveLabels" as ["key", ...]

Returns:
    bool: True after the update

Input parameters:

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

Output parameters:

- `result` (boolean)

### `node_remove` (~102 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:
    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)

### `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` (~374 tokens)

Install a plugin from a registry, or with `dry_run=True` only show the host privileges it would get.

The daemon grants every privilege a plugin requests without prompting, so for anything not
already trusted call this with `dry_run=True` first: plugins routinely ask for host mounts,
devices and elevated capabilities, and a granted privilege is host-level access, not
container-scoped. A dry run reads the plugin from its registry and installs nothing; for 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`. After installing,
\`plugin_inspect` shows whether it is enabled; `plugin_configure` any settings it declares, then
\`plugin_enable` it. `plugin_list` lists every plugin and `plugin_remove` uninstalls.

Args:
    remote: Registry plugin reference, `author/name:tag`, e.g. "vieux/sshfs:latest"; the `:latest` tag is optional
        and is the default if omitted
    local_name: Alias to refer to the plugin locally; defaults to remote
    dry_run: Return the privileges the plugin requests and install nothing

Returns:
    dict: The installed plugin's full document ({"Id", "Name", "Enabled", "Settings", "Config"}); with `dry_run`,
        {"remote", "privileges"} where `privileges` holds one {"Name", "Description", "Value"} per requested
        privilege, e.g. Name "mount" with Value ["/data"], and is empty if the plugin requests none

Input parameters:

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

### `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` (~143 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:
    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` (~281 tokens)

Upgrade an installed plugin to a newer version, or with `dry_run=True` only show the privileges it would get.

The daemon grants whatever privileges the new version requests without prompting, exactly as
\`plugin_install` does, so a dry run first shows them and changes nothing. Returns only once the
upgrade has finished, and raises if the daemon reports a failure partway or it runs too long. 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:
    remote: Reference to upgrade to, e.g. "vieux/sshfs:next" (default: same as name)
    dry_run: Return the privileges the upgrade would grant and change nothing

Returns:
    bool | dict: True after the upgrade completes; with `dry_run`, {"remote", "privileges"} where `privileges`
        holds one {"Name", "Description", "Value"} per requested privilege

Input parameters:

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

Output parameters:

- `result`

### `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_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` (~289 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. Pass `status=True`
for a whole-swarm health sweep: it adds each service's running-versus-desired task counts in
the one call, where the alternative is a `service_ps` per service. It is a count, not a
diagnosis - when a service comes back short, `service_ps` on that one name is what names the
failing task and its error.

Args:
    filters: Filter by attributes (id, name, label, mode)
    status: Add a `ServiceStatus` entry with the running, desired and completed task counts (needs daemon API
        v1.41+); omit when only the specs are wanted
    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; with `status` each also carries
        "ServiceStatus" ({"RunningTasks", "DesiredTasks", "CompletedTasks"})

Input parameters:

- `filters` (object)
- `managed_only` (boolean)
- `status` (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` (~373 tokens)

List swarm tasks: one service's, or with no service every task in the swarm, like `docker service ps`.

Omit `id_or_name` for the cluster-wide view - what is failing anywhere, or with the `node` filter
one node's workload (the CLI's `docker node ps`) - in one call rather than one per service;
\`stack_ps` covers one stack. Find a single task with the `id` filter (full id or prefix) or
\`name` (the full `<service>.<slot>.<taskid>`, or `<service>.<slot>` for every task that slot has
run). Both match by prefix, so an ambiguous prefix returns several tasks rather than an error, and
\`web.1` also matches `web.10`. Only container tasks come back unless `runtime` is filtered (e.g.
\`attachment`). Each task carries `Status` (State/Message/ContainerStatus), `DesiredState`,
\`NodeID`, `Slot` and its full `Spec`; `service_logs(task=...)` reads what one printed. Prefer this
over `container_list` for services, whose tasks may run on other nodes. Read-only. Requires a swarm
manager: on any other node the daemon refuses, and its refusal is what comes back.

Args:
    id_or_name: The service whose tasks to list; omit for every task in the swarm
    filters: Filter dict; keys: id, name, service, node, label, desired-state (running|shutdown|accepted), runtime

Returns:
    list: One full task document per task (ID, ServiceID, NodeID, Slot, Spec, Status, DesiredState)

Input parameters:

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

### `service_logs` (~423 tokens)

Get a bounded snapshot of a swarm service's logs, or of one of its tasks (never follows).

Pass `id_or_name` for every task of a service interleaved, or `task` for one replica - the one
that actually failed, found with `service_ps` - just as `docker service logs` takes either.
\`container_logs` is no substitute on a multi-node swarm: a task's container lives on whichever
node the scheduler chose, and this server talks to one daemon. `follow` is not exposed (the
stream is joined into one string, so following would never finish). `tail` and `max_bytes` apply
to what was asked for, so a quiet replica is not crowded out by noisy ones; `tail="all"` can
exceed the agent's context, so prefer an integer or `since`. docker-py has no public call for a
task's logs, so that path uses its private request helpers and raises if those internals move.

Args:
    id_or_name: The service whose logs to read; give this or `task`, not both
    task: One task's id, an unambiguous id prefix, or its full `<service>.<slot>.<taskid>` name; give this or
        `id_or_name`, not both
    details: Include the extra attributes given to the log driver (`--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)
- `max_bytes` (integer)
- `since` (integer)
- `stderr` (boolean)
- `stdout` (boolean)
- `tail`
- `task` (string)
- `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)

### `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` (~497 tokens)

Find this server's tools by a few words, or list them by domain or category, as compact rows.

The way to find a tool when your client's own search misses: pass the words for the job as
\`keyword` ("logs one task", "prune build cache") and every tool matching any of them comes back,
those matching the most words first. Each word is matched as a substring of tool names, summaries
and parameter names, with common endings trimmed ("failing" also finds "fail") and filler words
ignored. `matched: 0` is a definite negative, which a client's fuzzy search cannot give. Also
answers what no per-tool search can express: `domain="buildx"` briefs on an area in one line per
tool, `category="destructive"` shows the blast radius. Covers this server's own surface;
\`docs_lookup` covers external Docker reference documentation. Rows are summaries, not definitions -
load 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: One or more words, case-insensitive; a tool matching any word is returned

Returns:
    dict: {"matched": int, "tools": [{"name", "domain", "category", "summary"}], "domains": {domain: count},
        "no_domain": int, "hidden_by_configuration": {domain: count}, "configuration_warnings": [sentence],
        "switches", "filters"}, with `tools` best match first. 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. `configuration_warnings` explains a DOCKER_MCP_SERVER_DISABLE mistake, empty when the switch
        is sound. `filters.keywo…

Input parameters:

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

### `scout_cves` (~347 tokens)

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

Needs `docker login` on the host that runs the CLI, even for a public image - this server's host,
or the target `ssh://` host itself when no local scout plugin is installed; without it the CLI
exits 1 asking to log in. 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 - 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` (~192 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`. Needs `docker login`, as `scout_cves` does.
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 - 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` (~237 tokens)

Suggest base-image upgrades for an image.

Computed against Docker Scout's catalog; needs `docker login`, as `scout_cves` does. 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 - 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: Which platform of a multi-platform image to analyse, e.g. "linux/arm64"

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` (~364 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). Needs `docker login`, as `scout_cves` does.
Does not raise on a non-zero CLI exit - inspect `raw.stderr`. When the call has to run on a remote
\`ssh://` host, a local directory or archive in `to` is refused.

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: Which platform of a multi-platform image to analyse, e.g. "linux/arm64"

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` (~277 tokens)

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

Package inventory only - `scout_cves` adds vulnerability matching on top. The one Scout tool that
works without `docker login`. 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 - inspect `raw.stderr`.

Args:
    format: "spdx" (default, SPDX JSON), "cyclonedx" (CycloneDX JSON), "json" (Scout's native JSON), or "list"
        (plain-text package list)
    platform: Which platform of a multi-platform image to analyse, e.g. "linux/arm64"

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` (~70 tokens)

Remove a Swarm secret; requires a swarm manager.

Fails while any service still references the secret, and the error names those services -
move them to another secret with `service_update`, or remove them, first.

Returns:
    bool: True after removal

Input parameters:

- `id_or_name` (string, required)

Output parameters:

- `result` (boolean)

### `stack_deploy` (~331 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. Returns once the service specs are submitted, never waiting for the rollout: wait
for it with `system_wait(kind="stack", target=name, until="converged")`, or see its state without
waiting with `stack_services` / `stack_ps`. The swarm analogue of `compose_up`.
Does not raise on a non-zero CLI exit - inspect `returncode`/`stderr`.

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")
    cwd: Working directory for resolving relative Compose paths (defaults to the server's cwd; copied to the target
        host if no local docker CLI)

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

Input parameters:

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

### `stack_list` (~82 tokens)

List the stacks deployed to the swarm, with how many services each has.

Requires a swarm manager. `compose_list` is the non-swarm equivalent; drill into one stack with
\`stack_services`, or wait for one with `system_wait(kind="stack")`.

Returns:
    list: One {"name", "services"} per stack, sorted by name

### `stack_services` (~236 tokens)

List the services of a stack, with how many of their tasks are running against how many are wanted.

Service-level rollup; use `stack_ps` for the individual tasks, `service_inspect` for one service's
full spec, and `system_wait(kind="stack", until="converged")` to wait for the stack to settle.
Requires a swarm manager.

Args:
    name: The stack to list services for
    filters: Engine service filters, e.g. {"name": "web"}; a list value matches any of them, except
        `label`, where every one must match and the stack's own label key is refused

Returns:
    list: One {"id", "name", "mode", "running_tasks", "desired_tasks", "image", "ports", "update_state"} per
        service, sorted by name; `ports` lists published ports as "published:target/protocol", and
        `update_state` is a rolling update's state, or null when there has been none

Input parameters:

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

### `stack_ps` (~222 tokens)

List the tasks of a stack: where each runs, its state, and why it failed.

Task-level view across every service in the stack (`service_ps` covers one service). Includes the
finished and failed tasks the swarm still keeps, so pass {"desired-state": "running"} for only the
current ones. Requires a swarm manager.

Args:
    name: The stack to list tasks for
    filters: Engine task filters, e.g. {"desired-state": "running"}; a list value matches any of them,
        except `label`, where every one must match and the stack's own label key is refused

Returns:
    list: One {"id", "name", "service", "node", "image", "desired_state", "state", "error", "since"} per task,
        sorted by name; `node` is the hostname, `error` is null unless the task failed, and `since` is
        when it entered its state

Input parameters:

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

### `stack_remove` (~219 tokens)

Remove one or more stacks from the swarm: their services, secrets, configs and networks.

Destructive: this deletes every service in the named stack(s) - the reverse of `stack_deploy` and the
swarm analogue of `compose_down`. Returns once removal is requested, never waiting for teardown:
\`system_wait(kind="stack", target=<name>, until="removed")` waits for it. A part that cannot be
removed is reported under `failed` and the rest still go, as `docker stack rm` does.

Args:
    names: One or more stack names to remove. At least one is required.

Returns:
    list: One {"name", "found", "removed", "failed"} per stack; `found` is false when nothing carried the
        stack's label, `removed` maps "services"/"secrets"/"configs"/"networks" to the names removed, and
        `failed` lists {"kind", "name", "error"}

Input parameters:

- `names` (array, required)

### `swarm_init` (~489 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: Prefix length of each network carved from the default address pool, e.g. 24
    data_path_addr: Address or interface for data-path (VXLAN) traffic, e.g. "192.168.1.1" or "eth0"
    data_path_port: UDP port for data-path traffic, 1024-49151 (the daemon's default is 4789)
    labels: Labels to set on the swarm
    autolock_managers: Require the unlock key after every manager restart
    log_driver: Default log driver for tasks, as {"Name": "json-file", "Options": {...}}

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` (~289 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 or interface for data-path (VXLAN) traffic, e.g. "192.168.1.1" or "eth0"

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>}

### `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` (~132 tokens)

Remove a single volume by name.

Fails if any container, running or stopped, still references the volume, even with
\`force` - remove those containers first (`container_list` with a `volume` filter finds
them). For bulk cleanup of volumes with no container references at all, use
\`volume_prune` instead.

Args:
    force: Drop Docker's record of a volume its driver plugin has already deleted out of band; it
        does not override a container's reference

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#diagnostics

## Score history

- 2026-10-23: 39
- 2026-10-22: 39
- 2026-10-21: 39
- 2026-10-20: 39
- 2026-10-19: 39
- 2026-10-18: 38
- 2026-10-17: 38
- 2026-10-16: 36
- 2026-10-15: 36
- 2026-10-14: 36
- 2026-10-13: 7
- 2026-10-12: 7
- 2026-10-10: 7
- 2026-10-09: 7

## 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).

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

Docker MCP Server scores 39 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 154 tools: system_ping, system_version, system_info, system_df, host_list, and 149 more. Their descriptions and schemas cost roughly 35,492 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 23 October 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

## Links

- GitHub Container Registry: https://ghcr.io/l337-org/docker-mcp-server
- 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.xml
- Changelog JSON feed: https://verifymcp.io/servers/l337-org-docker-mcp-server/ghcr-io-l337-org-docker-mcp-server.json
- HTML version of this page: https://verifymcp.io/servers/l337-org-docker-mcp-server/ghcr-io-l337-org-docker-mcp-server
