·

Docker MCP With Cursor

Set up Docker MCP in Cursor so your AI agent can manage containers, images, and logs right from your editor.

Cursor's Agent Mode combines file editing, terminal access, and MCP tool calls in one loop, which makes it well suited to Docker work that spans editing a Dockerfile or compose file and immediately verifying the change against a running container — without the copy-paste round trip between editor and terminal that a plain VS Code + separate CLI workflow requires. This topic covers connecting Docker MCP to Cursor, editing infrastructure files with live container feedback, reproducing environment-specific bugs inside containers, and the integration's current rough edges.


Connecting Docker MCP to Cursor Agent Mode

Cursor reads MCP configuration from JSON files at two scopes:

  • Global: ~/.cursor/mcp.json — available across all projects.
  • Project: .cursor/mcp.json in the project root — scoped to that repo, and the right default for Docker given how tightly it's tied to a specific Dockerfile/compose setup.

Project configuration

{
  "mcpServers": {
    "docker": {
      "command": "uvx",
      "args": ["mcp-server-docker"]
    }
  }
}

If the project uses a devcontainer or a non-default socket path, set it explicitly:

{
  "mcpServers": {
    "docker": {
      "command": "uvx",
      "args": ["mcp-server-docker"],
      "env": {
        "DOCKER_HOST": "unix:///var/run/docker.sock",
        "COMPOSE_FILE": "./docker/docker-compose.yml"
      }
    }
  }
}

Commit .cursor/mcp.json to the repo — as with the other clients in this module, there's no secret embedded in it, since access is governed entirely by the OS-level Docker socket permission of whoever runs Cursor.

Enable Agent Mode and verify

Open the Composer/Agent panel (Cmd+I or Ctrl+I), switch to Agent Mode, and confirm the server is live:

List your available Docker MCP tools and show me all running containers.

Cursor should return the container list inline, with the option to expand full JSON per container. If the server shows as disconnected, check Cursor's MCP settings panel (Cursor Settings → MCP) for a connection error — the most common cause is uvx not being on the PATH that Cursor's subprocess inherits, which sometimes differs from your interactive shell's PATH on macOS.

which uvx
{
  "mcpServers": {
    "docker": {
      "command": "/Users/you/.local/bin/uvx",
      "args": ["mcp-server-docker"]
    }
  }
}

Tips
- If uvx isn't found, use its absolute path in mcp.json rather than debugging Cursor's inherited environment — it's the faster fix and avoids relying on shell profile side effects Cursor may not source.
- Restart Cursor's MCP connection from the settings panel after editing mcp.json — a full app restart isn't usually necessary, just a reconnect.
- Keep the Docker MCP server project-scoped; a globally connected Docker server active across every Cursor window makes it easy to accidentally run a command against the wrong project's containers.


Editing Dockerfiles and Compose Files with Live Container Feedback

The workflow that Cursor's combination of file access and Docker MCP genuinely improves on a plain editor-plus-terminal setup is tight edit-verify loops on infrastructure files.

Example: tightening a healthcheck that's too slow to catch a real failure.

services:
  api:
    build: .
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3

A 30-second interval with 3 retries means up to 90+ seconds before Compose marks the service unhealthy — too slow if you want fast feedback during local iteration.

Open docker-compose.yml. Reduce the api healthcheck interval to 5s and
retries to 3, keep timeout at 10s. Then rebuild and restart the stack,
and watch compose_ps until the healthcheck reports a status.

Cursor edits the file, then immediately drives the verification without you switching windows:

services:
  api:
    build: .
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 5s
      timeout: 10s
      retries: 3
Docker MCP output: compose_ps shows "api: Up 12 seconds (health: starting)",
then a follow-up call a few seconds later shows "Up 18 seconds (healthy)".

Example: fixing a Dockerfile EXPOSE/compose ports mismatch.

A common source of "why can't I reach my container" bugs is a mismatch between what the Dockerfile documents and what compose actually publishes:

EXPOSE 3000
services:
  api:
    build: .
    ports:
      - "3000:8080"
The api service isn't reachable on localhost:3000. Check the Dockerfile's
EXPOSE directive against the ports mapping in docker-compose.yml, fix
any mismatch, and verify by exec-ing into the container and confirming
what port the app is actually listening on.
Exec into "api" and run: netstat -tlnp | grep LISTEN
tcp   0.0.0.0:3000   LISTEN   1/node

The app listens on 3000 inside the container, but compose was mapping host 3000 to container 8080 — nothing was listening there. Cursor corrects the mapping and re-verifies:

services:
  api:
    build: .
    ports:
      - "3000:3000"

The value of doing this inside Cursor rather than switching between an editor and a terminal is less about any single step and more about the loop staying unbroken — edit, rebuild, inspect, confirm, all inside one agent turn, with the file diff and the live container state visible in the same context.

Tips
- Ask explicitly for verification after every infra file edit ("rebuild and confirm") — Cursor will happily make a plausible-looking YAML change without checking it actually fixes the running behavior unless you ask.
- For port and networking mismatches, exec_in_container plus a listening-port check (netstat/ss) resolves ambiguity faster than reasoning about the Dockerfile and compose file in isolation — verify what's actually listening, don't just read the config.
- Keep healthcheck intervals fast (a few seconds) during local iteration and reserve slower, more conservative intervals for your production compose overrides — ask the agent to maintain both if you use docker-compose.override.yml.


Reproducing Environment-Specific Bugs Inside Containers from Cursor

"Works on my machine but not in the container" is a recurring class of bug, and Cursor's ability to exec into the container and compare its actual runtime state against your local host is the most direct way to close that gap.

Scenario: A date-parsing function behaves differently in the container than locally.

Exec into the "api" container and run: node -e "console.log(new Date().toString())"
Then tell me what timezone and Node version the container reports, and
compare it against what's in package.json's engines field.
Container: Thu Aug 21 2026 00:00:00 GMT+0000 (Coordinated Universal Time)
Node: v20.11.1

package.json engines.node: >=20.0.0 (satisfied)

The container runs UTC while the developer's laptop runs in a local timezone — a classic source of "off by a few hours" bugs in date-sensitive logic that only shows up once code leaves a developer's machine.

Grep the codebase for any use of "new Date()" combined with local-time
formatting methods like toLocaleString() without an explicit timezone,
and flag them as candidates for this class of bug.

Scenario: a native dependency compiled for the wrong architecture.

Apple Silicon developers building images that later run on linux/amd64 production hosts hit this constantly with native Node/Python addons.

Exec into the container and run: node -e "console.log(process.arch, process.platform)"
Compare that against the host architecture reported by inspect_container's
Platform field on the image itself.
Container process.arch: x64
Image inspect Platform: linux/amd64
Host (Cursor's machine): arm64 (Apple Silicon)

If the build was done with the wrong --platform flag or without buildx multi-arch support, a native module compiled for arm64 can end up inside an amd64 image and fail at runtime with a cryptic ELF header or wrong ELF class error rather than a clear message.

Rebuild the image explicitly with --platform linux/amd64 using buildx,
then exec in and confirm the native module loads without an
architecture mismatch error.
docker buildx build --platform linux/amd64 -t myapp:amd64 --load .

Tips
- When a bug only reproduces "in the container," start by diffing timezone, Node/Python/runtime version, and architecture between the container and your host — these three account for a large share of environment-specific bugs and are each a single exec_in_container call away.
- wrong ELF class or similarly cryptic native-module load errors almost always mean an architecture mismatch, not a code bug — check process.arch inside the container before debugging application logic.
- For teams with both Apple Silicon and x86 developers, standardize on docker buildx build --platform linux/amd64 (or your actual production target) for any image meant to run in production, regardless of which machine built it.


Known Limitations and Workarounds for Docker MCP in Cursor

Agent Mode occasionally under-uses the terminal in favor of MCP tools even when a raw shell command would be simpler. For very simple checks (docker ps), asking Cursor to just run the CLI command in its integrated terminal is sometimes faster and more transparent than routing through MCP tool calls — don't force every interaction through MCP if a one-line shell command is clearer.

Just run "docker ps --format 'table {{.Names}}\t{{.Status}}'" in the
terminal instead of using the MCP tool for this one.

No persistent "watch" tool. Like the other clients in this module, Docker MCP is pull-based — there's no tool that streams container events (docker events) into the agent's context continuously. For active monitoring during a deploy, keep docker events or docker compose logs -f running in Cursor's integrated terminal alongside the agent session.

Large inspect_container payloads can crowd out file context in a single turn. If you're mid-edit on a large Dockerfile/compose setup and also pull a full container inspect, Cursor's context management may summarize one or the other more aggressively than you'd like. Ask for specific fields rather than the full payload when you also need substantial file context in the same turn.

Just give me the State and NetworkSettings fields from inspecting "api",
not the full inspect output — I need room for the compose file too.

Compose project name assumptions. mcp-server-docker's compose tools generally infer the project name from the directory, matching default docker compose behavior — but if your team explicitly sets COMPOSE_PROJECT_NAME or uses -p flags in scripts, make sure the MCP server's environment matches, or compose_ps/compose_down may target a differently-named (and possibly empty) project.

{
  "mcpServers": {
    "docker": {
      "command": "uvx",
      "args": ["mcp-server-docker"],
      "env": { "COMPOSE_PROJECT_NAME": "myapp" }
    }
  }
}

Windows and WSL2 path translation. If you're on Windows with Docker Desktop's WSL2 backend, socket paths and bind-mount paths in compose files can differ between what Cursor's Windows-side process sees and what the Linux-side daemon expects. Run Cursor itself inside the WSL2 environment (via the Remote-WSL-equivalent connection) rather than mixing Windows-native Cursor with a WSL2-only Docker setup.

Tips
- Let the agent fall back to a plain terminal command for trivial one-off checks — MCP tool calls aren't free of overhead, and transparency sometimes matters more than structure for a throwaway docker ps.
- Set COMPOSE_PROJECT_NAME explicitly in the MCP server's environment if your team's scripts do, so compose tool calls target the same project your CLI-driven workflows do.
- On Windows, keep the whole toolchain — Cursor, the Docker MCP server, and the Docker CLI — on the same side of the WSL2 boundary to avoid path-translation surprises in bind mounts.


Tips

Tips
- Use Cursor's combined file-and-tool-call context for edit-verify loops on Dockerfiles and compose files specifically — that's the workflow where it outperforms a separate editor-plus-terminal setup, more than for pure read-only container inspection.
- When "works locally, fails in container" comes up, default to checking timezone, runtime version, and CPU architecture first via exec_in_container — cheap checks that rule out the most common environment-specific bug classes before you dig into application logic.
- Keep a terminal-native docker events or compose logs -f running for anything needing continuous monitoring; treat the MCP-driven agent session as the investigation layer, not a live dashboard.