Skip to main content

secrets-mcp-server

An MCP server for storing secrets (API keys, tokens, credentials) encrypted at rest on your own machine, built so an AI agent can manage them without the plaintext value ever entering its context. There is no get_secret tool that returns a value as text — that was a deliberate design decision, not an oversight. Using a secret is always indirect: run a command with the value injected into its environment, or write the value straight into a destination file.

Why

Giving an agent free-form shell access to your .env files means every secret it touches can end up echoed back into its own transcript, its logs, or a chat history — anywhere from a debugging session to a support ticket. This server keeps secrets in one encrypted store and only exposes them through two narrow, auditable operations: inject-into-subprocess and write-to-file. The agent can use a secret to authenticate a request or populate a config file; it can never see, print, or leak the raw value through a normal tool call.

Tools

Category Tool Description
CRUD create_secret(name, value, description=None) Creates a secret; fails if name already exists
CRUD create_secrets_batch(items) Creates several at once (items: [{name, value, description?}]). Does not abort on the first error — each item reports its own status (created/skipped_exists/error)
CRUD import_secrets_from_file(source_path, name_pattern=None, prefix=None, overwrite=False) Reads a .env-style file (KEY=VALUE per line, export KEY=VALUE and # comments supported) and imports each key as a secret. name_pattern (regex) filters which keys get imported; prefix is prepended to the name; overwrite controls whether existing secrets get updated
CRUD update_secret(name, value) Overwrites the value; fails if the secret doesn't exist
CRUD delete_secret(name) Removes a secret
CRUD list_secrets() Lists name/description/timestamps for every secret. Never includes the value
Opaque use run_with_secret(secret_name, command, env_var_name=None, cwd=None, timeout=None) Runs command (a list of args, no shell) with the value injected as an environment variable (env_var_name, or the secret name upper-cased by default) only inside that subprocess. Returns exit_code/stdout/stderr, with the value redacted if the command happens to print it
Opaque use apply_secrets_to_file(names, dest_path, key_names=None, format="env") Writes one or more secrets straight into dest_path (env-style .env, or json), never returning any value as text. names is always an explicit list — there is no "export everything" option

run_with_secret never uses a shell (shell=False, command is a list of args) — this avoids command injection even if an argument comes from untrusted text.

apply_secrets_to_file only writes inside directories listed in SECRETS_MCP_ALLOWED_WRITE_DIRS — without that configured, every write is rejected.

import_secrets_from_file can read any file the process has OS permission to read — this is a deliberate choice, with no read-side allowlist (unlike writes). If you need to restrict that, add a SECRETS_MCP_ALLOWED_READ_DIRS check following the same pattern as validate_dest_path in security.py.

Environment variables

Variable Default Purpose
SECRETS_MCP_MASTER_PASSPHRASE (required) Passphrase used to derive the store's encryption key. Without it, the server refuses every operation
SECRETS_MCP_STORE_PATH ~/.secrets-mcp/store.enc Where the encrypted store file lives
SECRETS_MCP_ALLOWED_WRITE_DIRS (empty) Comma-separated list of directories apply_secrets_to_file may write into. Empty means no writes are allowed
SECRETS_MCP_COMMAND_TIMEOUT_SECONDS 30 Timeout for run_with_secret (1-300)

Security model

  • The store is a single file (salt + ciphertext) encrypted as a whole with Fernet (cryptography); the key is derived from the passphrase via Scrypt with a fresh random salt on every write. A wrong passphrase or a corrupted file fails loudly — there is no silent fallback.
  • The store file and any file written by apply_secrets_to_file end up with 0600 permissions.
  • run_with_secret never uses shell=True; command is always a list of args, never a shell string.
  • apply_secrets_to_file resolves the destination path (realpath, following symlinks) and rejects anything outside SECRETS_MCP_ALLOWED_WRITE_DIRS.
  • No tool logs or returns a raw value. run_with_secret does a best-effort redaction (security.redact) that strips literal occurrences of the value from stdout/stderr in case the command echoes it by accident — this is not a guarantee against every leak (e.g. a command that writes the value to a file outside this tool's control), but it covers the common case.

Requirements

  • Python 3.11+
  • uv to install dependencies and run the server

Installation

From PyPI, no clone needed:

uvx secrets-mcp-server

or install it as a persistent CLI tool:

uv tool install secrets-mcp-server
# or: pipx install secrets-mcp-server

From source, for local development:

git clone https://github.com/KauaLealz/secrets-mcp-server.git
cd secrets-mcp-server
uv sync

Registering with Claude Code

Using the published package (no clone required):

claude mcp add --scope user secrets \
  --env SECRETS_MCP_MASTER_PASSPHRASE=<your-passphrase> \
  --env SECRETS_MCP_ALLOWED_WRITE_DIRS=/path/to/your/projects \
  -- uvx secrets-mcp-server

Using a local clone instead:

claude mcp add --scope user secrets \
  --env SECRETS_MCP_MASTER_PASSPHRASE=<your-passphrase> \
  --env SECRETS_MCP_ALLOWED_WRITE_DIRS=/path/to/your/projects \
  -- uv run --directory /path/to/secrets-mcp-server secrets-mcp-server

Replace /path/to/your/projects with whichever directories apply_secrets_to_file should be allowed to write into (comma-separated for more than one), and /path/to/secrets-mcp-server with wherever you cloned the repo, if using the local-clone form.

--scope user makes it available in every Claude Code session. Changing the passphrase between registrations produces a different store (the encryption key depends on it) — keep the same passphrase to keep accessing an existing store, and store it somewhere safe (a password manager). There is no recovery if you lose it.

A .env.example is included as a reference for every variable below — it is not auto-loaded, it just documents the shape a .env for this project would take if you build tooling around it.

Registering with other MCP clients

Any MCP client that supports stdio servers can run this the same way: launch uvx secrets-mcp-server (or uv run --directory /path/to/secrets-mcp-server secrets-mcp-server for a local clone) with the environment variables above set in its process environment. Check your client's documentation for how it declares stdio MCP servers (e.g. a mcpServers entry in its config file).

Tests

uv run pytest

Contributing

Issues and pull requests are welcome. See AGENTS.md for the design constraint this project is built around (no tool ever returns a raw secret value) — please keep new tools consistent with it, or open an issue to discuss before changing it.

License

MIT — see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

secrets_mcp_server-0.1.0.tar.gz (56.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

secrets_mcp_server-0.1.0-py3-none-any.whl (11.2 kB view details)

Uploaded Python 3

File details

Details for the file secrets_mcp_server-0.1.0.tar.gz.

File metadata

  • Download URL: secrets_mcp_server-0.1.0.tar.gz
  • Upload date:
  • Size: 56.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for secrets_mcp_server-0.1.0.tar.gz
Algorithm Hash digest
SHA256 aa95c99444d2f598419c212b66aae30fff5d43308ce3924051b2d6b27a772545
MD5 b6e45f30ceeabe32ccbb272eff604b8c
BLAKE2b-256 0094f28b287d1490a8de3fbcf842ea2545b2758a1ecedbb2526ff9ff48e90e72

See more details on using hashes here.

File details

Details for the file secrets_mcp_server-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: secrets_mcp_server-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 11.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for secrets_mcp_server-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 baa4a4dcef3ece17974eaca14d27bd81203ab30602bb3146468b2eecad8b51c2
MD5 3f0e0472df9916e6457dd4c195022cbe
BLAKE2b-256 286d8bd689fcba073cd0420a726decf669ef9b3b4769ad0a7c627f736b3a539a

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page