docker / docker/mcp-gateway

mcp-add ignores the --secrets .env file for remote servers when the secrets engine is unreachable

Open
#554 0 comments 0 reactions 0 assignees View on GitHub

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-tools enabled.
  • Secret supplied via a .env file passed with --secrets.
Steps to reproduce
  1. Enable dynamic tools: docker mcp feature enable dynamic-tools.
  2. 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}"
    
  3. printf 'MY_TOKEN=real-token\n' > /secrets/my.env
  4. Run the gateway (no Docker Desktop):
    docker mcp gateway run --catalog my.yaml --static --secrets=/secrets/my.env --port 8080 --transport streaming
  5. From a client, call mcp-add with {"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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.