Skip to content

[Feature]: MCPPROXY_API_KEY_FILE (and ${file:} for api_key) for container and systemd secrets #1706

Closed

Description

@james-crowley

Problem / motivation

Spec 111 (FR-030) plans ${file:/absolute/path} references so that upstream server secrets can come from a file (Docker/Compose secrets, Kubernetes secrets, systemd LoadCredential=). The code does not have it yet (no ${file: handling in internal/secret, only ${env:} and keyring), and the spec does not cover the proxy's own credential:

  • api_key can come from MCPPROXY_API_KEY, but there is no file form. In a container the choices today are an environment variable (visible in docker inspect, /proc/<pid>/environ and crash dumps) or the key saved in mcp_config.json on the data volume.
  • Both Docker Compose secrets and Kubernetes mount a secret as a file (/run/secrets/<name>), and the convention used by most official images is a <NAME>_FILE variable that points at it.

I checked MCPPROXY_API_KEY_FILE and ${file:} in the tree and in the open and closed issues and found neither (#58 asked for credentials not to be stored in plaintext in the config, and was closed).

Proposed solution

Add MCPPROXY_API_KEY_FILE: when set, and MCPPROXY_API_KEY is not, read the key from that file (strip one trailing newline, as FR-030 does), never write it back to the config, never log it. Same rule for ${file:} if it is also accepted inside api_key. If MCPPROXY_API_KEY_FILE is the only thing that is wanted, it can ship before FR-030 does.

I can send a PR for this if it fits Spec 111's plan. I did not want to overlap with whoever implements FR-030.

Alternatives considered

  • Keep using MCPPROXY_API_KEY and set it from a wrapper entrypoint that reads the file. This needs a shell, which the slim image does not have.
  • Let the key be generated and saved to the volume (FR-014). Works for a single operator, but does not fit a secrets manager, rotation, or an agent that needs to be given the key without the config file.

Which edition?

Both (personal and server).

Activity

  1. Dumbris commented on Oct 20, 2026

    @Dumbris
    Member

    Yes to both, and nobody has picked up Spec 111 FR-030 yet, so it is yours if you want it. I would do it as two small PRs: MCPPROXY_API_KEY_FILE first through one shared helper (there are six places that read the env key, including the tray, which re-injects it into the core), then the ${file:} provider with LF/CRLF, size cap and absolute-path tests. Two things to keep in mind: an unreadable file must hard-fail rather than fall back to generating and saving a new key, and ${file:} inside api_key has to resolve on a clone or as a process override so a PATCH never writes the secret back into the config file. If you amend FR-014/FR-030/US7 in the same PR so the spec covers the proxy's own key, even better.

  2. james-crowley commented on Oct 20, 2026

    @james-crowley
    ContributorAuthor

    Two PRs as suggested: #1716 adds MCPPROXY_API_KEY_FILE through one shared helper (the loader, EnsureAPIKey, the CLI client, status, the tray and the core it launches), with a hard fail on an unusable file and the Spec 111 FR-014/FR-030/US7 amendments. #1717 (draft, stacked on #1716) adds the ${file:} provider, including api_key as a process-only override so a PATCH never writes the secret back. Open questions for you are in the two PR descriptions: which of the two variables wins when both are set, and whether ${file:} should get a path allow-list.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions