mcp-add ignores the --secrets .env file for remote servers when the secrets engine is unreachable
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 1.6k
- Forks
- 278
- Avg merge
- 7h 6m
- Merged PRs (30d)
- 3
Description
What happened
With dynamic-tools enabled, adding a remote server via mcp-add fails to authenticate when its Bearer token is provided through a --secrets .env file.
The server connects but returns Unauthorized / 0 tools.
Verbose logs show the token resolving to an empty value:
- Fetching secret: MY_TOKEN
- Got secret for: MY_TOKEN (len=0)
> Can't start my-remote: failed to connect: calling "initialize": sending "initialize": Unauthorized
Environment
- Gateway run outside Docker Desktop (Kubernetes / plain Docker) - the Docker
Desktop secrets engine is unreachable. - Transport
streaming,--static,dynamic-toolsenabled. - Secret supplied via a
.envfile passed with--secrets.
Steps to reproduce
- Enable dynamic tools:
docker mcp feature enable dynamic-tools. - Custom catalog with a remote server that authenticates via a header, e.g.:
registry: my-remote: type: remote secrets: - name: MY_TOKEN env: MY_TOKEN remote: url: https://my-remote.example.com/mcp transport_type: streamable headers: Authorization: "Bearer ${MY_TOKEN}" printf 'MY_TOKEN=real-token\n' > /secrets/my.env- Run the gateway (no Docker Desktop):
docker mcp gateway run --catalog my.yaml --static --secrets=/secrets/my.env --port 8080 --transport streaming - From a client, call
mcp-addwith{"name":"my-remote"}.
Expected
mcp-add resolves MY_TOKEN from the --secrets file (as startup-enabled servers already do via Find()), expands the Authorization header, and the server initializes successfully.
Actual
mcp-add resolves secrets only via BuildSecretsURIs (the Docker Desktop secrets engine). When the engine is unreachable, buildFallbackURIs emits an se:// reference for every declared secret and AddSecrets overwrites the concrete value that was loaded at startup from --secrets. remote.go then skips se:// values and falls back to the engine → empty token → Unauthorized.
Root cause
pkg/gateway/mcpadd.go builds availableSecrets from BuildSecretsURIs and merges them with AddSecrets, downgrading a concrete file-provided value to an se:// reference. The --secrets file is only honored for servers enabled at startup, and startup-enable (--servers / --enable-all-servers) disables dynamic-tools - so a secret-authenticated remote can't be both dynamically
discovered and file-authenticated.
Impact
Remote MCP servers that require per-request header auth (e.g. Bearer) cannot be used via mcp-add in any environment without the Docker Desktop secrets engine (Kubernetes, CI, plain Docker).
I have a fix and will open a PR referencing this issue.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in pkg/gateway/mcpadd.go, tracing availableSecrets through BuildSecretsURIs and AddSecrets, then compare the startup --secrets handling described via Find(). Read pkg/gateway/remote.go to follow how se:// values are handled; done means a remote server added with mcp-add retains the .env token and initializes successfully when the secrets engine is unreachable.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- authentication, backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100