Skip to content

Document secret storage for every runtime, and add MCP_INSPECTOR_SECRET_KEY_FILE #2447

Description

@cliffhall

Problem

How the Inspector chooses a secret store is only explained in the Docker guide (docs/docker.md, "Where secrets go, and how to make them survive (#1950)"). That covers the keychain probe, the memory/file fallback, the secrets.json location, encryption with MCP_INSPECTOR_SECRET_KEY, the 0600 mode and cross-process locking.

The automatic fallback isn't specific to containers, though. In core/auth/node/secret-store-selection.ts, any host where the keychain probe fails falls back to the file store. That includes a Linux box without libsecret or a Secret Service, a headless server or SSH session with no D-Bus, and Android/Termux (#1905). None of those users would think to open a Docker guide.

What non-container users see today:

  • docs/environment-variables.md → "Secret store": a reference table of the three variables and one paragraph on container detection, which points back to the Docker guide for the details.
  • The startup banner and the settings-dialog footer, which tell them a fallback happened but not what it means or how to change it.

Proposal

Move the store-selection material out of docs/docker.md into its own user-facing guide, e.g. docs/secret-storage.md, written for every runtime:

  • What is stored as a secret (OAuth client secret, enterprise IdP client secret, stdio env: values) and why it is kept out of mcp.json (Move oauthClientSecret out of mcp.json into the OS keychain #1356).
  • The selection order: MCP_INSPECTOR_SECRET_STORE explicit override → keychain probe → fallback (memory in an unmounted container, file otherwise), with the same "which store do I get" table extended with host rows.
  • The file store: path lookup (MCP_INSPECTOR_SECRET_FILEMCP_STORAGE_DIR~/.mcp-inspector), plaintext vs MCP_INSPECTOR_SECRET_KEY encryption, what happens when the passphrase changes or is lost, 0600 re-tightening, locking.
  • Getting a keychain back: installing libsecret moves secrets.json into the keychain on the next start (absorbFileSecretsIntoKeyring), with the keychain winning on conflict and the file deleted only if every value was copied.
  • Where the active store is reported: startup banner, GET /api/config, the Client Settings / Server Settings footers.

Then:

  • docs/docker.md keeps only what is container-specific: mount the volume to get a durable store, the upgrade chown, and passing -e MCP_INSPECTOR_SECRET_KEY. It links to the new guide for everything else.
  • docs/environment-variables.md "Secret store" links to the new guide instead of the Docker guide.
  • Add the new guide to the Documentation table in the root README.md.
  • Consider linking to it from the startup fallback banner (warnAboutSecretStorage) so the message tells users where to read more.

Notes

Documentation only, no behavior change. Follow-up to #1950 (PR #2076), #2082 and #2365.

Rescoped (2026-09-22): add MCP_INSPECTOR_SECRET_KEY_FILE

This is no longer documentation-only. The only way to supply the file store's passphrase was MCP_INSPECTOR_SECRET_KEY. In a container that value is readable through docker inspect, docker exec and the process environment, and Docker and Compose secrets, which deliver a file, could not supply it at all. So this issue also covers:

  • MCP_INSPECTOR_SECRET_KEY_FILE: reads the passphrase from a file, with trailing line breaks stripped.
  • Both variables set is an error.
  • Missing, unreadable or empty key file: the store refuses to read or write rather than falling back to plaintext, and reports it through the existing "File (unreadable)" warning and settings footer.
  • Docs: a loud Docker warning that mounting the volume turns on plaintext file storage unless a key is supplied, plus a written threat model for the file store.

The docs-site follow-up is #2450.

Activity

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

Metadata

Metadata

Assignees

Labels

enhancementNew feature requestv2Issues and PRs for v2

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions