BoundBench

Docker MCP Gateway

Docker MCP CLI plugin/gateway running MCP servers as isolated containers

github.com/docker/mcp-gateway · 2026-10-03 · a34df45

Defense-in-depth score

4.0 / 10

Minimal

The gateway runs every MCP server in its own short-lived Docker container with only that server's own secrets, scrubs the environment, refuses risky host mounts, verifies signatures on Docker's official images, and scans tool traffic for secret patterns by default. It is still a relay, not a firewall: tool results reach the model as they are, containers get full network egress by default, the gateway's own management tools and the code-mode/mcp-exec wrappers carry no risk annotations, and nothing bounds how long a call may run. A hijacked client can read through one enabled server and act or exfiltrate through another without the gateway stopping it, and the model can save its own server configuration into persistent profiles.

Key gaps (1)

  1. In the default configuration nothing in the gateway breaks the Rule-of-Two combination: injected content relayed from one server can drive credentialed writes and exfiltration through other enabled servers over unrestricted container networking, with no gateway-side approval (C5-WORSTCASE). C5 · Untrusted input blast radius

Criteria

C1 Identity & least privilege

Minimal 0.45 / 1.00

Each MCP server receives only the secrets its catalog entry declares, passed as references that Docker Desktop resolves when the container starts, and the docker process gets a scrubbed environment (PATH plus those secrets). Remote servers get their own per-server OAuth token, and client tokens are never forwarded. The credentials themselves are whatever the user stored (often broad personal tokens), one per server for both reads and writes, and per-request authorization exists only when Docker Desktop's governance feature is on; otherwise the policy client is a no-op. If several servers are enabled, a hijacked client can write to several external systems.

C2 Approval gates

Minimal 0.20 / 1.00

As a tool server, the gateway passes through whatever risk annotations downstream servers declare, but it adds none to its own management tools (mcp-config-set, mcp-remove, mcp-create-profile, code-mode, mcp-exec). The mcp-exec and code-mode wrappers can call any enabled tool behind one tool name, which hides the inner tool's annotations from the host's approval logic. There is no dry-run, read-only mode or confirmation step enforced by the server. Downstream actions such as sending messages or deleting repositories are irreversible.

C3 Tool & action scoping

Moderate 0.53 / 1.00

The gateway validates the inputs it acts on itself. Host bind mounts are resolved through symlinks, kept read-only and limited to temp directories unless the operator allowlists a path, and credential and system paths are refused. Values that look like docker flags are dropped, model-supplied server config is checked against the server's JSON schema, and management tools only reach enabled servers. Arguments to downstream tools are passed through unvalidated. All enabled servers' tools plus the dynamic management tools are exposed by default, though per-server tool lists can narrow that.

C4 Code-execution isolation

Moderate 0.57 / 1.00

Every local MCP server runs as a fresh `docker run --rm --init` container with no-new-privileges, one CPU and 2 GB of memory, and there is no path that runs a server directly on the host or falls back to the host when Docker fails. The container is otherwise stock: whatever user the image picks (often root), Docker's default capabilities, a writable root filesystem and no process limit. It also gets full network egress by default, along with that server's own secrets. The Docker socket and system or credential paths cannot be mounted, and `--privileged` is added only under an explicit Docker-in-Docker environment variable. Code-mode scripts run in an embedded goja JavaScript engine that only exposes the tools injected into it.

C5 Untrusted input blast radius

Minimal 0.00 / 1.00

The gateway relays downstream tool results, resources and tool descriptions to the client unchanged, with no provenance or untrusted-content marking. Its own mcp-add response pastes third-party tool descriptions into a message that also tells the model to assume the server is ready to use. The only related protections are a check that stops one server shadowing another's tool or prompt names, the secret-pattern scan, and opt-in network blocking. Because the gateway aggregates servers, one session commonly mixes untrusted content, credentialed servers and egress. Nothing in the gateway breaks that combination by default, so a hijacked client can leak data and take irreversible actions through it.

C6 Memory, context & configuration integrity

Minimal 0.30 / 1.00

The gateway has no conversational memory, but the model can change persistent configuration. mcp-config-set swaps in new config for any enabled server (checked only against that server's schema), and mcp-create-profile saves the current servers and config into any named profile, including `default`, with no human confirmation. That profile is what the next gateway start loads, and its config feeds container command lines, environment and mounts. For Claude Code clients the profile is also written to `profiles.json` in the working directory, and on connect the gateway reads that file back. In the default configuration activation is refused for any server not already enabled, so a repository's profiles.json cannot add servers.

C7 Third-party extensions

Moderate 0.55 / 1.00

MCP servers are third-party container images (or remote endpoints) the user enables. Images under docker.io/mcp/ must be pinned by digest and pass cosign signature verification against an embedded Docker public key before pull, which is on by default. Community images, custom catalogs, --oci-ref and remote servers get no verification, and a tool definition that changes is not re-approved. The model cannot enable a new server by default. Each server runs in its own container with only its own secrets, but with open network access.

C8 Secrets & sensitive-data protection

Moderate 0.53 / 1.00

Secrets come from Docker Desktop's secrets store and reach containers as se:// references that Docker Desktop resolves at container start, so the gateway passes handles rather than values. By default every tool call's arguments and text results are scanned for secret patterns and blocked on a match. Call logs record only the shape of arguments, and verbose output masks secrets after four characters. Resource reads and prompts are not scanned. Content-free usage telemetry (tool, server and client names) is always on through the Docker CLI's OpenTelemetry provider. Stored credentials are mostly long-lived user API keys.

C9 Audit & traceability

Minimal 0.38 / 1.00

By default every incoming tool call is logged to stderr with the tool name, the shape of its arguments (not their values) and its duration. Config changes and profile writes are also logged. These are plain text lines on the gateway's stderr, which the MCP client may or may not keep. Structured audit events with client identity and policy decisions go to Docker Desktop only when its governance feature is on, and they are queued, dropped under backpressure and sent without error handling. Calls made inside code-mode scripts don't pass through the call log.

C10 Limits & kill switch

Moderate 0.50 / 1.00

Each server container is limited to one CPU and 2 GB of memory by default, and the model can't change that. There is no timeout on tool calls, no rate limiting, and no limit on how many containers or code-mode tools a session can create. The code-mode JavaScript engine has no interrupt, so a looping script can't be stopped by cancellation. Closing a client session or stopping the gateway closes its server sessions, and the containers are removed via --rm.