Skip to main content
Glama
KhushalB25

encrypted-vault-mcp

by KhushalB25
README.md
# 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.

[![MIT License](https://img.shields.io/badge/license-MIT-yellow.svg)](LICENSE)
[![Node ≥ 18](https://img.shields.io/badge/node-%E2%89%A518-green.svg)](https://nodejs.org)
[![MCP](https://img.shields.io/badge/MCP-Model%20Context%20Protocol-blueviolet)](https://modelcontextprotocol.io)
[![AES-GCM](https://img.shields.io/badge/cipher-AES--256--GCM-red)](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

A4.1/5.0

Scored across 9 tools

Disambiguation5/5

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.

Naming Consistency5/5

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'.

Tool Count5/5

9 tools is ideal for an encrypted vault: covering initialization, authentication, CRUD, listing, status, and PIN rotation. No unnecessary tools and no critical gaps.

Completeness5/5

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.

Maintenance

ActivityInactive
ResponsivenessNo issues