v0.3.3 · No server, no account, no networkApache-2.0

Your agent uses the keys. It never sees them.

Offline, encrypted secrets for AI agents. Your .env holds addresses, not values. mcpv run hands the real values to one process and masks them in everything it prints.

Set it up with your agent
See how it works
$ curl -fsSL https://raw.githubusercontent.com/mcpmastersh/mcpv/main/install.sh | sh

Node.js 20+. One file on your PATH. Zero runtime dependencies.

~/code/acme-api
.env safe to commit
# Addresses, not values. Paste it, commit it,
# let an agent read it.
DATABASE_URL=mcpm://acme/api/dev/DATABASE_URL
STRIPE_KEY=mcpm://acme/api/dev/STRIPE_KEY
OPENAI_API_KEY=mcpm://acme/api/dev/OPENAI_API_KEY
PORT=3000
Agent sees the command, key names, masked output
Agent never sees a value
terminal values in memory only
$ mcpv check
✓ 3 references resolve in mcpm://acme/api/dev

$ mcpv run -- npm run dev
> acme-api@1.4.0 dev
> node server.js
db    postgres://app:[redacted]@db:5432
stripe key=[redacted] mode=test
✓ listening on :3000

Works with any coding agent that runs a shell

Claude CodeCodexCursorWindsurfOpenCodeVS CodeCI
Data path

One secret, start to finish.

Your agent reads addresses on the way in and redactions on the way out. The real value only exists inside one process, in memory.

Fig. 1 · System schematicData path of one secret
How mcpv moves a secret A .env file holds only mcpm:// addresses. mcpv run reads them, decrypts the matching values from the AES-256-GCM vault in memory, using a master key held in the OS keychain, and injects them into one child process. Vault and child process sit inside a dashed trust boundary. The child's stdout and stderr pass through a mask on the way out, so the agent only ever sees [redacted] in place of a value. TRUST BOUNDARY · PLAINTEXT ONLY IN MEMORY ZONE A · mcpv run FILE .env STRIPE_KEY= mcpm://acme/api/dev/ STRIPE_KEY PORT=3000 SAFE TO COMMIT 1 ADDR mcpv vault AES-256-GCM fresh IV per value AAD = mcpm:// address ~/.mcpv · 0700 MASTER KEY Keychain · Secret Svc 2 DECRYPT INJECT CHILD PROCESS · PID 4182 $ npm run dev ENV (IN MEMORY) STRIPE_KEY= no MCPV_KEY REAL VALUES · THIS PID 3 MASK STDOUT STDERR 4 AGENT · TRANSCRIPT stripe key= [redacted] db app:[redacted] ✓ listening :3000 SEES NAMES + OUTPUT NEVER A VALUE 5 AGENT READS: ADDRESSES AGENT READS: [redacted] $ mcpv run -- npm run dev ONE COMMAND · ONE PROCESS

Scroll the drawing sideways →

  1. .envHolds mcpm:// addresses, not values. Paste it, commit it, let an agent read it.
  2. VaultAES-256-GCM, fresh IV, address bound as AAD. Master key in the OS keychain.
  3. Child processGets the real values in its environment. Never gets the master key.
  4. MaskEvery secret in stdout and stderr becomes [redacted], even across split writes.
  5. AgentSees the command, key names and masked output. Never a value.
How it works

Five commands. None of them reveal a value.

There is no get and no reveal. On purpose. Values only ever go into a child process's environment.

01 Import

Move your existing .env into the vault.

mcpv stores each value, then rewrites the file so it holds mcpm:// references instead. It shows what it would store before anything is written.

  • --only picks which keys to move
  • Plain config like PORT stays as-is
terminal
$ mcpv import .env --into mcpm://acme/api/dev \
    --only DATABASE_URL,STRIPE_KEY --rewrite
✓ stored 2 secrets in mcpm://acme/api/dev
✓ rewrote .env · 2 references, 1 plain value
02 Set

Add a key at a hidden prompt. Never as an argument.

Arguments end up in shell history, in ps and in agent transcripts. mcpv only accepts values from a hidden prompt or stdin, so setting a secret needs you.

  • Pipe from your clipboard: pbpaste | mcpv set …
  • Off a terminal it never prompts; it exits 2 with the fix
terminal
$ mcpv set mcpm://acme/api/dev/OPENAI_API_KEY
Value (hidden): ••••••••••••••••••••
✓ saved OPENAI_API_KEY

$ mcpv set acme/api/dev
✗  "acme/api/dev" isn't a full address yet.
→  mcpv set mcpm://acme/api/dev/<KEY>
03 Run

Decrypt in memory. Pass to one process. Mask the output.

run resolves references and injects values into that one child's environment. It scans stdout and stderr and replaces every secret with [redacted], even when a value is split across two writes.

  • Uses ./.env, --env-file or a whole --env environment
  • The child never receives the master key
terminal
$ mcpv run -- npm test
$ mcpv run --env mcpm://acme/api/prod -- ./migrate.sh
  migrating postgres://app:[redacted]@prod-db
✓ 4 migrations applied
04 Check & list

Safe for agents: key names only, never values.

An agent can see what's missing and ask you to add it. It can search across every environment without touching a single value.

  • --json on list, check and doctor
  • Search, filter and page like the web UI
terminal
$ mcpv list --search stripe --environment prod
  mcpm://acme/api/prod/STRIPE_KEY
  mcpm://acme/billing/prod/STRIPE_WEBHOOK_SECRET

$ mcpv check
✗ 1 missing: mcpm://acme/api/dev/SENTRY_DSN
→ ask the user to run: mcpv set mcpm://acme/api/dev/SENTRY_DSN
Addresses

Every secret has an address.

Same format as mcpmaster Cloud Agent Secrets, so a .env of references works with either one.

mcpm://  acmeworkspace/ apiproject/ devenvironment/ STRIPE_KEYkey

Forgiving by design

Case and the scheme are repaired. A trailing slash is just punctuation.

mcpv set Acme/API/Dev/STRIPE_KEY

Prefixes list everything

Stop at any level to list what lives under it.

mcpv list acme/api

Asks for the rest

In a terminal, it prompts for missing pieces and offers what already exists.

mcpv set acme/api → pick env, key
mcpv ui Local web UI

A browser page that never shows a value, either.

Browse environments and key names, add or replace values, import a .env and check that references resolve. It runs only while you use it.

  • 127.0.0.1 only, new token each run, never written to disk
  • Paste a whole mcpm:// address into any box and it splits
  • Stops on Close, Ctrl+C or after 15 idle minutes
Built for agents

One prompt. Your agent does the rest.

Paste it into Claude Code, Codex, Cursor or any coding agent. It installs mcpv, moves your .env into the vault and saves a skill so later sessions know the rules.

curl -fsSL https://raw.githubusercontent.com/mcpmastersh/mcpv/main/install.sh | MCPV_SKILL=1 sh
setup-prompt.txt
Set up mcpv, an offline secrets vault, so you can use my API keys without ever seeing them.

1. Install it (needs Node 20+; no sudo):
   curl -fsSL https://raw.githubusercontent.com/mcpmastersh/mcpv/main/install.sh | MCPV_SKILL=1 sh
   Then run `mcpv --version` and `mcpv init`.
2. Read the skill it saved (~/.claude/skills/mcpv/SKILL.md, or run `mcpv help`) before doing anything else.
3. If I have a .env, ask me first, then run:
   mcpv import .env --into mcpm://<workspace>/<project>/<environment> --rewrite
4. Check and run my app: `mcpv check`, then `mcpv run -- <my dev command>`.

Rules:
- Never print, log, commit or ask me for a secret value. Refer to secrets by their mcpm:// address.
- There is no command to reveal a value, on purpose. Do not look for a workaround.
- To add a key, tell me to run `mcpv set mcpm://<workspace>/<project>/<environment>/<KEY>` and type it at the hidden prompt.
- Do not guess flags. Run `mcpv help <command>` and use what it lists.
- If a step fails, quote what it printed and give me the next thing to try.
Security model

What's protected, and how.

One encrypted file on your machine. On its own, vault.json is only ciphertext plus key names, in a backup, a synced folder or a stray commit.

AES-256-GCM at rest

A fresh IV per value, with its own address bound in as authenticated data. Move, edit or mis-key a ciphertext and it fails loudly.

Key lives elsewhere

The master key sits in macOS Keychain or Linux Secret Service, otherwise a 0600 key file. In CI, set MCPV_KEY.

No display surface

No command prints a value and no page of mcpv ui shows one. Values only flow into a child's environment.

Locked-down local UI

Loopback only, Host header checked, per-run token, strict CSP, no cross-origin or non-JSON writes, idle shutdown.

Output masking

run masks resolved values in stdout and stderr, even across split writes. --no-mask for interactive programs.

Private files

Vault directory is 0700, files are 0600 and written atomically. The child never inherits MCPV_KEY.

What it can't do.

Any process running as your user, an agent's shell included, could run mcpv run --no-mask -- printenv, read the key file or ask the keychain. Masking catches accidents, not a process that sets out to leak a value.

For a hard boundary, run the agent as a different OS user or in a container with no access to the vault. mcpv says this plainly instead of pretending otherwise.

Pairs with mcpmaster

Now give that agent every tool.

mcpmaster is one local MCP endpoint for every API and MCP server you use. Agents see one execute tool instead of a thousand schemas, and credentials stay out of their context.

Explore mcpmaster
terminal
$ mcpmaster add https://api.github.com/openapi.json \
    --bearer --token-env GITHUB_TOKEN
✓ github · openapi · 1,081 tools
$ mcpmaster connect claude-code | sh
✓ Claude Code now sees 1 tool: execute
mcpmaster Cloud

Share secrets with your team. Still never in the chat.

mcpv is the offline one. Cloud Agent Secrets uses the same mcpm:// addresses, so the same .env works with both, plus workspaces, roles and an audit trail.

mcpv, self-hosted

Apache-2.0

Free. One encrypted file on your machine.

  • Encrypted vault, no network
  • Output masking, local web UI
  • Agent skill, CLI and TypeScript library
  • Shared team workspaces
  • Audit trail and instant revocation
Get it on GitHub
FAQ

Questions, answered.

Anything else? Open an issue on GitHub.

Why can't I read a secret back?

Because anything printed can land in a transcript, a log or a commit. mcpv has no get and no reveal. If you need a value, it's wherever you got it from. Your app gets it through mcpv run.

Does it need a server or an account?

No. No server, no account, no network. One encrypted file in ~/.mcpv and a master key in your OS keychain.

How do I use it in CI?

Set MCPV_KEY to the 64-hex-character master key. It overrides the vault's key store, and the child process never receives it.

Can I use it from code?

Yes. import { Vault } from "@mcpmastersh/mcpv", then Vault.open().resolveKey("mcpm://…") resolves a value in memory for your own code.

How is this different from mcpmaster Cloud?

mcpv is offline and personal. Cloud Agent Secrets is hosted and shared with your team, with roles, audit and revocation. Both read the same mcpm:// addresses.

Let agents run your app. Keep every key.