envcloak
A tiny MCP server that lets a Claude Code agent read and edit .env files
without ever seeing real secret values — while keeping the file informative
enough to actually debug with.
- Zero dependencies. Pure Python 3.8+ standard library. No pip, no npm.
- Self-contained. Everything lives in this one folder.
- Installable as a Claude Code plugin. One command registers the MCP server
and the
.env-blocking hook — see Install.
- Tested.
python3 -m unittest (54 tests).
How it works
When the agent reads a file it gets a blurred, length-preserving view:
NODE_ENV=production
API_KEY=xx_xxxx_0xX00XxXxxXXxxxxX0xxx0xx
DATABASE_URL=xxxxxxxx://xxxxx:x0xx0x@xx:0000/xxx
PORT=8080
The masking rules:
| Input | Output | Why |
|---|
| Latin letter | x / X | hides content, keeps case & length |
| digit | 0 | hides content, keeps length |
| punctuation/space | unchanged | keeps structure (://, @, ., -, quotes) |
| non-ASCII | x | so unicode can't leak |
So a secret keeps its shape (a JWT still looks like xxx.xxx.xxx, a URL
keeps its scheme/host/port skeleton) but not its value. Crucially the blurred
text is the exact same length as the real file, character for character —
that is what makes char-range edits safe.
Non-secret config is shown in clear via an allow-list, because that is what
you need when debugging: NODE_ENV, *_ENV, LOG_LEVEL, DEBUG, PORT,
*_PORT, TZ, LANG, plus any boolean value (true/false/yes/no/on/off) and
empty values. Edit the list in config.json.
Safety properties
- No round-trip corruption. The agent never needs to know a secret to edit
one.
env_set_value refuses to write a value that looks like a mask (only
x/X/0 + punctuation), so a masked value can't be echoed back over a real
one. Renames touch only the key.
- Structural-only range edits.
env_replace_range refuses to overlap any
masked value (use env_set_value/env_rename_key for those).
- Path fence. Only env-looking files (
*.env, .env, .env.*) are
accepted. Extend via allowed_path_globs in config.json.
- Atomic writes preserving the original file mode.
- File-level destructive ops require
confirm: true (env_delete_file,
overwriting via env_create_file).
To make this airtight, the agent must not be able to read env files any other
way (Read tool, cat, grep, …). The plugin does this for
you — see Install:
- A
PreToolUse hook (block-env-files.py) — the
enforcer. It blocks raw .env access (Read/Edit/Write/Grep/Glob/Bash,
including cat/grep/wildcards/exfiltration) across every project in every
permission mode.
- An allow rule for the envcloak MCP tools baked into
~/.claude/settings.json
at SessionStart, so the secure env_read path never prompts.
Why no permissions.deny rules? Earlier versions (≤1.0.2) also baked
Read(.env.*) deny rules. That breaks Claude Code's auto mode: its safety
classifier sees env_read reading a .env while a deny rule exists and rejects
it as "circumventing the deny rule by switching tools." An allow rule can't
override the classifier. So as of 1.1.0 the plugin injects no .env deny
rules and, at SessionStart, retracts any it wrote before. Raw-read blocking
is the hook's job; the deny rules were redundant with it anyway.
Tools
| Tool | Purpose |
|---|
env_read | Blurred view + per-key summary (masked vs clear) |
env_set_value | Set an existing key's value (round-trip guarded) |
env_rename_key | Rename a var; value preserved untouched |
env_add_key | Add a new key |
env_delete_key | Delete a key |
env_replace_range | Advanced structural splice by blurred-view offsets |
env_create_file | Create a new env file (confirm to overwrite) |
env_delete_file | Delete a file (confirm required) |
Install as a Claude Code plugin (recommended)
envcloak ships as a self-contained plugin: installing it registers the
envcloak MCP server and wires the .env-blocking hook for you — no
manual settings.json editing. The repo is its own marketplace, so: