Skip to main content
Glama

Docker MCP Server — Docker tools for AI agents

A Docker MCP server that lets AI agents operate Compose stacks with structured, Docker-native tools — instead of brittle shell commands and terminal-shaped output.

Inspect containers, read logs, check health, query databases, take dumps, and bring stacks up or down — on your laptop or on a server across the world by naming its profile.

It speaks Docker the way you do: your compose files, project names, and service names. Answers come back as named fields with enough context for an agent to act on them, rather than parse an ASCII table and guess.

Works with Claude Code, Codex CLI, Cursor, opencode, Gemini CLI, Qwen Code and other MCP clients.

MCP Registry npm version npm downloads tests License: MIT

Install · Tools · Setup · Security · Docs · Changelog


Install in 30 seconds

No global installation required. npx downloads the package on first use:

npx -y @hypnosis/docker-mcp-server

Add it to your MCP client — Claude Code, for example — for every project:

claude mcp add docker -s user -- npx -y @hypnosis/docker-mcp-server

That is the whole setup for the Docker on this machine. No profiles file, no environment variables: the server uses the local Docker socket, and the compose project of your working directory is the project it answers about.

For a Docker host on another machine, add one file with the servers you reach over SSH:

claude mcp add docker -s user \
  -e DOCKER_MCP_PROFILES_FILE="$HOME/.claude/docker-profiles.json" \
  -- npx -y @hypnosis/docker-mcp-server
{
  "default": "local",
  "profiles": {
    "local": { "mode": "local" },
    "production": {
      "host": "prod.example.com",
      "username": "deployer",
      "privateKeyPath": "~/.ssh/id_ed25519"
    }
  }
}

Now every tool takes a profile, and docker_health({ action: "profiles" }) lists what the server was given.

Codex, Cursor, opencode and other clients are covered in Set up the Docker MCP server.

Install as a plugin

Some clients — Claude Code, for example — can take the whole thing as a plugin instead:

/plugin marketplace add hypnosis/docker-mcp-server
/plugin install docker-mcp-server@docker-mcp-server

The plugin brings the server up against the Docker on this machine, with no configuration at all. Point it at other hosts the same way as above, with DOCKER_MCP_PROFILES_FILE.

Requirements

Node.js TypeScript MCP SDK

Node.js 18+ and a Docker daemon you can already reach — the local socket, or a remote host over SSH with a key.

The server starts whether or not Docker is up. A call made while the daemon is down answers "start Docker" and works on the next try, without restarting your MCP client.

Prefer a pinned version, offline work, or one less registry check per launch: npm install -g @hypnosis/docker-mcp-server, then use docker-mcp-server as the command instead of npx.

Related MCP server: ChatGPT MCP Server

Who this Docker MCP server is for

  • AI-assisted developers who build, run, and debug applications with Docker Compose.

  • Backend and full-stack developers who need their coding agent to inspect containers, logs, health checks, databases, and Compose services while solving a problem.

  • Independent developers and small product teams who deploy Docker applications to their own servers and want one agent workflow for local development and production.

  • Platform engineers who maintain Docker environments across development, staging, and production hosts.

  • DevOps engineers and SREs using AI coding agents for incident checks, routine container work, and faster investigation of Compose-based services.

  • Agencies and technical teams maintaining client applications, where an agent needs to understand an unfamiliar Compose stack before making a change.

  • QA and release engineers who need repeatable visibility into test stacks, service health, logs, and databases before a release.

Why use a Docker MCP server instead of raw Docker CLI?

Docker CLI is excellent for a person at a terminal. An AI agent needs something different: Docker and Compose concepts it can query directly, results it can reason about, and clear boundaries between what happened, what was not found, and what could not be checked.

Less context, lower AI cost

docker ps, docker stats, and docker logs are designed to be read on a screen. Their output mixes labels, values, units, and formatting into text an agent must parse before it can use it.

A Docker MCP tool returns the fields the task needs: service, project, state, health, ports, timestamps, byte counts, exit codes, and log streams. The agent receives less terminal noise, spends fewer tokens interpreting it, and keeps more context for the problem it is solving.

Debug Docker Compose as a system

A Docker application is more than a list of containers. It is a Compose project with services, dependencies, health checks, logs, databases, and resources that affect one another.

The server lets an agent ask about that system in the names you use every day: the project, service, and Compose file. One call can show the state and health of a stack; another can inspect a bounded log window, measure a service, or query the database already running beside it. The agent spends its turns finding the cause, not assembling and decoding shell commands.

Make decisions from explicit results

A shell command can return an empty table, clipped output, or an error printed alongside ordinary text. For an agent, those are different situations — and treating them as the same leads to guesses.

Docker MCP results say what was found, what was not found, what was cut short, and why an action did not run. A missing healthcheck is not a failed healthcheck; an empty list is not a failed read; a command with no exit code never started. That gives the agent a reliable basis for its next step, and gives you fewer confident-looking but wrong fixes.

Manage local and remote Docker hosts over SSH

Use the same Docker MCP tools on the Docker daemon beside you and on a remote server. A remote Docker host is a profile: a stable name, its SSH connection, and the authentication it needs. The agent names that profile in a tool call:

docker_container({ "action": "list", "profile": "production" })

Leave profile out and the default Docker host answers, so local development stays frictionless. Name a profile the server does not know and the response lists the profiles it does know instead of quietly sending a request to another machine.

Remote Docker Compose projects are resolved by the labels their containers already carry. Ask for a project by name wherever it lives on that host — no remote working directory or fixed Compose path to maintain. For a project that has not run there yet, pass its file explicitly with compose_path.

Built for the Model Context Protocol

A native Docker MCP server built on the official MCP SDK, with structured tools designed for AI agents rather than terminal automation.

Its behaviour is covered by unit tests and end-to-end tests that run against real Docker containers, not only mocks.


Docker MCP vs Docker CLI: practical examples

The Docker CLI is the right interface when a person is driving the terminal. These examples show what changes when an AI agent needs to inspect a Docker Compose application, understand the result, and choose the next safe step.

Each comparison uses the same real-world task: first the commands and output an agent would have to work through, then the structured Docker MCP result it can use directly.

Diagnose the health of a Docker Compose stack

Situation: A deploy just went out. The site is slow, and you do not know whether a container is down, a healthcheck is failing, or something is eating the memory.

Question: "Is this stack healthy?"

Raw docker CLI

$ docker compose ps
NAME              IMAGE               STATUS                     PORTS
shop-api-1        shop-api:latest     Up 4 minutes (healthy)     0.0.0.0:8080->8080/tcp
shop-postgres-1   postgres:16         Up 4 minutes (unhealthy)   5432/tcp
shop-worker-1     shop-worker:latest  Exited (1) 2 minutes ago
$ docker stats --no-stream
CONTAINER ID   NAME              CPU %   MEM USAGE / LIMIT     MEM %   NET I/O          BLOCK I/O
1f2c4d5e6a7b   shop-api-1        3.10%   214.8MiB / 7.66GiB    2.74%   12.4MB / 8.9MB   0B / 4.1MB
9a8b7c6d5e4f   shop-postgres-1   0.42%   1.204GiB / 7.66GiB    15.7%   3.2MB / 44MB     112MB / 890MB
$ docker inspect --format '{{.State.Health.Status}}' shop-postgres-1
unhealthy
$ docker inspect --format '{{.State.Health.Status}}' shop-worker-1
Template parsing error: executing "" at <.State.Health.Status>: nil pointer evaluating *types.Health.Status

Three commands, three formats, and the last one fails because the worker image declares no healthcheck at all. Nothing is broken — there is simply nothing to read — but the agent now has to tell "no healthcheck" apart from "failing", and a table gives it no help.

Structured MCP result

docker_container({ "action": "list", "project": "shop" })
{
  "action": "list",
  "project": "shop",
  "containers": [
    { "name": "shop-api-1", "service": "api", "project": "shop", "state": "running",
      "health": "healthy", "image": "shop-api:latest",
      "ports": ["0.0.0.0:8080->8080/tcp"], "created": "2026-08-24T09:14:02Z" },
    { "name": "shop-postgres-1", "service": "postgres", "project": "shop", "state": "running",
      "health": "unhealthy", "image": "postgres:16",
      "ports": ["5432/tcp"], "created": "2026-08-24T09:14:01Z" },
    { "name": "shop-worker-1", "service": "worker", "project": "shop", "state": "exited",
      "health": null, "image": "shop-worker:latest",
      "ports": [], "created": "2026-08-24T09:14:01Z" }
  ],
  "reason": null,
  "legend": {
    "containers[].state=running": "Running now.",
    "containers[].state=exited": "Stopped. Its logs survive, its processes do not.",
    "containers[].health=healthy": "Its own healthcheck passed last run.",
    "containers[].health=unhealthy": "Its own healthcheck failed enough times in a row to give up on it.",
    "containers[].health=null": "The image declares no healthcheck, so nothing is measured. Not the same as failing."
  }
}

What changes for the agent

Raw docker CLI

Structured MCP

Your gain

Three commands and three output formats

One call with state, health and ports per service

Fewer round trips

A missing healthcheck errors out or reads as failure

health: null is explained in the answer itself

No container blamed for a check nobody wrote

Container names must be mapped back to services

Every row carries service, project and name

The agent speaks compose, not container ids

The legend explains only the words this answer used, right next to the fields that used them — so the meaning of unhealthy is not sitting hundreds of messages back in a tool description.

Need numbers rather than states? docker_container({ action: "stats", service: "postgres" }) returns CPU, memory against its limit, network and disk as numbers, with the answer saying plainly that it is one instant reading and not an average.

Need the healthchecks themselves? docker_health({ action: "services" }) reports each service's own verdict, counts its checks and failures, and calls a service with no healthcheck none rather than counting it as sick.

Investigate container logs without losing context

Situation: The API started returning 502 about ten minutes ago. The container has been running for a week and has written hundreds of thousands of lines.

Question: "What did it print when it broke?"

Raw docker CLI

$ docker compose logs api --tail 200
api-1  | 2026-08-24T10:31:07.104Z INFO  request GET /health 200 3ms
api-1  | 2026-08-24T10:31:07.882Z INFO  request GET /health 200 2ms
... 196 more lines of the same ...
api-1  | 2026-08-24T10:39:14.522Z ERROR database connection timed out after 30000ms
$ docker compose logs api --since 10m | grep -i error
api-1  | 2026-08-24T10:39:14.522Z ERROR database connection timed out after 30000ms

The first call spent two hundred lines of context on health-check noise. The second found the error but threw away the lines around it, and nothing in either output says whether the stream was stdout or stderr, or whether anything was dropped on the way.

Structured MCP result

docker_logs({ "service": "api", "project": "shop", "since": "10m", "lines": 50 })
{
  "service": "api",
  "project": "shop",
  "date": "2026-08-24",
  "lines": [
    { "stream": "stdout", "time": "10:39:14.518",
      "text": "INFO  pool exhausted, waiting for a free connection" },
    { "stream": "stderr", "time": "10:39:14.522",
      "text": "ERROR database connection timed out after 30000ms" },
    { "stream": "stderr", "time": "10:39:14.530",
      "text": "ERROR upstream 502 while proxying GET /api/orders" }
  ],
  "returned_lines": 3,
  "since": "10m",
  "until": null,
  "truncated": false,
  "truncated_reason": null,
  "clipped_lines": 0,
  "follow": false,
  "reason": null,
  "legend": {
    "lines[].stream=stderr": "the stream the container wrote to; many programs write their ordinary progress there, so a line is not an error for being here"
  }
}

What changes for the agent

Raw docker CLI

Structured MCP

Your gain

A tail is a guess: too few lines miss it, too many bury it

since and until cut the window by time, then lines counts

Fewer tokens on noise

One text blob; stream and timestamp are glued into it

Every line carries its own stream, time and text

Errors are found by field, not by grep

A dropped middle looks exactly like a quiet log

truncated, truncated_reason and clipped_lines name every cut

No "the logs are clean" from a partial read

The day the lines fall on is said once, at the top, and each line then carries only its time — the same stamp repeated on every line is text you would be paying for. Lines spread across more than one day keep their full stamps, and the answer says why.

since also takes a date or a UNIX timestamp, and follow: true waits for lines still to come, returning what arrived within its own time and size ceiling instead of hanging on an open stream.

Query a database running in Docker

Situation: Orders stopped appearing on the dashboard. You want to look in the database — which is a container, with a client inside it and no port published to your machine.

Question: "What does the table actually hold?"

Raw docker CLI

$ docker compose exec -T postgres psql -U app -d shop -c "select status, count(*) from orders group by status"
  status   | count
-----------+-------
 paid      |  1284
 pending   |    17
(2 rows)
$ docker compose exec -T redis redis-cli info keyspace
db0:keys=41822,expires=41822,avg_ttl=3600000

Every engine needs its own client, its own flags and its own way of naming the user and the database. Get one flag wrong and the shell prints an error the agent has to read as text — the call itself "succeeded".

Structured MCP result

docker_db({ "action": "query", "service": "postgres", "project": "shop",
            "query": "select status, count(*) from orders group by status" })
{
  "action": "query",
  "service": "postgres",
  "project": "shop",
  "engine": "postgresql",
  "query": "select status, count(*) from orders group by status",
  "output": "  status   | count\n-----------+-------\n paid      |  1284\n pending   |    17\n(2 rows)",
  "stderr": "",
  "exit_code": 0,
  "clipped_bytes": 0,
  "warnings": [],
  "reason": null,
  "legend": {}
}

What changes for the agent

Raw docker CLI

Structured MCP

Your gain

A different client, flag set and user for every engine

One tool for PostgreSQL, MySQL/MariaDB, Redis, MongoDB and SQLite

One thing to learn, five databases

A refused statement still looks like a successful command

exit_code and stderr stay apart from output

A failure reads as a failure

Credentials get retyped into the command line

The container's own user and database are the default

Fewer secrets in the transcript

status asks the database about itself instead — version, size, uptime, connections — and format: "csv" lays PostgreSQL rows out for parsing. A statement that would destroy a database or a whole keyspace does not run until it carries the confirmation marker; see Destructive command protection for AI agents.

Back up a database before a risky migration

Situation: A migration is about to rewrite a table. You want a dump first — and you want to know the dump is real before the migration touches anything.

Question: "Do I actually have a backup?"

Raw docker CLI

$ docker compose exec -T postgres pg_dump -U app shop | gzip > backup.sql.gz
$ echo $?
0
$ ls -lh backup.sql.gz
-rw-r--r--  1 you  staff    20B 24 Aug 11:02 backup.sql.gz

Exit code zero came from gzip, the last command in the pipe — not from pg_dump. Twenty bytes is an empty archive: the dump failed on a wrong user, the error went to the terminal, and the shell reported success. A migration now runs on the strength of a backup that does not exist.

Structured MCP result

docker_db_admin({ "action": "backup", "service": "postgres", "project": "shop" })
{
  "action": "backup",
  "service": "postgres",
  "project": "shop",
  "engine": "postgresql",
  "file": "/backups/shop-2026-08-24T11-02-17.dump.gz",
  "bytes": 48219553,
  "verified": true,
  "message": null,
  "confirmed": false,
  "restarted": false,
  "reason": null,
  "legend": {
    "verified": "The file was read back around the call, and bytes is its size on disk. This is the difference between a dump that exists and a command that did not fail."
  }
}

What changes for the agent

Raw docker CLI

Structured MCP

Your gain

A pipe reports the exit code of its last command

The dump is read back and its size named

An empty backup cannot pass for a good one

Each engine needs its own dump command and flags

One call for PostgreSQL, MySQL, MongoDB, SQLite and Redis

Same workflow whatever the stack runs

"Done" is a word

verified is a field, and bytes: null says why it is unknown

The agent knows what it does not know

When the file cannot be read back, the answer says so instead of claiming a size: verified stays false, bytes is null, and message names what stopped it — an unknown size is never reported as an empty file.

Filling the database back is the same tool: action: "restore". It overwrites what is there, so it is refused until the call carries the confirmation marker in its confirm field — and the refusal tells you to take a backup here first.

Understand Docker disk usage before cleaning up

Situation: The server is at 90% disk. Docker is the obvious suspect, but you do not know whether it is images, volumes, stopped containers or build cache — and pruning the wrong one destroys a database.

Question: "What is safe to reclaim?"

Raw docker CLI

$ docker system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          48        9         31.2GB    22.4GB (71%)
Containers      21        7         1.8GB     412MB (22%)
Local Volumes   19        6         64.9GB    38.1GB (58%)
Build Cache     264       0         9.7GB     9.7GB
$ docker system df -v | head -40
... several screens of per-image, per-container and per-volume tables ...

The summary is readable by a person and expensive for an agent: percentages in parentheses, sizes as text with mixed units, and the detail view is several screens long. To compare "reclaimable volumes" against "reclaimable images" the agent has to parse 64.9GB and 38.1GB (58%) back into numbers.

Structured MCP result

docker_resource({ "action": "disk" })
{
  "action": "disk",
  "count": 4,
  "total_bytes": 115534368358,
  "disk": [
    { "group": "images", "count": 48, "active": 9,
      "size_bytes": 33500985344, "unused_bytes": 24051816448 },
    { "group": "containers", "count": 21, "active": 7,
      "size_bytes": 1932735283, "unused_bytes": 431994470 },
    { "group": "volumes", "count": 19, "active": 6,
      "size_bytes": 69686362112, "unused_bytes": 40908324864 },
    { "group": "build_cache", "count": 264, "active": 0,
      "size_bytes": 10414285619, "unused_bytes": 10414285619 }
  ],
  "reason": null,
  "legend": {
    "disk[].active": "How many of them something uses right now: a running container, or an image behind one.",
    "disk[].unused_bytes": "Size of what nothing uses at the moment. Removing it frees less than this: what is shared stays until its last holder is gone.",
    "disk[].group=images": "Images are counted by their layers on disk, so this is what they really take; the sizes of separate images add up to more, because a shared layer belongs to each of them.",
    "disk[].group=containers": "Only what a container wrote on top of its image. What the image itself takes is counted with the images."
  }
}

What changes for the agent

Raw docker CLI

Structured MCP

Your gain

Sizes as text with mixed units and percentages

Bytes as numbers, in four named groups

The agent compares instead of parsing

The detail view costs several screens

One call, one number per group

Fewer tokens for the same answer

"Reclaimable" hides what is holding a volume

active counts what is in use, unused_bytes what is not

Build cache goes, the database volume stays

images, volumes and networks list what the host holds — volumes with their size and how many containers hold them. An empty list means asked and found nothing, which is not the same as never asked.

Built-in destructive command protection for AI agents

An AI agent can generate a valid command that is still the wrong command to run. This server adds a local guard before anything reaches Docker or a database client, so irreversible operations need an explicit confirmation in the call that requests them.

The guard distinguishes between deleting the thing that holds data — a database, volume, or top-level directory — and changing or deleting data inside it. The first category is refused until confirmed. The second can run, but returns a warning that states what changed.

Block irreversible loss, warn about destructive changes

Refused — the vessel itself

Only warned about — its contents

DROP DATABASE, DROP SCHEMA, dropdb

DROP TABLE, TRUNCATE

FLUSHALL, FLUSHDB (Redis)

DELETE FROM with no WHERE

db.dropDatabase() (MongoDB)

deleteMany({}), updateMany({}) with an empty filter

docker volume rm, docker volume prune

docker image prune, docker container prune

docker system prune

docker network prune

rm -rf /, rm -rf ~, rm -rf /srv

rm -rf /srv/app/cache

mkfs, dd of=/dev/...

A warning is not a refusal: the operation runs, and what it destroyed is stated as a fact in the answer's warnings. DELETE FROM orders with a WHERE is not remarked on at all — that is a normal statement.

Removal reads by where it points. A directory named directly under the root holds all of something — the application, the data, the system — so rm -rf /srv is refused, while rm -rf /srv/app/cache is what deleting files is for. A command wrapped in sh -c is unwrapped and read however many shells it hides behind. The command runs as one argv, so a separator at the top level is an argument: echo "hi"; rm -rf /data prints a line and removes nothing.

Confirm a deliberate destructive operation

Nothing is forbidden permanently. Three tools take the confirmation, each where the danger is:

docker_exec({ "service": "api",
              "command": "rm -rf /srv/legacy # CONFIRMED-DESTRUCTIVE" })

docker_db({ "action": "query", "service": "redis",
            "query": "FLUSHDB # CONFIRMED-DESTRUCTIVE" })

docker_compose_control({ "action": "down", "volumes": true,
                         "confirm": "# CONFIRMED-DESTRUCTIVE" })

The marker is carried inside the call, so it lifts the refusal for that one call and no other. The marker itself never reaches the database or the shell — it is taken out of the statement before it runs.

Every refusal names what would have gone, so it can be read before the call is repeated rather than stepped over: DROP DATABASE shop destroys the database itself. Add # CONFIRMED-DESTRUCTIVE to the command to run it.

The guard reads one call at a time. It cannot connect a delete in one call with a read in the next, and it knows the tools it knows — a custom binary that wipes a directory is not something it recognizes. It is a seatbelt, not a policy engine: recoverable operations remain your call.

Docker MCP tools for containers, compose and databases

A focused Docker MCP toolkit for the work AI agents do most often: inspect a Compose stack, diagnose a service, control its lifecycle, work with its database, and understand host resources. Full parameters and examples live in docs/tools.md.

Every tool speaks the same Docker Compose vocabulary. profile selects the Docker host; project selects the Compose project; service selects the service from its Compose file, not the generated container name. Leave profile out and the default host answers. Leave project out and the project in the working directory answers. Use compose_path only when Docker cannot resolve a project because it has not run on that host yet.

Inspect a Docker Compose stack

Tool

What it does

docker_container

List a project with state, health and ports, or measure what one service consumes

docker_logs

Read what a container printed, line by line, in a window bounded by time

docker_compose

Read what the project declares: the resolved compose file, or its variables

docker_health

Healthchecks of the services, health of this server, the hosts it was given

docker_resource

Images, volumes and networks of a host, and where the disk went

Control services and stack lifecycle

Tool

What it does

docker_container_control

Start, stop or restart one service, reporting where it stood before

docker_compose_control

Bring the whole stack up or take it down

Work with databases running in Docker

Tool

What it does

docker_db

Run a statement, or ask the database about itself

docker_db_admin

Take a dump, or fill a database from one

PostgreSQL, MySQL/MariaDB, Redis, MongoDB and SQLite are spoken to through the client the image already carries — nothing is installed into your containers.

Secrets stay hidden by default

Reading a project's variables with docker_compose hides the values whose key names a secret — anything containing PASSWORD, TOKEN, KEY, SECRET, PRIVATE or CREDENTIALS. Each variable says whether it was hidden, so a masked value is never mistaken for the real one, and mask: false returns it when you actually need it.

Run a command when no dedicated tool fits

Tool

What it does

docker_exec

Run a command inside a service container, stdout and stderr apart

Safe defaults in MCP clients

Standard MCP annotations tell your client which tools are safe to run without asking. The five reading tools declare readOnlyHint. The two control tools declare destructiveHint with idempotentHint — the same call twice leaves the same state. docker_db, docker_db_admin and docker_exec declare destructiveHint alone: they carry something the server did not write, so what happens is decided by what you handed in.

Set up the Docker MCP server

For local Docker, add the server to your MCP client and start working. No profile file or Docker endpoint configuration is required: the server uses the Docker socket on this machine.

Configure profiles only when an AI agent needs to reach remote Docker hosts over SSH.

Configure remote Docker hosts

Store the profiles file wherever your MCP client keeps its configuration. Each profile gives the agent a name for a Docker host and the SSH connection details it needs:

{
  "default": "local",
  "profiles": {
    "local": { "mode": "local" },
    "production": {
      "host": "prod.example.com",
      "username": "deployer",
      "port": 22,
      "privateKeyPath": "~/.ssh/id_ed25519"
    },
    "staging": {
      "host": "staging.example.com",
      "username": "deployer",
      "port": 2222,
      "privateKeyPath": "~/.ssh/id_ed25519_staging"
    }
  }
}

A profile with mode: "local" is the Docker on this machine. A remote profile needs host and username; port defaults to 22. Where the compose projects live on that server is not asked for: a project is found by the labels its containers carry. default names the profile used when a call leaves profile out.

Prefer keys. A profile without privateKeyPath uses your SSH agent, which is the better answer when the key is encrypted.

The host key of the machine is checked: the first connection remembers it in ~/.ssh/known_hosts, and a machine that later answers with a different key is refused instead of being talked to. knownHostsPath in a profile points at another file when you keep those keys apart.

Configure Claude Code, Codex, Cursor and other MCP clients

Claude Code — one command; -s user makes the server available in every project:

claude mcp add docker -s user \
  -e DOCKER_MCP_PROFILES_FILE="$HOME/.claude/docker-profiles.json" \
  -- npx -y @hypnosis/docker-mcp-server

Codex CLI

codex mcp add docker \
  --env DOCKER_MCP_PROFILES_FILE="$HOME/.codex/docker-profiles.json" \
  -- npx -y @hypnosis/docker-mcp-server

Cursor — in ~/.cursor/mcp.json:

{
  "mcpServers": {
    "docker": {
      "command": "npx",
      "args": ["-y", "@hypnosis/docker-mcp-server"],
      "env": {
        "DOCKER_MCP_PROFILES_FILE": "~/.cursor/docker-profiles.json"
      }
    }
  }
}

opencode — in ~/.config/opencode/opencode.json:

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "docker": {
      "type": "local",
      "command": ["npx", "-y", "@hypnosis/docker-mcp-server"],
      "enabled": true,
      "environment": {
        "DOCKER_MCP_PROFILES_FILE": "~/.config/opencode/docker-profiles.json"
      }
    }
  }
}

Other MCP clients — Gemini CLI, Qwen Code, Cline, an editor plugin or your own agent work the same way. All they need is a command to run and, for remote hosts, one environment variable.

Verify the connection

Restart your MCP client, then run docker_health({ action: "profiles" }). It lists the Docker hosts the server received from configuration and identifies any profile it could not read, along with the field that needs attention.

Docker MCP server configuration

Variable

What it does

Default

DOCKER_MCP_PROFILES_FILE

Path to the profiles JSON. Without it, only local Docker

DOCKER_PROFILES

The same JSON inline, used when no profiles file is set

DOCKER_MCP_ALLOW_SSH_FALLBACK

Keep going when a profile's key is missing, instead of refusing

false

LOG_LEVEL

debug, info, warn, error

info

DOCKER_PROFILES_FILE is the deprecated spelling of the first variable. It still works and logs a line asking you to rename it.

A profile pointing at a key that does not exist is refused by default, with the path and the ways out named — a missing key otherwise turns into a connection attempt that fails much later, somewhere less obvious.

Docker MCP server limitations

The server favours bounded, explicit tool calls over open-ended or implicit behaviour. These are the current limits to keep in mind:

  • Log following is bounded. follow: true returns what arrived within 10 seconds or 1 MB, whichever comes first. It is a look at a live stream, not a subscription.

  • The guard reads one call at a time. It cannot connect a delete in one call with a read in the next, and it recognizes the tools it knows — a custom binary that wipes a directory is not one of them.

  • Host resources are per host, not per project. docker_resource answers about everything the daemon holds; narrowing to one project is docker_container.

  • Windows is not verified. Nothing in the server is written against one platform, but it has not been run end to end on Windows.

Develop and test the Docker MCP server

npm install
npm run build            # tsc
npx tsc --noEmit         # types
npm run test:unit        # unit tests
npm run docker:test:up   # start the test containers
npm run test:e2e         # end-to-end suite against those containers

The end-to-end suite runs against real PostgreSQL, MySQL, MariaDB, MongoDB, Redis, and web service containers. It verifies Docker integration behaviour that unit tests and mocks cannot prove alone. See docs/architecture.md for the project layout.

Support Docker MCP Server

If the tool helps your team, star the project on GitHub. It helps other developers find it too.

Contribute to the Docker MCP server

Issues and pull requests are welcome at github.com/hypnosis/docker-mcp-server.

License

MIT — see LICENSE.

Available Tools

10 tools
docker_composeA
Read-only

Reads what the project declares: config for the resolved compose file, env for the variables with the file each came from. Both read files rather than a running process — a variable here is what was written down, not what the container now holds. What is actually running is docker_container.

ParametersJSON Schema
NameRequiredDescriptionDefault
maskNoHide values whose key names a secret. Default: true.
actionYesconfig: the compose configuration, as fields and as the file itself. env: the variables the project declares, each naming the file it came from.
profileNoWhich Docker host, by profile name. Default: the profile marked as default.
projectNoWhich compose project. Default: the project of the working directory. A name matching nothing is an error, not a fall back to every container on the host.
resolveNoSubstitute variables the way Docker does before starting anything, which is what turns declared ports into published ones. Costs a call to docker compose. Default: false.
serviceNoService name as written in the compose file. Narrows the answer to this one service.
compose_pathNoWhere the compose file is: the file itself, or the directory holding it. Needed only when no container of the host carries the project label — a project that was never brought up there. A directory holding several compose files is refused with their names rather than guessed through.

Output Schema

ParametersJSON Schema
NameRequiredDescription
textNo
actionNo
legendNoWhat the words in this answer mean. A key names the field before the value — "state=running", "containers[].health=unhealthy" — and only the values this answer used are listed.
reasonNo
projectNo
serviceNo
volumesNo
networksNo
resolvedNo
servicesNo
variablesNo
compose_fileNo

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already mark the tool readOnlyHint=true, and the description adds meaningful behavioral context: it reads files, not a running process, and values reflect declared state rather than current container state. This clarifies the tool's semantics beyond the structured hint without contradicting it.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tightly written sentences, front-loaded with the core purpose and then the key distinction from the runtime tool. Every sentence earns its place with no filler or restatement of schema details.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given a rich 7-parameter schema, complete schema descriptions, an output schema, and read-only annotations, the description covers the essential conceptual gap: declared config vs actual running state. It could have explicitly referenced the resolve-cost or project-label edge cases, but those are already handled in the schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so every parameter is already documented. The description mostly mirrors the schema's action enum wording ('config', 'env') rather than adding new parameter-level meaning. It earns the baseline score for not undermining the schema, but adds little beyond it.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description opens with a specific verb and resource: 'Reads what the project declares', then names the two actions (config, env) and explicitly distinguishes itself from docker_container. An agent can tell exactly what this tool accesses and how it differs from related tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It clearly frames the tool as the read-only declarative view of a compose project and points to docker_container for what is actually running. It gives useful context for when to pick this over the runtime-oriented sibling, though it does not enumerate exclusions for every sibling like docker_compose_control.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

docker_compose_controlA
DestructiveIdempotent

Brings the stack up or takes it down, and reports the containers standing afterwards as read from the daemon, not as compose described them. A stack that half came up says so instead of passing for a success. Taking volumes down with it destroys their data and is refused without confirm.

ParametersJSON Schema
NameRequiredDescriptionDefault
buildNoRebuild the images before starting, for action up. Default: false.
scaleNoHow many copies of a service to run, for action up: {"web": 3}.
actionYesup: create and start the services, always in the background. down: stop and remove the containers of the project.
confirmNoWritten out as "# CONFIRMED-DESTRUCTIVE", this allows deleting volumes for one call. Anything else leaves the refusal standing.
profileNoWhich Docker host, by profile name. Default: the profile marked as default.
projectNoWhich compose project. Default: the project of the working directory. A name matching nothing is an error, not a fall back to every container on the host.
timeoutNoSeconds each container gets to shut down before Docker kills it, for action down. Default: 10.
volumesNoDelete the volumes of the project along with the containers, for action down. The data inside them is destroyed and does not come back, so the call is refused without confirm. Default: false.
servicesNoOnly these services, for action up. Default: every service of the file.
compose_pathNoWhere the compose file is: the file itself, or the directory holding it. Needed only when no container of the host carries the project label — a project that was never brought up there. A directory holding several compose files is refused with their names rather than guessed through.
removeOrphansNoRemove containers left behind by services no longer in the file, for action down. Default: false.

Output Schema

ParametersJSON Schema
NameRequiredDescription
actionNo
legendNoWhat the words in this answer mean. A key names the field before the value — "state=running", "containers[].health=unhealthy" — and only the values this answer used are listed.
outputNo
reasonNo
projectNo
removedNo
servicesNo
confirmedNo
destroyedNo
compose_fileNo

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the destructive and idempotent hints, the description reveals critical behaviors: it reports daemon truth rather than compose intent, it fails loudly on partial stack bring-up, and volume deletion requires confirmation. This gives the agent important operational context that annotations alone do not provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is only three sentences but packs in the core action, the critical reporting behavior, and the destructive edge case. Every sentence earns its place, and the most important information is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex tool with 11 parameters and nested objects, the description stays focused on the operational essentials and leaves parameter-level details to the well-covered schema. It is complete enough for an agent to invoke correctly, though it does not explicitly guide selection versus sibling tools.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds meaningful behavioral semantics around parameters like volumes and confirm by explaining the destructive consequence and the refusal without confirmation. It also clarifies that results reflect daemon state, which helps interpret the action parameter's effect.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names the exact operation on a specific resource: bringing a compose stack up or down. It also distinguishes its reporting behavior from compose's own description by saying it reads actual daemon state, which separates it from sibling tools like docker_compose.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description makes clear it acts on the whole stack and reports real container state, so an agent can infer this is the stack-level control tool rather than a single-container tool. It does not explicitly name alternatives or state when not to use it, but the context is clear enough for correct selection.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

docker_containerA
Read-only

Lists the containers of a project with their state, health and published ports, or measures with stats what one service consumes right now. A project name no container carries is refused with the names that exist, so an empty list means the host itself runs nothing under compose. Moving a container between states is docker_container_control.

ParametersJSON Schema
NameRequiredDescriptionDefault
actionYeslist: every container of the project, with state and health. stats: CPU, memory, network and disk of one service, read at this moment.
profileNoWhich Docker host, by profile name. Default: the profile marked as default.
projectNoWhich compose project. Default: the project of the working directory. A name matching nothing is an error, not a fall back to every container on the host. Omitted with action list: every container the host runs under compose.
serviceNoService name as written in the compose file. Required by action stats.
compose_pathNoWhere the compose file is: the file itself, or the directory holding it. Needed only when no container of the host carries the project label — a project that was never brought up there. A directory holding several compose files is refused with their names rather than guessed through.

Output Schema

ParametersJSON Schema
NameRequiredDescription
statsNo
actionNo
legendNoWhat the words in this answer mean. A key names the field before the value — "state=running", "containers[].health=unhealthy" — and only the values this answer used are listed.
reasonNo
projectNo
containersNo

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already mark the tool as readOnlyHint=true, and the description adds meaningful behavioral detail beyond that: unknown project names are refused with existing names, an empty list means the host runs nothing under compose, and compose_path is used when no container carries the project label. This gives the agent context about edge cases without contradicting annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences with no filler. The primary functions are stated first, then a useful edge-case clarification, then the sibling routing. Every sentence earns its place and the structure is easy to scan.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present and a readOnlyHint annotation, the description covers all essential guidance for both actions: what list returns, what stats measures, project name refusal behavior, empty-list meaning, and compose_path usage. Nothing critical is missing for an agent to select and invoke this tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the input schema already documents all five parameters thoroughly. The description adds contextual color around project name refusal and compose_path, but it does not significantly extend parameter-level meaning beyond what the schema provides. Baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly names the resource (docker containers of a project) and the specific actions: listing state/health/published ports and measuring current resource consumption via stats. It also distinguishes itself from docker_container_control by noting that moving containers between states belongs to that sibling tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives solid guidance on when to use this tool: for read-only listing and stats measurement. It explicitly refers moving containers between states to docker_container_control, which prevents misuse. It does not discuss other sibling tools like docker_logs or docker_health, but the main read-oriented selection is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

docker_container_controlA
DestructiveIdempotent

Moves one service between states — start, stop, restart — and reports where it stood before and where it stands now, with changed saying whether the call did anything at all. A stop that ran out of time comes back killed with its exit code, never as a clean stop. The whole stack at once is docker_compose_control.

ParametersJSON Schema
NameRequiredDescriptionDefault
actionYesstart: bring the container up. stop: shut it down, giving it timeout seconds. restart: stop then start, keeping the same container.
profileNoWhich Docker host, by profile name. Default: the profile marked as default.
projectNoWhich compose project. Default: the project of the working directory. A name matching nothing is an error, not a fall back to every container on the host.
serviceYesService name as written in the compose file.
timeoutNoSeconds the container gets to shut down before Docker kills it, for stop and restart. Default: 10.
compose_pathNoWhere the compose file is: the file itself, or the directory holding it. Needed only when no container of the host carries the project label — a project that was never brought up there. A directory holding several compose files is refused with their names rather than guessed through.

Output Schema

ParametersJSON Schema
NameRequiredDescription
actionNo
healthNo
killedNo
legendNoWhat the words in this answer mean. A key names the field before the value — "state=running", "containers[].health=unhealthy" — and only the values this answer used are listed.
reasonNo
changedNo
projectNo
serviceNo
timeoutNo
exit_codeNo
state_afterNo
state_beforeNo
restart_countNo

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already flag idempotent and destructive. The description adds meaningful behavior beyond that: it returns the previous and current states, uses 'changed' to signal whether the call did anything, and explains that a timed-out stop surfaces as killed with its exit code, not a clean stop.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three dense sentences with no filler; the core operation and scope appear first, and the key edge-case behavior follows. Every sentence adds information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given a 100%-covered schema, an output schema, and annotations for idempotency/destructiveness, the description covers the important runtime semantics (state reporting, changed flag, timeout kill behavior) and the sibling alternative. Nothing essential is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline applies. The description does not expand on individual parameters; the schema already documents action values, timeout default, profile/project defaults, and compose_path behavior.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names the exact operation ('Moves one service between states'), lists the three actions, and clarifies scope ('one service') while pointing to docker_compose_control for the whole stack. This distinguishes it from siblings immediately.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly states the single-service scope and names the alternative for whole-stack operations: 'The whole stack at once is docker_compose_control.' This is direct routing guidance with the closest sibling, so an agent can decide correctly.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

docker_dbA
Destructive

query runs a statement against a database container, status asks the database about itself — version, size, uptime. PostgreSQL, MySQL/MariaDB, Redis, MongoDB and SQLite are spoken to through the client the image carries. A database that refuses answers with its exit code, not with a failed call. A statement that destroys data needs # CONFIRMED-DESTRUCTIVE.

ParametersJSON Schema
NameRequiredDescriptionDefault
userNoDatabase user. Default: the one the container was started with.
queryNoStatement in the language of this database — SQL, a Redis command, a MongoDB expression. Required by action query, unused by status. A statement that destroys a database or a whole keyspace is refused until it carries # CONFIRMED-DESTRUCTIVE.
actionYesquery runs a statement and brings back what the client printed. status asks the database about itself: version, size, uptime, and whether it answers at all.
formatNoHow the client lays out rows. Default: table. Only PostgreSQL has a CSV flag.table
profileNoWhich Docker host, by profile name. Default: the profile marked as default.
projectNoWhich compose project. Default: the project of the working directory. A name matching nothing is an error, not a fall back to every container on the host.
serviceYesDatabase service name as written in the compose file.
databaseNoDatabase name. Default: the one the container was started with.
compose_pathNoWhere the compose file is: the file itself, or the directory holding it. Needed only when no container of the host carries the project label — a project that was never brought up there. A directory holding several compose files is refused with their names rather than guessed through.

Output Schema

ParametersJSON Schema
NameRequiredDescription
sizeNo
queryNo
actionNo
engineNo
healthNo
legendNoWhat the words in this answer mean. A key names the field before the value — "state=running", "containers[].health=unhealthy" — and only the values this answer used are listed.
memoryNo
outputNo
reasonNo
stderrNo
uptimeNo
detailsNo
messageNo
projectNo
serviceNo
versionNo
warningsNo
exit_codeNo
connectionsNo
clipped_bytesNo

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The annotations already signal destructiveHint=true, and the description adds meaningful behavioral context: destructive statements require # CONFIRMED-DESTRUCTIVE, databases that refuse return their exit code instead of a failed call, and the client comes from the image. This does not contradict the annotations and helps the agent anticipate failure modes and safety gates.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is short, front-loaded with the core query/status distinction, and every sentence earns its place. It avoids restating schema details and packs essential behavioral facts into four efficient sentences.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complex 9-parameter schema and the presence of an output schema, the description covers the most decision-relevant aspects: what the tool does, supported engines, destructive-statement handling, and error semantics. It leaves profile/project/compose_path selection behavior to the already-rich schema, which is acceptable.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the parameters are already well documented in the schema. The description adds value by clarifying the query/status behavior and the destructive-confirmation convention, but it does not provide additional parameter-level meaning beyond what the schema already states.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states specific verbs and objects: 'query runs a statement against a database container' and 'status asks the database about itself'. It also names the supported database types, giving a clear resource scope. It does not explicitly differentiate this tool from sibling tools such as docker_db_admin, so it stops short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear context for choosing between the query and status actions, and notes the destructive-confirmation requirement. However, it does not say when to prefer this tool over alternatives like docker_exec or docker_db_admin, nor does it state exclusions or prerequisites such as needing a running container. The guidance is implicit rather than explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

docker_db_adminA
Destructive

backup takes a dump of a database container, restore fills the database from one. The answer names the file, its size and whether it was read back: a call that says done without naming a file of known size says nothing. Restore overwrites what is there and is refused without confirm — take a backup here first.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileNoPath of the dump inside the container. Required by action restore. For backup the default is a name with the time of day under /backups, which must be a directory the container already has.
cleanNoDrop what is already there before writing, for action restore. Archive backups only (.dump, .backup) for PostgreSQL. Default: false.
actionYesbackup writes a dump inside the container and reads it back to say how large it is. restore fills the database from a dump that is already there, overwriting what it holds.
formatNoShape of the dump, for action backup. Default: the one this engine dumps in — an archive for PostgreSQL, plain SQL for MySQL, its own archive for MongoDB. A format the client has no flag for is refused.
tablesNoOnly these tables, for action backup. Default: the whole database. MongoDB takes one collection at a time.
confirmNoWritten out as "# CONFIRMED-DESTRUCTIVE", this allows one restore to overwrite the database. Anything else leaves the refusal standing.
profileNoWhich Docker host, by profile name. Default: the profile marked as default.
projectNoWhich compose project. Default: the project of the working directory. A name matching nothing is an error, not a fall back to every container on the host.
serviceYesService name as written in the compose file.
compressNoCompress the dump, for action backup. Only where the client compresses by itself. Default: true.
dataOnlyNoWrite the rows and leave the schema alone, for action restore. Default: false.
databaseNoWhich database to fill, for action restore. Default: the one the container was started with.
schemaOnlyNoWrite the schema and none of the rows, for action restore. Default: false.
compose_pathNoWhere the compose file is: the file itself, or the directory holding it. Needed only when no container of the host carries the project label — a project that was never brought up there. A directory holding several compose files is refused with their names rather than guessed through.

Output Schema

ParametersJSON Schema
NameRequiredDescription
fileNo
bytesNo
actionNo
engineNo
legendNoWhat the words in this answer mean. A key names the field before the value — "state=running", "containers[].health=unhealthy" — and only the values this answer used are listed.
reasonNo
messageNo
projectNo
serviceNo
verifiedNo
confirmedNo
restartedNo

TDQS

A4.1/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the destructiveHint annotation, the description reveals important behavioral traits: restore overwrites data, is refused without confirm, backup verifies by reading back, and the result must name a file of known size. This materially helps an agent understand what to expect and demand from the tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two dense sentences with no filler. The actions are front-loaded, and every clause adds either behavioral guidance or a verification requirement that an agent would otherwise miss.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex 14-parameter tool, the description covers the critical safety and verification behaviors while relying on the richly documented schema for parameter details. It is missing only an explicit distinction from docker_db, but otherwise gives sufficient context.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the structured schema already documents all parameters. The description adds useful context around confirmation and output verification, but does not add parameter-specific semantics beyond what the schema provides.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names specific actions ('backup takes a dump of a database container, restore fills the database from one') and the resource being operated on. It clearly distinguishes the two modes, though it does not explicitly differentiate this tool from the sibling docker_db.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear operational context: restore is destructive, requires confirmation, and should be preceded by a backup ('take a backup here first'). It does not explicitly state when to choose this tool over docker_db, so it stops short of full alternative routing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

docker_execA
Destructive

Runs a command inside a service container and brings back its exit code, with stdout and stderr apart. exit_code null means the command never ran and reason says why; an empty stdout means it ran and said nothing. What destroys data is refused until the command carries # CONFIRMED-DESTRUCTIVE. Reading logs, asking a database and taking dumps have tools of their own.

ParametersJSON Schema
NameRequiredDescriptionDefault
userNoRun as this user instead of the one the image declares. Default: the image user.
commandYesThe command line as it would be typed inside the container: "npm test", "alembic upgrade head". What destroys data — wiping a directory, dropping files — is refused until the line carries # CONFIRMED-DESTRUCTIVE.
profileNoWhich Docker host, by profile name. Default: the profile marked as default.
projectNoWhich compose project. Default: the project of the working directory. A name matching nothing is an error, not a fall back to every container on the host.
serviceYesService name as written in the compose file.
workdirNoWhere inside the container the command runs. Default: working_dir of this service in the compose file, and the directory of the image where the file names none.
interactiveNoGive the command a terminal. Both streams then arrive merged in stdout with stderr empty, and nothing is sent to its input: one call is not a dialogue. Default: false.
compose_pathNoWhere the compose file is: the file itself, or the directory holding it. Needed only when no container of the host carries the project label — a project that was never brought up there. A directory holding several compose files is refused with their names rather than guessed through.

Output Schema

ParametersJSON Schema
NameRequiredDescription
legendNoWhat the words in this answer mean. A key names the field before the value — "state=running", "containers[].health=unhealthy" — and only the values this answer used are listed.
reasonNo
stderrNo
stdoutNo
commandNo
projectNo
serviceNo
warningsNo
exit_codeNo
truncatedNo
clipped_bytesNo

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the destructiveHint annotation, the description explains that destructive commands are refused until they carry '# CONFIRMED-DESTRUCTIVE', and clarifies the exit_code null and empty stdout semantics. These are meaningful behavioral details not present in the annotations or schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and front-loaded with the core action and result, then covers output semantics, safety, and alternatives in a few tight sentences. Every sentence earns its place with no redundant filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the high schema coverage, the output schema, and annotations, the description covers the essential non-obvious context: exit code null meaning, empty stdout meaning, the destructive-command guard, and the boundary with sibling tools. No critical gap remains for a tool this complex.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all parameters. The description adds general behavioral context about commands and output, but it does not materially deepen parameter-level meaning beyond what the schema provides. Baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific verb ('runs a command'), a specific resource ('inside a service container'), and its key outcome ('brings back its exit code, with stdout and stderr apart'). It also differentiates from siblings by pointing out that logs, database queries, and dumps have dedicated tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives useful context for when to use this tool versus alternatives: reading logs, asking a database, and taking dumps are explicitly routed elsewhere. It also explains the destructive-command refusal mechanism. However, it does not name the exact sibling tools or spell out a crisp when-not-to-use condition beyond those categories.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

docker_healthA
Read-only

services says whether the containers answer their healthchecks, server whether this process is well, profiles which Docker hosts it was given. The word healthy means something different in each branch, so every answer explains its own verdict. A service that declares no healthcheck is said to have none, not counted as sick.

ParametersJSON Schema
NameRequiredDescriptionDefault
actionYesservices: what each container says about itself through its own healthcheck. server: whether the daemon answers this server and what it knows about itself. profiles: the hosts it was configured with, read from the configuration and not connected to.
profileNoWhich Docker host, by profile name. Default: the profile marked as default.
projectNoWhich compose project. Default: the project of the working directory. A name matching nothing is an error, not a fall back to every container on the host.
servicesNoOnly these services, for action services. Default: every container of the project.
compose_pathNoWhere the compose file is: the file itself, or the directory holding it. Needed only when no container of the host carries the project label — a project that was never brought up there. A directory holding several compose files is refused with their names rather than guessed through.

Output Schema

ParametersJSON Schema
NameRequiredDescription
actionNo
brokenNo
legendNoWhat the words in this answer mean. A key names the field before the value — "state=running", "containers[].health=unhealthy" — and only the values this answer used are listed.
reasonNo
serverNo
sourceNo
overallNo
projectNo
profilesNo
servicesNo

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the readOnlyHint annotation, the description discloses that 'healthy' is branch-specific, that every answer explains its own verdict, and that services without a healthcheck are reported as 'none' rather than sick. This materially reduces the risk of misinterpreting results.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences cover the branches, the core ambiguity, and the no-healthcheck edge case with no filler. The most decision-relevant nuance—'healthy means something different in each branch'—is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only health tool with an output schema and fully documented parameters, the description supplies the key behavioral caveats. It is complete enough to invoke correctly, though it could be slightly stronger with an explicit statement that it performs no container changes.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema already describes all five parameters with 100% coverage, including the meaning of each action value. The description adds only a small amount of context about the healthcheck interpretation, so it does not need to compensate for missing schema details.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names the three actions and what each reports: container healthchecks, server health, and configured Docker hosts. It is clear, but it never explicitly contrasts itself with sibling tools like docker_container_control or docker_logs, so some differentiation is left to inference.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The action descriptions imply this is the tool for health and status checks, and the description warns about the different meanings of 'healthy.' It does not state when to prefer this over a control or log sibling, nor does it give explicit when-not-to-use guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

docker_logsA
Read-only

Reads what a container printed, line by line: each line carries the stream that wrote it and its own time. An empty list means the window held nothing, not that the read failed, and lines dropped from the middle are counted rather than quietly lost. Narrow the window with since and until instead of raising the count.

ParametersJSON Schema
NameRequiredDescriptionDefault
linesNoHow many of the last lines to read. Default: 100.
sinceNoOnly lines written after this: a duration (30s, 10m, 2h, 1d), a date (2026-08-22T10:00:00Z) or a UNIX timestamp. Applied before lines counts them.
untilNoOnly lines written before this, written the same way as since.
followNoWait for lines still to come; the call returns what arrived within 10s or 1024KB.
profileNoWhich Docker host, by profile name. Default: the profile marked as default.
projectNoWhich compose project. Default: the project of the working directory. A name matching nothing is an error, not a fall back to every container on the host.
serviceYesService name as written in the compose file.
compose_pathNoWhere the compose file is: the file itself, or the directory holding it. Needed only when no container of the host carries the project label — a project that was never brought up there. A directory holding several compose files is refused with their names rather than guessed through.

Output Schema

ParametersJSON Schema
NameRequiredDescription
dateNo
linesNo
sinceNo
untilNo
followNo
legendNoWhat the words in this answer mean. A key names the field before the value — "state=running", "containers[].health=unhealthy" — and only the values this answer used are listed.
reasonNo
projectNo
serviceNo
truncatedNo
clipped_linesNo
returned_linesNo
truncated_reasonNo

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, and the description goes well beyond that: empty list means empty window, not failure; dropped lines are counted, not silently lost; stream and timestamp are returned per line. This is exemplary behavioral disclosure.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, no filler. The main action is front-loaded and every sentence adds either purpose or behavioral nuance. It earns its length.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The output schema covers return values, annotations cover the read-only safety profile, and the schema covers parameter meanings. The description fills the remaining gaps: empty-vs-failure semantics, dropped-line accounting, and window-vs-count guidance. Nothing essential is missing for an agent to call it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema covers all eight parameters with individual descriptions, earning the baseline 3. The description adds a non-obvious parameter relationship — 'Narrow the window with since and until instead of raising the count' — which gives agents useful decision logic beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: 'Reads what a container printed, line by line.' It also adds useful detail about line content (stream and timestamp). However, it doesn't explicitly distinguish itself from sibling tools like docker_container or docker_exec, so it misses the top bar.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description makes the use case clear (reading container output) and even gives strategic advice: narrow with since/until rather than increasing the line count. It doesn't name alternative tools or say when not to use this tool, so it's clear context without exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

docker_resourceA
Read-only

images, volumes and networks list what a host holds; disk counts what they take, with unused bytes apart from the total. An empty list means asked and found nothing, which is not the same as never asked. What a running container consumes right now is docker_container with stats.

ParametersJSON Schema
NameRequiredDescriptionDefault
actionYesimages, volumes and networks each list what the host holds — volumes with their size and how many containers hold them. disk answers where the space went: four groups, each with what it takes and what nothing uses.
profileNoWhich Docker host, by profile name. Default: the profile marked as default.

Output Schema

ParametersJSON Schema
NameRequiredDescription
diskNo
countNo
actionNo
imagesNo
legendNoWhat the words in this answer mean. A key names the field before the value — "state=running", "containers[].health=unhealthy" — and only the values this answer used are listed.
reasonNo
volumesNo
networksNo
total_bytesNo

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The readOnlyHint annotation already establishes safety, so the description adds meaningful extra context: an empty list means the query ran and found nothing rather than never having been executed. It also clarifies the semantic difference between listing resources and measuring disk usage.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tightly written sentences convey purpose, scope, empty-list semantics, and the sibling alternative with no wasted words. The core distinction is front-loaded and every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With a full input schema, a readOnlyHint annotation, and an output schema present, the description covers the key behavioral nuance and the main alternative tool. Nothing an agent needs to select and invoke this tool correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents both the action enum and profile parameter. The description adds little beyond the schema for parameters, which matches the baseline of 3.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states a specific verb and resource for each action: images, volumes, and networks list what the host holds, while disk measures usage. It also explicitly distinguishes itself from the docker_container sibling by noting that current running-container consumption belongs to docker_container with stats.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear context on what the tool is for and explicitly points to docker_container as the right tool for live container consumption. It does not enumerate every sibling alternative, but the main decision boundary is stated clearly.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

  1. 10 tool updatesv2.0.1
    • First observeddocker_compose
    • First observeddocker_compose_control
    • First observeddocker_container
    • First observeddocker_container_control
    • First observeddocker_db
    • First observeddocker_db_admin
    • First observeddocker_exec
    • First observeddocker_health
    • First observeddocker_logs
    • First observeddocker_resource

TDQS

A4.3/5.0
Disambiguation5/5

Each tool maps to a distinct area: read vs control for containers and compose, db query vs db admin, plus exec, logs, health, and resource inspection. Cross-references in descriptions actively prevent misselection, and even tools with multiple sub-actions keep their scope clearly separated.

Naming Consistency5/5

All names use the docker_ prefix and snake_case, with a predictable pattern: base nouns for read/inspect tools, _control for lifecycle actions, and _admin for database maintenance. The container, compose, and db families all follow the same naming convention.

Tool Count5/5

Ten tools is well within the ideal range and matches the server's breadth: container/compose control, exec, logs, database operations, health, and resource inspection. There is no redundant tool or unnecessary bloat.

Completeness4/5

The set covers the core Docker Compose workflow: inspect config, up/down stacks, control services, exec, logs, database query/backup/restore, health, and resource listing. Missing standalone container create/remove or image build/pull are plausible gaps, but the described Compose-focused scope has no glaring dead ends.

Maintenance

ActivityMaintained
ResponsivenessSlow

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    A Model Context Protocol (MCP) server that enables LLMs to run ANY code safely in isolated Docker containers.
    121
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Universal MCP server that wraps any CLI tool, enabling AI assistants to run commands via natural language.
    MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/hypnosis/docker-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server