Docker MCP
Supports an optional remote connection path through Cloudflare Access and Tunnel, where the included gateway verifies the Cloudflare Access JWT, configured audience, and allowed identity before forwarding MCP traffic to the Docker MCP server.
Provides tools for inspecting and maintaining a local Docker Desktop environment via a restricted Docker API proxy, including container, image, and Compose inspection; logs; stats; audits; narrow maintenance operations such as image pull, container restart, safe pruning/cleanup, and Compose redeploy; and Docker Scout quickview, CVE, recommendation, SBOM, and compare capabilities.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Docker MCPlist my running Docker containers and show their status"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Docker MCP
A deliberately restricted Model Context Protocol (MCP) server for inspecting and maintaining a local Docker Desktop environment without giving the MCP client the raw Docker socket or a general host shell.
It is intended for an AI-assisted local infrastructure stack where the model should be able to answer questions such as:
Which containers are running?
Which image or Compose deployment is stale?
Does this container run as root?
Is a newer image available?
Which known repository checkout is behind its remote?
Can this one allowlisted service be safely restarted or redeployed?
The answer should not require handing the model arbitrary Docker administrator access.
Independent project. This repository is not a fork of
docker/hub-mcpordocker/mcp-gateway. It can integrate with Docker MCP Toolkit/Gateway, but the restricted local maintenance layer in this repository is its own implementation.
Why this project exists
Mounting /var/run/docker.sock into a model-facing service is effectively equivalent
to granting broad Docker administrator capability. From there, generic container
creation or host mounts can become host compromise.
Docker MCP inserts explicit policy boundaries between the MCP client and that socket.
MCP client
|
v
Docker MCP server
|
v
restricted Docker API proxy
|
v
Docker EngineOnly the restricted socket proxy receives the Docker socket. The model-facing MCP container does not.
Windows host operations that cannot safely happen inside the container use a second, separate boundary:
MCP client
|
v
Docker MCP
|
v
host Maintenance Runner
|
+--> exact allowlisted repository alias
+--> exact allowlisted Scheduled Task alias
+--> exact allowlisted Compose project/serviceThe caller supplies an alias or a bounded operation — not an arbitrary path, branch, remote, command line or PowerShell expression.
Related MCP server: Docker MCP Server
What it can do
The tool surface has three main jobs.
Area | Examples |
Inspection | containers, logs, stats, images, Compose state, deployment drift |
Narrow maintenance | fast-forward an allowlisted repo, restart an allowlisted task/container, redeploy an allowlisted Compose target, clean known stale resources |
Docker Scout | CVEs, recommendations, SBOM and image comparisons |
The repository also includes a small host-side MCP port registry so local services can reserve stable ports instead of independently guessing from the same range.
For runtimes that genuinely require host-side recovery after Docker restarts — for example because credentials live only in tmpfs — an optional event-driven runtime supervisor can invoke preconfigured local recovery scripts. The supervisor itself is not exposed as a generic MCP command surface.
What it deliberately does not expose
The following are intentionally outside the MCP contract:
arbitrary
docker exec.arbitrary
docker run.a general shell or PowerShell runner.
raw Docker socket access.
arbitrary host filesystem paths.
arbitrary Git remotes or branches.
arbitrary Compose files/build contexts.
generic container deletion.
arbitrary Scheduled Task names.
arbitrary process arguments.
This is the core design constraint, not an accidental limitation.
Security model
The maintained deployment uses several layers rather than trusting one gateway:
the MCP server runs non-root.
the model-facing container never receives the raw Docker socket.
the restricted proxy exposes only the Docker API operations required by the reviewed tools;
container_inspectdeliberately omits environment variables and command lines while still exposing fields needed for privilege audits;repository maintenance is fail-closed: exact local root, clean checkout, expected HTTPS GitHub origin,
mainbranch and fast-forward-only update;host maintenance resolves aliases from ignored local configuration.
public access, when enabled, is authenticated through Cloudflare Access and a reviewed gateway;
machine paths, identities, allowlists, Access configuration and credentials remain outside Git.
Local and remote architecture
Local use:
local MCP client
|
v
Docker MCP Toolkit / profile
|
v
Docker MCP server
|
v
restricted socket proxy
|
v
Docker Desktop EngineOptional remote use:
ChatGPT / remote MCP client
|
v
Cloudflare Access
|
v
authenticated MCP gateway
|
v
Docker MCP server
|
v
restricted socket proxy
|
v
Docker Desktop EngineThe public edge does not replace the local policy boundary; it adds authentication in front of it.
Quick start
Requirements:
Windows with Docker Desktop.
Docker MCP Toolkit.
PowerShell.
Docker Engine running.
Install the local profile from the repository root:
Set-ExecutionPolicy -Scope Process Bypass
.\install.ps1
.\test.ps1The default profile is dockerlocal.
Run the profile locally with Docker MCP Gateway:
docker mcp gateway run --profile dockerlocalThe detailed sections below document the exact tool surface, optional Cloudflare path, host maintenance runner, port registry and runtime supervisor.
Scope
Read-only inspection:
containers_listcontainer_inspect— includes the configured container user for privilege audits without exposing environment variables or command linescontainer_logscontainer_statscompose_statusimages_listimage_inspectmcp_deployment_audit— checks every running Compose project for Compose files newer than its containers, and additionally comparesdocker compose config --hashplus desired image for allowlisted projects. This catches both broad file-level drift and exact desired-config drift without a background watcher.image_usage_audit— reports container usage plus repository-level superseded hints; hints are not deletion-safe because unused images can still be Dockerfile build-stage inputsmaintenance_job_statusmaintenance_runner_statusport_registry_status— persistent host MCP port reservations plus listeners in the managed range that are not registered
Narrow maintenance:
repo_status— HEAD/branch/clean/conflict/origin eligibility for one local allowlisted repository aliasrepo_pull_ff— exactgit pull --ff-only origin mainsemantics for a clean allowlisted main checkoutscheduled_task_status— state for one allowlisted Windows Scheduled Task aliasscheduled_task_control— start/stop/restart one allowlisted Windows Scheduled Task aliasimage_pullcontainer_restartimage_prune_danglingcleanup_stale_mcp_containerscleanup_superseded_images— deletes only explicitly supplied local image refs after verifying that no running or stopped container uses the resolved image; multiple aliases must be supplied explicitly and are untagged one by one without forcecompose_redeploy
Host disk capacity (read-only)
host_disk_usage queries the existing allowlisted Windows maintenance runner for
local fixed-volume capacity and available space. The result includes volume
letters, total/available/used bytes, used percent and an observation timestamp.
It takes no user-controlled path or command, does not inspect file names or
credentials, and cannot clean up or resize disks. The MCP gateway and runtime
socket-proxy retain their existing restrictions.
Treat measurement_status=unknown or a runner timeout as UNKNOWN, not
0% utilization. The host drive measurement is not the same as Docker's internal
WSL2 virtual-disk usage. The daily watch alerts at roughly 80% used only when
valid host capacity and available bytes were measured.
Docker Scout:
scout_quickviewscout_cvesscout_recommendationsscout_sbomscout_compare
The project intentionally does not expose arbitrary docker exec, docker run, shell access, generic container deletion, arbitrary Compose paths, arbitrary builds, or the raw Docker socket.
Requirements
Windows with Docker Desktop
Docker MCP Toolkit
PowerShell
Docker Engine running
Local installation
Clone or download the repository, open PowerShell in the repository root, and run:
Set-ExecutionPolicy -Scope Process Bypass
.\install.ps1The default Docker MCP profile is dockerlocal. To use another profile:
.\install.ps1 -Profile my-profileThe installer:
builds the restricted Docker API proxy;
verifies the proxy on loopback;
builds the Docker MCP server image;
installs the local Docker MCP catalog entry, adding the Maintenance Runner control directory bind mount (
%LOCALAPPDATA%\DockerLocalMCP\control→/control) to the installed copy only;adds the server to the selected Docker MCP profile;
enables the server tools and performs tool discovery.
Verify the installation:
.\test.ps1To run the Docker MCP Gateway locally with this profile:
docker mcp gateway run --profile dockerlocalFor supported local MCP clients, Docker MCP Toolkit can also connect a client to the profile:
docker mcp client connect <client-name> --profile dockerlocalThe exact client names supported by Docker MCP Toolkit depend on the installed Docker Desktop version.
Because of the control-directory mount, every process that runs the gateway for this
profile must set MCP_GATEWAY_DOCKER_BIND_ALLOW_WRITABLE_PATHS to that directory;
otherwise Docker MCP Gateway refuses to start the server. See
docs/CONNECTING.md.
Gateway-started containers get random Docker names. For stdio clients that should show up
under an identifying name (dockerlocal-stdio-<client>-<pid>), use
local-mcp/stdio-launcher.mjs instead; see
docs/CONNECTING.md.
Remote connection through Cloudflare Access
The remote path is optional. It is intended for an MCP client that cannot directly reach the local Docker MCP profile.
Use placeholders such as:
Public hostname: docker-mcp.example.com
MCP endpoint: https://docker-mcp.example.com/mcp
Access team: team-name.cloudflareaccess.com
Origin target: http://dockerlocal-gateway:8080No real hostname, tunnel identifier, account identifier, email address, audience tag, token, or credential belongs in this repository.
1. Start the remote stack
Run:
Set-ExecutionPolicy -Scope Process Bypass
.\install-public.ps1The installer prompts at runtime for:
Cloudflare Access team domain.
Access application audience tag.
allowed identity email.
Those values are written only to the ignored local file:
public/gateway.env2. Create the Cloudflare side
In Cloudflare:
create a Tunnel or use an existing Tunnel;
create a public hostname such as
docker-mcp.example.com;point the origin to
http://dockerlocal-gateway:8080;protect the hostname with a Cloudflare Access self-hosted application;
configure the Access policy for the identity that is allowed to use the MCP endpoint.
If cloudflared runs as a Docker container, attach it to the Docker MCP edge network:
docker network connect dockerlocal-public_edge <cloudflared-container>The public MCP client should then connect to:
https://docker-mcp.example.com/mcpThe included gateway verifies the Cloudflare Access JWT, the configured audience, and the configured identity before forwarding MCP traffic.
See docs/CONNECTING.md for the full local and remote connection flow.
Host-side Compose maintenance runner
The Docker MCP container deliberately does not receive arbitrary host filesystem access. Narrow Compose redeploy operations are therefore delegated to a small Windows runner.
Install it with:
.\install-maintenance-runner.ps1The runner accepts only:
an allowlisted project.
an allowlisted service for direct Compose projects.
redeploy_current.rebuild_and_redeploy.
By default only this project's own Compose services are allowlisted.
Additional host projects can be configured locally in:
%LOCALAPPDATA%\DockerLocalMCP\maintenance-projects.local.jsonStart from maintenance-projects.example.json. The local file is ignored by Git and should not contain secrets. The example includes an avanza-mcp-public entry; replace its placeholder with the local Avanza checkout path to allow safe gateway-only redeploys.
The allowlist is the source of truth for exact desired-Compose hash checks and write maintenance. Separately, the audit performs a read-only file-age check for every running Compose project using Docker's existing Compose labels and the host filesystem. This broad check does not grant redeploy rights. mcp_deployment_audit only performs these checks when called; there is no background reconciliation loop.
Two execution modes are supported:
Compose mode: the runner executes only the configured Compose files and allowlisted services.
Script-backed mode: the runner invokes one absolute, allowlisted PowerShell
.ps1wrapper with a fixed argument array for the requested operation. This is intended for projects whose wrapper owns required runtime setup such as DPAPI-backed or tmpfs secret injection.
Script-backed projects are whole-project operations: callers must omit the services argument. The local config contains the script path and fixed arguments only; credentials and secret values must remain in the project-specific secret store and must not be copied into the maintenance config. An operation may use an empty argument array when the allowlisted wrapper itself requires no parameters.
AIRSEA aggregate coverage: on-demand read-only access
airsea_coverage_report is a dedicated, no-argument, read-only inspection
of the existing AIRSEA-Monitor shadow pilot. It returns up to 14 days of
privacy-preserving AIS intake counts, acceptance/rejection reasons and coarse
W/M/E-zone aggregates. This is not a Market Engine integration, recurring
monitor, second collector, or trade signal. No raw vessel records, credentials,
positions or host paths are accessible to the caller.
The existing Windows Maintenance Runner permits this function only when the
specific MarketObservationPilotShadow scheduled-task alias is already
allowlisted in local-only host maintenance configuration. Both the current
DockerLocal MCP image and installed host-side runner must be updated before
the tool appears. See AIRSEA read-only report design and release gate.
Allowlisted Windows host maintenance
The same host-side Maintenance Runner can expose a small Windows maintenance surface without exposing PowerShell or a generic process runner.
The MCP client supplies only an alias. Repository paths, the exact origin URL, the fixed main branch, Scheduled Task names and Task Scheduler paths are resolved from the local-only file:
%LOCALAPPDATA%\DockerLocalMCP\host-maintenance.local.jsonStart from host-maintenance.example.json. The real file is covered by *.local.json in .gitignore and must not be committed.
Example shape:
{
"repositories": {
"example-repo": {
"path": "<absolute-path-to-repository>",
"origin_url": "https://github.com/example/example-repo.git",
"branch": "main"
}
},
"scheduled_tasks": {
"example-service": {
"task_name": "ExampleScheduledTask",
"task_path": "\\"
}
}
}Repository pulls fail closed unless all of these are true:
the configured path is the exact Git repository root.
the current branch is
main.tracked and untracked status is clean.
there are no unresolved index conflicts.
the current
originURL exactly matches the local allowlist.the update can complete with
--ff-only.
The runner also disables Git hooks and recursive submodule updates for maintenance Git calls, and disables interactive credential prompts. The MCP schema has no path, remote, branch, argument or command-string field.
Scheduled Task lookup is exact after local alias resolution. The remote MCP caller cannot choose a Task Scheduler name/path or PowerShell argument.
The four host-maintenance tools return compact structured JSON. If a bounded synchronous call times out, it returns a job_id that can be inspected with maintenance_job_status.
For the initial activation on an already-installed DockerLocal stack, enable-host-maintenance.ps1 validates one repository and one Scheduled Task, merges them into the local allowlist, preserves the existing Cloudflare gateway configuration, rebuilds the MCP image and restarts the Maintenance Runner. Re-run it with another pair of aliases to extend the local allowlist later.
.\enable-host-maintenance.ps1 `
-RepositoryAlias example-repo `
-RepositoryPath C:\path\to\example-repo `
-ScheduledTaskAlias example-service `
-ScheduledTaskName ExampleScheduledTaskTo allowlist an additional repository without creating a Scheduled Task mapping, use the repo-only helper:
.\add-host-maintenance-repository.ps1 `
-RepositoryAlias another-repo `
-RepositoryPath C:\path\to\another-repoThe helper performs the same exact-root, main, and HTTPS GitHub-origin validation, preserves the existing task allowlist, and restarts only the local Maintenance Runner so it reloads the updated configuration.
Host MCP port registry
Local MCP services should not independently guess static ports. Docker MCP installs a small host-side registry at:
%LOCALAPPDATA%\DockerLocalMCP\port-registry.jsonThe default managed range is 8760–8799. A service reserves a port once through
port-registry.ps1 and then reuses that reservation on every normal start. Allocation is protected
by a named Windows mutex and an atomic JSON write.
The intended lifecycle is:
first install
-> request preferred port (optional)
-> skip ports reserved by another service
-> skip ports with an unrelated live listener
-> reserve first safe port
-> persist mapping
normal start / restart
-> read the existing service reservation
-> use exactly that port
-> if another process owns it, fail closedDo not automatically move to another port at runtime. Cloudflare ingress, health checks and MCP clients rely on stable local endpoints. Dynamic allocation happens only when a service has no reservation yet.
For migration of an already-running service, the helper can adopt a listening port only when the
installer supplies an expected command-line fragment and the listener owner matches it. This is for
local installer use; the remote Docker MCP surface exposes only port_registry_status, never an
arbitrary process/command matching primitive.
The allocator also inspects Docker's configured host-port bindings, including stopped containers. A Docker-backed service therefore keeps its host port protected even while its container is down; another MCP cannot claim the port during that outage.
The read-only status tool reports both Docker-configured host ports and live listeners inside 8760–8799 that are not yet in the registry. This makes legacy/manual allocations visible before they create another collision.
Existing non-Docker services can be seeded once with the local-only helper:
.\reserve-mcp-port.ps1 -Service example-http -PreferredPort 8765 -AdoptIfCommandContains "expected-process-fragment"The command-line fragment is checked only on the host during migration and is not exposed through the remote MCP surface.
Event-driven local runtime supervisor
Some local MCP runtimes intentionally keep credentials only in ephemeral container storage such as tmpfs. Those credentials disappear when Docker Desktop or the container is restarted. Requiring a user to rerun a bootstrap script after every Docker restart defeats the purpose of a resilient local runtime.
Docker MCP therefore includes an optional host-side runtime supervisor. It is event-driven rather than a fixed polling watchdog:
Windows logon
-> Runtime Supervisor
-> Docker events
-> relevant container start / restart / health change
-> runtime health check
-> allowlisted local recovery scriptWhen Docker Engine itself disappears, the supervisor waits. When Docker becomes available again it subscribes to the Docker event stream before the initial reconciliation pass, so container events that occur during a longer recovery are buffered rather than lost. While idle it refreshes only its local heartbeat state; it does not query healthy containers every few minutes.
The supervisor contains no credentials and is not exposed as an MCP tool. It only invokes PowerShell scripts explicitly listed in the trusted local configuration file:
%LOCALAPPDATA%\DockerLocalMCP\runtime-supervisor.local.jsonThe configuration can define one or more container health checks per runtime: whether each named container is running, whether Docker reports it as healthy, and whether expected ephemeral files still exist inside it.
Recovery is restricted to an absolute local .ps1 file plus an argument array. There is no inline shell-command field. Recovery scripts are launched as isolated child PowerShell processes so Docker/Compose progress written to stderr cannot be mistaken for a supervisor failure. recovery_wait_seconds controls the bounded post-recovery health wait for runtimes that need a few seconds to become healthy.
Start with:
Copy-Item .\runtime-supervisor.example.json "$env:LOCALAPPDATA\DockerLocalMCP\runtime-supervisor.local.json"Edit only the local copy to point at runtimes that genuinely need post-restart recovery, then install:
.\install-runtime-supervisor.ps1The installer registers DockerLocalRuntimeSupervisor at Windows logon. By default it uses a tiny Windows Script Host wrapper so the long-running supervisor stays completely backgrounded even when Windows Terminal is the system's default console host. Pass -Visible only for foreground troubleshooting. The process remains attached to docker events; polling is used only while Docker Engine is unavailable so it can reconnect. Start/restart events use a short startup grace period for health-only runtimes, while tmpfs-file checks remain fast. Healthy healthcheck events are ignored so normal startup cannot create a recovery feedback loop.
Do not add a runtime merely because it is Dockerized. Persistent .env configuration and ordinary Docker restart policies do not need this supervisor. It is intended for runtimes with a real host-side recovery step, especially ephemeral secret rehydration.
Secret handling
Do not commit:
public/gateway.env..envfiles.API keys, tokens, passwords, Access audience values, or identity allowlists.
maintenance-projects.local.json.runtime logs, heartbeats, or maintenance job files.
Dependency and security automation
This repository includes:
Dependabot version updates for Python, Dockerfiles, Docker Compose and GitHub Actions.
CodeQL analysis for Python, JavaScript/TypeScript and GitHub Actions.
GitHub Dependabot vulnerability alerts through the repository's security settings.
License
MIT. See LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
Read-only MCP access to a documented IT fleet: state, changes, posture. 15 tools.
Securely control computers you explicitly pair through files, terminals, processes, screenshots, desktop UI/input, clipboard, browser automation, diagnostics, and document tools.
Read-only access to an ET Ducky organization's agents, alerts, and fleet summary
Read-only finance and operations controls for AI agents with evidence and safe next actions.
Related MCP Servers
- AlicenseNot gradedqualityNot gradedmaintenanceEnables AI assistants to interact with Docker containers through safe, permission-controlled access to inspect, manage, and diagnose containers, images, and compose services with built-in timeouts and AI-powered analysis.-
- AlicenseNot gradedqualityCmaintenanceProvides a local API to manage Docker containers and volumes, enabling operations like listing, inspecting, starting, stopping, and removing containers, as well as managing volumes, all through HTTP endpoints without shell commands.47 npmMIT
- AlicenseAqualityAmaintenanceA secure, local-first MCP server for read-only inspection and troubleshooting of development environments, exposing narrow, typed, auditable capabilities for repository inspection, log summarization, Docker review, and security scanning without granting unrestricted machine access.8MIT
- FlicenseNot gradedqualityBmaintenanceEnables AI-assisted DevOps by exposing scoped, role-based tools to inspect and remediate a Docker app stack, with audit logging and allow-listed commands for safe incident response.-