Docker MCP Gateway Setup: Profiles, Clients and Secrets
Set up Docker MCP Gateway step by step: run docker mcp gateway run, build profiles from a catalog, connect Claude Code, Cursor and VS Code, and manage secrets.
The Docker MCP Gateway is an open-source proxy that runs MCP servers as isolated Docker containers and exposes all of them to your AI client through a single MCP endpoint. You add servers to a profile, start the gateway with docker mcp gateway run --profile <name>, and point Claude Code, Cursor, VS Code or any other MCP client at that one gateway instead of configuring every server separately.
This guide covers the current (profile-based) workflow end to end: install, catalog, profiles, client connection, secrets, the security defaults you get for free, and running the gateway headless with Docker Compose. Every command below was checked against Docker's MCP Toolkit docs and the docker/mcp-gateway README as of October 2026.
MCP Toolkit vs MCP Gateway vs MCP Catalog
The three names describe one stack:
| Piece | What it is | Where you see it |
|---|---|---|
| MCP Catalog | A curated list of containerized MCP servers, published as the OCI image mcp/docker-mcp-catalog | hub.docker.com/mcp, the Catalog tab in Docker Desktop |
| MCP Gateway | The proxy that starts server containers, injects credentials, applies policy and routes calls | docker mcp gateway run |
| MCP Toolkit | The Docker Desktop UI on top of both | MCP Toolkit in Docker Desktop's sidebar |
docker mcp CLI | The CLI plugin that powers all of the above; it also works without Docker Desktop | docker mcp --help |
The architecture is simple: AI client → MCP Gateway → MCP servers (containers). Because the client only ever talks to the gateway, you configure a server once and every connected client sees it.
Prerequisites
- Docker Desktop 4.62 or later. Docker's CLI reference states the current
docker mcpcommands need 4.62+; older versions have a different Toolkit UI. The plugin ships with Docker Desktop, sodocker mcp --helpshould already work. - Docker CE, WSL2 or a container instead of Desktop? The CLI still works. Set
DOCKER_MCP_IN_CONTAINER=1if you see "Docker Desktop is not running" while the daemon is up, and enable profiles explicitly:
export DOCKER_MCP_IN_CONTAINER=1
docker mcp feature enable profiles
docker mcp feature list
Step 1: Browse the catalog
The default catalog is mcp/docker-mcp-catalog. Pull it and list the servers it contains:
docker mcp catalog pull mcp/docker-mcp-catalog
docker mcp catalog ls
docker mcp catalog server ls mcp/docker-mcp-catalog
In Docker Desktop the same list lives under MCP Toolkit → Catalog. Each entry is a server name such as github-official, duckduckgo or playwright, which you reference in the next step as catalog://mcp/docker-mcp-catalog/<server>.
Step 2: Create a profile
A profile is a named set of servers (plus their config and tool allowlist) that a client connects to. Profiles replaced the older global "enabled servers" list; configurations from earlier Toolkit versions are migrated automatically into a profile called default.
# Create a profile with two catalog servers
docker mcp profile create --name dev-tools \
--server catalog://mcp/docker-mcp-catalog/github-official+duckduckgo
# Add another server later
docker mcp profile server add dev-tools \
--server catalog://mcp/docker-mcp-catalog/playwright
# Inspect what you have
docker mcp profile list
docker mcp profile show dev-tools
docker mcp profile server ls --filter profile=dev-tools
Server references are not limited to the catalog. A profile can mix catalog:// entries, your own images (docker://my-server:latest), local definitions (file://./my-server.yaml) and MCP Registry URLs (https://registry.modelcontextprotocol.io/v0/servers/<id>). The repo's profile docs note that registry references are not fully implemented yet, so prefer catalog or image references for now.
Two profile features are worth using from day one:
# Per-server configuration (keys differ per server)
docker mcp profile config dev-tools --set github-official.<key>=<value>
docker mcp profile config dev-tools --get-all
# Tool allowlists: expose only what the agent needs
docker mcp profile tools dev-tools --disable-all github-official
docker mcp profile tools dev-tools --enable github-official.list_issues
Trimming tools matters more than it looks. Every exposed tool costs context tokens, and fewer tools means fewer wrong tool choices (see our guide to MCP tool discovery).
If you prefer the UI: MCP Toolkit → Profiles → Create profile, then add servers from the Catalog tab with the profile dropdown. The Tools tab of a profile toggles individual tools.
Step 3: Run the gateway
# stdio, using the "default" profile
docker mcp gateway run
# stdio, a specific profile
docker mcp gateway run --profile dev-tools
# Debug: show config, images and servers, then exit without listening
docker mcp gateway run --profile dev-tools --verbose --dry-run
With no --profile, the gateway uses the profile named default, or an empty configuration if none exists. Most of the time you won't start the gateway yourself in stdio mode: your MCP client launches docker mcp gateway run as a subprocess, exactly like any other stdio server.
Step 4: Connect your clients
The fastest way is docker mcp client connect, which writes the right config file for the client. Supported client names (from the generated CLI reference) include claude-code, claude-desktop, cursor, vscode, codex, gemini, zed, cline, continue, goose, kiro, lmstudio and opencode.
docker mcp client ls
docker mcp client connect <client> --profile dev-tools
docker mcp client disconnect <client>
Claude Code
docker mcp client connect claude-code --profile dev-tools --global
Or add it manually with the standard claude mcp add syntax (everything after -- is the server command):
claude mcp add --scope user MCP_DOCKER -- docker mcp gateway run --profile dev-tools
Run /mcp inside Claude Code to confirm the gateway connected. If you set CLAUDE_CONFIG_DIR, client connect writes to that directory's .claude.json instead of ~/.claude.json. More on Claude Code setup in our Claude Code MCP guide.
Cursor
docker mcp client connect cursor --profile dev-tools
Manual equivalent in ~/.cursor/mcp.json (global) or .cursor/mcp.json (project):
{
"mcpServers": {
"MCP_DOCKER": {
"command": "docker",
"args": ["mcp", "gateway", "run", "--profile", "dev-tools"]
}
}
}
VS Code (GitHub Copilot)
Run this inside your project folder. It creates .vscode/mcp.json, which is user-specific, so keep it out of git:
docker mcp client connect vscode --profile dev-tools
echo ".vscode/mcp.json" >> .gitignore
The manual config uses VS Code's servers key:
{
"servers": {
"MCP_DOCKER": {
"type": "stdio",
"command": "docker",
"args": ["mcp", "gateway", "run", "--profile", "dev-tools"]
}
}
}
Claude Desktop is a single click: MCP Toolkit → Clients → Connect next to Claude Desktop, then restart it. For other clients, browse the MCP client directory.
Step 5: Secrets and OAuth
Servers that need API keys get them from the gateway, not from environment variables in your client config. By default the gateway reads secrets from Docker Desktop's store (--secrets=docker-desktop), which is backed by your OS keychain.
# Pipe the value in via stdin so it never lands in shell history
echo "$GITHUB_TOKEN" | docker mcp secret set github.personal_access_token
docker mcp secret ls
docker mcp secret rm github.personal_access_token
The secret name is server-specific (check the server's Configuration tab in Docker Desktop or docker mcp catalog server inspect). Secrets are scoped to the server that declares them, so one server cannot read another server's keys.
For OAuth-based servers, open the server's Configuration tab in Docker Desktop, choose OAuth, and complete the browser flow; revoke access later from the OAuth tab. The CLI equivalent lives under docker mcp oauth --help. Two caveats from Docker's profile docs: OAuth credentials are shared across all profiles, and profiles you push to a registry never include credentials, so teammates authorize on their own machine. For the protocol side of this, read MCP authentication with OAuth.
What the gateway does for security by default
This is the main reason to put servers behind the gateway instead of running npx some-server directly. Per the repo's security model:
| Protection | Default | Flag / setting |
|---|---|---|
Container isolation, no-new-privileges, no host env passed in | On | — |
| CPU / memory cap per server | 1 CPU, 2 GB | --cpus, --memory |
Signature verification for mcp/ catalog images | On | --verify-signatures |
| Scan tool arguments and responses for secret-like values | On | --block-secrets |
| Call logging (tool name and argument shape only, not raw values) | On | --log-calls |
| Restrict network egress | Off | --block-network |
| Bearer token on HTTP transports | On | MCP_GATEWAY_AUTH_TOKEN, opt out with --allow-unauthenticated |
| Reject tool/prompt/resource name collisions between servers | On | — |
Host bind mounts are read-only by default and limited to allowlisted paths. Remote MCP URLs must be public HTTPS. None of this stops prompt injection inside the content a tool returns, so keep pairing it with the habits in MCP security best practices.
Serving several clients over HTTP
stdio gives each client its own gateway process. To share one gateway, switch to the Streamable HTTP transport:
export MCP_GATEWAY_AUTH_TOKEN="$(openssl rand -hex 32)"
docker mcp gateway run --profile dev-tools --transport streaming --port 8080
Clients connect to http://localhost:8080/mcp and must send Authorization: Bearer <token>. In Claude Code:
claude mcp add --transport http docker-gateway http://localhost:8080/mcp \
--header "Authorization: Bearer $MCP_GATEWAY_AUTH_TOKEN"
Browser requests are only accepted from localhost origins, and /health is unauthenticated for health checks.
Running the gateway with Docker Compose
The gateway is also published as the image docker/mcp-gateway, so agents and CI can use it without Docker Desktop. It needs the Docker socket to start server containers. In this mode you list servers with --servers (catalog names) instead of a profile, and secrets can come from a file:
services:
gateway:
image: docker/mcp-gateway
command:
- --transport=streaming
- --port=8811
- --servers=github-official,duckduckgo
- --secrets=docker-desktop:/run/secrets/mcp_secret
environment:
- MCP_GATEWAY_AUTH_TOKEN=${MCP_GATEWAY_AUTH_TOKEN}
volumes:
- /var/run/docker.sock:/var/run/docker.sock
secrets:
- mcp_secret
secrets:
mcp_secret:
file: .env
The .env file holds lines like github.personal_access_token=.... Your agent service then connects to http://gateway:8811/mcp. The repo's examples folder has variants for a Python client, PostgreSQL, interceptors, health checks and remote servers. --secrets takes a colon-separated lookup chain: Docker Desktop first, then the mounted file.
Dynamic MCP: let the agent add servers
With the Toolkit enabled, the gateway also exposes management tools such as mcp-find (search the catalog), mcp-add and mcp-remove (add or drop a server for this session) and mcp-config-set. Servers added this way last only for the current session and are not saved to the profile. Docker labels the feature experimental; disable it with docker mcp feature disable dynamic-tools if you want a fixed tool list (docs).
Troubleshooting
- Client shows no tools. Run
docker mcp gateway run --profile dev-tools --verbose --dry-run. It prints which servers are enabled, which images are pulled and how many tools load, which catches typos, missing secrets and missing images. - Isolate one tool call.
docker mcp tools ls, thendocker mcp tools call <tool> key=value. Add--verboseto see what the gateway does. - "Docker Desktop is not running" on Linux/WSL2. Export
DOCKER_MCP_IN_CONTAINER=1. - "unsupported dialect" schema errors. Update the gateway; current versions translate draft-07 tool schemas to JSON Schema 2020-12 automatically.
- Your tutorial uses
docker mcp server enable. That is the pre-profile workflow. On 4.62+, add servers to a profile withdocker mcp profile server addinstead. - 401 on the HTTP transport. Send the bearer token from
MCP_GATEWAY_AUTH_TOKEN. Use MCP Inspector to test the endpoint outside your client.
Next steps
Start with a small profile of two or three servers you trust, trim each one to the tools you actually call, and connect a single client. Good first candidates are the GitHub MCP Server, Playwright MCP for browser automation (see the Playwright setup guide) and Context7 for up-to-date docs. When you're ready for more, browse the MCP.so server directory to find servers for your stack.
Frequently asked questions
What is the Docker MCP Gateway?
- It is an open-source proxy, shipped as the docker mcp CLI plugin, that runs MCP servers in isolated containers and exposes them to AI clients through a single MCP endpoint. Docker Desktop's MCP Toolkit is the graphical interface on top of it.
Is Docker MCP Gateway free?
- Yes. The gateway is MIT-licensed open source on GitHub, and the MCP Toolkit is included in Docker Desktop. Normal Docker Desktop licensing terms still apply to Desktop itself.
Can I use the Docker MCP Gateway without Docker Desktop?
- Yes. You can run the docker/mcp-gateway image with Docker Compose or use the CLI on Docker CE. Set DOCKER_MCP_IN_CONTAINER=1 and run 'docker mcp feature enable profiles' if you want profiles outside Desktop.
How do I connect Claude Code to the Docker MCP Gateway?
- Run 'docker mcp client connect claude-code --profile <your-profile> --global', or add it manually with 'claude mcp add MCP_DOCKER -- docker mcp gateway run --profile <your-profile>'. Then run /mcp in Claude Code to confirm it is connected.