docker-mcp-server 2.1.2__tar.gz → 2.1.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/.github/copilot-instructions.md +67 -17
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/.github/workflows/publish-homebrew.yaml +7 -4
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/CLAUDE.md +74 -17
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/PKG-INFO +3 -3
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/README.md +2 -2
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/tools/buildx.py +7 -1
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/tools/compose.py +10 -1
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/tools/configs.py +6 -2
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/tools/containers.py +50 -18
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/tools/networks.py +18 -9
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/tools/nodes.py +7 -3
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/tools/plugins.py +14 -5
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/tools/secrets.py +11 -5
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/tools/services.py +9 -3
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/tools/system.py +8 -2
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/tools/volumes.py +7 -3
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/manifest.json +1 -1
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/pyproject.toml +1 -1
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/uv.lock +1 -1
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/.claude/commands/docker-sdk.md +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/.claude/settings.json +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/.dockerignore +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/.github/CODEOWNERS +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/.github/actions/file-failure-issue/action.yaml +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/.github/dependabot.yaml +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/.github/release.yml +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/.github/workflows/canary.yaml +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/.github/workflows/codeql.yaml +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/.github/workflows/images.yaml +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/.github/workflows/premerge.yaml +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/.github/workflows/publish.yaml +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/.gitignore +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/.mcpbignore +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/.python-version +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/CODE_OF_CONDUCT.md +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/CONTRIBUTING.md +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/DOCKERHUB.md +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/Dockerfile +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/LICENSE +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/MIGRATION-2.0.md +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/PRIVACY.md +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/SECURITY.md +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/assets/README.md +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/assets/icon.png +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/__init__.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/__main__.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/_env.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/_hosts.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/server.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/tools/__init__.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/tools/_cli.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/tools/_labels.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/tools/_ssh_proxy.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/tools/_utils.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/tools/context.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/tools/images.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/tools/prompts.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/tools/registry.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/tools/resources.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/tools/scout.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/tools/stack.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/docker_mcp/tools/swarm.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/glama.json +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/mcpb_run.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/scripts/build-mcpb.sh +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/scripts/docker-mcp-server.rb.tpl +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/server.json +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/__init__.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/conftest.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/integration/__init__.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/integration/conftest.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/integration/test_buildx.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/integration/test_cli.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/integration/test_compose.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/integration/test_containers.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/integration/test_context.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/integration/test_file_payloads.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/integration/test_networks.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/integration/test_nodes.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/integration/test_registry.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/integration/test_scout.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/integration/test_services.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/integration/test_smoke.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/integration/test_stack.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_buildx.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_cli.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_compose.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_configs.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_containers.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_context.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_env.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_hosts.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_images.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_labels.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_main.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_naming.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_networks.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_nodes.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_plugins.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_prompts.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_pyproject_pins.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_registry.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_resources.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_scout.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_secrets.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_server.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_services.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_ssh_proxy.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_stack.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_swarm.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_system.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_utils.py +0 -0
- {docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/tests/test_volumes.py +0 -0
|
@@ -4,7 +4,7 @@ This file provides guidance to GitHub Copilot when working with code in this rep
|
|
|
4
4
|
|
|
5
5
|
## Project
|
|
6
6
|
|
|
7
|
-
`docker-mcp` is a Python MCP (Model Context Protocol) server that exposes the Docker SDK for Python — plus selected docker CLI features (Compose, Context, Buildx, Scout) and direct OCI-registry HTTPS access — as MCP tools. It requires Python >=3.14 and is managed with `uv`. It is published to PyPI as **`docker-mcp-server`** and as a container image to GHCR (`ghcr.io/l337-org/docker-mcp-server`), mirrored to Docker Hub (`gavinlucas/docker-mcp-server`) when the opt-in `DOCKERHUB_*` release secrets are configured (the import package stays `docker_mcp`, the repo stays `…/docker-mcp`); two console scripts — `docker-mcp` and `docker-mcp-server` — both target `docker_mcp:main`. A third distribution channel packages the server as a **Claude Desktop Extension (`.mcpb`)** attached to each GitHub Release (see "Desktop Extension (MCPB)" below). A fourth channel (**Homebrew tap**) exists in `
|
|
7
|
+
`docker-mcp` is a Python MCP (Model Context Protocol) server that exposes the Docker SDK for Python — plus selected docker CLI features (Compose, Context, Buildx, Scout) and direct OCI-registry HTTPS access — as MCP tools. It requires Python >=3.14 and is managed with `uv`. It is published to PyPI as **`docker-mcp-server`** and as a container image to GHCR (`ghcr.io/l337-org/docker-mcp-server`), mirrored to Docker Hub (`gavinlucas/docker-mcp-server`) when the opt-in `DOCKERHUB_*` release secrets are configured (the import package stays `docker_mcp`, the repo stays `…/docker-mcp`); two console scripts — `docker-mcp` and `docker-mcp-server` — both target `docker_mcp:main`. A third distribution channel packages the server as a **Claude Desktop Extension (`.mcpb`)** attached to each GitHub Release (see "Desktop Extension (MCPB)" below). A fourth channel (**Homebrew tap**) exists in `L337-org/homebrew-tap` but is currently **paused** — see "Homebrew tap" below.
|
|
8
8
|
|
|
9
9
|
The `docker` dep uses the `[ssh]` extra (paramiko), so `DOCKER_HOST=ssh://…` works via a pure-Python transport (no system `ssh` binary; works in the container images). docker-py auto-selects paramiko for `ssh://`, so there's no transport code — only the `ssh://` branch in `system._connection_help`. CLI-backed tools (Compose, Buildx, Context, Scout) shell out to `docker`, which would otherwise need the system `ssh` client — instead, `_cli.py:run_docker` detects `DOCKER_HOST=ssh://…` and routes the subprocess through a per-call local TCP proxy (`docker_mcp/tools/_ssh_proxy.py`) that opens its own paramiko connection and runs `docker system dial-stdio` over it, so the CLI authenticates the same way the docker-py-backed tools do, with no system `ssh` binary involved (except a `ProxyCommand` in `~/.ssh/config` for bastion/jump-host setups, which paramiko runs as an external command — commonly `ssh -W %h:%p ...` — for both tool families alike).
|
|
10
10
|
|
|
@@ -91,9 +91,9 @@ An additional distribution channel alongside the uvx-from-git install (unchanged
|
|
|
91
91
|
|
|
92
92
|
A third distribution channel for one-click install in Claude Desktop. Repo-root sources: `manifest.json` (MCPB manifest, `manifest_version` 0.4, `server.type: "uv"`), `mcpb_run.py` (bundle entry point — `from docker_mcp import main; main()`, at the root so `import docker_mcp` resolves under the host's managed `uv`), `.mcpbignore` (trims the packed bundle to source + `pyproject.toml` + `uv.lock` + `README.md`/`LICENSE` + manifest/entry-point + `assets/`), and `assets/icon.png` (512×512). It's a `uv`-type bundle: the host resolves deps from `pyproject.toml` at install time (no vendored venv). `manifest.json`'s `user_config` maps install-dialog fields to env — `DOCKER_MCP_SERVER_HOSTS` (the single host field; `DOCKER_HOST` is **not** exposed — the bare-value shorthand keeps the one-daemon case a one-liner) and the `DOCKER_MCP_SERVER_READONLY` / `_NO_DESTRUCTIVE` / `_DISABLE` switches (the container-only `_ALLOW_SELF_TERMINATE` is intentionally omitted — the bundle never runs containerized). The `mcpb` job in `.github/workflows/publish.yaml` rewrites the manifest `version` from the release tag, packs the `.mcpb` via `npx @anthropic-ai/mcpb`, and attaches it (plus a `.sha256` recording the bare filename, so `sha256sum -c` works on downloads) to each GitHub Release. `scripts/build-mcpb.sh` is the **developer-only** local equivalent of that pack step (version from `pyproject.toml`, auto-incrementing output in `dist/`, never mutates `manifest.json`) for smoke-testing in Claude Desktop — it is deliberately **not** used by CI. `PRIVACY.md` (no-telemetry statement) is referenced by the manifest's `privacy_policies`. Keep the manifest `version` aligned with `pyproject.toml`; update this section when the manifest's tool/env surface or the bundled file set changes.
|
|
93
93
|
|
|
94
|
-
### Homebrew tap (`
|
|
94
|
+
### Homebrew tap (`L337-org/homebrew-tap`) — PAUSED
|
|
95
95
|
|
|
96
|
-
The infrastructure exists (`scripts/docker-mcp-server.rb.tpl`, `.github/workflows/publish-homebrew.yaml`, `
|
|
96
|
+
The infrastructure exists (`scripts/docker-mcp-server.rb.tpl`, `.github/workflows/publish-homebrew.yaml`, `L337-org/homebrew-tap`) but the **release trigger is disabled** pending resolution of a Homebrew dylib linkage issue: pre-built PyPI wheels for `pydantic_core` lack `-headerpad_max_install_names`, so Homebrew's post-install relocation step fails to rewrite the `@rpath` ID (the binary's Mach-O header has no room for the longer absolute path). The workflow is `workflow_dispatch`-only until a fix is found. The channel is not advertised in the README. To re-enable: add `release: types: [published]` back to `publish-homebrew.yaml`'s `on:` block and re-add the Homebrew section to the README.
|
|
97
97
|
|
|
98
98
|
### MCP Registry (`server.json`)
|
|
99
99
|
|
|
@@ -148,12 +148,18 @@ def mcp_example(name: str):
|
|
|
148
148
|
"""
|
|
149
149
|
Say hello to someone by name.
|
|
150
150
|
|
|
151
|
-
|
|
151
|
+
Use it for a single greeting; use `mcp_example_bulk` to greet many names in one call.
|
|
152
|
+
Read-only, no side effects.
|
|
153
|
+
|
|
154
|
+
args: name - The name to say hello to (any non-empty string)
|
|
152
155
|
returns: str - The greeting
|
|
153
156
|
"""
|
|
154
157
|
return f"Hello, {name}!"
|
|
155
158
|
```
|
|
156
159
|
|
|
160
|
+
(`mcp_example` and `mcp_example_bulk` are illustrative only and exist nowhere in the repo — in a
|
|
161
|
+
real docstring the discriminator must name an actually-registered sibling tool.)
|
|
162
|
+
|
|
157
163
|
- One-line summary sentence, then a blank line
|
|
158
164
|
- `args:` section lists each parameter as `name - description`. Do **not** repeat the parameter's
|
|
159
165
|
type — the type annotation already lands in the tool's `inputSchema`, which the client sees
|
|
@@ -163,19 +169,63 @@ def mcp_example(name: str):
|
|
|
163
169
|
- Keep descriptions terse: state every functional fact (defaults, accepted formats/values, return
|
|
164
170
|
keys, important caveats) but cut redundancy and verbose phrasing. The docstring is the entire
|
|
165
171
|
tool `description` the client pays tokens for on every session.
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
172
|
+
|
|
173
|
+
#### Docstring quality standard (review checklist — push back on non-compliant docstrings)
|
|
174
|
+
|
|
175
|
+
Tool descriptions are scored externally on Glama's six-dimension Tool Definition Quality rubric
|
|
176
|
+
(<https://glama.ai/mcp/servers/L337-org/docker-mcp/score>): Purpose Clarity 25%, Usage Guidelines
|
|
177
|
+
20%, Behavioral Transparency 20%, Parameter Semantics 15%, Conciseness & Structure 10%, Contextual
|
|
178
|
+
Completeness 10%. Repeated cleanup PRs (#97, the 2.0 rename, #129, the 2026-07 bottom-20 pass) all
|
|
179
|
+
chased the same failure: docstrings that state *what* the tool does but never *when to use it over
|
|
180
|
+
its neighbors*, plus `args:`/`returns:` lines that merely restate the schema. **When a PR adds or
|
|
181
|
+
modifies an `@tool()` docstring, check it against every numbered item below and request changes
|
|
182
|
+
naming the specific item it fails.** The standard is a ratchet: it applies to docstrings the PR
|
|
183
|
+
touches — do not demand rewrites of untouched legacy docstrings.
|
|
184
|
+
|
|
185
|
+
1. **Summary = specific verb + resource**, with the distinguishing trait up front when a sibling
|
|
186
|
+
could be confused ("Send a signal to a running container (default SIGKILL — immediate, no
|
|
187
|
+
graceful shutdown)").
|
|
188
|
+
2. **A usage-guidance paragraph (1–5 sentences between the summary and `args:`) is required for
|
|
189
|
+
every tool, not just complex ones.** Flag a new/edited tool whose docstring jumps straight from
|
|
190
|
+
summary to `args:`. The paragraph must carry:
|
|
191
|
+
- at least one *discriminator* naming the sibling tool(s) an agent could reach for instead and
|
|
192
|
+
when to prefer which (`container_stop` vs `container_kill` vs `container_restart`;
|
|
193
|
+
`service_ps` vs `stack_ps` vs the `service-tasks://` resource);
|
|
194
|
+
- preconditions in prose (swarm manager only, plugin required, container must be
|
|
195
|
+
running/paused);
|
|
196
|
+
- side effects and destructive/irreversible behavior in prose — the scorer explicitly discounts
|
|
197
|
+
`readOnlyHint`/`destructiveHint` annotations as a substitute for description text;
|
|
198
|
+
- for CLI-backed tools, the error style ("does not raise on a non-zero CLI exit — inspect
|
|
199
|
+
`returncode`/`stderr`" vs "raises `RuntimeError` on CLI failure"). Flag an absolute "never
|
|
200
|
+
raises" claim — a missing binary/plugin or a subprocess timeout still raises even in action
|
|
201
|
+
tools.
|
|
202
|
+
Scale to the tool: a trivial read-only tool needs one discriminator sentence, not five.
|
|
203
|
+
3. **Every `args:` line adds semantics the schema cannot carry**: format, accepted values/ranges,
|
|
204
|
+
defaults, units, parameter interactions. Flag a line that merely echoes the parameter name
|
|
205
|
+
("name - The volume name") — it scores 2/5 on the rubric. Canonical shared-param prefixes in
|
|
206
|
+
`tests/test_naming.py` still apply — tool-specific detail is appended after the canonical
|
|
207
|
+
prefix, not reworded.
|
|
208
|
+
4. **`returns:` names the shape, not just the type** — for dict/list returns the load-bearing keys
|
|
209
|
+
must be listed (`{"Titles", "Processes"}`); flag a bare "dict - The X's attrs" on a
|
|
210
|
+
new/modified tool.
|
|
211
|
+
5. **Front-loaded and terse** — the description is paid for in every session's context.
|
|
212
|
+
6. **Every factual claim must be verifiable** against the docker-py docs / Engine API spec (Docker
|
|
213
|
+
SDK Policy below). Flag claims that contradict them — especially identifier semantics ("name or
|
|
214
|
+
id" for a resource actually addressed by name only), default values, and signal/timeout
|
|
215
|
+
behavior.
|
|
216
|
+
|
|
217
|
+
**Division of labor across the three discovery layers.** For a lazy-loading client (e.g. Claude
|
|
218
|
+
Code), tool schemas load on demand; always in context are only (1) the **tool names** and (2) the
|
|
219
|
+
**`instructions` router** (`_DOMAIN_BLURBS` / `build_instructions()` in `server.py`). Docstrings
|
|
220
|
+
are layer (3): a deferred tool cannot be invoked without fetching its definition, so it is read at
|
|
221
|
+
the moment of choice, side by side with sibling definitions — where the item-2 discriminators do
|
|
222
|
+
their work. When reviewing, apply the boundary: a *cross-domain* selection caveat that must be
|
|
223
|
+
visible before any schema is fetched (e.g. "prefer `dest_path` for large output") belongs in the
|
|
224
|
+
router's caveat list, not buried in one docstring; *sibling-level* discriminators belong in
|
|
225
|
+
docstrings and should not be duplicated into the router; flag docstrings padded with search
|
|
226
|
+
keywords (discoverability is the naming convention's and the router's job); and flag sibling
|
|
227
|
+
references that don't use the exact tool name (`container_kill`, never "the kill tool") — lazy
|
|
228
|
+
clients keyword-search descriptions, so exact names double as retrieval anchors.
|
|
179
229
|
|
|
180
230
|
### Bounding rules
|
|
181
231
|
|
|
@@ -2,7 +2,7 @@ name: 'Publish Homebrew formula'
|
|
|
2
2
|
|
|
3
3
|
# On each GitHub Release: downloads pre-built macOS wheels for arm64 and x86_64 from PyPI (using
|
|
4
4
|
# the frozen lockfile so versions are pinned), packages each set into a wheelhouse tarball, attaches
|
|
5
|
-
# both to the release, then pushes an updated Formula/docker-mcp-server.rb to
|
|
5
|
+
# both to the release, then pushes an updated Formula/docker-mcp-server.rb to L337-org/homebrew-tap.
|
|
6
6
|
#
|
|
7
7
|
# Runs on ubuntu-latest. Both wheelhouses are built via cross-platform pip downloads:
|
|
8
8
|
# arm64: --platform macosx_<floor>_arm64 + --platform macosx_<floor>_universal2
|
|
@@ -23,8 +23,11 @@ name: 'Publish Homebrew formula'
|
|
|
23
23
|
# guardrail; do not remove it.
|
|
24
24
|
#
|
|
25
25
|
# Requires the repository secret TAP_GITHUB_TOKEN: a fine-grained PAT with Contents read+write on
|
|
26
|
-
#
|
|
27
|
-
#
|
|
26
|
+
# L337-org/homebrew-tap, issued with **L337-org as the resource owner** (a fine-grained PAT scoped
|
|
27
|
+
# to a personal account cannot access org repos, and org settings must allow fine-grained PAT
|
|
28
|
+
# access). Create it at github.com/settings/tokens and add it as a secret on this repo under
|
|
29
|
+
# Settings → Secrets → Actions. The pre-transfer token (resource owner GavinLucas) is invalid for
|
|
30
|
+
# the moved tap — re-issue before re-enabling this channel.
|
|
28
31
|
#
|
|
29
32
|
# workflow_dispatch lets you backfill a release that predates this workflow (supply the tag, e.g.
|
|
30
33
|
# v1.9.2). The release must already exist and docker-mcp-server must be live on PyPI for that version.
|
|
@@ -206,7 +209,7 @@ jobs:
|
|
|
206
209
|
GH_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }}
|
|
207
210
|
run: |
|
|
208
211
|
set -euo pipefail
|
|
209
|
-
git clone "https://x-access-token:${GH_TOKEN}@github.com/
|
|
212
|
+
git clone "https://x-access-token:${GH_TOKEN}@github.com/L337-org/homebrew-tap.git" tap
|
|
210
213
|
cd tap
|
|
211
214
|
git config user.name "github-actions[bot]"
|
|
212
215
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
@@ -11,7 +11,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|
|
11
11
|
|
|
12
12
|
## Project
|
|
13
13
|
|
|
14
|
-
`docker-mcp` is a Python MCP server (requires Python >=3.14) managed with `uv` that exposes the Docker SDK for Python as MCP tools. The entry point is the `docker_mcp` package, run with `python -m docker_mcp` or via the installed console script. It is **published to PyPI as `docker-mcp-server`** (the `docker-mcp` name was already taken) and as a container image to GHCR (`ghcr.io/l337-org/docker-mcp-server`), mirrored to Docker Hub (`gavinlucas/docker-mcp-server`) when the opt-in `DOCKERHUB_*` release secrets are configured; the import package stays `docker_mcp` and the repo stays `…/docker-mcp`. Two console scripts are installed — `docker-mcp` and `docker-mcp-server` — both targeting `docker_mcp:main`. A third channel packages the server as a **Claude Desktop Extension (`.mcpb`)** attached to each GitHub Release — see "Desktop Extension (MCPB bundle)" below. A fourth channel (**Homebrew tap**) exists in `
|
|
14
|
+
`docker-mcp` is a Python MCP server (requires Python >=3.14) managed with `uv` that exposes the Docker SDK for Python as MCP tools. The entry point is the `docker_mcp` package, run with `python -m docker_mcp` or via the installed console script. It is **published to PyPI as `docker-mcp-server`** (the `docker-mcp` name was already taken) and as a container image to GHCR (`ghcr.io/l337-org/docker-mcp-server`), mirrored to Docker Hub (`gavinlucas/docker-mcp-server`) when the opt-in `DOCKERHUB_*` release secrets are configured; the import package stays `docker_mcp` and the repo stays `…/docker-mcp`. Two console scripts are installed — `docker-mcp` and `docker-mcp-server` — both targeting `docker_mcp:main`. A third channel packages the server as a **Claude Desktop Extension (`.mcpb`)** attached to each GitHub Release — see "Desktop Extension (MCPB bundle)" below. A fourth channel (**Homebrew tap**) exists in `L337-org/homebrew-tap` but is currently **paused** — see "Homebrew tap" below.
|
|
15
15
|
|
|
16
16
|
The `docker` dependency is pulled with its `[ssh]` extra (paramiko), so `DOCKER_HOST=ssh://…` works through a pure-Python transport — no system `ssh` binary, identical on the host and in the container images. docker-py auto-selects paramiko for `ssh://` when present, so there is no transport code to maintain (just the `ssh://` branch in `system._connection_help`). CLI-backed tools (Compose, Buildx, Context, Scout) shell out to `docker`, which would otherwise use the *system* `ssh` — instead, `_cli.py:run_docker` detects `DOCKER_HOST=ssh://…` and routes the subprocess through a per-call local TCP proxy (`docker_mcp/tools/_ssh_proxy.py`) that opens its own paramiko connection (mirroring docker-py's `SSHHTTPAdapter` defaults) and runs `docker system dial-stdio` over it, so the CLI authenticates identically to the docker-py-backed tools with no system `ssh` binary involved (the one exception being a `ProxyCommand` in `~/.ssh/config` for bastion/jump-host setups, which paramiko runs as an external command — commonly `ssh -W %h:%p ...` — same as it would for the docker-py-backed tools).
|
|
17
17
|
|
|
@@ -157,9 +157,9 @@ An additional distribution channel alongside uvx-from-git (which is unchanged).
|
|
|
157
157
|
|
|
158
158
|
A third distribution channel (alongside uvx/PyPI and the container images) for one-click install in Claude Desktop. The repo root carries the bundle sources: `manifest.json` (the MCPB manifest, `manifest_version` 0.4, `server.type: "uv"`), `mcpb_run.py` (the bundle entry point — `from docker_mcp import main; main()`, kept at the root so `import docker_mcp` resolves however the host's managed `uv` lays out `sys.path`), `.mcpbignore` (trims the packed bundle to source + `pyproject.toml` + `uv.lock` + `README.md`/`LICENSE` + `manifest.json`/`mcpb_run.py` + `assets/`), and `assets/icon.png` (512×512). Because it's a `uv`-type bundle, the host resolves dependencies from `pyproject.toml` at install time — there's no vendored venv, so the bundle stays ~250 KB and cross-platform. `manifest.json`'s `user_config` block renders the install-dialog fields and maps them to env: `DOCKER_MCP_SERVER_HOSTS` (the single host field — `DOCKER_HOST` is deliberately **not** exposed; the bare-value shorthand keeps the simple one-daemon case a one-liner) plus the `DOCKER_MCP_SERVER_READONLY` / `_NO_DESTRUCTIVE` / `_DISABLE` switches (the container-only `_ALLOW_SELF_TERMINATE` is deliberately **not** exposed — the bundle never runs containerized). The `manifest.json` `version` is kept in step with `pyproject.toml` (`tests/test_pyproject_pins.py` asserts it), but the `mcpb` job in `.github/workflows/publish.yaml` rewrites it from the release tag at pack time so it can't drift; that job packs the `.mcpb` with `npx @anthropic-ai/mcpb` and attaches it (plus a `.sha256` recording the bare filename, so `sha256sum -c` works on downloads) to each GitHub Release. `scripts/build-mcpb.sh` is the **developer-only** local equivalent of that pack step (reads the version from `pyproject.toml`, packs to `dist/` with an auto-incrementing name, never mutates `manifest.json`) — for smoke-testing a bundle in Claude Desktop; it is deliberately **not** wired into CI, which uses the workflow above. `PRIVACY.md` (a no-telemetry/no-backend statement) is referenced by the manifest's `privacy_policies` and summarized in the README. **When the manifest's tool/env surface or the bundled file set changes, update this section.**
|
|
159
159
|
|
|
160
|
-
### Homebrew tap (`
|
|
160
|
+
### Homebrew tap (`L337-org/homebrew-tap`) — PAUSED
|
|
161
161
|
|
|
162
|
-
The infrastructure exists (`scripts/docker-mcp-server.rb.tpl`, `.github/workflows/publish-homebrew.yaml`, `
|
|
162
|
+
The infrastructure exists (`scripts/docker-mcp-server.rb.tpl`, `.github/workflows/publish-homebrew.yaml`, `L337-org/homebrew-tap`) but the **release trigger is disabled** pending resolution of a Homebrew dylib linkage issue: pre-built PyPI wheels for `pydantic_core` lack `-headerpad_max_install_names`, so Homebrew's post-install relocation step fails to rewrite the `@rpath` ID (the binary's Mach-O header has no room for the longer absolute path). The workflow is `workflow_dispatch`-only until a fix is found. The channel is not advertised in the README. To re-enable: add `release: types: [published]` back to `publish-homebrew.yaml`'s `on:` block and re-add the Homebrew section to the README.
|
|
163
163
|
|
|
164
164
|
### MCP Registry (`server.json`)
|
|
165
165
|
|
|
@@ -247,12 +247,18 @@ def mcp_example(name: str):
|
|
|
247
247
|
"""
|
|
248
248
|
Say hello to someone by name.
|
|
249
249
|
|
|
250
|
-
|
|
250
|
+
Use it for a single greeting; use `mcp_example_bulk` to greet many names in one call.
|
|
251
|
+
Read-only, no side effects.
|
|
252
|
+
|
|
253
|
+
args: name - The name to say hello to (any non-empty string)
|
|
251
254
|
returns: str - The greeting
|
|
252
255
|
"""
|
|
253
256
|
return f"Hello, {name}!"
|
|
254
257
|
```
|
|
255
258
|
|
|
259
|
+
(`mcp_example` and `mcp_example_bulk` are illustrative only and exist nowhere in the repo — in a
|
|
260
|
+
real docstring the discriminator must name an actually-registered sibling tool.)
|
|
261
|
+
|
|
256
262
|
- One-line summary sentence, then a blank line
|
|
257
263
|
- `args:` section lists each parameter as `name - description`. Do **not** repeat the parameter's
|
|
258
264
|
type — the type annotation already lands in the tool's `inputSchema`, which the client sees
|
|
@@ -262,19 +268,70 @@ def mcp_example(name: str):
|
|
|
262
268
|
- Keep descriptions terse: state every functional fact (defaults, accepted formats/values, return
|
|
263
269
|
keys, important caveats) but cut redundancy and verbose phrasing. The docstring is the entire
|
|
264
270
|
tool `description` the client pays tokens for on every session.
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
271
|
+
|
|
272
|
+
#### Docstring quality standard
|
|
273
|
+
|
|
274
|
+
Tool descriptions are scored externally on Glama's six-dimension Tool Definition Quality rubric
|
|
275
|
+
(<https://glama.ai/mcp/servers/L337-org/docker-mcp/score>): Purpose Clarity 25%, Usage Guidelines
|
|
276
|
+
20%, Behavioral Transparency 20%, Parameter Semantics 15%, Conciseness & Structure 10%, Contextual
|
|
277
|
+
Completeness 10%. Four rounds of cleanup (#97, the 2.0 rename, #129, and the 2026-07 bottom-20
|
|
278
|
+
pass — see [[project_glama_docstring_quality]] in memory) all chased the same failure: docstrings
|
|
279
|
+
that state *what* the tool does but never *when to use it over its neighbors*, plus `args:` /
|
|
280
|
+
`returns:` lines that merely restate the schema. The standard below exists to prevent a fifth
|
|
281
|
+
round. It applies to **every `@tool()` docstring added or modified in a PR** (a ratchet — untouched
|
|
282
|
+
legacy docstrings are cleaned opportunistically, not churned):
|
|
283
|
+
|
|
284
|
+
1. **Summary = specific verb + resource**, with the distinguishing trait up front when a sibling
|
|
285
|
+
could be confused ("Send a signal to a running container (default SIGKILL — immediate, no
|
|
286
|
+
graceful shutdown)").
|
|
287
|
+
2. **A usage-guidance paragraph (1–5 sentences between the summary and `args:`) is required for
|
|
288
|
+
every tool, not just complex ones.** Every tool in a 150+-tool server has neighbors. It must
|
|
289
|
+
carry:
|
|
290
|
+
- at least one *discriminator* naming the sibling tool(s) an agent could reach for instead and
|
|
291
|
+
when to prefer which (`container_stop` vs `container_kill` vs `container_restart`;
|
|
292
|
+
`service_ps` vs `stack_ps` vs the `service-tasks://` resource);
|
|
293
|
+
- preconditions in prose (swarm manager only, plugin required, container must be
|
|
294
|
+
running/paused);
|
|
295
|
+
- side effects and destructive/irreversible behavior in prose — the scorer explicitly discounts
|
|
296
|
+
`readOnlyHint`/`destructiveHint` annotations as a substitute for description text;
|
|
297
|
+
- for CLI-backed tools, the error style ("does not raise on a non-zero CLI exit — inspect
|
|
298
|
+
`returncode`/`stderr`" vs "raises `RuntimeError` on CLI failure"). Don't overpromise "never
|
|
299
|
+
raises" — a missing binary/plugin or a subprocess timeout still raises even in action tools.
|
|
300
|
+
Scale it to the tool: a trivial read-only tool needs one discriminator sentence, not five.
|
|
301
|
+
3. **Every `args:` line adds semantics the schema cannot carry**: format, accepted values/ranges,
|
|
302
|
+
defaults, units, and interactions with other parameters. A line that echoes the parameter name
|
|
303
|
+
("name - The volume name") scores 2/5 on the rubric — say what makes a value valid or how it
|
|
304
|
+
behaves ("name - The volume name (volumes have no separate id)"). Canonical shared-param
|
|
305
|
+
prefixes in `tests/test_naming.py` still apply — append tool-specific detail after the
|
|
306
|
+
canonical prefix rather than rewording it.
|
|
307
|
+
4. **`returns:` names the shape, not just the type.** There is no output schema, so this line is
|
|
308
|
+
all an agent gets: for dict/list returns name the load-bearing keys (`{"Titles", "Processes"}`;
|
|
309
|
+
`{"LayersSize", "Images", "Containers", "Volumes", "BuildCache"}`), never just "the attrs".
|
|
310
|
+
5. **Front-load and stay terse** — the description is paid for in every session's context; every
|
|
311
|
+
sentence must earn its place.
|
|
312
|
+
6. **Verify every factual claim** against the live docker-py docs / Engine API spec per the Docker
|
|
313
|
+
SDK Policy below — an unverified claim about identifier semantics (e.g. "name or id" for a
|
|
314
|
+
resource actually addressed by name only) is exactly the kind of thing PR review catches late.
|
|
315
|
+
|
|
316
|
+
**Division of labor across the three discovery layers.** For a lazy-loading client (e.g. Claude
|
|
317
|
+
Code), tool schemas load on demand; what is always in context is only (1) the **tool names** and
|
|
318
|
+
(2) the **`instructions` router**. Docstrings are layer (3): a deferred tool cannot be invoked
|
|
319
|
+
without fetching its definition, so the docstring is guaranteed to be read at the moment of
|
|
320
|
+
choice — typically side by side with the sibling definitions the same search returned, which is
|
|
321
|
+
where the item-2 discriminators do their work. Consequences: pre-fetch discoverability belongs to
|
|
322
|
+
the naming convention and the router, not the docstring (don't pad docstrings with search
|
|
323
|
+
keywords); a *cross-domain* selection caveat that must be visible before any schema is fetched
|
|
324
|
+
(e.g. "prefer `dest_path` for large output") goes in the router's caveat list (`_DOMAIN_BLURBS` /
|
|
325
|
+
`build_instructions()` — see "Server singleton" above), while *sibling-level* discriminators stay
|
|
326
|
+
in docstrings and are never duplicated into the router; and sibling references must use the exact
|
|
327
|
+
tool name (`container_kill`, never "the kill tool") — lazy clients keyword-search descriptions, so
|
|
328
|
+
exact names double as retrieval anchors that surface the right alternative even when the agent
|
|
329
|
+
searched for the wrong one.
|
|
330
|
+
|
|
331
|
+
Self-check before opening the PR: read the docstring as an agent holding 150+ tool names and
|
|
332
|
+
nothing else — could you pick this tool over its neighbors and call it correctly on the first try?
|
|
333
|
+
**Write it this way the first time a tool is added or its behavior changes** — don't wait for a
|
|
334
|
+
future Glama pass to catch it.
|
|
278
335
|
|
|
279
336
|
### MCP resources
|
|
280
337
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: docker-mcp-server
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.3
|
|
4
4
|
Summary: MCP server for managing Docker resources via the Docker SDK for Python
|
|
5
5
|
Project-URL: Homepage, https://github.com/L337-org/docker-mcp
|
|
6
6
|
Project-URL: Repository, https://github.com/L337-org/docker-mcp
|
|
@@ -30,7 +30,7 @@ Description-Content-Type: text/markdown
|
|
|
30
30
|
|
|
31
31
|
# docker-mcp-server
|
|
32
32
|
|
|
33
|
-
[](https://glama.ai/mcp/servers/L337-org/docker-mcp)
|
|
34
34
|
|
|
35
35
|
<!-- mcp-name: io.github.L337-org/docker-mcp-server -->
|
|
36
36
|
|
|
@@ -411,7 +411,7 @@ Connecting this server to an AI agent grants it the same level of access as a lo
|
|
|
411
411
|
| Docker Hub (container) | [gavinlucas/docker-mcp-server](https://hub.docker.com/r/gavinlucas/docker-mcp-server) |
|
|
412
412
|
| Desktop Extension (.mcpb) | [GitHub Releases](https://github.com/L337-org/docker-mcp/releases) |
|
|
413
413
|
| Official MCP Registry | [io.github.L337-org/docker-mcp-server](https://registry.modelcontextprotocol.io/v0.1/servers/io.github.L337-org%2Fdocker-mcp-server/versions) |
|
|
414
|
-
| Glama | [docker-mcp-server](https://glama.ai/mcp/servers/
|
|
414
|
+
| Glama | [docker-mcp-server](https://glama.ai/mcp/servers/L337-org/docker-mcp) |
|
|
415
415
|
| mcp.so | [docker-mcp-server](https://mcp.so/server/docker-mcp-server/GavinLucas) |
|
|
416
416
|
| awesome-mcp-servers | [punkpeye/awesome-mcp-servers](https://github.com/punkpeye/awesome-mcp-servers#cloud-platforms) |
|
|
417
417
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# docker-mcp-server
|
|
4
4
|
|
|
5
|
-
[](https://glama.ai/mcp/servers/L337-org/docker-mcp)
|
|
6
6
|
|
|
7
7
|
<!-- mcp-name: io.github.L337-org/docker-mcp-server -->
|
|
8
8
|
|
|
@@ -383,7 +383,7 @@ Connecting this server to an AI agent grants it the same level of access as a lo
|
|
|
383
383
|
| Docker Hub (container) | [gavinlucas/docker-mcp-server](https://hub.docker.com/r/gavinlucas/docker-mcp-server) |
|
|
384
384
|
| Desktop Extension (.mcpb) | [GitHub Releases](https://github.com/L337-org/docker-mcp/releases) |
|
|
385
385
|
| Official MCP Registry | [io.github.L337-org/docker-mcp-server](https://registry.modelcontextprotocol.io/v0.1/servers/io.github.L337-org%2Fdocker-mcp-server/versions) |
|
|
386
|
-
| Glama | [docker-mcp-server](https://glama.ai/mcp/servers/
|
|
386
|
+
| Glama | [docker-mcp-server](https://glama.ai/mcp/servers/L337-org/docker-mcp) |
|
|
387
387
|
| mcp.so | [docker-mcp-server](https://mcp.so/server/docker-mcp-server/GavinLucas) |
|
|
388
388
|
| awesome-mcp-servers | [punkpeye/awesome-mcp-servers](https://github.com/punkpeye/awesome-mcp-servers#cloud-platforms) |
|
|
389
389
|
|
|
@@ -477,7 +477,13 @@ def buildx_create(
|
|
|
477
477
|
host: str | None = None,
|
|
478
478
|
) -> dict:
|
|
479
479
|
"""
|
|
480
|
-
Create a new builder instance.
|
|
480
|
+
Create a new BuildKit builder instance.
|
|
481
|
+
|
|
482
|
+
Needed when the default `docker` driver falls short: multi-platform builds and cache export
|
|
483
|
+
require a `docker-container` (or `kubernetes`/`remote`) builder. Pass `use=True` to make it
|
|
484
|
+
the default for later `buildx_build` calls (else switch with `buildx_use`); `bootstrap=True`
|
|
485
|
+
starts the builder now rather than on first build. Does not raise on a non-zero CLI exit —
|
|
486
|
+
inspect `returncode`/`stderr` in the result.
|
|
481
487
|
|
|
482
488
|
args:
|
|
483
489
|
name - Name for the new builder (defaults to a generated name)
|
|
@@ -538,6 +538,10 @@ def compose_images(
|
|
|
538
538
|
"""
|
|
539
539
|
List the images used by a compose project's services, parsed from `--format json`.
|
|
540
540
|
|
|
541
|
+
Answers "what image and tag does each service container actually run?" — the containers must
|
|
542
|
+
exist (`compose_up`/`compose_create` first). Use `compose_ps` for container state and
|
|
543
|
+
`image_list` for daemon-wide images. Raises RuntimeError if the CLI call fails.
|
|
544
|
+
|
|
541
545
|
args:
|
|
542
546
|
project_dir - Dir with the compose file (default: server cwd)
|
|
543
547
|
files - Explicit compose file paths (repeatable, `-f`)
|
|
@@ -750,7 +754,12 @@ def compose_pause(
|
|
|
750
754
|
host: str | None = None,
|
|
751
755
|
) -> dict:
|
|
752
756
|
"""
|
|
753
|
-
Pause the containers of a compose project (freezes their processes).
|
|
757
|
+
Pause the containers of a compose project (freezes their processes in place).
|
|
758
|
+
|
|
759
|
+
Paused containers stop consuming CPU but keep memory, network endpoints, and state; resume
|
|
760
|
+
with `compose_unpause`. To actually stop containers (each one's configured stop signal, freeing
|
|
761
|
+
resources) use `compose_stop`; to stop and delete them use `compose_down`. Does not raise on a
|
|
762
|
+
non-zero CLI exit — inspect `returncode`/`stderr` in the result.
|
|
754
763
|
|
|
755
764
|
args:
|
|
756
765
|
services - Restrict to these services (default: all)
|
|
@@ -38,10 +38,14 @@ def config_create(
|
|
|
38
38
|
@tool()
|
|
39
39
|
def config_inspect(id_or_name: str, host: str | None = None) -> dict:
|
|
40
40
|
"""
|
|
41
|
-
Get a swarm config by id or name.
|
|
41
|
+
Get a swarm config's full inspect payload by id or name.
|
|
42
|
+
|
|
43
|
+
Requires a swarm manager. Unlike a secret, a config's payload IS readable after creation:
|
|
44
|
+
`Spec.Data` in the result holds the base64-encoded contents. Use `config_list` to enumerate
|
|
45
|
+
configs; use this to read one config's contents and metadata.
|
|
42
46
|
|
|
43
47
|
args: id_or_name - The config id or name
|
|
44
|
-
returns: dict - The config's attrs
|
|
48
|
+
returns: dict - The config's attrs (ID, CreatedAt, UpdatedAt, Spec{Name, Labels, Data base64})
|
|
45
49
|
"""
|
|
46
50
|
return _get_client(host).configs.get(id_or_name).attrs
|
|
47
51
|
|
|
@@ -245,12 +245,18 @@ def container_start(id_or_name: str, host: str | None = None) -> dict:
|
|
|
245
245
|
@tool()
|
|
246
246
|
def container_stop(id_or_name: str, stop_timeout_seconds: int = 10, host: str | None = None) -> dict:
|
|
247
247
|
"""
|
|
248
|
-
|
|
248
|
+
Gracefully stop a running container (its configured stop signal, then SIGKILL after a timeout).
|
|
249
|
+
|
|
250
|
+
Prefer this over `container_kill` for a clean shutdown: the main process receives the
|
|
251
|
+
container's stop signal (`STOPSIGNAL`, default SIGTERM) and has stop_timeout_seconds to exit
|
|
252
|
+
before the daemon force-kills it. Use `container_restart` to stop and start again in one call,
|
|
253
|
+
or `container_pause` to freeze processes without stopping. When the server runs containerized
|
|
254
|
+
it refuses to stop its own container.
|
|
249
255
|
|
|
250
256
|
args:
|
|
251
257
|
id_or_name - The container id or name
|
|
252
|
-
stop_timeout_seconds - Seconds
|
|
253
|
-
returns: dict - The container's attrs after stop
|
|
258
|
+
stop_timeout_seconds - Seconds between the stop signal and SIGKILL (default 10)
|
|
259
|
+
returns: dict - The container's attrs after the stop (exit code under State.ExitCode)
|
|
254
260
|
"""
|
|
255
261
|
container = _get_client(host).containers.get(id_or_name)
|
|
256
262
|
guard_not_self(container, host=host)
|
|
@@ -279,12 +285,18 @@ def container_restart(id_or_name: str, stop_timeout_seconds: int = 10, host: str
|
|
|
279
285
|
@tool()
|
|
280
286
|
def container_kill(id_or_name: str, signal: str | None = None, host: str | None = None) -> dict:
|
|
281
287
|
"""
|
|
282
|
-
Send a signal to a container.
|
|
288
|
+
Send a signal to a running container (default SIGKILL — immediate, no graceful shutdown).
|
|
289
|
+
|
|
290
|
+
Use it to force-kill a container that ignores `container_stop`, or with `signal` to poke a
|
|
291
|
+
process without stopping it (e.g. SIGHUP for a config reload). For a normal shutdown prefer
|
|
292
|
+
`container_stop`, which sends the container's configured stop signal first. Fails with a
|
|
293
|
+
conflict error if the container is not running. When the server runs containerized it refuses
|
|
294
|
+
to signal its own container.
|
|
283
295
|
|
|
284
296
|
args:
|
|
285
297
|
id_or_name - The container id or name
|
|
286
|
-
signal - Signal
|
|
287
|
-
returns: dict - The container's attrs after
|
|
298
|
+
signal - Signal name or number as a string (e.g. "SIGHUP", "9"); default SIGKILL
|
|
299
|
+
returns: dict - The container's attrs after the signal
|
|
288
300
|
"""
|
|
289
301
|
container = _get_client(host).containers.get(id_or_name)
|
|
290
302
|
guard_not_self(container, host=host)
|
|
@@ -316,10 +328,13 @@ def container_pause(id_or_name: str, host: str | None = None) -> dict:
|
|
|
316
328
|
@tool()
|
|
317
329
|
def container_unpause(id_or_name: str, host: str | None = None) -> dict:
|
|
318
330
|
"""
|
|
319
|
-
Resume all processes in a paused container.
|
|
331
|
+
Resume all processes in a paused container (the reverse of `container_pause`).
|
|
332
|
+
|
|
333
|
+
Only valid on a paused container — it fails if the container is merely stopped; use
|
|
334
|
+
`container_start` for stopped containers. Processes continue from where they were frozen.
|
|
320
335
|
|
|
321
336
|
args: id_or_name - The container id or name
|
|
322
|
-
returns: dict - The container's attrs after unpause
|
|
337
|
+
returns: dict - The container's attrs after unpause (State.Paused becomes false)
|
|
323
338
|
"""
|
|
324
339
|
container = _get_client(host).containers.get(id_or_name)
|
|
325
340
|
container.unpause()
|
|
@@ -429,10 +444,15 @@ def container_logs(
|
|
|
429
444
|
@tool()
|
|
430
445
|
def container_stats(id_or_name: str, host: str | None = None) -> dict:
|
|
431
446
|
"""
|
|
432
|
-
Get
|
|
447
|
+
Get one point-in-time resource-usage snapshot for a container (non-streaming).
|
|
448
|
+
|
|
449
|
+
Returns the raw engine stats payload; CPU percent must be computed from the delta between
|
|
450
|
+
`cpu_stats` and `precpu_stats`. For a pre-computed human-readable summary prefer the
|
|
451
|
+
`docker-stats://{id_or_name}` resource; for a process listing use `container_top`.
|
|
433
452
|
|
|
434
453
|
args: id_or_name - The container id or name
|
|
435
|
-
returns: dict -
|
|
454
|
+
returns: dict - Engine stats payload (read, cpu_stats, precpu_stats, memory_stats, networks,
|
|
455
|
+
pids_stats, ...)
|
|
436
456
|
"""
|
|
437
457
|
container = _get_client(host).containers.get(id_or_name)
|
|
438
458
|
# `decode` is only valid with stream=True; a one-shot stream=False read already returns a dict.
|
|
@@ -530,12 +550,16 @@ def _read_stats_summary(id_or_name: str, host: str | None = None) -> dict:
|
|
|
530
550
|
@tool()
|
|
531
551
|
def container_top(id_or_name: str, ps_args: str | None = None, host: str | None = None) -> dict:
|
|
532
552
|
"""
|
|
533
|
-
|
|
553
|
+
List the processes running inside a container (the daemon runs `ps` on the host).
|
|
554
|
+
|
|
555
|
+
Works on any running container without executing anything in it, so it needs no shell or `ps`
|
|
556
|
+
binary in the image — unlike `container_exec` with `ps`. Use `container_stats` for resource
|
|
557
|
+
usage rather than process lists. Fails if the container is not running.
|
|
534
558
|
|
|
535
559
|
args:
|
|
536
560
|
id_or_name - The container id or name
|
|
537
|
-
ps_args -
|
|
538
|
-
returns: dict -
|
|
561
|
+
ps_args - Extra ps arguments (e.g. "aux"); default is the daemon's standard ps invocation
|
|
562
|
+
returns: dict - {"Titles": [ps column names], "Processes": [[one row of values per process]]}
|
|
539
563
|
"""
|
|
540
564
|
container = _get_client(host).containers.get(id_or_name)
|
|
541
565
|
return cast(dict, container.top(ps_args=ps_args))
|
|
@@ -649,10 +673,14 @@ def container_commit(
|
|
|
649
673
|
@tool()
|
|
650
674
|
def container_diff(id_or_name: str, host: str | None = None) -> list:
|
|
651
675
|
"""
|
|
652
|
-
|
|
676
|
+
List filesystem changes a container has made relative to its image.
|
|
677
|
+
|
|
678
|
+
Use it to audit what a container wrote before `container_commit` or `container_archive_get`,
|
|
679
|
+
or to debug unexpected writes. Only the writable container layer is compared — files in
|
|
680
|
+
volumes and bind mounts never show up.
|
|
653
681
|
|
|
654
682
|
args: id_or_name - The container id or name
|
|
655
|
-
returns: list -
|
|
683
|
+
returns: list - Dicts of {"Path", "Kind"}; Kind 0=modified, 1=added, 2=deleted
|
|
656
684
|
"""
|
|
657
685
|
container = _get_client(host).containers.get(id_or_name)
|
|
658
686
|
return container.diff()
|
|
@@ -661,12 +689,16 @@ def container_diff(id_or_name: str, host: str | None = None) -> list:
|
|
|
661
689
|
@tool()
|
|
662
690
|
def container_rename(id_or_name: str, name: str, host: str | None = None) -> dict:
|
|
663
691
|
"""
|
|
664
|
-
Rename a container.
|
|
692
|
+
Rename a container in place; its id, state, and configuration are unchanged.
|
|
693
|
+
|
|
694
|
+
Use it to free up or claim a container name (names are unique per daemon) — e.g. before
|
|
695
|
+
starting a replacement under the old name. Fails with a conflict error if the new name is
|
|
696
|
+
already taken. Not related to `image_tag`, which names images.
|
|
665
697
|
|
|
666
698
|
args:
|
|
667
699
|
id_or_name - The container id or name
|
|
668
|
-
name - The new name
|
|
669
|
-
returns: dict - The container's attrs after rename
|
|
700
|
+
name - The new name; must not be in use by any other container
|
|
701
|
+
returns: dict - The container's attrs after the rename
|
|
670
702
|
"""
|
|
671
703
|
container = _get_client(host).containers.get(id_or_name)
|
|
672
704
|
container.rename(name)
|
|
@@ -24,19 +24,24 @@ def network_create(
|
|
|
24
24
|
"""
|
|
25
25
|
Create a network.
|
|
26
26
|
|
|
27
|
+
The daemon default driver is `bridge` (single-host); use `overlay` for swarm-wide networks.
|
|
28
|
+
Creating a network attaches nothing — connect containers afterwards with `network_connect` or
|
|
29
|
+
at start via `container_run(network=...)`. Created networks are stamped with provenance labels
|
|
30
|
+
(find them later via `network_list(managed_only=True)`).
|
|
31
|
+
|
|
27
32
|
args:
|
|
28
33
|
name - The name of the network
|
|
29
|
-
driver - Driver name (
|
|
30
|
-
options - Driver-specific options
|
|
31
|
-
ipam - IPAM configuration as a dict
|
|
32
|
-
check_duplicate - Reject creation if a duplicate name exists
|
|
34
|
+
driver - Driver name (daemon default `bridge`; `overlay` for swarm scope)
|
|
35
|
+
options - Driver-specific options dict
|
|
36
|
+
ipam - IPAM configuration as a dict (engine shape: {"Driver", "Config": [{"Subnet", "Gateway", ...}]})
|
|
37
|
+
check_duplicate - Reject creation if a duplicate name exists (deprecated: recent daemons always check)
|
|
33
38
|
internal - Restrict external access
|
|
34
39
|
labels - Labels to set on the network
|
|
35
40
|
enable_ipv6 - Enable IPv6 networking
|
|
36
|
-
attachable - Allow standalone containers to attach (swarm)
|
|
37
|
-
scope - Network scope
|
|
41
|
+
attachable - Allow standalone containers to attach (swarm overlay networks)
|
|
42
|
+
scope - Network scope: "local", "global", or "swarm"
|
|
38
43
|
ingress - Make this an ingress network for swarm routing-mesh
|
|
39
|
-
returns: dict - The created network's attrs
|
|
44
|
+
returns: dict - The created network's attrs (Id, Name, Driver, Scope, IPAM)
|
|
40
45
|
"""
|
|
41
46
|
kwargs: dict = {
|
|
42
47
|
"internal": internal,
|
|
@@ -187,10 +192,14 @@ def network_disconnect(id_or_name: str, container: str, force: bool = False, hos
|
|
|
187
192
|
"""
|
|
188
193
|
Disconnect a container from a network.
|
|
189
194
|
|
|
195
|
+
The container keeps running with its other network attachments; only this endpoint is removed
|
|
196
|
+
(the reverse of `network_connect`). A network with connected containers cannot be deleted, so
|
|
197
|
+
disconnect them before `network_remove`.
|
|
198
|
+
|
|
190
199
|
args:
|
|
191
200
|
id_or_name - The network id or name
|
|
192
|
-
container - The container id or name
|
|
193
|
-
force - Force disconnect
|
|
201
|
+
container - The container id or name to disconnect
|
|
202
|
+
force - Force the disconnect; use to clear a stale endpoint (e.g. from a deleted container)
|
|
194
203
|
returns: bool - True after the container is disconnected
|
|
195
204
|
"""
|
|
196
205
|
network = _get_client(host).networks.get(id_or_name)
|
|
@@ -11,10 +11,14 @@ from docker_mcp.tools.system import _get_client
|
|
|
11
11
|
@tool()
|
|
12
12
|
def node_inspect(id_or_name: str, host: str | None = None) -> dict:
|
|
13
13
|
"""
|
|
14
|
-
Get a swarm node by id or name.
|
|
14
|
+
Get a swarm node's full inspect payload by id or name.
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
Must run against a swarm manager. Shows role, availability, status, and manager reachability —
|
|
17
|
+
use `node_list` to enumerate nodes first, or the `docker://nodes` resource for a fleet
|
|
18
|
+
summary; `service_ps(filters={"node": ...})` shows what a service runs on one node.
|
|
19
|
+
|
|
20
|
+
args: id_or_name - The node id or hostname (as shown by `node_list`)
|
|
21
|
+
returns: dict - The node's attrs (Spec{Role, Availability}, Status, ManagerStatus for managers)
|
|
18
22
|
"""
|
|
19
23
|
return _get_client(host).nodes.get(id_or_name).attrs
|
|
20
24
|
|
|
@@ -42,9 +42,14 @@ def plugin_install(remote: str, local_name: str | None = None, host: str | None
|
|
|
42
42
|
@tool()
|
|
43
43
|
def plugin_list(host: str | None = None) -> list:
|
|
44
44
|
"""
|
|
45
|
-
List installed plugins.
|
|
45
|
+
List installed engine plugins with their full attrs.
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
Covers managed engine plugins (volume/network/logging drivers installed via `plugin_install`)
|
|
48
|
+
— not docker CLI plugins such as compose, buildx, or scout. Use it to find exact plugin names
|
|
49
|
+
for `plugin_inspect`/`plugin_enable`/`plugin_disable`/`plugin_remove`; the `Enabled` key shows
|
|
50
|
+
each plugin's state.
|
|
51
|
+
|
|
52
|
+
returns: list - One attrs dict per installed plugin (Id, Name, Enabled, Settings, Config)
|
|
48
53
|
"""
|
|
49
54
|
return [p.attrs for p in _get_client(host).plugins.list()]
|
|
50
55
|
|
|
@@ -110,11 +115,15 @@ def plugin_enable(name: str, timeout_seconds: int = 0, host: str | None = None)
|
|
|
110
115
|
@tool()
|
|
111
116
|
def plugin_remove(name: str, force: bool = False, host: str | None = None) -> bool:
|
|
112
117
|
"""
|
|
113
|
-
|
|
118
|
+
Uninstall an engine plugin from the daemon.
|
|
119
|
+
|
|
120
|
+
Permanent removal — to deactivate but keep a plugin installed use `plugin_disable` instead. An
|
|
121
|
+
enabled plugin must be disabled first unless `force=True`. Plugin names come from
|
|
122
|
+
`plugin_list`.
|
|
114
123
|
|
|
115
124
|
args:
|
|
116
|
-
name - The plugin name
|
|
117
|
-
force -
|
|
125
|
+
name - The plugin name (e.g. "vieux/sshfs:latest")
|
|
126
|
+
force - Remove even if the plugin is enabled (default False)
|
|
118
127
|
returns: bool - True after removal
|
|
119
128
|
"""
|
|
120
129
|
_get_client(host).plugins.get(name).remove(force=force)
|
|
@@ -11,14 +11,20 @@ def secret_create(
|
|
|
11
11
|
name: str, data: bytes, labels: dict | None = None, driver: dict | None = None, host: str | None = None
|
|
12
12
|
) -> dict:
|
|
13
13
|
"""
|
|
14
|
-
Create a swarm secret.
|
|
14
|
+
Create a swarm secret; requires a swarm manager.
|
|
15
|
+
|
|
16
|
+
Write-once: the payload can never be read back through the API (`secret_inspect` returns
|
|
17
|
+
metadata only) and cannot be changed later — to rotate, create a new secret and update the
|
|
18
|
+
consuming services, keeping your own copy of the value. For non-sensitive data that should
|
|
19
|
+
stay readable, use `config_create` instead. Created secrets are stamped with provenance
|
|
20
|
+
labels.
|
|
15
21
|
|
|
16
22
|
args:
|
|
17
|
-
name -
|
|
18
|
-
data - The secret payload
|
|
23
|
+
name - Name for the secret (unique within the swarm)
|
|
24
|
+
data - The secret payload (max 500 KB; must be empty when driver is set)
|
|
19
25
|
labels - Labels to set on the secret
|
|
20
|
-
driver - Optional secret
|
|
21
|
-
returns: dict - The created secret's attrs
|
|
26
|
+
driver - Optional secret-driver config for values held in an external store
|
|
27
|
+
returns: dict - The created secret's attrs (ID and Spec metadata; never the payload)
|
|
22
28
|
"""
|
|
23
29
|
kwargs: dict = {
|
|
24
30
|
"name": name,
|
|
@@ -174,12 +174,18 @@ def service_remove(id_or_name: str, host: str | None = None) -> bool:
|
|
|
174
174
|
@tool()
|
|
175
175
|
def service_ps(id_or_name: str, filters: dict | None = None, host: str | None = None) -> list:
|
|
176
176
|
"""
|
|
177
|
-
List
|
|
177
|
+
List a swarm service's tasks (per-replica scheduling units), like `docker service ps`.
|
|
178
|
+
|
|
179
|
+
Shows where replicas run and why they fail: each task carries `Status`
|
|
180
|
+
(State/Message/ContainerStatus), `DesiredState`, `NodeID`, and `Slot`. Prefer this over
|
|
181
|
+
`container_list` for services (tasks may run on other nodes), `stack_ps` for a whole stack,
|
|
182
|
+
and the `service-tasks://{id_or_name}` resource for a computed rollout summary. Requires a
|
|
183
|
+
swarm manager.
|
|
178
184
|
|
|
179
185
|
args:
|
|
180
186
|
id_or_name - The service id or name
|
|
181
|
-
filters - Filter
|
|
182
|
-
returns: list -
|
|
187
|
+
filters - Filter dict; keys: id, name, node, label, desired-state (running|shutdown|accepted)
|
|
188
|
+
returns: list - Task dicts (ID, Slot, NodeID, Status, DesiredState, Spec)
|
|
183
189
|
"""
|
|
184
190
|
service = _get_client(host).services.get(id_or_name)
|
|
185
191
|
return service.tasks(filters=filters)
|
|
@@ -261,9 +261,15 @@ def system_info(host: str | None = None) -> dict:
|
|
|
261
261
|
@tool()
|
|
262
262
|
def system_df(host: str | None = None) -> dict:
|
|
263
263
|
"""
|
|
264
|
-
|
|
264
|
+
Summarize Docker disk usage: layer storage plus per-object sizes for images, containers, volumes, build cache.
|
|
265
265
|
|
|
266
|
-
|
|
266
|
+
Equivalent to `docker system df`. Use it to find what to reclaim before `image_prune` /
|
|
267
|
+
`container_prune` / `volume_prune` / `buildx_prune`; use `system_info` for daemon config and
|
|
268
|
+
counts rather than sizes. The reply enumerates every object on the daemon, so expect a large
|
|
269
|
+
payload on busy hosts.
|
|
270
|
+
|
|
271
|
+
returns: dict - {"LayersSize", "Images", "Containers", "Volumes", "BuildCache"} with per-object
|
|
272
|
+
size fields
|
|
267
273
|
"""
|
|
268
274
|
return _get_client(host).df()
|
|
269
275
|
|
|
@@ -42,10 +42,14 @@ def volume_create(
|
|
|
42
42
|
@tool()
|
|
43
43
|
def volume_inspect(name: str, host: str | None = None) -> dict:
|
|
44
44
|
"""
|
|
45
|
-
Get a volume by name.
|
|
45
|
+
Get a volume's full inspect payload by name.
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
Use it after `volume_list` to see a volume's on-disk location, driver, and labels — e.g.
|
|
48
|
+
before a backup or `volume_remove`. Volumes are addressed purely by name; they have no
|
|
49
|
+
separate id.
|
|
50
|
+
|
|
51
|
+
args: name - The volume name (volumes have no ids)
|
|
52
|
+
returns: dict - The volume's attrs (Name, Driver, Mountpoint, CreatedAt, Labels, Options, Scope)
|
|
49
53
|
"""
|
|
50
54
|
return _get_client(host).volumes.get(name).attrs
|
|
51
55
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"manifest_version": "0.4",
|
|
3
3
|
"name": "docker-mcp-server",
|
|
4
4
|
"display_name": "Docker MCP Server",
|
|
5
|
-
"version": "2.1.
|
|
5
|
+
"version": "2.1.3",
|
|
6
6
|
"description": "Manage Docker through the Docker SDK for Python and the docker CLI: containers, images, networks, volumes, Compose, Swarm, Buildx, Scout, and OCI registries, exposed as MCP tools.",
|
|
7
7
|
"long_description": "Manage one or more Docker daemons through your AI agent — containers, images, networks, volumes, Compose, Swarm, Buildx, Scout, and OCI registries, exposed as MCP tools.\n\nDiscovery is automatic for your local socket with no configuration required. Or point it at additional remote daemons over TCP, TLS, or SSH (and mark any of them read-only to monitor without the risk of accidental changes). It also surfaces logs and stats as resources for triage. Read-only / no-destructive / disabled-domain switches let you register a restricted tool surface.\n\nAnswer questions like: ‘why did my local container crash?’, ‘is my production system under memory pressure?’, ‘what differences are there between my staging and production systems?’\n\nRuns entirely on your machine and sends no telemetry — see the Privacy Policy.",
|
|
8
8
|
"author": {
|
|
@@ -282,7 +282,7 @@ ssh = [
|
|
|
282
282
|
|
|
283
283
|
[[package]]
|
|
284
284
|
name = "docker-mcp-server"
|
|
285
|
-
version = "2.1.
|
|
285
|
+
version = "2.1.3"
|
|
286
286
|
source = { editable = "." }
|
|
287
287
|
dependencies = [
|
|
288
288
|
{ name = "cryptography", marker = "platform_machine == 'x86_64' and sys_platform == 'darwin'" },
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/.github/ISSUE_TEMPLATE/feature_request.md
RENAMED
|
File without changes
|
|
File without changes
|
{docker_mcp_server-2.1.2 → docker_mcp_server-2.1.3}/.github/actions/file-failure-issue/action.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|