@mcpmastersh/mcpv
v0.3.5
Published
Offline, encrypted secrets for AI agents. Commit .env files full of mcpm:// references; `mcpv run` resolves them in memory, injects them into one process and masks its output — the agent never sees a value.
Downloads
1,186
Maintainers
Readme
Your .env holds addresses, not values. mcpv run hands the real values to one
process and masks them in everything it prints.
# .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$ mcpv check
✓ 3 references resolve in mcpm://acme/api/dev
$ mcpv run -- npm run dev
> [email protected] dev
> node server.js
db postgres://app:[redacted]@db:5432
stripe key=[redacted] mode=test
✓ listening on :3000- ✅ The agent sees the command, the key names and the masked output.
- ❌ The agent never sees a value.
AI coding agents like Claude Code are right not to handle raw secrets. A value
pasted into a prompt ends up in the transcript, in logs, and possibly in a
commit. mcpv keeps the values out of the agent's reach but still lets it run
your app. One encrypted file on your machine.
Works with any coding agent that runs a shell: Claude Code · Codex · Cursor · Windsurf · OpenCode · VS Code · CI
Contents
- Install
- Set it up with your agent
- How it works: five commands
- Addresses
- Web UI
- Security model
- Environment variables
- As a library
- Upgrade
- Troubleshooting
- Pairs with mcpmaster
- mcpmaster Cloud
- FAQ
- Brand
Install
curl -fsSL https://raw.githubusercontent.com/mcpmastersh/mcpv/main/install.sh | shThis downloads one file, puts an mcpv command on your PATH and leaves your
vault alone. Needs Node.js 20+. No runtime dependencies.
Prefer npm, or no install at all?
npm i -g @mcpmastersh/mcpv # npm
npx @mcpmastersh/mcpv doctor # no installSet it up with your agent
One prompt. Your agent does the rest. Paste this 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.
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.Install + skill only:
curl -fsSL https://raw.githubusercontent.com/mcpmastersh/mcpv/main/install.sh | MCPV_SKILL=1 shAgent skill. skills/mcpv/SKILL.md is the standing
instruction sheet: what an agent can do, what deliberately doesn't exist, and
how to answer "show me my key". Claude Code loads it on demand from
~/.claude/skills/mcpv/. Save it with MCPV_SKILL=1 on the installer, or copy
the file. For any other agent, append it to your project's AGENTS.md.
By hand, in the project instructions file your agent reads at startup
(CLAUDE.md, AGENTS.md, …):
Secrets are in mcpv. `.env` holds mcpm:// references, not values.
Start anything that needs them with `mcpv run -- <command>`.
Never ask for secret values. `mcpv check` shows what's missing.
If a key is missing, ask me to run `mcpv set <address>`.In CI, give the job the master key and run as usual:
MCPV_KEY=<64 hex chars> mcpv run -- npm testSetting a secret needs you: values are only accepted from a hidden prompt or
from stdin, never as command arguments, which end up in shell history, in ps
and in agent transcripts.
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.
# Move an existing .env into the vault and swap its values for references
mcpv import .env --into mcpm://acme/api/dev --only DATABASE_URL,STRIPE_KEY --rewrite
# Add or replace one secret. Type it at a hidden prompt, or pipe it in.
mcpv set mcpm://acme/api/dev/OPENAI_API_KEY
pbpaste | mcpv set mcpm://acme/api/dev/OPENAI_API_KEY
# Run anything with ./.env resolved (or --env-file, or a whole --env environment)
mcpv run -- npm test
mcpv run --env mcpm://acme/api/prod -- ./migrate.sh
# Safe for agents: key names only, never values
mcpv check # does every reference in ./.env resolve?
mcpv list # environments and key names, or search across them:
mcpv list --search stripe --environment prod --limit 20 --page 201 · 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.
$ 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 value02 · 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 with pbpaste | mcpv set …. Off a terminal it never
prompts; it exits 2 with the fix.
$ 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. It uses ./.env,
--env-file or a whole --env environment. The child never receives the
master key.
$ mcpv run -- npm test
$ mcpv run --env mcpm://acme/api/prod -- ./migrate.sh
migrating postgres://app:[redacted]@prod-db
✓ 4 migrations applied04 · Check & list: 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 works on list,
check and doctor.
$ 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_DSNAlso: rm <address> deletes a secret, init creates the vault, and doctor
shows where the vault and its key are and whether it unlocks.
Addresses
Every secret has an address. Same format as mcpmaster Cloud Agent Secrets,
so a .env of references works with either one.
mcpm://<workspace>/<project>/<environment> a whole environment
mcpm://<workspace>/<project>/<environment>/<KEY> one secret| Scheme | Workspace | Project | Environment | Key |
| --- | --- | --- | --- | --- |
| mcpm:// | acme | api | dev | STRIPE_KEY |
Workspace, project and environment are lowercase slugs. KEY is an environment
variable name.
You rarely have to type that. Every command reads an address the way you'd actually write one, and repairs what it can:
- Forgiving by design. Case and the scheme are repaired. A trailing slash is just punctuation.
- Prefixes list everything. Stop at any level to list what lives under it.
- Asks for the rest. In a terminal, it prompts for missing pieces and offers what already exists.
mcpv set Acme/API/Dev/STRIPE_KEY # same address: case and the scheme are repaired
mcpv set acme/api/dev/STRIPE_KEY/ # a trailing slash is punctuation
mcpv list acme/api # a prefix lists everything under itStop early and it asks for the rest, one piece at a time, offering what already
exists in the vault to pick from. Paste a whole address into any one of the
prompts and it fills the rest. Off a terminal (in a script, or from an agent)
it never prompts: it exits 2 naming the piece that's missing and the command
that would have worked.
A .env reference is read the same way, so mcpm://Acme/API/Dev/KEY in a file
resolves against the same secret as mcpm://acme/api/dev/KEY. Anything that
doesn't start with mcpm: is left alone as a value: LOG_PATH=var/log/app/err
is a path, not an address.
Web UI
A browser page that never shows a value, either.
mcpv uiThis opens a local page in your browser. From it you can browse environments
and key names, add or replace values, import a .env, delete secrets, and
check that your project's .env references all resolve. It never shows a
value, the same as the CLI.
- 127.0.0.1 only, with a new token each run that is never written to disk.
- Paste a whole
mcpm://address into any box and it splits across them. - Stops when you click Close, press Ctrl+C, or after 15 idle minutes. Nothing keeps running in the background.
Search and filter by workspace, project, environment or key name, and page
through the result. It uses the same matching rule as mcpv list, so the page
and the terminal never disagree about what a search found.
Import takes either the .env beside where you started mcpv ui, any file
you choose in the browser, or text you paste. It tells you what it would store
(how many plain values, what it skips) before anything is written, and if the
file already holds mcpm:// references it fills the environment in from them.
Adding a secret asks for the address as separate boxes (workspace, project,
environment, key) with a live readout of the address they add up to. What the
vault already has is offered as you go: pick an existing environment and the
boxes fill themselves. A partial address says which piece is missing instead of
refusing the lot, and a name it repairs (Acme API → acme-api) says so. The
value box is write-only.
The mcpm CLI in this project talks to a hosted workspace instead; mcpv is
the offline one.
Security model
What's protected, and how. One encrypted file on your machine. On its own,
vault.json is only ciphertext plus key names, whether it's in a backup, a
synced folder or a stray commit.
| | Protection | How |
| --- | --- | --- |
| 🔐 | AES-256-GCM at rest | Each value is encrypted with a fresh IV, with its own address bound in as additional authenticated data. If a ciphertext is moved to another key's slot, edited, or opened with the wrong key, it fails loudly. It never decrypts to the wrong value. |
| 🗝️ | The key lives elsewhere | The master key is stored in the macOS Keychain or the Linux Secret Service (secret-tool) when available, otherwise in a 0600 key file. In CI, set MCPV_KEY (64 hex characters). |
| 🙈 | No display surface | No command prints a value, and no page of mcpv ui shows one. There is no get and no reveal. Values only go into a child process's environment. |
| 🧱 | Locked-down local UI | mcpv ui listens on 127.0.0.1 only, checks the Host header (which blocks DNS rebinding), and requires a per-run token that is never written to disk. It refuses cross-origin requests and non-JSON writes, sends a strict Content-Security-Policy, and shuts itself down when idle. |
| ▓ | Output masking | run masks resolved values in the child's stdout and stderr, even when a value is split across two writes. Use --no-mask for fully interactive programs. |
| 📁 | Private files, no key inheritance | The vault directory is 0700; its files are 0600 and written atomically. The child process never receives MCPV_KEY. |
[!WARNING] What it can't do. Any process running as your user, an agent's shell included, can run
mcpv run --no-mask -- printenv, read the key file, or ask the keychain for the key. Masking catches accidents, not a process that sets out to leak a value (it could base64-encode it first). Values shorter than 4 characters aren't masked, because masking them would shred the output;runwarns when that happens. For a hard boundary, run the agent as a different OS user or in a container that has no access to the vault. This tool can't enforce that for you, and says so plainly instead of pretending otherwise.
Environment variables
| Variable | Meaning |
| --- | --- |
| MCPV_HOME | Vault directory (default ~/.mcpv) |
| MCPV_KEY | Master key, 64 hex chars. Overrides the vault's own key store (for CI) |
| MCPV_PLAIN / MCPV_ASCII / NO_COLOR | Terminal output: no decoration / ASCII glyphs / no color |
| MCPV_DEBUG=1 | Show stack traces for unexpected errors |
As a library
import { Vault, parseDotEnv, resolveDotEnv } from "@mcpmastersh/mcpv";
const vault = Vault.open();
const token = vault.resolveKey("mcpm://acme/api/dev/GITHUB_TOKEN"); // in memory, for your code to useUpgrade
Run the installer again. It fetches the newest version straight from the npm registry, so a stale npm cache can't hold you back, and it tells you what changed. Your vault and its key are never touched.
curl -fsSL https://raw.githubusercontent.com/mcpmastersh/mcpv/main/install.sh | sh
mcpv --versionAlready installed? mcpv update does the same thing, and mcpv update --check
only says whether a newer version exists.
Installed with npm? npm i -g @mcpmastersh/mcpv@latest --prefer-online.
(npm update -g takes package names only; to pick a version use
npm i -g @mcpmastersh/[email protected].) Still the old version afterwards? See
Troubleshooting: a second copy earlier on your PATH is
almost always the reason.
Troubleshooting
npm caches registry responses. If 0.2.0 was published recently, your local
cache might still believe 0.1.0 is the only version. Force an uncached
install:
npm i -g @mcpmastersh/[email protected] --prefer-onlineOr clear the cache entirely:
npm cache clean --force
npm i -g @mcpmastersh/[email protected]The one-line installer already put an mcpv command at that path (for example
~/.local/bin/mcpv), and npm won't overwrite a file it didn't create. You
don't need npm: run mcpv update. To switch to the npm copy instead, remove
the installer's file first, then install:
rm ~/.local/bin/mcpv
npm i -g @mcpmastersh/mcpv@latest --prefer-online--force also works but overwrites without asking. Your vault and its key are
not touched either way.
The new version can be installed and still not be the one that runs. npm i -g
writes into whichever Node is active, and a second global copy can sit earlier
on your PATH: another version manager's prefix, a pnpm/yarn/bun
global, or Homebrew.
which -a mcpv # every copy that would answer, in order
mcpv --version # the one that actually runs
npm ls -g --depth=0 # what npm's global prefix holdsInstall into the prefix that is actually first, or remove the stale copy. npx
also keeps its own cache of everything it has run, so an occasional
npx @mcpmastersh/mcpv can serve an old version:
npx --yes @mcpmastersh/mcpv@latest --version # force the newest
rm -rf ~/.npm/_npx # or drop npx's cacheThe same two steps apply to any package installed globally, these ones included.
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.
$ 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: executemcpmaster 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 | Cloud Agent Secrets |
| --- | --- |
| Free, Apache-2.0. One encrypted file on your machine. | 14-day trial. Hosted at mcpmaster.sh. Nothing to install. |
| ✅ Encrypted vault, no network | ✅ Same mcpm:// format, same .env |
| ✅ Output masking, local web UI | ✅ Workspaces and team roles |
| ✅ Agent skill, CLI and TypeScript library | ✅ Audit trail of every access |
| — Shared team workspaces | ✅ Instant access revocation |
| — Audit trail and instant revocation | ✅ Plus a hosted mcpmaster endpoint for every tool |
FAQ
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.
No. No server, no account, no network. One encrypted file in ~/.mcpv and a
master key in your OS keychain.
Set MCPV_KEY to the 64-hex-character master key. It overrides the vault's
key store, and the child process never receives it.
Yes. import { Vault } from "@mcpmastersh/mcpv", then
Vault.open().resolveKey("mcpm://…") resolves a value in memory for your own
code. See As a library.
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.
Anything else? Open an issue.
Brand
Logo files, PNG exports and usage notes are in brand/.
