MCP server for creating and managing SOPS-encrypted secret files using age encryption.
- ✓Actively maintained (<30d)
- ✓Clear description
- ✓Topics declared
- ✓Documented (README)
- !Licence file present but not machine-readable
claude mcp add sops-mcp -- python -m -e{
"mcpServers": {
"sops-mcp": {
"command": "python",
"args": ["-m", "-e"],
"env": {
"SOPS_MCP_AGE_PUBLIC_KEY": "<sops_mcp_age_public_key>",
"SOPS_AGE_KEY": "<sops_age_key>"
}
}
}
}SOPS_MCP_AGE_PUBLIC_KEYSOPS_AGE_KEYResumen de MCP Servers
# sops-mcp
<!-- mcp-name: io.github.privacyplaybook/sops-mcp -->
MCP server for creating and managing [SOPS](https://github.com/getsops/sops)-encrypted secret files using [age](https://github.com/FiloSottile/age) encryption.
Designed for Claude Code (or any MCP client) to produce encrypted `secrets.enc.yaml` files without the model ever seeing plaintext values. All file content is passed as text parameters and returned as text — the server has no filesystem access to the client.
## Why
Two goals drive the design:
**1. Keep secrets in your source tree without leaking them.** For a small project, running a full secrets manager (Vault, AWS Secrets Manager, etc.) is overkill for a handful of credentials. Encrypting secrets at rest in git and decrypting them in your CI/CD pipeline at deploy time is much cheaper:
1. Create `secrets.enc.yaml` via this server — age-encrypted against your public key, safe to commit.
2. Commit it alongside your code.
3. Your CI/CD pipeline holds the age private key, decrypts at deploy time, and injects plaintext as environment variables into your container orchestrator.
The age private key lives in exactly one place: your CI/CD secrets store. Everywhere else — your laptop, your git remote, your container images — sees only ciphertext. See the [worked example](#example-integrating-with-a-cicd-deployment-pipeline) below.
**2. Let an AI coding agent generate secrets it can never read.** Claude (or any MCP client) can create passwords, rotate them, derive hashes, rename and delete them — but plaintext values never cross the MCP boundary back to the model.
The server holds the encryption key; the client only submits requests and receives metadata.
There is deliberately no "decrypt this one secret" tool.
This prevents a prompt injection or a misbehaving agent from exfiltrating a secret.
The simplest setup uses a single age recipient (the one CI private key). If you need several — a CI key plus an operator's key, or separate key sets for separate parties — see [Key domains](#key-domains).
## Secret sources
Every secret is one of three sources, recorded in `_meta_unencrypted`:
- **`generated`** — Cryptographically random values (Python `secrets` / OS CSPRNG). You specify length and charset; the server stores both so it can regenerate on rotation.
- **`external`** — User-provided values encrypted as-is (SMTP credentials, third-party API keys, etc.). Preserved across rotation. Updated via `sops_update_external`.
- **`derived`** — Computed from another key in the same file via a named transform. When the source is rotated (or an external source is updated), the derived value is automatically recomputed in topological order. Useful for things like Authelia's PBKDF2 hashes of OIDC client secrets.
## Design
Three ideas shape the tool surface:
1. **No plaintext crosses the MCP boundary.** Generated secret values are never returned to the client. There is deliberately no "decrypt this one key" tool. If you need plaintext, run `sops decrypt` yourself with the age private key.
2. **Metadata in plaintext.** A `_meta_unencrypted` block sits alongside the encrypted values (using SOPS's `unencrypted_suffix` feature) and records each secret's source, how it was generated, when it was last rotated, and which [key domain](#key-domains) it belongs to. This lets the server list and rotate secrets without decrypting. SOPS's MAC covers these values by default, so a tampered block fails to decrypt. Files that switch that off with `mac_only_encrypted` are refused. Tools that read the block *without* a key still cannot check the MAC, which is why recipients are verified separately.
3. **No in-place value update for `generated` or `derived` secrets.** Those change only via rotation, where neither the server nor the caller has access to the plaintext. `External` secrets (e.g. an upstream API key the user controls) can be updated with `sops_update_external`.
### Transforms (for `derived` secrets)
| Transform | Purpose | Deterministic |
|-----------|---------|---------------|
| `pbkdf2_sha512_authelia` | PBKDF2-SHA512 hash in Authelia's `configuration.yml` format (`$pbkdf2-sha512$310000$...`) | No — random salt per call |
| `sha256_hex` | Hex-encoded SHA-256 digest | Yes |
## Tools
### Creation and listing
| Tool | What it does |
|------|--------------|
| `sops_create_secrets` | Create a new encrypted file with one or more secrets (any mix of sources). |
| `sops_list_secrets` | List keys, sources, and descriptions from a file **without decrypting**. |
| `sops_create_oidc_secret` | Convenience: create an Authelia OIDC client secret as a `generated` + `derived` (`pbkdf2_sha512_authelia`) pair in one call. The hash is returned in the response for pasting into `configuration.yml`. |
| `sops_list_domains` | List the configured key domains, their age recipients, and whether each can decrypt or only encrypt. Never returns private key material. |
### Mutation (require a private key for the target domain)
| Tool | What it does |
|------|--------------|
| `sops_rotate_generated` | Regenerate all `generated` secrets. Derived secrets whose source was rotated are recomputed; others are preserved. External secrets are preserved. |
| `sops_add_secrets` | Add new secrets to an existing file. Supports all three sources. Rejects collisions with existing keys. |
| `sops_update_external` | Replace the value of an `external` secret. Cascades to any derived secrets that reference it. Rejects attempts to update `generated` or `derived`. |
| `sops_rename_secret` | Rename a key, preserving its value and metadata. Updates `from:` references in any derived secrets. |
| `sops_delete_secrets` | Remove one or more keys. Rejects deleting a secret that another derived secret still references (unless the dependent is deleted in the same call). |
| `sops_add_metadata` | Retrofit `_meta_unencrypted` onto a legacy SOPS file that lacks it. Supports `generated`, `external`, and `derived` entries. |
| `sops_rekey` | Re-encrypt a file onto its domain's current recipient list. Run this after a domain's recipients change, or to clear a "recipients do not match" refusal. Requires an explicit `domain`. Leaves a legacy file without a metadata block untouched in that respect, so `sops_add_metadata` still works on it. |
Every tool takes a `domain` argument except `sops_list_domains`, which needs none. It is optional everywhere but `sops_rekey`, where it is required because the call changes who can read the file. See [Key domains](#key-domains).
## Setup
### Prerequisites
- Python 3.11+
- [sops](https://github.com/getsops/sops) CLI binary
- An age keypair (see below)
### Generating an age keypair
If you don't already have one, install [age](https://github.com/FiloSottile/age) and run:
```bash
age-keygen -o age-key.txt
```
The file looks like:
```
# created: 2026-04-22T12:34:56Z
# public key: age1abc...xyz
AGE-SECRET-KEY-1HH...
```
- **Public key** (`age1...`) — pass to this server as `SOPS_MCP_AGE_PUBLIC_KEY`. Safe to share anywhere.
- **Private key** (`AGE-SECRET-KEY-...`) — store as a CI/CD secret (commonly named `SOPS_AGE_KEY`). Never commit to source control. Anyone with this key can decrypt every `secrets.enc.yaml` encrypted to the matching public key.
Back up the private key somewhere safe (password manager, hardware token). Losing it means losing access to every secret you've encrypted.
### Installation
```bash
git clone <repo-url>
cd sops-mcp
python3 -m venv .venv
.venv/bin/pip install -e .
```
### Claude Code configuration
Add to your project's `.mcp.json`:
```json
{
"mcpServers": {
"sops-mcp": {
"command": "/path/to/sops-mcp/.venv/bin/python",
"args": ["-m", "sops_mcp"],
"env": {
"SOPS_MCP_SOPS_BINARY": "/path/to/sops",
"SOPS_MCP_AGE_PUBLIC_KEY": "<your-age-public-key>"
}
}
}
}
```
### Environment variables
| Variable | Required | Purpose |
|----------|----------|---------|
| `SOPS_MCP_AGE_PUBLIC_KEY` | Yes* | Age public key for encryption |
| `SOPS_AGE_RECIPIENTS` | Yes* | Alternative to `SOPS_MCP_AGE_PUBLIC_KEY` |
| `SOPS_MCP_SOPS_BINARY` | No | Path to sops binary (default: `sops`) |
| `SOPS_MCP_LOG_LEVEL` | No | Log level (default: `WARNING`) |
| `SOPS_AGE_KEY` | Sometimes | Age private key for the `default` domain — required to mutate a file belonging to it. Named domains take their keys from the domains file instead. |
| `SOPS_MCP_DOMAINS_FILE` | No | Path to a YAML file defining named [key domains](#key-domains). Use when one server needs more than one recipient set. |
| `SOPS_MCP_DOMAINS` | No | The same domains document [inline](#defining-domains-without-a-file), as YAML or compact JSON. Public recipient sets only — a domain here may not carry `keys` or `key_file`. Merged with `SOPS_MCP_DOMAINS_FILE`. |
| `SOPS_MCP_REQUIRE_DOMAIN` | No | Set to `1` to require every tool call to name its domain. The file's recorded domain and the `default` fallback are both disabled. |
| `SOPS_MCP_TRANSPORT` | No | `stdio` (default) or `sse` |
| `SOPS_MCP_HOST` / `SOPS_MCP_PORT` | No | Bind host/port for SSE transport (default: `127.0.0.1:55090`). Binding to `0.0.0.0` requires `SOPS_MCP_API_TOKEN` — the server refuses to start otherwise. |
| `SOPS_MCP_ALLOWED_HOSTS` | No | Comma-separated allowlist for the SSE `Host` header (DNS rebinding protection). Default: `127.0.0.1,127.0.0.1:*,localhost,localhost:*`. Set explicitly when binding to a non-loopback address — e.g. `mcp.example.com,mcp.example.com:*`. |
| `SOPS_MCP_API_TOKEN` | Sometimes | Required when SSE transport binds to `0.0.0.0`; otherwise optional. When set, SSE requires `Authorization: Bearer <token>`. |
\* One of `SOPS_MCP_AGE_PUBLIC_KEY` or `SOPS_AGE_RECIPIENTS` must be set, unless `SOPS_MCP_DOMAINS_FILE` or `SOPS_MCP_DOMAINS` supplies at least one domain. The server refuses to start with no domains at all.
Both recipient variables accept a comma-separated list, and `SOPS_AGE_KEY` accepts several newline-separaLo que la gente pregunta sobre sops-mcp
¿Qué es privacyplaybook/sops-mcp?
+
privacyplaybook/sops-mcp es mcp servers para el ecosistema de Claude AI. MCP server for creating and managing SOPS-encrypted secret files using age encryption. Tiene 0 estrellas en GitHub y su última actualización registrada es del 2026-09-10.
¿Cómo se instala sops-mcp?
+
Puedes instalar sops-mcp clonando el repositorio (https://github.com/privacyplaybook/sops-mcp) o siguiendo las instrucciones del README en GitHub. ClaudeWave también te ofrece bloques de instalación rápida en esta misma página.
¿Es seguro usar privacyplaybook/sops-mcp?
+
Nuestro agente de seguridad ha analizado privacyplaybook/sops-mcp y le ha asignado un Trust Score de 80/100 (tier: Trusted). Revisa el desglose completo de comprobaciones superadas y flags en esta página.
¿Quién mantiene privacyplaybook/sops-mcp?
+
privacyplaybook/sops-mcp es mantenido por privacyplaybook. La última actividad registrada en GitHub es del 2026-09-10, con 2 issues abiertos.
¿Hay alternativas a sops-mcp?
+
Sí. En ClaudeWave puedes explorar mcp servers similares en /categories/mcp, ordenados por popularidad o actividad reciente.
Despliega sops-mcp en tu cloud
Lleva este repo a producción en minutos. Cada plataforma genera su propio entorno con variables de entorno editables.
¿Mantienes este repo? Añade un badge a tu README
Pega el badge en tu README de GitHub para mostrar que está auditado por ClaudeWave. Cada badge enlaza de vuelta a esta página y muestra el Trust Score actual.
[](https://claudewave.com/repo/privacyplaybook-sops-mcp)<a href="https://claudewave.com/repo/privacyplaybook-sops-mcp"><img src="https://claudewave.com/api/badge/privacyplaybook-sops-mcp" alt="Featured on ClaudeWave: privacyplaybook/sops-mcp" width="320" height="64" /></a>Más MCP Servers
Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.
User-friendly AI Interface (Supports Ollama, OpenAI API, ...)
An open-source AI agent that brings the power of Gemini directly into your terminal.
Real-time global intelligence dashboard. AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking in a unified situational awareness interface
The fastest path to AI-powered full stack observability, even for lean teams.
🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl!