Claude Secrets
БесплатноНе проверенEncrypted token store for Claude Code sessions, providing MCP tools for secure secret management with macOS Keychain integration, per-project allowlists, and na
Описание
Encrypted token store for Claude Code sessions, providing MCP tools for secure secret management with macOS Keychain integration, per-project allowlists, and native dialog input.
README

@vaultry/claude-secrets
Encrypted token store for Claude Code sessions and your shell/apps.
macOS Keychain backed · MCP server · CLI · .env placeholder expansion.
Stop pasting tokens into every new Claude session. Store them once, reference them everywhere — including commit-safe
.envfiles.
Quick install (one line)
curl -fsSL https://raw.githubusercontent.com/vaultry/claude-secrets/main/install.sh | bash
Or via npm:
npm install -g @vaultry/claude-secrets
claude-secrets-setup
claude mcp add claude-secrets --scope user -- claude-secrets-mcp
Three interfaces
| Interface | Who uses it | Policy-gated |
|---|---|---|
| MCP server | Claude Code sessions | Yes (per-project allowlist) |
CLI claude-secrets |
Your shell, apps, scripts | No (user has Keychain access) |
.env placeholders |
Any tool that reads .env | Via CLI |
Store secrets

Pipe from stdin (keeps the value out of shell history):
echo "ghp_xxxxx" | claude-secrets set GITHUB_TOKEN
op read "op://Private/Gitea/token" | claude-secrets set GITEA_TOKEN
claude-secrets set DB_PASSWORD # interactive — type, Ctrl-D
Encrypted with AES-256-GCM, master key stored in macOS Keychain (syncs between Macs via iCloud Keychain).
Commit-safe .env files

Replace real values with secret://NAME references. The .env file can now be committed to git safely — it contains only names, no credentials.
# .env
API_KEY=secret://GITHUB_TOKEN
DB_URL=postgres://app:secret://[email protected]/app
PORT=3000
Placeholder syntax: secret://NAME — URI-style, avoids bash parameter-expansion collisions. Names can contain A-Z, 0-9, _, ., -.
Expand placeholders

Resolve placeholders to real values at runtime:
eval "$(claude-secrets export)" # into current shell
claude-secrets export --format json > resolved.json
claude-secrets export --format dotenv > .env.resolved
claude-secrets export --file .env.staging # different file
claude-secrets export --on-missing empty # empty for missing refs
--on-missing |
Behavior |
|---|---|
throw (default) |
Exit 1 with list of missing names |
empty |
Placeholder becomes empty string |
keep |
Placeholder left literal (secret://NAME) |
Run a command with secrets

claude-secrets exec -- pnpm dev # inject then run
claude-secrets exec -- node build.js
claude-secrets exec --file .env.prod -- npm run deploy
Secrets live only in the child process — not in the parent shell's environment, history, or ps output.
package.json scripts work seamlessly:
{
"scripts": {
"dev": "claude-secrets exec -- ts-node src/index.ts",
"test": "claude-secrets exec --file .env.test -- vitest",
"deploy": "claude-secrets exec --file .env.prod -- node deploy.js"
}
}
MCP server (for Claude Code)
After registering with claude mcp add, Claude Code gets 6 tools under mcp__claude-secrets__*:
| Tool | Policy check | Effect |
|---|---|---|
set_secret |
yes | Store/overwrite (requires allowlist) |
get_secret |
yes | Return value or Denied |
delete_secret |
yes | Remove or Denied |
list_secrets |
filter | {total, visible, names} |
search_secrets |
filter | Array of matches (regex, case-insensitive) |
input_secret |
yes | Native macOS dialog prompts user for value → stored directly. Value never passes through the model or chat. |
input_secret — secure user input

Use case: Claude needs a token the user hasn't stored yet. Instead of asking "please paste your token in chat" (which leaks the value into transcripts, API logs, and plan files), Claude calls input_secret — a native macOS dialog pops up, user types the value, value goes straight from dialog to encrypted store without Claude ever seeing it.
Claude: "I need a GITEA_TOKEN to push that branch. May I prompt you?"
User: "yes"
Claude: [calls input_secret with name=GITEA_TOKEN]
→ macOS dialog appears on your screen (hidden input)
→ you type the token, press OK
→ value stored in encrypted vault
→ Claude gets back "OK: 'GITEA_TOKEN' stored"
The value never appears in chat, transcripts, or API traffic.
list and search only return names that pass the allowlist. total shows the real count — Claude knows more secrets exist but can't see their names from a non-authorized project.
Per-project allowlist — .claude/secrets.yml
Without this file: all MCP reads and writes blocked — prevents Claude in project A from reading or overwriting secrets from project B.
allow:
- GITEA_TOKEN
- GITHUB_* # glob patterns supported
- OP_SERVICE_*
Special values:
allow: "*" # allow everything (not recommended)
allow:
- GITEA_TOKEN
inject_values: true # SessionStart hook injects values (opt-in)
secrets.yml is safe to commit — it contains names only.
SessionStart Hook (optional)
Inject available secret names into Claude's context at session start so Claude knows what's available without asking. Add to ~/.claude/settings.json:
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "claude-secrets-session-hook"
}
]
}
]
}
}
With inject_values: true in secrets.yml, the hook also injects values — they appear in transcripts, history.jsonl, plan files, and API logs. Only use for short-lived tokens in trusted projects. The hook emits a visible warning when values are injected.
Slash commands for Claude Code
Two slash commands included in the package:
| Command | Effect |
|---|---|
/secret-set <NAME> |
Native macOS dialog (hidden input) → stored via CLI |
/secret-get <NAME> |
CLI fetch → copied to clipboard (value never in chat) |
Link them into Claude Code's commands directory:
ln -sf $(npm root -g)/@vaultry/claude-secrets/commands/secret-set.md ~/.claude/commands/secret-set.md
ln -sf $(npm root -g)/@vaultry/claude-secrets/commands/secret-get.md ~/.claude/commands/secret-get.md
(The one-line installer handles this automatically.)
Security model
Protects against
- Master key outside the encrypted file — held in Keychain, user-locked
- Per-project allowlist blocks cross-project leakage through Claude
- AES-256-GCM — authenticated encryption, tamper detection
- File mode
0600— owner-only - Atomic writes (write-to-temp + rename) — no partial-state corruption on crash
.envwith refs — commit-safe by constructionexec --injection — secrets stay out of shell history andpsoutput
Know the trade-offs
inject_values: trueputs values in Claude's system prompt → they appear in transcripts,history.jsonl, plan files, and API logs- CLI bypasses policy — anyone with your user ID and an unlocked Mac can read all secrets (correct: Keychain is what protects you as a user, policy protects Claude from itself)
- Keychain ACL — after the first "Always Allow",
nodecan read the key without a prompt - MCP writes are gated by allowlist since v0.1 — prevents cross-project overwrites
Not a defense against
- Malicious local processes running as your user
- Physical access to an unlocked Mac
- A compromised Keychain (root-level malware)
CLI reference
claude-secrets help
get <name> Print secret to stdout
set <name> [value] Store secret (value from stdin if omitted)
delete|rm <name> Delete a secret
list|ls List all secret names
search <pattern> Regex search (case-insensitive)
export [--file .env] Print 'export KEY=VAL' lines for shell eval
[--format shell|dotenv|json] Default: shell
[--on-missing throw|empty|keep] Default: throw
exec [--file .env] -- <cmd...> Run cmd with expanded env from .env
[--on-missing throw|empty|keep] Default: throw
Architecture
~/.claude/
└── secrets.encrypted # AES-256-GCM, mode 0600
@vaultry/claude-secrets (installed)
├── dist/
│ ├── index.js # MCP server (stdio)
│ ├── crypto.js # AES-256-GCM + Keychain I/O
│ ├── store.js # atomic read/write secrets.encrypted
│ ├── policy.js # isAllowed() via secrets.yml
│ ├── dotenv.js # .env parser + placeholder expansion
│ └── bin/
│ ├── setup.js # init CLI
│ ├── session-hook.js # SessionStart hook
│ └── cli.js # claude-secrets CLI
└── commands/ # Claude Code slash commands
Encryption details
- Algorithm: AES-256-GCM (authenticated encryption, tamper detection)
- IV: 12 bytes, random per write
- Key: 32 bytes, in macOS Keychain as service
claude-secrets-mcp, accountmaster-key - File format:
{iv_b64}:{authtag_b64}:{ciphertext_b64} - Decrypted payload: JSON
{name: value} - Writes: temp-file + rename, atomic on POSIX
Sync between Macs
- iCloud Keychain auto-syncs the master key
- Sync
secrets.encryptedvia Dropbox/iCloud Drive/git-crypt if needed — it's useless without the key
Requirements
- macOS (uses the
securityCLI for Keychain access) - Node.js ≥ 18
Cross-platform support (Linux/Windows via keytar) is planned for v0.2.
Workflow example
cd ~/projects/new-thing
git init
# Secrets already stored? Check:
claude-secrets search 'GITEA|DB'
# Add new ones:
op read "op://Private/Gitea/token" | claude-secrets set GITEA_TOKEN
claude-secrets set DB_PASSWORD # type, Ctrl-D
# Commit-safe .env:
cat > .env <<EOF
GITEA_TOKEN=secret://GITEA_TOKEN
DATABASE_URL=postgres://app:secret://DB_PASSWORD@localhost:5432/mydb
PORT=3000
EOF
# Let Claude read specific tokens (optional):
mkdir -p .claude
cat > .claude/secrets.yml <<EOF
allow:
- GITEA_TOKEN
- DB_PASSWORD
EOF
# Dev server with secrets injected:
claude-secrets exec -- pnpm dev
Troubleshooting
"Keychain entry not found"
Setup wasn't run. Run claude-secrets-setup.
"Invalid ciphertext format"
secrets.encrypted is corrupt or was encrypted with a different key. Restore from backup or delete and start over.
Keychain prompt on every read
Normal on the first session after login. Check "Always Allow". If it keeps prompting: Keychain Access app → find claude-secrets-mcp → right-click → Access Control → add the node binary.
claude-secrets: command not found
export PATH="$(npm bin -g):$PATH"
MCP not visible in Claude
claude mcp list | grep claude-secrets # should show "✓ Connected"
If not: restart the Claude session.
Secret in .env but expansion fails
Check: claude-secrets get NAME — exists? Name match is case-sensitive. Placeholder syntax must be exactly secret:// (not @secrets: or ${secrets:}).
Uninstall
claude mcp remove claude-secrets --scope user
security delete-generic-password -s claude-secrets-mcp -a master-key
rm ~/.claude/secrets.encrypted
npm uninstall -g @vaultry/claude-secrets
rm -f ~/.claude/commands/secret-set.md ~/.claude/commands/secret-get.md
# Remove the session-hook line from ~/.claude/settings.json manually
License
Source-available under the Vaultry Source-Available License v1.0. Free for personal, educational, and internal use. Commercial use (resale, SaaS, bundled products) requires a separate commercial license — contact [email protected].
See LICENSE.md for full terms.
Made by Joris Slagter · Report an issue · npm
Установка Claude Secrets
У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.
▸ github.com/vaultry/claude-secretsFAQ
Claude Secrets MCP бесплатный?
Да, Claude Secrets MCP бесплатный — установка в пару кликов через Unyly без оплаты.
Нужен ли API-ключ для Claude Secrets?
Нет, Claude Secrets работает без API-ключей и переменных окружения.
Claude Secrets — hosted или self-hosted?
Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.
Как установить Claude Secrets в Claude Desktop, Claude Code или Cursor?
Открой Claude Secrets на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.
Похожие MCP
GitHub
PRs, issues, code search, CI status
автор: GitHubFilesystem
Secure file operations with configurable access controls.
Memory
Knowledge graph-based persistent memory system.
Template MCP Server
A CLI tool to create a new Model Context Protocol server project with TypeScript support, dual transport options, and an extensible structure
автор: mcpdotdirectCompare Claude Secrets with
Не уверен что выбрать?
Найди свой стек за 60 секунд
Автор?
Embed-бейдж для README
Похожее
Все в категории development
