Skip to main content
ClaudeWave

MCP server for creating and managing SOPS-encrypted secret files using age encryption.

MCP ServersOfficial Registry0 stars0 forksPythonNOASSERTIONUpdated today
ClaudeWave Trust Score
80/100
Trusted
Passed
  • Actively maintained (<30d)
  • Clear description
  • Topics declared
  • Documented (README)
Flags
  • !Licence file present but not machine-readable
Last scanned: 9/11/2026
Install in Claude Code / Claude Desktop
Method: pip / Python · -e
Claude Code CLI
claude mcp add sops-mcp -- python -m -e
claude_desktop_config.json (Claude Desktop)
{
  "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>"
      }
    }
  }
}
1. Run the command above in your terminal (Claude Code), or paste the JSON config into claude_desktop_config.json (Claude Desktop).
2. Replace any <placeholder> values with your API keys or paths.
3. Restart Claude. The MCP server and its tools appear automatically.
💡 Install first: pip install -e
Detected environment variables
SOPS_MCP_AGE_PUBLIC_KEYSOPS_AGE_KEY
Use cases

MCP Servers overview

# 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-separa
age-encryptionencryptionmcpmcp-servermodel-context-protocolsecrets-managementsops

What people ask about sops-mcp

What is privacyplaybook/sops-mcp?

+

privacyplaybook/sops-mcp is mcp servers for the Claude AI ecosystem. MCP server for creating and managing SOPS-encrypted secret files using age encryption. It has 0 GitHub stars and its last recorded update is dated 2026-09-10.

How do I install sops-mcp?

+

You can install sops-mcp by cloning the repository (https://github.com/privacyplaybook/sops-mcp) or following the README instructions on GitHub. ClaudeWave also provides quick install blocks on this page.

Is privacyplaybook/sops-mcp safe to use?

+

Our security agent has analyzed privacyplaybook/sops-mcp and assigned a Trust Score of 80/100 (tier: Trusted). See the full breakdown of passed checks and flags on this page.

Who maintains privacyplaybook/sops-mcp?

+

privacyplaybook/sops-mcp is maintained by privacyplaybook. The last recorded GitHub activity is dated 2026-09-10, with 2 open issues.

Are there alternatives to sops-mcp?

+

Yes. On ClaudeWave you can browse similar mcp servers at /categories/mcp, sorted by popularity or recent activity.

Deploy sops-mcp to your cloud

Ship this repo to production in minutes. Each platform spins up its own environment with editable env vars.

Maintain this repo? Add a badge to your README

Drop the badge into your GitHub README to show it's tracked on ClaudeWave. Each badge links back to this page and reflects the live Trust Score.

Featured on ClaudeWave: privacyplaybook/sops-mcp
[![Featured on ClaudeWave](https://claudewave.com/api/badge/privacyplaybook-sops-mcp)](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>

More MCP Servers

sops-mcp alternatives