# Docker MCP Gateway and CLI plugin: profiles and containers

> Docker's MCP Gateway runs MCP servers as containers and fronts them behind a single endpoint, with profiles, tool allowlists and OCI distribution.

**docker/mcp-gateway** — docker mcp CLI plugin / MCP Gateway

- Repository: https://github.com/docker/mcp-gateway
- Stars: 1,613 · Forks: 289
- Language: Go
- License: MIT
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/docker-mcp-gateway

## Two products in one repository

The relationship is worth stating first because it is not obvious from the name. The MCP Toolkit in Docker Desktop lets developers configure and consume MCP servers from the Docker MCP Catalog. Underneath, the Toolkit is powered by a Docker CLI plugin called `docker-mcp`, and this repository is the code of that plugin. The README says it can work in Docker Desktop or independently.

So there are two things. The CLI plugin is a `docker mcp` subcommand you install into `~/.docker/cli-plugins/`. The Gateway is the main feature of that CLI, and it is the thing that runs and deploys MCP servers.

 The `Makefile` reads the module path and Go version out of `go.mod` with awk, and derives a Git tag with `git describe --tags --exact-match HEAD` falling back to a rev-parse, and a dev version with `git describe --tags --abbrev=0` suffixed `-dev`. It injects the version with `GO_LDFLAGS = -X $(MODULE)/cmd/docker-mcp/version.Version=$(GIT_TAG)`, which is the standard way to stamp a build with its version in Go.

Then there is a separate `push-module-image` target that copies `dist` into `module-image`, so there is at least one OCI image to publish alongside the binary. A `docker-bake.hcl` file drives the multi-target build.

Licence is MIT, the last push was on 2026-09-23, and the newest tag is v0.44.1 from the same day. Go 1.25.12 is what `go.mod` declares.

## The problem it names is lifecycle, not connection

The Overview section lists the barriers developers face when putting MCP tools into production workflows, and the first one is not about the protocol.

Managing MCP server lifecycle is listed first. Each local MCP server in the catalog runs in an isolated Docker container, and npx and uvx servers are granted minimal host privileges. That is the core of the project: the isolation is containerised, and the specific claim is that package-runner-based servers do not get broad host access.

The other three are consequences of having many servers. A unified interface, so AI models reach all of them through a single Gateway rather than each client configuring each server. Authentication and security, with the specific approach of keeping secrets out of environment variables by using Docker Desktop's secrets management. And dynamic tool discovery and configuration, so each MCP client such as VS Code, Cursor or Claude Desktop connects to the same Gateway configuration and stays consistent across clients.

That last point is the one that is genuinely hard to do yourself. Without shared configuration, client configurations can diverge. With one, the clients are interchangeable and the configuration is yours.

OAuth flows for MCPs that need an access token service connection are listed as a fifth capability, alongside container-based servers, server management across tools, resources and prompts, catalog management, dynamic discovery, and monitoring through built-in logging and call tracing.

## Profiles are the unit of configuration, and they are shareable

Servers are organised into profiles. A profile groups related MCP servers together and can be connected to clients, exported, and shared via OCI registries.

A server inside a profile is referenced by one of four URI schemes, and the scheme determines where it comes from. `catalog://mcp/docker-mcp-catalog/github` is a catalog reference. `docker://my-server:latest` is an OCI image reference. `https://registry.modelcontextprotocol.io/v0/servers/<id>` is an MCP Registry reference. `file://./server.yaml` is a local file.

That mix is the interesting design decision. A catalog entry, a container image, a registry record and a local file are four different distribution models, and the profile treats them as interchangeable. It means a team can start with a local YAML file and move to a published catalog entry without changing the profile.

The command surface follows the object model:

```bash
docker mcp profile create --name dev-tools \
  --server catalog://mcp/docker-mcp-catalog/github \
  --connect cursor

docker mcp profile list
docker mcp profile show <profile-id>
```

`--connect cursor` at creation time is the ergonomic touch: you name the client when you build the profile rather than wiring it afterwards.

And profiles are versionable artefacts, not just local state:

```bash
docker mcp profile export <profile-id> output.yaml
docker mcp profile import input.yaml
docker mcp profile push <profile-id> <oci-reference>
docker mcp profile pull <oci-reference>
```

Export and import give you a file you can review in a pull request. Push and pull put the profile in an OCI registry, which means profile distribution uses the same mechanism as image distribution. That is worth understanding as a team, because a profile you push is now something with versions and access control like any other published artefact.

You need the catalog present first, which the README notes directly: make sure to run `docker mcp catalog pull mcp/docker-mcp-catalog` first.

## The tool allowlist is the security control that matters

Configuration and allowlisting are separate subcommands, and separating them is the right call for a tool that fronts model-accessible actions.

Configuration is namespaced per server, written as `<server-name>.<config-key>=<value>`:

```bash
docker mcp profile config <profile-id> --set github.timeout=30
docker mcp profile config <profile-id> --get github.timeout
docker mcp profile config <profile-id> --get-all
docker mcp profile config <profile-id> --del github.timeout
```

The allowlist uses dot notation as well, `<server>.<tool>`, and operates per tool:

```bash
docker mcp profile tools <profile-id> --enable github.create_issue --enable github.list_repos
docker mcp profile tools <profile-id> --disable github.search_code
docker mcp profile tools <profile-id> --enable-all github
docker mcp profile tools <profile-id> --disable-all github
```

This is the feature to think hardest about. An MCP server can expose tools written by its author, and a model may call them when they are visible. Narrowing the tool set per profile is how you scope what a given client can do, and `--enable-all` and `--disable-all` for a whole server exist for the cases where you have decided to trust a server completely.

Server membership is managed separately, with filtering by name or profile:

```bash
docker mcp profile server ls --filter name=github
docker mcp profile server ls --filter profile=dev-tools
docker mcp profile server add dev-tools \
  --server catalog://mcp/docker-mcp-catalog/notion
docker mcp profile server remove dev-tools github slack
```

Note that removal takes multiple names at once, which matters when you are tearing down a profile and want to be sure you removed what you meant to.

Catalogs themselves are managed as OCI artefacts, with the default being the `mcp/docker-mcp-catalog` image, and you can create your own from a mix of catalog and file references:

```bash
docker mcp catalog create myorg/catalog:latest --title "My Catalog" \
  --server catalog://mcp/docker-mcp-catalog/github \
  --server file://./my-server.yaml
```

So a private catalog is an image you build from server references, including your own local YAML files.

## Docker Desktop is assumed, with two documented escape hatches

The prerequisites are Docker Desktop `4.59+` with the MCP Toolkit feature enabled, and Go 1.24+ for development. That is a high floor and it is not incidental: several features reach into Docker Desktop's backend sockets.

Two notes give you the ways out. The first handles the case where the Docker daemon is running but you get a Docker Desktop is not running error, which happens because Desktop's backend sockets are absent:

```bash
export DOCKER_MCP_IN_CONTAINER=1
```

The README positions this for WSL2, containerised environments, or Docker CE where Desktop backend sockets are unavailable.

The second is about profiles specifically:

```bash
docker mcp feature enable profiles
```

Profiles are enabled automatically inside Docker Desktop, so outside it you have to turn them on. That is the CLI surface revealing its own feature flags, which is a hint that the standalone path is supported but second.

Installation, if you want the latest rather than the bundled version, is a clone and a make target:

```bash
git clone https://github.com/docker/mcp-gateway.git
cd mcp-gateway
mkdir -p "$HOME/.docker/cli-plugins/"

make docker-mcp
```

The `Makefile` handles the Windows path separately, appending `.exe` and using `%USERPROFILE%\.docker\cli-plugins\` rather than `$HOME`. After installation the plugin is available as `docker mcp --help`.

The Dockerfile shows the CI matrix. The Go version is an argument defaulting to 1.25.12, linting runs `golangci-lint` from a pinned `v2.8.0-alpine` image, and the lint, format and test targets all cross-compile with `CGO_ENABLED=0` and explicit `GOOS` and `GOARCH`. The cross-build target produces binaries for linux, darwin and windows on both amd64 and arm64, and linting is run across all three of those platforms.

There is also a docs pipeline that generates reference documentation and then fails the build if the result differs from what is committed, telling you to rebase on main and rerun `make docs`. Generated docs that are verified in CI is the difference between a reference that is current and one that is not.

## Where it does not fit

The clearest limit is the Docker Desktop dependency for anything beyond basic CLI use. The README documents the workarounds rather than pretending they are equivalent, and the fact that profiles need an explicit `docker mcp feature enable` outside Desktop means the standalone path is a supported configuration with less polish.

The second limit is what a profile means as a security boundary. Container isolation per server and secrets held outside environment variables are real improvements. But a tool allowlist is a filter on top of a server's own capability, not a sandbox around it. If you enable a tool that can write files or make network calls, the container boundary is the thing actually containing it, and the allowlist only limits what the model chooses to call. Decide on the allowlist per profile rather than `--enable-all` as a default.

The third is the dependency weight. The `go.mod` is long and includes the Docker client, cosign, sigstore, the modelcontextprotocol Go SDK, an OpenTelemetry set, cobra, pflag, yq, a validator, sqlite, goja and golang-migrate. A `vendor/` directory is present in the repository. This is a binary that does a lot, and every one of those is a supply-chain surface even if most users never trigger them.

The fourth is that the catalog is a curated list maintained elsewhere. The default catalog is an OCI image, `mcp/docker-mcp-catalog`, so what you can install depends on what somebody published there. That is fine for discovery and poor as a dependency lock, which is the argument for building your own catalog from `file://` references and pushing it to a registry you control.

Finally, `docker/secrets-engine/x` appears in the dependency list at version `0.2.2-do.not.use`. Whatever that is, the version string says not to depend on it, and it is worth finding out why before relying on the secrets management path.

## Conclusion

Adopt the docker MCP gateway if you want every MCP server to run as an isolated container behind one endpoint, need a per-profile tool allowlist so a client only sees the tools you enabled, and already use Docker Desktop 4.59 or later. Do not adopt it as a bare CLI on a developer laptop without Docker Desktop, because the profiles feature has to be enabled separately and Desktop's backend sockets are expected in several paths. Before you commit, decide how much of your configuration lives in profiles, since `docker mcp profile push` distributes them through an OCI registry and that makes a profile a versioned artefact your team now has to review.

## FAQ

### What is the difference between the docker mcp CLI plugin and the Docker MCP Gateway?

The MCP Toolkit in Docker Desktop is powered underneath by a Docker CLI plugin called `docker-mcp`, and this repository is the code of that plugin. The Gateway is the CLI's main feature, running and deploying MCP servers as isolated containers behind a single endpoint. The plugin can be used with Docker Desktop or independently.

### How do I install the docker mcp CLI plugin?

It ships with recent Docker Desktop versions. To build the latest, clone the repository, run `mkdir -p "$HOME/.docker/cli-plugins/"` and then `make docker-mcp`, after which the plugin is available as `docker mcp --help`. Go 1.24 or newer is required for development.

### How do I limit which MCP tools a client can call?

Use the profile tools subcommand with dot notation in the form `<server>.<tool>`, for example `docker mcp profile tools <profile-id> --enable github.create_issue`. There are also `--disable`, `--enable-all` and `--disable-all` for an entire server.

### What source types can a profile reference?

Four URI schemes: catalog references like `catalog://mcp/docker-mcp-catalog/github`, OCI image references like `docker://my-server:latest`, MCP Registry references like `https://registry.modelcontextprotocol.io/v0/servers/<id>`, and local files like `file://./server.yaml`.

### How do I use docker mcp outside Docker Desktop?

Set `export DOCKER_MCP_IN_CONTAINER=1` to bypass Desktop feature checks when the backend sockets are unavailable, which the README recommends for WSL2, containerised environments and Docker CE. Profiles are enabled automatically in Desktop but need `docker mcp feature enable profiles` when running the CLI independently.

## Sources

- [docker/mcp-gateway on GitHub](https://github.com/docker/mcp-gateway)
- [Issues](https://github.com/docker/mcp-gateway/issues)
- [License: MIT](https://github.com/docker/mcp-gateway/blob/main/LICENSE)
- [README](https://github.com/docker/mcp-gateway/blob/main/README.md)
- [Releases](https://github.com/docker/mcp-gateway/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/docker-mcp-gateway
