encrypted-vault-mcp
# encrypted-vault-mcp
> MCP server providing a **local-first encrypted key-value vault**. Store API keys, secrets, notes, or any string data under a PIN. AES-GCM 256-bit cipher, PBKDF2 (600k iterations, SHA-256) derived key. Zero cloud. Zero telemetry. Zero network.
[](LICENSE)
[](https://nodejs.org)
[](https://modelcontextprotocol.io)
[](https://en.wikipedia.org/wiki/Galois/Counter_Mode)
## Why
Storing secrets in plain text in chat history is bad. Pasting API keys into prompts is worse. This MCP gives the agent a vault: it can `store` a value once under a name, then later `fetch` it by name without you re-typing the secret.
- AI agent can hold long-lived secrets safely between chats
- You unlock once per session with a PIN
- All data stays on the machine as encrypted bytes
- Wrong PIN = wrong key = nothing decrypts (AES-GCM auth tag fails)
## Install
```bash
npm install -g encrypted-vault-mcp
```
Or `npx`:
```bash
npx encrypted-vault-mcp
```
## Use with Claude Desktop
Add to `claude_desktop_config.json` (Windows: `%APPDATA%\Claude\claude_desktop_config.json`):
```json
{
"mcpServers": {
"vault": {
"command": "npx",
"args": ["-y", "encrypted-vault-mcp"]
}
}
}
```
Restart Claude Desktop. First time:
> "Use vault to init with pin 1234"
Then any time:
> "Unlock vault with pin 1234, then store my-openai-key as sk-..."
>
> "Fetch my-openai-key"
>
> "List vault keys"
## Tools
| Tool | Args | Description |
|---|---|---|
| `init` | `pin` | Create a new vault file with this PIN. Fails if one already exists. |
| `unlock` | `pin` | Derive key from PIN. Required before store/fetch/list/remove. |
| `lock` | — | Clear key from memory. |
| `store` | `key`, `value` | Encrypt + save under name. |
| `fetch` | `key` | Decrypt + return value. |
| `list` | — | List all key names. Values stay encrypted on disk. |
| `remove` | `key` | Delete an item. |
| `change_pin` | `old_pin`, `new_pin` | Rotate PIN. Re-encrypts everything with new key. |
| `status` | — | Show whether vault exists / is unlocked / path / item count. |
## Crypto
- **Cipher:** AES-256-GCM (authenticated encryption — wrong PIN = decryption fails cleanly)
- **Key derivation:** PBKDF2-HMAC-SHA256, **600,000 iterations** (OWASP 2023), 16-byte salt
- **IV:** 96-bit random per encryption (NIST SP 800-38D)
- **PIN verification:** separate PBKDF2 hash with its own salt; the PIN itself is never persisted
- **File permissions:** vault file is written with mode `0o600` (owner read/write only)
## Storage location
Default: `~/.encrypted-vault-mcp/vault.json`
Override:
```json
{
"mcpServers": {
"vault": {
"command": "npx",
"args": ["-y", "encrypted-vault-mcp"],
"env": { "VAULT_PATH": "/secure/drive/my-vault.json" }
}
}
}
```
## Threat model
| Threat | Protected? |
|---|---|
| Disk read by attacker without PIN | ✅ items unrecoverable without PIN |
| Wrong PIN | ✅ AES-GCM auth fails, no data leaked |
| PIN brute-force | ⚠️ 600k PBKDF2 iterations slow it; use a real password for high-value secrets |
| Process memory dump while unlocked | ❌ key sits in memory between unlock and lock — lock when done |
| Malicious MCP client | ❌ if the agent itself is hostile, it can call fetch on whatever it wants — only run trusted agents |
## Local development
```bash
git clone https://github.com/KhushalB25/encrypted-vault-mcp.git
cd encrypted-vault-mcp
npm install
npm run build
npm start
```
Inspect:
```bash
npx @modelcontextprotocol/inspector node dist/index.js
```
## Author
[Khushal Bhandari](https://www.khushalbhandari.everyai.in) · [GitHub](https://github.com/KhushalB25)
## License
MIT
TDQS
Scored across 9 tools
Each tool addresses a unique action: init, unlock/lock for session management, store/fetch/remove for CRUD, list for key enumeration, status for vault info, and change_pin for rotation. No two tools overlap in purpose.
All tool names use consistent lowercase snake_case with a verb_noun pattern (e.g., change_pin, list, store). No mixing of conventions or vague verbs like 'process' or 'do'.
9 tools is ideal for an encrypted vault: covering initialization, authentication, CRUD, listing, status, and PIN rotation. No unnecessary tools and no critical gaps.
The tool surface covers the complete vault lifecycle: init (create), unlock/lock (session), store/fetch/remove (data management), list (inventory), status (health), and change_pin (maintenance). Missing a 'delete vault' tool but that's a rare operation.