passkey-mcp
This server lets AI assistants manage and inspect passkey MCP integration without exposing secret values.
List all passkey entry names (
passkey_list)List field names inside an entry (
passkey_fields)Show security status of MCP servers across detected tools (
passkey_status)Run diagnostics, including optional deep checks (
passkey_doctor)Wrap an MCP server config to use passkey for secret injection (
passkey_wrap_server)
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@passkey-mcp run myapi -- python app.py"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Passkey
A local OS-keychain injector for MCP and CLI secrets. It stores named
credential sets in your system keychain (keyring, service name passkey)
and injects them as environment variables at runtime.
This is not a team password manager, a shared vault, or a 1Password replacement. Alpha, 0.x.

No plaintext secrets in config files. No cloud sync. No network. Primary
protection is the OS keychain ACL. Extra sudo/pkexec is optional and off
by default (passkey config require-auth on) and never applies to
passkey run (headless MCP). macOS Keychain ACLs are the strong case;
Linux Secret Service and Windows Credential Manager are best-effort by
comparison (Linux also needs a running, unlocked keyring daemon).
How it works
You store named entries (one or more fields per entry) in the OS keychain. Values never go in
mcp.jsonor in argv. Interactive input usesgetpass.passkey init(opt-in) rewrites existing MCP configs so the editor startspasskey run ENTRY -- <original command>instead of a command with tokens inenv. Install does not scan Claude, Cursor, or other tools. Firstpasskeywith no args is onboarding (new), not a wrap.At start,
passkey runreads the keychain and injects env vars into that child process only. MCP tools (passkey_list,passkey_fields,passkey_status,passkey_doctor,passkey_wrap_server) return names, field names, and status — never secret values.passkey unwraprestores the original commands. A.backupis written beforeinitandunwrap.
Diagram source: docs/architecture.mmd. Disclosure
and threat model: SECURITY.md.
Related MCP server: keyvault
Features
Store multiple secrets per entry in the system keychain (macOS Keychain, Windows Credential Manager, Linux Secret Service)
Inject secrets as environment variables for subprocesses
Tool-agnostic — works with Claude, Gemini, VS Code, Cursor, OpenCode, Windsurf, Cline, Zed, and more
Interactive fuzzy-search entry picker (arrow keys + type-to-filter)
Tab completion for bash, zsh, and fish
Generate cryptographically secure random secrets (configurable length, guaranteed character diversity)
Templates for popular services (GitHub, AWS, Slack, OpenAI, Stripe, Vercel, PostgreSQL, MySQL)
Share & receive entries via encrypted file + passphrase (no key exchange needed)
Lifecycle tracking —
last_rotatedtimestamps,passkey rotate,passkey doctor --deepImport from Chrome password exports, existing MCP configs, or passkey backups
Encrypted bundle export/import for safe machine-to-machine transfer
Full audit logging with
passkey audit --summaryOS keychain ACL protection, with optional Touch ID / sudo prompt for interactive terminal commands (opt-in, off by default; never on
passkey run)Cross-platform: macOS (strong Keychain ACLs); Linux and Windows are best-effort vs that
Installation
pipx install passkey-mcp==0.4.0or:
uv tool install passkey-mcpStay on the pinned pipx line for team installs. Unpinned pip install passkey-mcp is not the documented path.
Installing does not wrap MCP configs. Wrapping is passkey init after
you have entries you want injected.
This repo / clone:
uv run passkeyRequirements
Python 3.10+
System keychain access (macOS Keychain, Windows Credential Manager, or a running Linux keyring daemon)
Quick Start
# First run — guided onboarding
passkey
# ┌─────────────────────────────────────────────┐
# │ Welcome to passkey │
# │ Secrets in your keychain. Not in config. │
# └─────────────────────────────────────────────┘
# Create your first entry now? [Y/n]
# Create an entry
passkey new
# Entry name: github
# Field name: GITHUB_TOKEN
# Value: [hidden input]
# List all entries
passkey list
# Run a command with secrets injected
passkey run github -- python app.pyShell Completion
Set up tab completion for your shell:
# Zsh (macOS default)
eval "$(passkey completion --zsh)"
# Bash
eval "$(passkey completion --bash)"
# Fish
passkey completion --fish > ~/.config/fish/completions/passkey.fishAfter setup, tab completion works everywhere:
passkey <TAB> # Show entries + subcommands
passkey hugg<TAB> # Fuzzy-match: huggingface_key
passkey get <TAB> # Complete entry names
passkey run <TAB> # Pick entries to loadInteractive Mode
When you omit an entry name (or type a partial match), passkey shows an interactive picker with fuzzy search:
$ passkey get
? Select entry to browse: (Use arrow keys, type to filter)
❯ github
openai
slack
myapi_read
myapi_write
# Type to filter:
$ passkey get my
# Automatically shows: myapi_read, myapi_write
# Single match auto-selects:
$ passkey get github
Copied 'GITHUB_TOKEN' to clipboard (auto-clears in 30s)
# Shortcut: passkey <entry> opens the picker directly
$ passkey githubFuzzy Matching
Prefix:
mymatchesmyapi_read,myapi_writeContains:
apimatchesmyapi_read,myapi_writeSequence:
ghmatchesgithubCase-insensitive:
GITHUBmatchesgithub
CLI Reference
Entry Management
Command | Description |
| Create a new entry interactively |
| List all entry names |
| Entry names only (one per line, for scripting) |
| Modify an existing entry |
| Delete an entry (exact match required) |
| Show entry metadata and field names |
| Clone an entry under a new name |
| Upsert a single field |
Retrieving Secrets
Command | Description |
| Interactive field picker — select a field to copy |
| Copy all fields to clipboard |
| Shortcut: same as |
Running Commands
# Run with secrets from one entry
passkey run github -- python app.py
# Run with secrets from multiple entries (later entries override)
passkey run aws github -- python deploy.py
# Secrets injected as env vars
passkey run myapi -- curl -H "Authorization: Bearer $API_TOKEN" https://api.example.com/v1/dataTool Integration
Wrapping is explicit. passkey init only sees adapter paths that already
exist on disk (Claude, Cursor, VS Code, …). A machine with none of those
configs is a first run, not a failed install — passkey doctor stays
quiet about missing clients.
# Scan configs and migrate plaintext secrets to keychain
passkey init # Auto-detect all tools
passkey init --tool claude # Specific tool
passkey init --tool vscode
# Restore wrapped configs back to inline commands (the way out)
passkey unwrap # Auto-detect all tools
passkey unwrap --dry-run # Preview first
# Add or update credentials for an MCP server
passkey add my-server # Interactive field entry
passkey add my-server --tool claude # Target a specific tool config
passkey add my-server --fields API_KEY TOKEN # Field names only; values via getpass
# Show security status across all tools
passkey status
# Run diagnostics
passkey doctor
# List MCP servers across tools
passkey serversSupported tools: claude, claude_desktop, gemini, vscode, cursor, opencode, windsurf, cline, zed
Export and Backup
# Export all entries to file (owner-only permissions set automatically)
passkey export backup.json
# Export specific entries
passkey export backup.json --entries github openai
# Export metadata only (no secret values)
passkey export backup.json --no-secrets
# Encrypted export (AES-256-GCM + scrypt)
passkey export backup.passkey.enc --encryptImport
# Import from passkey backup
passkey import backup.json
# Preview without importing
passkey import backup.json --dry-run
# Handle duplicates: skip (default), overwrite, merge
passkey import backup.json --mode merge
# Import from encrypted bundle
passkey import backup.passkey.enc --decrypt
# Import from Chrome password export (CSV)
passkey import passwords.csv --filter github.com
# Import from an existing MCP config file
passkey import claude_desktop_config.jsonAudit Log
passkey audit # View recent operations
passkey audit --limit 50 # Show more entries
passkey audit --clear # Clear the log
passkey audit --summary # Aggregate statisticsVerify Entry Health
# Exit non-zero if any required fields are missing
passkey check github GITHUB_TOKEN GITHUB_USERNAMEGenerate Secrets
# Generate a 32-char random secret (default)
passkey generate
# Generate with custom length
passkey generate --length 64
# Generate without auto-copying to clipboard
passkey generate --no-copyCredential Templates
# List built-in templates
passkey template list
# Show a template's fields
passkey template show github
# Apply a template to create a new entry
passkey template apply github
# Save a custom template from an existing entry
passkey template add my-service --from myapiShare & Receive
# Share an entry (generates encrypted file + passphrase)
passkey share github --output github-shared.passkey
# Receive a shared entry
passkey receive github-shared.passkeyLifecycle Management
# Mark an entry as rotated (updates last_rotated timestamp)
passkey rotate github
# Run expanded diagnostics
passkey doctor --deepAuthentication
The primary protection for your secrets is the OS keychain's own access control — macOS Keychain, Windows Credential Manager, and Linux Secret Service all gate access per application and may show their own unlock prompts. Passkey relies on that by default, so wrapped MCP servers can start headless (no terminal, no password prompt).
If you want an additional OS-auth prompt for interactive terminal
commands (get, new, edit, export, ...), opt in:
passkey config require-auth onThe tradeoff, explicitly:
Off (default): Anyone with an unlocked session as your user can read secrets via
passkey— same as any other tool reading your keychain (e.g.security find-generic-password). MCP servers work everywhere, including stock macOS with no tty.On: Interactive commands first run
sudo -v(macOS, Touch ID ifpam_tidis configured) orpkexec(Linux). This breaks any non-interactive use of those commands —passkey runis never gated either way, because MCP servers invoke it headless.Windows: the setting is a no-op — UAC cannot authenticate the current process, so protection comes from Credential Manager ACLs alone.
Unwrapping (leaving passkey)
passkey init rewrites your MCP configs to the wrapped form — but it is
not a one-way door. Restore the original inline commands with:
passkey unwrap # all detected tools
passkey unwrap --tool claude # one tool
passkey unwrap --server github # one server
passkey unwrap --restore-secrets # also write secret values back into the config
passkey unwrap --dry-run # preview onlyA .backup of each config is written before changes (init does this too),
so you can also restore by hand.
MCP Config Example
passkey init rewrites detected servers from the first form to the second.
passkey unwrap goes the other way.
Before (insecure — secret in plaintext):
{
"mcpServers": {
"github": {
"command": "python",
"args": ["-m", "github.server"],
"env": {
"GITHUB_TOKEN": "ghp_PLAINTEXT_SECRET_HERE"
}
}
}
}After (secure — secret loaded from keychain at runtime):
{
"mcpServers": {
"github": {
"command": "passkey",
"args": ["run", "github", "--", "python", "-m", "github.server"]
}
}
}MCP Server Mode
Passkey can run as an MCP server itself, letting AI assistants invoke passkey tools via natural language:
{
"mcpServers": {
"passkey": {
"command": "passkey-mcp-server"
}
}
}Available tools: passkey_list, passkey_fields, passkey_status, passkey_doctor, passkey_wrap_server.
AI assistants can discover entry names but never see secret values — secrets only flow out via passkey run.
Data Storage
Platform | Data directory |
macOS |
|
Linux |
|
Windows |
|
Override with PASSKEY_DATA_DIR. Secrets themselves are in the system
keychain, not in that directory. On disk you get metadata only: lock file,
audit log, and entries.json (names — not values).
Security
Encrypted at rest: System keychain handles encryption (AES-256-GCM on macOS, DPAPI on Windows)
No secrets logged: Only entry names and operation types appear in the audit log
Hidden input:
getpassused for all secret entryClipboard auto-clear: Copied secrets cleared after 30 seconds
Secure export: Files created with
chmod 600(owner-only)Encrypted bundles: AES-256-GCM + scrypt (N=2^20) for portable transfer
LLM-safe: AI assistants can list entries but never read values
No network: no telemetry, no cloud sync; supply chain is the install path
See SECURITY.md for the disclosure policy and threat model (private vulnerability reporting; aim to acknowledge within 7 days).
Troubleshooting
"Failed to access Keychain" (macOS)
Grant Terminal / Python keychain access in System Settings → Privacy & Security.
Touch ID not working
Ensure pam_tid.so is configured in /etc/pam.d/sudo:
sudo sed -i '' '2i\
auth sufficient pam_tid.so
' /etc/pam.d/sudoLinux headless / no keyring daemon
Set a backend explicitly:
export PYTHON_KEYRING_BACKEND=keyring.backends.SecretService.Keyring
# or for headless environments:
export PYTHON_KEYRING_BACKEND=keyring.backends.null.KeyringEntry not found
passkey list # check exact names (case-sensitive)Import shows "skipped (exists)"
passkey import backup.json --mode mergeDevelopment
Python 3.10+. uv only — see CONTRIBUTING.md.
uv sync
uv run pytest -q
uv run ruff check passkey/ tests/Regenerate the architecture graphic (requires @mermaid-js/mermaid-cli):
mmdc -i docs/architecture.mmd -o docs/architecture.svg -b transparent
mmdc -i docs/architecture.mmd -o docs/architecture.png -b '#ffffff' -s 2License
MIT — see LICENSE
Available Tools
5 toolspasskey_doctorA
Run diagnostics on passkey and MCP configurations.
Checks for common issues including:
Config file existence and validity for all detected tools
Passkey command availability in PATH
Keychain access permissions
MCP servers with missing passkey entries
MCP servers with exposed secrets
(deep=True) entry age analysis and bundle file permissions
Args: deep: Include extended checks (entry age, bundle permissions)
Returns: Dictionary with diagnostic results
Inputs: deep.
passkey_fieldsA
List field names in a passkey entry (without revealing values).
Returns the names of secret fields stored in the specified entry. This allows discovering what secrets are available without exposing the actual secret values.
Args: entry_name: Name of the passkey entry (e.g., "slack")
Returns: List of field names (e.g., ["SLACK_TOKEN", "SLACK_COOKIE"])
Raises: Exception: If entry not found or keychain access fails
Inputs: entry_name (required). Returns: result
passkey_listA
List all available passkey entries.
Returns the names of all secret entries stored in the system keychain. Each entry can contain multiple key-value secret pairs.
Returns: List of entry names (e.g., ["slack", "github", "aws"])
Inputs: no named parameters. Returns: result
passkey_statusA
Show security status of MCP servers across all detected tools.
Analyzes configs for Claude, Gemini, VS Code, Cursor, OpenCode, Windsurf, Cline, and Zed to identify which MCP servers have their secrets secured via passkey and which still have credentials exposed in plaintext.
Returns: Dictionary with per-tool server status and summary counts
Inputs: no named parameters.
passkey_wrap_serverA
Update MCP config files to use passkey wrapper for a server.
Prerequisites: Entry must already exist in passkey keychain.
Args: server_name: Name of the server (must match passkey entry) config_paths: Config files to update (default: all detected configs)
Returns: - configs_updated: List of updated config paths - configs_skipped: List of configs where server not found - errors: Any errors encountered
Inputs: server_name (required), config_paths.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 5 tools
Each tool has a distinct role: listing entries, exploring fields, showing status, running diagnostics, and updating configs. passkey_status and passkey_doctor both touch on security posture, but descriptions clarify that one is a quick status check while the other is a detailed diagnostic. No two tools are truly interchangeable.
All tools share the passkey_ prefix, making the set cohesive. However, the suffix pattern is mixed: passkey_list is verb-like, while passkey_fields, passkey_status, and passkey_doctor are noun-like, and passkey_wrap_server is verb_noun. The prefix provides enough consistency that the naming is still predictable.
Five tools is a well-scoped set for the domain of passkey management for MCP servers. Each tool addresses a clear need without redundancy or bloat, and the count is solidly within the ideal 3-15 range.
The tool surface covers the key workflows: discovering entries, inspecting fields, assessing security status, diagnosing issues, and applying the wrapper. Missing operations like creating or deleting passkey entries are likely out of scope since passkey itself manages those. The only minor gap is the lack of an 'unwrap' or 'revert' operation, but the core purpose is well covered.
Maintenance
Related MCP Connectors
Secrets for developers and agents—secure context and workflows without exposing secret values.
Encrypted secret store and rotation for autonomous agent credentials
Encrypted store for API keys and database URLs your code needs. Use them without reading them.
A secret store for AI agents: the agent never sees the plaintext.
Related MCP Servers
- AlicenseAqualityFmaintenanceA GitHub-native secrets manager that allows AI assistants to securely manage, generate, and validate credentials without exposing sensitive values in conversation history. It supports secret scanning, environment diffing, and secure command execution by injecting masked variables directly into the runtime environment.88 npm2MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to securely manage API keys and secrets via the MCP protocol, with encrypted storage at rest and a simple CLI and Python SDK.1MIT
- AlicenseCqualityCmaintenanceEnables AI agents to securely store and manage encrypted secrets locally while using them indirectly through environment-variable injection or file writes, so plaintext values never enter the agent's context.8MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI coding agents to securely inject API keys and secrets into environment files, configs, or commands without the agent ever seeing the secret values.9 npmMIT