enpass-mcp
You can locally unlock registered Enpass vaults and read/search/export their entries, credentials, TOTP codes, and attachments; this schema exposes no write tools.
List registered vaults and see file, keychain-password, and unlocked status.
Unlock or lock a vault using the OS-keychain master password, never via a tool argument.
List/search/filter entries by title, username, URL, category, folder, and limit, without returning passwords.
Get a full entry including sensitive values like passwords and TOTP.
Get just an entry's password and current TOTP code.
Generate the current TOTP/2FA code for an entry.
List an entry's attachments (name, size, MIME).
Export/decrypt an attachment to disk, or return small files inline as base64.
Check folder-sync status and whether the sync-folder copy is newer.
All entry access requires unlocking first. Listing avoids secrets; get/export operations can return them.
Provides tools for managing Enpass password vaults, including unlocking vaults, listing and reading entries, retrieving passwords and TOTP codes, handling attachments, and optionally creating and deleting entries.
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., "@enpass-mcp unlock my personal vault and list all entries"
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.
enpass-mcp
A Model Context Protocol (MCP) server that gives an AI assistant controlled, local access to your Enpass password vaults: unlock a vault, list vaults, and list and read entries. Creating and deleting entries is possible too, but switched off until you enable it.
Runs locally over stdio. Your Enpass vault never leaves your machine, and your master password never passes through the model: it is stored in your operating system's keychain and read directly by the server.
Why this is safe
Master passwords live in the OS keychain (macOS Keychain, Windows Credential Manager, Linux Secret Service), not in config files, not in environment variables, and never as a tool argument. The
unlock_vaulttool deliberately takes no password parameter, so the password can never end up in the model's context or in logs.The vault stays local. The server reads the encrypted
vault.enpassdbfile directly with SQLCipher. Nothing is uploaded anywhere.Reads are explicit. Listing entries never returns passwords. Secrets are only returned by
get_item/get_password, when you explicitly ask for them.Read-only unless you say otherwise. Out of the box the server cannot change anything: the writing tools are not even advertised. Set
ENPASS_MCP_ALLOW_WRITES=1to enable them (see Writing).
Entry passwords are, by design, returned to the assistant when you ask for them, so only connect this to an assistant and vaults you trust.
Related MCP server: passwords-mcp
Requirements
Node.js 18 or newer
An Enpass 6 / 7 / 8 vault (
vault.enpassdb, SQLCipher format)On Linux: a Secret Service provider (GNOME Keyring or KWallet) for password storage
Native dependencies (better-sqlite3-multiple-ciphers, @napi-rs/keyring) ship
prebuilt binaries for common platforms, so no compiler is required in the normal case.
Install
git clone https://github.com/bitterdev/enpass-mcp.git
cd enpass-mcp
npm install
npm link # optional: makes the `enpass-mcp` command available globallyRegister your vaults (do this once, in a terminal)
This is the secure step that keeps the master password away from the model. You run it yourself; the password is typed into a hidden prompt and stored in the OS keychain.
# Find your vault files automatically
enpass-mcp discover
# Register a vault (you will be prompted for the master password)
enpass-mcp add-vault personal --path "/Users/you/Documents/Enpass/Vaults/primary/vault.enpassdb"
enpass-mcp add-vault work --path "/path/to/work/vault.enpassdb"
# With a keyfile
enpass-mcp add-vault personal --path "/path/vault.enpassdb" --keyfile "/path/vault.keyfile"
# Manage
enpass-mcp list-vaults
enpass-mcp test-unlock personal
enpass-mcp remove-vault workadd-vault verifies the password can actually unlock the vault before saving it.
The vault file is usually found at:
OS | Typical location |
macOS |
|
Windows |
|
Linux |
|
If you sync via Dropbox / OneDrive / WebDAV, point --path at the synced copy.
Connect it to your assistant
The server speaks MCP over stdio. Point your MCP client at enpass-mcp serve.
Claude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"enpass": {
"command": "enpass-mcp",
"args": ["serve"]
}
}
}If you did not run npm link, use the absolute path instead:
{
"mcpServers": {
"enpass": {
"command": "node",
"args": ["/absolute/path/to/enpass-mcp/src/cli.js", "serve"]
}
}
}Claude Code:
claude mcp add enpass -- enpass-mcp serveTools
Tool | Description |
| List registered vaults, whether their file exists, whether a password is stored, and whether they are unlocked. |
| Unlock a vault using the master password from the OS keychain. Takes only a vault name, never a password. |
| Lock a vault and clear its derived key from memory. |
| List entries (title, username, URL). Never returns passwords. Supports |
| Return a full entry including all field values (password, TOTP, etc.) and its attachment list. |
| Return the password and, if present, the current TOTP code of an entry. |
| Generate the current TOTP / 2FA one-time code for an entry, with seconds until it rotates. |
| List an entry's file attachments (name, size, MIME). |
| Decrypt an attachment; writes it to disk and returns the path (or base64 inline for small files). |
| List the vaults that use Enpass folder sync and whether the copy in the sync folder is newer. |
With ENPASS_MCP_ALLOW_WRITES=1 three more tools appear (see Writing):
Tool | Description |
| Create an entry, including custom fields; sensitive values are encrypted the way Enpass does it. |
| Delete an entry, or move it to the trash, leaving the tombstone Enpass uses so the deletion syncs. |
| Take in a newer copy from the sync folder, after backing up the local vault. |
list_items / get_item work for every Enpass entry type (logins, credit
cards, secure notes, identities, etc.), not just logins, and return all fields.
A typical assistant flow: list_vaults → unlock_vault → list_items → get_password.
How it works
Enpass stores each vault as a standard SQLCipher database (vault.enpassdb). The raw
encryption key is derived from your master password (optionally combined with a
keyfile) and the 16-byte salt at the start of the file:
PBKDF2-HMAC-SHA512, 100000 iterations (older vaults) or 320000 (newer vaults), the first 32 bytes used as the raw SQLCipher key
opened with
cipher_compatibility4 (Enpass 6.8+) or 3 (older vaults)
The server tries these combinations automatically, so it works across Enpass vault versions. The derived key is kept in memory only, for the lifetime of the server process, and is never written to disk or returned to the model.
References: Enpass Security Whitepaper, hazcod/enpass-cli.
Per-item field encryption
Enpass encrypts every value flagged as "sensitive" a second time, underneath
SQLCipher, with a key that belongs to the entry rather than the vault. Fields
carrying that layer have itemfield.algo_version = 1:
Piece | Where | Layout |
Key and nonce |
| 44 bytes: 32-byte AES-256 key, then a 12-byte GCM nonce |
Value |
| hex of |
Additional data | the entry's uuid | hyphens stripped, hex-decoded to 16 raw bytes |
Binding the AAD to the entry uuid is what makes a value unusable if it is copied into
another entry. Enpass did not re-encrypt existing entries when it introduced this
layer, so one vault mixes ciphertext and plaintext under the same algo_version; a
value is treated as encrypted only when it has the shape of a payload (pure hex, whole
bytes, longer than the tag on its own).
A value that looks encrypted but fails authentication is returned as null with
decryptionFailed: true, never as the raw column content: stored ciphertext is a
plausible-looking string, and handing that back would silently pass off a wrong secret
as a real one.
Two-factor codes (TOTP)
Entries with a one-time-password secret (stored by Enpass as an otpauth:// URI)
can produce a live 2FA code: get_otp returns the current 6-digit code and the
seconds until it rotates, and get_password includes the current code alongside the
password. This lets an assistant fill both the password and the 2FA prompt.
Attachments
Enpass keeps file attachments encrypted. Small files (up to 1 KB) sit inline in the
vault; larger files live in separate <uuid>.enpassattach SQLCipher files next to the
vault, each encrypted with its own key stored in the vault. export_attachment handles
both: it decrypts the file and, by default, writes it to disk and returns the path, so
it works for files of any size without pushing binary data through the model.
Two details of that format are easy to miss, and both were wrong here until they were measured against real vaults:
attachment.passwordis not a key blob. It holds the finished SQLCipher raw-key expression as text,x'<64 hex key chars><32 hex salt chars>', and has to be passed through verbatim. Hex-encoding it produces a key SQLCipher rejects, and every external attachment then fails with "file is not a database".The stored payload is frequently a base64 data URI (
data:application/pdf;base64,...) rather than the file itself, so it has to be decoded. Writing those bytes straight to disk yields a text file with a.pdfname.attachment.sizecounts the stored bytes, which is why it is larger than the exported file for anything wrapped this way.
If you hit a vault whose attachments still do not decrypt, please open an issue with the
(non-secret) schema of your attachment table.
Attachments also do not travel inside the folder-sync container: Enpass drops the
.enpassattach files into the sync folder next to it. sync_pull fetches the ones this
machine is missing, and publishing a write copies local ones the sync folder lacks.
Existing files are never overwritten or removed in either direction.
Writing (opt-in)
Writing is switched off by default. A password vault is the last place where a tool
should be able to change data just because a model decided to, so the server starts
read-only and does not even list create_item, delete_item, add_attachment and
sync_pull until you turn them on:
ENPASS_MCP_ALLOW_WRITES=1Set it in the server's environment (in your MCP client config, or in the .env next to
vaults.json). Nothing else changes: reading works exactly the same either way.
Enpass must be closed while writing. The app keeps the database in memory and would write its own cached copy back over any change made underneath it. Every writing tool refuses to run while Enpass is open.
Earlier versions of this README claimed writing was impossible because recent vaults (schema version 6) crash Enpass when entries are inserted directly. The crash was real, the diagnosis was wrong. Three concrete rules make it work, all of them derived from what Enpass itself writes:
Enpass never stores
NULL. The crash isEXC_BAD_ACCESSinstrlenon a null pointer: a column omitted from theINSERTdefaults toNULL, and the app callsstrlenon it. Every column is written explicitly, empty strings instead ofNULL.A template has a fixed field set.
login.defaultalways carries the same nine fields, in the same order, with the same field uids, even when most are empty. Writing only the fields you happen to have a value for produces an entry the app cannot render.The per-item key is reproducible.
item.keyis a 32-byte AES-256 key plus a 12-byte GCM nonce, stored ashex(ciphertext || tag)with the item UUID as additional authenticated data. Nothing in it is tied to Enpass internals, so a fresh random key per item is fine.
Verified end to end against a real vault: written, read back, decrypted to the original, deleted, synced in both directions, and Enpass opens the vault without crashing.
Attaching files
add_attachment takes a path, never base64 in the argument list, and stores the bytes
raw in the attachment's own encrypted database. Two rules come out of the same format
details as reading, and getting either wrong looks exactly like a corrupted file while
every byte in the vault is intact:
Store the raw bytes, not a data URI. A payload of
data:;base64,...is handed out verbatim by the Enpass app, so the export is a text file with a.pdfor.jpegname.add_attachmentrejects such input instead of storing it.Pass the key expression through unchanged. It is already the ASCII SQLCipher raw-key expression; hex-encoding it again produces "file is not a database".
The row is built from the columns the vault's attachment table actually has, because the
column set differs between vault generations, and every column gets an explicit value so
no NULL reaches Enpass.
Regression guard: npm test writes a payload containing 0x00 and invalid UTF-8
sequences, reads it back, and asserts the bytes are identical and stored unencoded.
Repairing attachments that were stored as a data URI
scripts/repair-data-uri-attachments.mjs decodes such payloads back into raw bytes. It is
a dry run unless --apply is given, refuses to run while Enpass is open, and checks the
decoded payload against the file signature its name implies before writing.
The bytes of an external attachment live in its own .enpassattach file, and folder sync
keeps a second copy of every one of those files in the sync folder. Repairing the
vault under the Enpass container therefore fixes one machine only. --sync-folders
repairs the shared copies too, and only ever writes a payload that decodes byte for byte
to what the local machine already holds.
Configuration
Master passwords are in the OS keychain; only non-secret data (vault names and paths)
is stored in a small vaults.json:
macOS:
~/Library/Application Support/enpass-mcp/vaults.jsonWindows:
%APPDATA%\enpass-mcp\vaults.jsonLinux:
~/.config/enpass-mcp/vaults.json
Override the directory with ENPASS_MCP_CONFIG_DIR.
Environment variables:
Variable | Effect |
|
|
| Where |
| Optional fallback master password for vaults without a keychain entry. Prefer the keychain. |
| Same, for one specific vault. |
Development
npm test # runs against genuine SQLCipher fixtures in test/fixtures
# Rebuild the fixtures from scratch with real SQLCipher (vault + entries + attachments)
npm install --no-save @journeyapps/sqlcipher
npm run generate-fixturesCI (GitHub Actions) creates a vault from scratch with real SQLCipher, seeds entries and attachments, then runs the full read-only test suite on Node 18/20/22.
Security notes and limitations
Anyone who can talk to this MCP server can read every password in a vault once it is unlocked. Only connect trusted clients.
The server does not implement Enpass sync, item history, or trashing.
The server is read-only and never modifies a vault. Creating or editing entries is intentionally not supported, because direct database writes crash recent Enpass vaults (see Why there is no write support).
This is an independent project and is not affiliated with or endorsed by Enpass.
License
MIT © Fabian Bitter
Available Tools
10 toolsexport_attachmentExport attachmentA
Decrypts a file attachment from an unlocked vault. By default it writes the file to disk and returns the path (works for any size). Set inline=true to get small files as base64 instead.
Inputs: uuid (required), vault (required), attachment (required), dest, +1 more.
get_itemGet entryA
Returns a full entry from an unlocked vault including sensitive field values (password, TOTP, etc.). Use this when the user explicitly wants the credentials.
Inputs: uuid (required), vault (required).
get_otpGet one-time codeA
Generates the current TOTP / two-factor one-time code for an entry that stores an OTP secret. Returns the code and how many seconds until it rotates, so it can be entered for 2FA.
Inputs: uuid (required), vault (required).
get_passwordGet passwordA
Returns just the password (and TOTP if present) of an entry in an unlocked vault.
Inputs: uuid (required), vault (required).
list_attachmentsList attachmentsA
Lists the file attachments of an entry in an unlocked vault (name, size, MIME). Use export_attachment to get the file itself.
Inputs: uuid (required), vault (required).
list_itemsList entriesA
Lists entries in an unlocked vault. Returns titles, usernames and URLs but no passwords or other sensitive values. Supports optional search, category and folder filters.
Inputs: vault (required), limit, query, folder, +1 more.
list_vaultsList vaultsA
Lists the Enpass vaults registered on this machine, whether their file exists, whether a master password is stored in the OS keychain, and whether they are currently unlocked in this session.
Inputs: no named parameters.
lock_vaultLock vaultA
Locks a vault and clears its derived key from memory for this session.
Inputs: vault (required).
sync_statusSync statusA
Lists every vault that uses Enpass folder sync, where its sync folder is, and whether the copy there is newer than the local one. Vaults without folder sync are not listed. Answers include a reason, so 'no baseline yet' is distinguishable from 'up to date'.
Inputs: no named parameters.
unlock_vaultUnlock vaultA
Unlocks a registered vault using the master password stored in the OS keychain. The password is never passed as an argument and never reaches the model. Must be called before listing or reading entries.
Inputs: vault (required).
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 10 tools
Each tool has a clearly distinct purpose: vault status management (list_vaults, unlock_vault, lock_vault), item retrieval (list_items, get_item, get_password, get_otp), attachment handling (list_attachments, export_attachment), and sync tracking (sync_status). No two tools overlap in function.
All tool names follow a consistent verb_noun pattern with snake_case (e.g., list_vaults, unlock_vault, get_password, export_attachment). The verb is always listed first and the noun is specific, making the naming predictable and readable.
With 10 tools, the server is well-scoped for its purpose. It covers vault lifecycle, item queries, OTP generation, attachments, and sync status without unnecessary bloat or sparse coverage.
The toolset fully covers read and credential-retrieval workflows, including attachments and OTP. However, there are no create, update, or delete operations for items, which may be intentional for security but limits management functionality.
Maintenance
Related MCP Connectors
An MCP server that provides read access to your cloud storage providers, bank accounts and more.
Authenticated MCP access to WebMoney wallets, transactions, messages and invoices.
MCP server for mandates, delegation, policy-gated execution, credential grants, and audit.
Governed MCP gateway: one endpoint for your tools, with credential custody and audit log.
Related MCP Servers
- FlicenseAqualityDmaintenanceA Model Context Protocol (MCP) server for Proton Pass. It wraps the official pass-cli binary and exposes 14 tools so any MCP-compatible AI assistant (Claude Desktop, Claude Code, etc.) can manage your vaults, credentials, and secrets.14-
- AlicenseAqualityBmaintenanceMCP server for the Nextcloud Passwords app, enabling reading and managing password entries (list, search, create, update, delete) with strong security guarantees.14MIT
- AlicenseNot gradedqualityCmaintenanceLocal, encrypted password vault with AES-256-GCM and PBKDF2, exposing MCP tools for secure credential management, password generation, and search.MIT
- AlicenseNot gradedqualityCmaintenanceMCP server for accessing Vaultwarden servers with multi-account support.1MIT