Releases: docker/mcp-gateway
Release list
v0.43.1
What changed
Remote URL handling is stricter by default
Remote MCP servers, OAuth discovery/DCR/token flows, catalog fetches, README fetches, and MCP registry imports now use a guarded URL path. By default, remote URLs must be public https:// destinations. The gateway rejects unsafe targets such as loopback, private networks, link-local addresses, metadata services, cluster-local names, URLs with userinfo, and unsafe redirects.
Docker Desktop's trusted local proxy socket is still supported when available. Generic proxy settings from the environment are not used for guarded remote URL paths because they can hide the final destination from client-side validation.
HTTP gateway transports require authentication
SSE and streaming gateway transports now require Bearer-token authentication by default, including when the gateway runs in a container. The /health endpoint remains available without authentication.
Use MCP_GATEWAY_AUTH_TOKEN to provide the token clients should send:
MCP_GATEWAY_AUTH_TOKEN=your-token docker mcp gateway run --transport=sse --port=8811Clients should send:
Authorization: Bearer your-tokenTo intentionally disable HTTP authentication, pass --allow-unauthenticated. The gateway warns when this is used on an externally reachable listener.
Local catalog and file inputs must live under the catalog directory
Local catalog paths passed through --catalog or --additional-catalog must now resolve under:
~/.docker/mcp/catalogs
The same trusted-root rule applies to file:// server references used by profile and catalog commands. Symlinks are resolved before the trusted-root check, so symlinks cannot be used to escape the catalog directory.
Move custom local catalog and server files into ~/.docker/mcp/catalogs, then reference them by name or with a catalog-root-relative file:// path:
docker mcp gateway run --catalog team-catalog.yaml
docker mcp profile create --name dev-tools --server file://servers/github.yamlAbsolute paths or ./ paths outside ~/.docker/mcp/catalogs are rejected.
Docker bind mounts are validated before containers start
Host-path bind mounts configured for MCP server containers are now checked before Docker is invoked. Host binds must be read-only and must resolve under an allowed root. By default, temporary directories are allowed. Additional trusted roots can be configured with MCP_GATEWAY_DOCKER_BIND_ALLOWED_PATHS.
Named volumes continue to work. Writable host binds, relative host paths, sensitive system paths, Docker socket binds, and credential directories such as .ssh, .docker, .kube, .aws, and .gnupg are blocked.
Example:
MCP_GATEWAY_DOCKER_BIND_ALLOWED_PATHS=/Users/alice/project-data docker mcp gateway runMCP server catalog entries should use read-only bind modes for host paths:
volumes:
- /Users/alice/project-data:/data:roDocker MCP images are verified before pull
docker mcp gateway run now verifies Docker Hub mcp/... server image signatures before pulling images. Dynamic server add and profile activation paths use the same verification flow.
When signature verification is enabled, Docker MCP images must be pinned by digest:
image: mcp/time@sha256:...Mutable Docker MCP tags such as mcp/time:latest are rejected by default. Non-mcp/... third-party images are pulled without Docker MCP signature verification. For local development or explicitly unverified images, use:
docker mcp gateway run --verify-signatures=falseThe gateway logs a warning when signature verification is disabled.
Tool names can no longer shadow each other
The gateway now rejects exposed tool names that collide across servers or shadow reserved gateway tools such as mcp-exec. mcp-add returns an explicit collision error, and catalog search results can warn about risky tool names.
If a collision is reported, enable the tool-name prefix feature or update catalog metadata so each exposed tool name is unique. Explicit catalog prefix values resolve collisions for image and remote MCP servers; POCI-style catalog tools should use unique exposed names or the global tool-name-prefix feature.
Prompts and resources can no longer shadow each other
The gateway now rejects duplicate prompt names, resource URIs, and resource-template URI templates before registering capabilities. This prevents one server from silently replacing another server's prompt or resource inside the MCP SDK.
When dynamic tools are enabled, the gateway also reserves the mcp-discover prompt name for its own discovery prompt. mcp-add reports a clear collision error if a dynamically added server would shadow an existing prompt, resource, or resource template.
If a collision is reported, disable one of the conflicting servers or update the server/catalog so prompt names, resource URIs, and resource-template URI templates are unique.
Tool call logs no longer include raw argument values
When tool call logging is enabled, the gateway logs argument shape metadata instead of raw argument values. Secret blocking also runs before built-in tool call logging, so secret-shaped arguments are blocked before they can be logged.
Action required
- Update HTTP/SSE/streaming clients to send
Authorization: Bearer <token>, or pass--allow-unauthenticatedonly when unauthenticated access is intentional. - Move local catalogs and
file://server files into~/.docker/mcp/catalogs. - Replace writable or broad host-path bind mounts with read-only binds under trusted roots, and configure
MCP_GATEWAY_DOCKER_BIND_ALLOWED_PATHSfor approved non-temporary directories. - Pin Docker MCP
mcp/...images by digest, or explicitly disable verification for development-only flows. - Rename or prefix tools if the gateway reports a tool-name collision.
- Rename or disable servers that expose duplicate prompt names, resource URIs, or resource-template URI templates. Avoid using
mcp-discoveras a server prompt name when dynamic tools are enabled. - For local HTTP or loopback remote MCP development, set
DOCKER_MCP_ALLOW_INSECURE_REMOTE_URLS=1. Do not use that setting for production.
v0.42.3
fix(db): batch catalog_server inserts to avoid SQLite variable limit …
v0.42.2
Narrow OCI label schema to descriptive fields only Narrow OCI label schema to descriptive fields only
v0.42.1
Removing McpGatewayOAuth feature flag Removing McpGatewayOAuth feature flag
v0.42.0
What's Changed
- Unhide oauth command in CLI help output by @jchangx in #480
- Add npm/npx server support to MCP catalog by @cutecatfann in #482
- Fix OAuth token exchange and refresh not using proxy transport by @cutecatfann in #484
- Remove MCPWorkingSets feature flag, always enable profiles by @bobbyhouse in #485
Full Changelog: v0.40.4...v0.42.0
v0.40.4
Merge pull request #477 from docker/fix-oauth-callback-server IPv6/AirPlay conflict, port 5000, and stale DCR cache
v0.40.3
Fallback se:// URI generation when secrets engine is unreachable (#448) * Fallback se:// URI generation when secrets engine is unreachable When GetSecrets() fails (e.g. MSIX-sandboxed Claude Desktop on Windows cannot follow AF_UNIX reparse points to the WSL2 secrets engine socket), generate se:// URIs for all declared secrets instead of silently setting them to <UNKNOWN>. Docker Desktop resolves se:// URIs at container runtime via named pipes, which are unaffected by MSIX restrictions. Also log the GetSecrets() error instead of silently discarding it. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address PR feedback: handle OAuth secrets in fallback path Refactored into separate functions for clarity: - buildFallbackURIs: generates se:// URIs for all declared secrets when the secrets engine is unreachable (OAuth preferred when configured) - buildVerifiedURIs: generates se:// URIs only for secrets that exist in the store (OAuth checked first, then direct secret) - oauthMapping: shared helper for OAuth provider lookup When GetSecrets() fails (e.g. MSIX sandbox on Windows), the fallback generates URIs for everything and lets Docker Desktop resolve at runtime. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
v0.41.0
Add --exclude flag to catalog-next create for community registry server blocklist
v0.40.2
Merge pull request #427 from docker/bugfix-preserve-empty-tools fix: Preserve empty tools after applying policy
v0.40.1
Add dynamic OAuth discovery for community MCP servers (#410) ## Summary - Community MCP servers from third-party registries (e.g., Kubit) require OAuth but have no `oauth.providers` metadata in their catalog entry, so the existing `IsRemoteOAuthServer()` gate prevents DCR entries from being created - Adds `RegisterProviderForDynamicDiscovery()` which probes remote servers using `DiscoverOAuthRequirements()` and creates pending DCR entries when OAuth is detected - Integrates the fallback into all three callsites: working set sync, `docker mcp server enable`, and gateway `mcpadd` - Expands the gateway OAuth condition to also match remote servers without `oauth.providers` ## Test plan - [x] `docker mcp catalog-next pull mcp/community-registry` - [x] `docker mcp profile server add default --server catalog://mcp/community-registry/com-notion-mcp` - [x] Verify DCR entry is created: `docker mcp oauth ls` - [x] Authorize the server: `docker mcp oauth authorize com-notion-mcp` - [x] Verify authorization: `docker mcp oauth ls` - [x] Revoke and re-authorize: `docker mcp oauth revoke com-notion-mcp` - [x] Verify existing servers with `oauth.providers` still work (no regression) - [x] Verify servers that don't require OAuth are not affected (probe returns no OAuth) Confirming `com-notion-mcp` from community registry successfully authorized via oauth: ``` ❯ docker mcp oauth ls ai-kubit-mcp-server | not authorized com-notion-mcp | authorized github | authorized miro-remote | not authorized ``` Confirming oauth flow for `com-notion-mcp` from community registry: ``` ❯ docker mcp oauth authorize com-notion-mcp Opening your browser for authentication. If it doesn't open automatically, please visit: ... ```