Chill-AI-Space

Vault MCP

Community Chill-AI-Space
Updated

MCP server for credential isolation in LLM agents — bots use passwords without seeing them

Vault MCP

MCP server for credential isolation in LLM agents. Your bot uses passwords and API keys — but never sees them in its context window.

The Problem

  User                     LLM                    Website
   │                        │                        │
   │  "password: MyP@ss!"   │                        │
   ├───────────────────────►│                        │
   │                        │──── MyP@ss! ──────────►│
   │                        │                        │
   │                        │◄──── 200 OK ───────────│
   │                        │                        │
   ▼                        ▼                        ▼
                     ┌──────────────┐
                     │ MyP@ss! is   │
                     │ now stored   │
                     │ in LLM       │
                     │ context,     │
                     │ conversation │
                     │ history,     │
                     │ session logs │
                     └──────────────┘

The Solution

  User          Browser Form       Vault MCP         LLM          Website
   │            (localhost)          │                 │              │
   │  ●●●●●●●●    │                 │                 │              │
   ├──────────────►│                 │                 │              │
   │               │── encrypt ─────►│                 │              │
   │               │                 │◄── vault_login ─┤              │
   │               │                 │── fill form ───────────────────►│
   │               │                 │◄──── 200 OK ───────────────────┤
   │               │                 │── { status: ok }─►│            │
   │               │                 │                 │              │
   ▼               ▼                 ▼                 ▼              ▼
                              ┌──────────────┐  ┌──────────────┐
                              │ Password:    │  │ LLM context: │
                              │ AES-256-GCM  │  │              │
                              │ encrypted    │  │ "status: ok" │
                              │ on disk      │  │ (no password)│
                              └──────────────┘  └──────────────┘

Scenarios

Scenario 1: First-time Login (vault_add → vault_login)

The agent needs credentials it doesn't have. It calls vault_add() — a browser form opens where you enter the password. Then vault_login() fills the form via Chrome.

  User              Claude Code           Vault MCP            Chrome
   │                    │                     │                   │
   │ "Log me into       │                     │                   │
   │  Jira"             │                     │                   │
   ├───────────────────►│                     │                   │
   │                    │── vault_list() ────►│                   │
   │                    │◄─ { credentials:[] }┤                   │
   │                    │                     │                   │
   │                    │── vault_add ───────►│                   │
   │                    │   { site: "jira" }  │                   │
   │                    │                     │                   │
   │   ┌────────────────────────────┐         │                   │
   │   │ Browser opens             │         │                   │
   │   │ localhost:9900/add         │         │                   │
   │   │                           │         │                   │
   │   │ Site ID:  [jira]          │         │                   │
   │   │ Email:    [[email protected]]   │         │                   │
   │   │ Password: [●●●●●●●●●●]   │         │                   │
   │   │ URL:      [jira.com/login]│         │                   │
   │   │                           │         │                   │
   │   │ [Add to Vault]            │         │                   │
   │   └────────────┬───────────────┘         │                   │
   │                │                         │                   │
   │                └── POST (encrypted) ────►│                   │
   │                                          │                   │
   │                    │◄─ { status: ok } ───┤                   │
   │                    │   site_id: "jira"   │                   │
   │                    │                     │                   │
   │                    │   (no password      │                   │
   │                    │    in this response) │                   │
   │                    │                     │                   │
   │                    │── vault_login ─────►│                   │
   │                    │   { site: "jira" }  │── decrypt ──┐     │
   │                    │                     │◄────────────┘     │
   │                    │                     │── fill email ────►│
   │                    │                     │── fill pass  ────►│
   │                    │                     │── click submit ──►│
   │                    │                     │◄─ page loaded ───┤
   │                    │                     │── clear pass ────►│
   │                    │                     │                   │
   │                    │◄─ { status: ok,  ───┤                   │
   │                    │    title: "Jira     │                   │
   │                    │    Dashboard" }     │                   │
   │                    │                     │                   │
   │◄── "You're logged  │                     │                   │
   │     into Jira!"    │                     │                   │

Scenario 2: API Key Proxy (vault_api_request)

The agent makes an API call. Vault injects the API key into headers — the key never appears in the LLM context.

  Claude Code           Vault MCP                  Stripe API
   │                      │                            │
   │── vault_api_request ►│                            │
   │   service: "stripe"  │                            │
   │   url: "/v1/charges" │                            │
   │   method: "GET"      │                            │
   │                      │── decrypt API key          │
   │                      │                            │
   │                      │── GET /v1/charges ────────►│
   │                      │   Authorization:           │
   │                      │   Bearer sk-live-****      │
   │                      │                            │
   │                      │◄── { data: [...] } ───────┤
   │                      │                            │
   │                      │── scan response            │
   │                      │   for leaked key           │
   │                      │   (replace with ***)       │
   │                      │                            │
   │◄── { status: ok,  ───┤                            │
   │     body: "..." }    │                            │
   │                      │                            │
   │   (API key NOT in    │                            │
   │    this response)    │                            │

Scenario 3: Returning User (credentials already stored)

If credentials already exist, the agent skips vault_add and goes straight to vault_login:

  User              Claude Code           Vault MCP            Chrome
   │                    │                     │                   │
   │ "Open GitHub"      │                     │                   │
   ├───────────────────►│                     │                   │
   │                    │── vault_list() ────►│                   │
   │                    │◄─ [{ siteId:       ─┤                   │
   │                    │     "github",       │                   │
   │                    │     active: true }] │                   │
   │                    │                     │                   │
   │                    │── vault_login ─────►│                   │
   │                    │   { site: "github" }│── decrypt ───┐    │
   │                    │                     │◄─────────────┘    │
   │                    │                     │── CDP login ─────►│
   │                    │                     │◄─ success ───────┤
   │                    │◄─ { status: ok } ───┤                   │
   │                    │                     │                   │
   │◄── "Done!"         │                     │                   │

Scenario 4: Credential Revocation

Remove access instantly — the agent can no longer use the credential:

  Admin (CLI)           Vault MCP            Claude Code
   │                      │                      │
   │── vault-mcp remove   │                      │
   │   "jira"             │                      │
   │                      │── delete from store   │
   │                      │── audit: removed      │
   │◄── "Removed: jira"   │                      │
   │                      │                      │
   │                      │    ... later ...      │
   │                      │                      │
   │                      │◄── vault_login ──────┤
   │                      │    { site: "jira" }  │
   │                      │                      │
   │                      │── { status: FAIL, ──►│
   │                      │   "Credential not    │
   │                      │    found: jira" }    │
   │                      │                      │

Scenario 5: Audit Trail

Every credential use is logged with a tamper-proof hash chain:

  ~/.vault-mcp/audit.jsonl

  ┌─────────────────────────────────────────────────────────────┐
  │ evt_001 │ credential.created │ jira    │ success │ hash_1  │
  │         │                    │         │         │    │    │
  │ evt_002 │ credential.used    │ jira    │ success │    │    │
  │         │ bot: claude        │         │         │    ▼    │
  │         │                    │         │ prevHash: hash_1  │
  │         │                    │         │         │ hash_2  │
  │         │                    │         │         │    │    │
  │ evt_003 │ credential.used    │ jira    │ success │    ▼    │
  │         │ bot: claude        │         │ prevHash: hash_2  │
  │         │                    │         │         │ hash_3  │
  │         │                    │         │         │    │    │
  │ evt_004 │ credential.removed │ jira    │ success │    ▼    │
  │         │                    │         │ prevHash: hash_3  │
  └─────────────────────────────────────────────────────────────┘

  Modify any entry → hash chain breaks → tamper detected

  $ vault-mcp audit
  Chain integrity: VALID (4 entries)

Quickstart

# 1. Clone and build
git clone https://github.com/Chill-AI-Space/vault-mcp.git
cd vault-mcp
npm install
npm run build

# 2. Register with Claude Code
claude mcp add -s user vault -- node ~/path/to/vault-mcp/dist/index.js

# 3. Use in Claude Code
#    "Log me into GitHub" →
#    Claude calls vault_add("github") → browser form opens → you enter password
#    Claude calls vault_login("github") → Chrome logs in via CDP
#    Claude sees only { status: "success" }

Or add credentials via CLI (outside of Claude Code):

vault-mcp add --site github --email [email protected] --url https://github.com/login
# Password is prompted interactively (masked with *)

MCP Tools

Tool What it does What the LLM sees
vault_add(site_id?) Opens browser form for secure credential entry { status, site_id }
vault_login(site_id) Logs into website via Chrome CDP { status, page_title }
vault_api_request(service, url, ...) Makes API call with injected credentials { status, body }
vault_list() Lists stored credentials [{ siteId, type, active }]
vault_status(site_id) Shows credential metadata + audit stats { siteId, active, lastUsed }

What the LLM never sees: passwords, API keys, emails, tokens, encrypted data.

CLI Commands

vault-mcp add                        # Interactive: add credential (password masked)
vault-mcp add --site X --email Y     # Semi-interactive
vault-mcp list                       # List credentials (no secrets)
vault-mcp remove <site_id>           # Remove credential
vault-mcp audit [site_id]            # View audit log + chain integrity
vault-mcp dashboard                  # Web UI on localhost:9900
vault-mcp serve                      # Start MCP server (for debugging)

Architecture

vault-mcp/
├── src/
│   ├── index.ts              ── Entry: CLI or MCP mode (auto-detect)
│   ├── server.ts             ── MCP server, 5 tools registered
│   ├── cli.ts                ── CLI commands (commander + inquirer)
│   ├── tools/
│   │   ├── vault-add.ts      ── Opens browser form, waits for submit
│   │   ├── vault-login.ts    ── Decrypt → CDP → fill form → status
│   │   ├── vault-api.ts      ── Decrypt → inject headers → fetch → sanitize
│   │   ├── vault-list.ts     ── Return metadata only
│   │   └── vault-status.ts   ── Metadata + audit stats
│   ├── store/
│   │   ├── encrypted-store.ts ── AES-256-GCM CRUD, JSON file backend
│   │   └── keychain.ts        ── Master key: env var or auto-generate
│   ├── browser/
│   │   └── cdp-bridge.ts     ── Playwright connectOverCDP, form fill
│   ├── audit/
│   │   └── logger.ts         ── Append-only JSONL, SHA-256 hash chain
│   └── dashboard/
│       ├── server.ts         ── HTTP server (127.0.0.1:9900 only)
│       ├── index.html        ── Full dashboard (CRUD + audit viewer)
│       └── add.html          ── Focused add-credential form (for vault_add)
└── test/                     ── 29 tests including credential sanitization

Configuration

Env Variable Default Description
VAULT_MASTER_KEY (auto-generated) Encryption key. Without it, a random key is saved to ~/.vault-mcp/.master-key
VAULT_CDP_URL http://localhost:9222 Chrome DevTools Protocol endpoint
# Register with env vars
claude mcp add -s user vault \
  -e VAULT_MASTER_KEY=my-secret-key \
  -e VAULT_CDP_URL=ws://localhost:9222 \
  -- node ~/path/to/vault-mcp/dist/index.js

Storage

~/.vault-mcp/
├── credentials.json    ── Encrypted credentials (AES-256-GCM, unique IV per entry)
├── audit.jsonl         ── Append-only log with SHA-256 hash chain
└── .master-key         ── Auto-generated master key (mode 0600)

Security

See SECURITY.md for full threat model.

  Protects against                 Does NOT protect against
  ─────────────────                ────────────────────────
  ✓ LLM context leakage           ✗ User typing password in chat
  ✓ Plaintext credential storage   ✗ Compromised host (root access)
  ✓ Audit log tampering            ✗ Malicious MCP client
  ✓ Accidental exposure in logs    ✗ Browser-level memory attacks

Testing

npm test              # 29 tests
npm run test:watch    # Watch mode

Tests verify: encryption round-trip, wrong-key rejection, credential sanitization in all tool responses, hash chain integrity, tamper detection, full lifecycle flows.

License

MIT

MCP Server · Populars

MCP Server · New