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_fileend up with0600permissions. run_with_secretnever usesshell=True;commandis always a list of args, never a shell string.apply_secrets_to_fileresolves the destination path (realpath, following symlinks) and rejects anything outsideSECRETS_MCP_ALLOWED_WRITE_DIRS.- No tool logs or returns a raw value.
run_with_secretdoes a best-effort redaction (security.redact) that strips literal occurrences of the value fromstdout/stderrin 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+
uvto 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aa95c99444d2f598419c212b66aae30fff5d43308ce3924051b2d6b27a772545
|
|
| MD5 |
b6e45f30ceeabe32ccbb272eff604b8c
|
|
| BLAKE2b-256 |
0094f28b287d1490a8de3fbcf842ea2545b2758a1ecedbb2526ff9ff48e90e72
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
baa4a4dcef3ece17974eaca14d27bd81203ab30602bb3146468b2eecad8b51c2
|
|
| MD5 |
3f0e0472df9916e6457dd4c195022cbe
|
|
| BLAKE2b-256 |
286d8bd689fcba073cd0420a726decf669ef9b3b4769ad0a7c627f736b3a539a
|