Skip to main content
Glama

vault-mcp

A personal secrets vault your AI coding agent can use without ever seeing your keys.

Store your API keys once. When the agent needs a Stripe key, a Google Maps key or a database URL, it searches the vault, finds the metadata, and writes the value straight into your .env. The value goes to disk; the conversation only ever sees the variable name.

You:    "Add a map to the dashboard showing user locations."

Agent:  [vault_search "google maps"]  -> finds google-maps/default/api_key
        [vault_inject_env]            -> writes NEXT_PUBLIC_GOOGLE_MAPS_API_KEY into .env
        "I set NEXT_PUBLIC_GOOGLE_MAPS_API_KEY in .env and wired up the map component."

No more pasting keys into chat.

Works with Claude Code, Claude Desktop, Cursor, Windsurf, Antigravity, VS Code, Zed — anything that speaks MCP over stdio.


What it does

  • Your agent stops asking you for keys. It searches the vault first and injects what it finds.

  • It never sees the values. All 29 tools return metadata and status only. Values go to a file, a process environment or a remote server, never back through the conversation.

  • Groups and accounts. stripe/acme and stripe/personal stay apart, each with its own secret key, publishable key and webhook secret.

  • Projects. A project holds its own secrets and links to shared ones, so one call sets up a whole .env.

  • Four injection targets. A local .env, any config file through {{vault:path}} placeholders, a command's environment with the output redacted, or a remote server's env file over SSH.

  • Two ways to add a key. Tell the agent the value, or have it open the vault in your browser so you type the key in yourself behind your access code.

  • Encrypted at rest, no password to remember. AES-256-GCM, with the master key in Windows Credential Manager or the macOS Keychain.

  • An access code for what matters. Gates the web UI, and individually gates any secret marked --protected.

  • Backups. Local snapshots for undoing mistakes, portable passphrase-sealed backups for a drive or another machine.

  • Guard rails. It refuses to write a secret into a git-tracked file, warns when a server-only key is headed for a browser variable, and logs every read and injection.

  • 15 service presets. Stripe, Google Maps, AWS, OpenAI, Anthropic, Cloudflare, Supabase, Firebase, Twilio, SendGrid, Resend, GitHub, a database URL, an SSH server, and a generic catch-all.

Related MCP server: Vault MCP

Install

git clone https://github.com/MHasnainJafri/vault-mcp.git
cd vault-mcp
npm install
npm run build
npm link                # makes `vault` available everywhere (optional)
vault init              # creates ~/.vault-mcp and the master key

Then register it with your editor or agent. vault install shows which clients it found on your machine:

vault install                       # list clients, and mark the ones detected
vault install --client cursor       # write that client's config
vault install --all                 # every client it detected
vault install --print               # just show the JSON to paste

The config it writes is merged into whatever is already there, and the original is copied to <config>.vault-backup first, so no other server or setting is disturbed.

Client

Config it writes

Claude Code

prints claude mcp add vault -s user -- node <path> serve

Claude Desktop

%APPDATA%\Claude\claude_desktop_config.json, or ~/Library/Application Support/Claude/…

Cursor

~/.cursor/mcp.json, or .cursor/mcp.json with --project

Windsurf

~/.codeium/windsurf/mcp_config.json

Antigravity (IDE and CLI)

~/.gemini/config/mcp_config.json, or .agents/mcp_config.json with --project

VS Code (Copilot)

.vscode/mcp.json with --project

Zed

use --print and paste under context_servers

Any other MCP client takes the standard block:

{
  "mcpServers": {
    "vault": { "command": "node", "args": ["/abs/path/to/vault-mcp/dist/cli.js", "serve"] }
  }
}

VS Code names that block servers instead of mcpServers. A newly registered server appears the next time the client starts.

Then set your access code, which you type yourself and never share with the agent:

vault set-pin

Add your keys

Three ways, none of which put the value in a chat window.

Guided, per service — walks the fields a service needs, hidden prompts:

vault add stripe/acme          # secret_key, publishable_key, webhook_secret
vault add google-maps/default  # api_key
vault setup                    # first-run wizard: init + first service + install

One key at a time:

vault add openai/personal/api_key --env OPENAI_API_KEY --tags ai
cat key.pem | vault add ssh/prod/private_key --stdin

Import a file you already have — a .env, or a JSON object of named fields:

vault import ./my-app/.env --to projects/my-app
vault import ./server.json --to vps/my-server

JSON is the quickest way to add something with several fields at once:

{ "host": "198.51.100.20", "username": "deploy", "password": "…", "port": 22 }

Each field becomes its own secret. Nested objects are flattened, so {"db":{"user":"root"}} becomes db/user, and an array is kept whole as one value. The format is picked from the file extension, or sniffed from the content if the name does not say.

Or use the local web UI:

vault ui

Opens http://127.0.0.1:5177 on loopback only. If you have set an access code it asks for that first. Add, edit, tag, import, back up, and reveal values (reveal works only in this local page, never through the agent).

Two ways to add a key mid-conversation

When the agent needs a key you do not have stored yet, you pick which way it goes in:

What happens

When to use it

You tell the agent the value

It calls vault_add_secret and stores it

Test keys, throwaways. The value is in the transcript, so rotate anything live.

"Open the vault for me"

It calls vault_open_ui; your browser opens on the vault's lock screen; you type the key in yourself

Production credentials, anything that matters. The agent never sees it.

For the second one, the agent is deliberately not given the URL token, so it cannot read that page. Your access code is what unlocks it.

vault set-pin      # set the access code once

Organize

Paths are group/account/field, so multiple accounts of the same service stay apart:

stripe/
  acme/       secret_key, publishable_key, webhook_secret
  personal/   secret_key
google-maps/
  default/    api_key
projects/
  my-shop/    database_url            <- project-specific

Link a shared key into a project instead of copying it:

vault link projects/my-shop stripe/acme/secret_key
vault link projects/my-shop stripe/acme/secret_key --env PAYMENTS_KEY   # rename per project

Then one call injects everything that project needs:

vault inject --project projects/my-shop --file .env

What the agent can do

The server registers as vault, so its tools appear as vault_*. All 29, with their parameters, are in TOOLS.md. The ones that matter day to day:

Finding a key

Tool

What it does

vault_search

Find a credential by service, purpose or env var name. Metadata only.

vault_list

Browse the tree of groups, accounts and projects.

vault_info

One secret in full; optional heavily masked preview (sk_l…9Xq).

vault_project

Everything one project needs, with the env var name for each.

vault_presets

The 15 service templates and their fields.

vault_status

Counts, and whether the access code is set or unlocked.

vault_audit

Recent activity: which secret went where.

Using a key

Tool

What it does

vault_inject_env

Write secrets into a .env. Refuses git-tracked files.

vault_render_file

Fill {{vault:stripe/acme/secret_key}} placeholders in any config file.

vault_run

Run a command with secrets in its environment, output redacted.

vault_ssh_inject

Write secrets into a remote server's env file over SSH.

vault_check_env

Compare a .env against the vault; report what is missing.

vault_write_env_example

Generate a committable .env.example.

vault_gitignore_check

Confirm a secrets file is git-ignored, and fix it if not.

Adding and organizing

Tool

What it does

vault_open_ui

Open the vault in your browser so you type the key in yourself.

vault_add_secret

Store a value you gave the agent. The value lands in the transcript.

vault_import_env

Pull an existing .env or JSON file into the vault; values never surface.

vault_update_meta

Change env var name, notes, tags, environment or expiry.

vault_create_container

Make a group, account or project by hand.

vault_link / vault_unlink

Let a project use a shared key without copying it.

vault_delete

Remove a secret or a whole container. Needs confirm.

vault_unlock / vault_lock

Unlock protected secrets for 15 minutes.

Backups

Tool

What it does

vault_backup

Encrypted local snapshot, sealed with this machine's key.

vault_list_backups

What backups exist, read from their headers alone.

vault_backup_info

Inspect one backup without decrypting it.

vault_restore

Put a local backup back. Takes a safety snapshot first.

vault_delete_backup

Remove a backup file.

Plus one resource, vault://catalog: every secret as metadata in a single document.

Placeholders

For anything that is not a .env, write a placeholder and let the vault fill it:

# docker-compose.yml
environment:
  STRIPE_KEY: "{{vault:stripe/acme/secret_key}}"
  SA_JSON: "{{vault:firebase/prod/service_account|base64}}"
vault_render_file { filePath: "docker-compose.yml" }

Transforms: |base64, |json, |uri.

Running commands

vault_run {
  command: "npx",
  args: ["wrangler", "secret", "put", "STRIPE_KEY"],
  secrets: [{ path: "stripe/acme/secret_key", varName: "STRIPE_KEY" }],
  stdinSecret: "stripe/acme/secret_key"
}

The child process gets the real value. Every occurrence of it — plain, base64, or URL-encoded — is stripped from the stdout and stderr you get back.

How the keys are protected

  • The vault is one AES-256-GCM encrypted file at ~/.vault-mcp/vault.enc.

  • The master key lives in your OS keychain: Windows Credential Manager or macOS Keychain, via @napi-rs/keyring. Nothing to type, nothing to remember, and another user account on the same machine cannot read it.

  • Headless or CI: set VAULT_MASTER_PASSWORD and the key is derived from it instead.

  • Access code (optional). vault set-pin sets a 4-12 digit code. It does two jobs: it unlocks the web UI, and it guards individual secrets marked --protected. Secrets added with --protected are sealed with sha256(masterKey || scrypt(pin)), so they need both the keychain and the PIN. Give the PIN to the agent when it needs one; it unlocks for 15 minutes. Five wrong tries locks it for ten. The PIN is a second gate on top of the keychain, not the primary encryption — a 4-digit PIN alone would be trivial to brute-force, which is why the master key is doing the real work.

Rules the code enforces

  1. No MCP tool result contains a secret value. A test drives all 29 tools and scans every response for known plaintexts, including base64 and URL-encoded forms.

  2. vault_run redacts secret values from command output.

  3. Injecting into a git-tracked file, or an un-ignored file inside a repo, is refused unless you pass force. addToGitignore: true fixes it properly instead.

  4. Nothing may be written inside ~/.vault-mcp itself.

  5. Every read and injection is appended to ~/.vault-mcp/audit.log — paths and destinations, never values.

  6. The web UI is loopback-only and needs a token generated at startup. When an access code is set, the page also asks for it before showing anything, and five wrong tries lock it for ten minutes.

  7. vault_open_ui starts that server but is not given the token, so the agent cannot read the page it opened.

  8. A portable backup refuses a passphrase under 10 characters, and no tool accepts a passphrase as an argument.

Backups

Everything in the vault, in one encrypted file. Two kinds, and the difference matters.

Sealed with

Restores on

Use it for

Local

this machine's master key

this machine only

Undoing a bad delete. No passphrase to remember.

Portable

a passphrase you type

any machine

A drive, cloud storage, moving Windows to Mac.

vault backup                 # portable: asks for a passphrase (10+ characters)
vault backup --local         # local snapshot, no passphrase
vault backups                # list what you have
vault restore <file>         # merge into the vault
vault restore <file> --replace   # make the vault match the backup exactly

Or use the Backup panel in vault ui, which creates the file and hands it straight to your browser's downloads so you can save it to a drive. The passphrase is typed in the page and never leaves it.

The agent can take local snapshots itself (vault_backup) and does so automatically before a restore or a delete from the UI. It cannot make a portable backup, because that needs a passphrase and a passphrase must never be typed into a conversation.

A few things worth knowing:

  • It is one encrypted file, not a zip of the vault folder. A zip would reveal how many keys you have and what they are called; this reveals only a date and a count.

  • Copying vault.enc on its own to another machine will not work — the master key lives in that machine's keychain. Portable backups exist precisely for this.

  • The file carries a checksum, so a truncated or tampered copy is refused rather than half-restored.

  • A restore always takes a safety snapshot first, so even --replace is undoable.

  • Lose the passphrase and the portable backup is gone. There is no recovery path, by design.

Commands

vault init                  Create the vault and master key
vault setup                 Guided first run
vault add <path>            Add a secret or a whole service preset
vault ls [path]             List what is stored
vault info <path>           Show one secret's metadata (--preview for a mask)
vault get <path>            Print a value to stdout (for piping)
vault rm <path>             Delete (-r for a container)
vault import <file> --to    Import a .env or JSON file
vault link <project> <path> Link a shared secret into a project
vault inject --project ...  Write secrets into a local .env
vault set-pin               Set the access code (web UI + protected secrets)
vault backup [--local]      Portable backup with a passphrase, or a local snapshot
vault restore <file>        Restore a backup (--replace to mirror it exactly)
vault backups               List backups on this machine
vault export / import-bundle  Older bundle format; backup/restore supersedes it
vault ui                    Local web UI
vault ui-url / ui-stop      Link to, or stop, a UI an agent started
vault serve                 Run the MCP server (what your client launches)
vault install               Register with Cursor, Windsurf, Antigravity, Claude…
vault status / audit / presets

Where things live

MCP server name

vault (tools appear as vault_*)

Registered by

vault install --client <id>, or the standard mcpServers JSON block

CLI command

vault after npm link, otherwise node dist/cli.js

Vault directory

~/.vault-mcp (override with VAULT_MCP_DIR)

Encrypted secrets

~/.vault-mcp/vault.enc

Backups

~/.vault-mcp/backups/*.vaultbak

Activity log

~/.vault-mcp/audit.log

Master key

Windows Credential Manager / macOS Keychain, service vault-mcp

Web UI

http://127.0.0.1:5177, loopback only, token plus access code

Development

npm run build       # tsup -> dist/
npm test            # vitest, 83 tests
npm run typecheck

Layout:

src/core/    crypto, keychain, store, envfile, importer, template, presets, git guards, audit, backup
src/mcp/     server + tools (read, inject, run, manage, ssh, backup, open-ui)
src/cli/     commander CLI
src/ui/      loopback web UI with the access-code lock screen
test/        core, vault, backup, git-guard, and the no-leak suite

How much fits

There is no hard limit. Measured on this machine:

Secrets

Vault file

Search

Read one

100

0.09 MB

11 ms

2 ms

1,000

0.91 MB

11 ms

45 ms

2,000

1.83 MB

5 ms

21 ms

Thousands of keys are fine. A single value can be as large as you like within reason — a PEM private key or a Firebase service-account JSON is normal. The whole vault is held in memory while the server runs, so this is built for a person's keys, not for a company-wide store.

Known limits

  • vault get prints a value by design; do not run it on a shared screen.

  • The web UI can reveal values. It is local-only, token-gated and code-gated, but it is a deliberate hole in the "never show a value" rule, for the human, not for the agent.

  • The audit log is append-only but not tamper-proof; anything that can read the vault file can also rewrite it.

  • The "the agent never sees a value" property covers the MCP tool surface. It keeps keys out of your transcript; it is not a sandbox against an agent that also has shell access to your machine.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

No tool schema history has been recorded yet.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables LLM agents to securely use credentials like passwords and API keys without exposing them in the context window, through encrypted storage and proxy-based injection.
    6
    2
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Enables AI agents to securely use real secrets (API keys, database passwords) by requiring human approval for each release, ensuring secrets never enter the model's context.
    2
    Apache 2.0
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI agents to securely use API keys by storing them in an encrypted vault and injecting them on demand with user approval, without exposing the key values to the model.
    1
    MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/MHasnainJafri/vault-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server