Skip to content

Releases: docker/mcp-gateway

v0.43.1

v0.43.1 Pre-release
Pre-release

Choose a tag to compare

@docker-read-write docker-read-write released this 25 Jun 22:28
8f422b6

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=8811

Clients should send:

Authorization: Bearer your-token

To 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.yaml

Absolute 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 run

MCP server catalog entries should use read-only bind modes for host paths:

volumes:
  - /Users/alice/project-data:/data:ro

Docker 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=false

The 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-unauthenticated only 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_PATHS for 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-discover as 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

v0.42.3 Pre-release
Pre-release

Choose a tag to compare

@docker-read-write docker-read-write released this 12 Jun 17:54
dcc3154
fix(db): batch catalog_server inserts to avoid SQLite variable limit …

v0.42.2

v0.42.2 Pre-release
Pre-release

Choose a tag to compare

@docker-read-write docker-read-write released this 28 May 20:52
439b220
Narrow OCI label schema to descriptive fields only

Narrow OCI label schema to descriptive fields only

v0.42.1

v0.42.1 Pre-release
Pre-release

Choose a tag to compare

@docker-read-write docker-read-write released this 05 May 16:44
b46ac89
Removing McpGatewayOAuth feature flag

Removing McpGatewayOAuth feature flag

v0.42.0

v0.42.0 Pre-release
Pre-release

Choose a tag to compare

@docker-read-write docker-read-write released this 30 Apr 14:18
f9494d4

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

v0.40.4 Pre-release
Pre-release

Choose a tag to compare

@docker-read-write docker-read-write released this 09 Apr 20:34
c195234
Merge pull request #477 from docker/fix-oauth-callback-server

IPv6/AirPlay conflict, port 5000, and stale DCR cache

v0.40.3

v0.40.3 Pre-release
Pre-release

Choose a tag to compare

@docker-read-write docker-read-write released this 20 Mar 17:33
c72db92
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

v0.41.0 Pre-release
Pre-release

Choose a tag to compare

@cutecatfann cutecatfann released this 18 Mar 15:42
5a6e192

Add --exclude flag to catalog-next create for community registry server blocklist

v0.40.2

v0.40.2 Pre-release
Pre-release

Choose a tag to compare

@docker-read-write docker-read-write released this 27 Feb 16:55
78b603e
Merge pull request #427 from docker/bugfix-preserve-empty-tools

fix: Preserve empty tools after applying policy

v0.40.1

v0.40.1 Pre-release
Pre-release

Choose a tag to compare

@docker-read-write docker-read-write released this 26 Feb 04:42
705a801
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: ...
```