Warden MCP Server
MCP server for Vaultwarden/Bitwarden vault management. Enables AI agents to securely create, search, read, and update vault items via the official Bitwarden CLI, with safe-by-default redaction and support for both stdio and SSE transports.
README
warden-mcp
Programmatic Vaultwarden/Bitwarden vault management over MCP (Model Context Protocol), backed by the official Bitwarden CLI (bw).
This project exists to let agents and automation create/search/read/update/move vault items without re-implementing Bitwarden’s client-side crypto.
Published package: @icoretech/warden-mcp
Highlights
- MCP Streamable HTTP (SSE) endpoint at
POST /sse+ health check atGET /healthz - Runtime guardrail metrics at
GET /metricsz - Item types: login, secure note, card, identity, plus an SSH key convention (secure note + standard fields)
- Attachments: create/delete/download
- Organization + collection helpers (list + org-collection CRUD)
- Safe-by-default: item reads are redacted unless explicitly revealed; secret helper tools return
nullunlessreveal: true - Strong fit for LLM automation: pair it with a browser-capable MCP host so an agent can fetch credentials, complete sign-in flows, read TOTP codes, and keep automating after login
LLM Automation Use Case
warden-mcp is not only useful for vault administration. A very practical use case is pairing it with an LLM that can also drive a browser.
That lets the agent do end-to-end authenticated workflows such as:
- open a site or backoffice in the browser
- read the right login from Vaultwarden or Bitwarden
- fill username and password without hardcoding secrets in prompts or config
- retrieve a current TOTP code with
keychain.get_totpfor TOTP-based MFA - continue the real task after login, such as navigation, data entry, exports, or routine admin work
In practice, this is what makes the server useful for full automation, not just secret lookup. The same MCP session that gives the model browser control can also give it scoped access to the credentials and MFA material needed to finish the workflow.
Runtime Requirement
This package shells out to the official Bitwarden CLI, bw.
Runtime resolution order:
BW_BINif you set it explicitly- bundled
@bitwarden/clioptional dependency if it is present - system
bwfromPATH
That means package installation can succeed even when the optional dependency is skipped by the environment. In that case you must install bw separately or point BW_BIN to it.
Explicit fallback install:
npm install -g @bitwarden/cli
Or run with an explicit binary path:
BW_BIN=/absolute/path/to/bw npx -y @icoretech/warden-mcp
Install And Run
Choose a transport
- Use
--stdiowhen you want a local MCP host to spawnwarden-mcpdirectly with one fixed Bitwarden profile - Use default HTTP mode when you want one running
warden-mcpservice to serve multiple clients or multiple Bitwarden profiles via per-requestX-BW-*headers
Local stdio mode
npx -y @icoretech/warden-mcp --stdio
For stdio mode, you must provide Bitwarden credentials up front via env vars:
BW_HOST=https://vaultwarden.example.com \
BW_USER=user@example.com \
BW_PASSWORD='your-master-password' \
npx -y @icoretech/warden-mcp --stdio
API key login works too:
BW_HOST=https://vaultwarden.example.com \
BW_CLIENTID=user.xxxxx \
BW_CLIENTSECRET=xxxxx \
BW_PASSWORD='your-master-password' \
npx -y @icoretech/warden-mcp --stdio
Shared HTTP mode
Start one long-lived MCP server:
npx -y @icoretech/warden-mcp
Verify it is up:
curl -fsS http://localhost:3005/healthz
This mode is what makes warden-mcp different from a simple local wrapper:
- the server stays stateless at the HTTP boundary
- Bitwarden/Vaultwarden credentials are sent per request via
X-BW-*headers - one running server can front different vault hosts or different identities without restarting
- it fits shared-agent and gateway setups much better than per-client local processes
Docker
docker run --rm -p 3005:3005 ghcr.io/icoretech/warden-mcp
Global install
npm install -g @icoretech/warden-mcp
warden-mcp
Connect From MCP Hosts
For local MCP hosts, stdio is the most portable option.
npx -y @icoretech/warden-mcp --stdio
The examples below use Bitwarden API-key auth. If you prefer username/password login, replace BW_CLIENTID + BW_CLIENTSECRET with BW_USER.
CLI-based hosts
These hosts let you register warden-mcp directly from the command line:
# Codex
codex mcp add warden \
--env BW_HOST=https://vaultwarden.example.com \
--env BW_CLIENTID=user.xxxxx \
--env BW_CLIENTSECRET=xxxxx \
--env BW_PASSWORD='your-master-password' \
-- npx -y @icoretech/warden-mcp --stdio
# Claude Code
claude mcp add-json warden '{"command":"npx","args":["-y","@icoretech/warden-mcp","--stdio"],"env":{"BW_HOST":"https://vaultwarden.example.com","BW_CLIENTID":"user.xxxxx","BW_CLIENTSECRET":"xxxxx","BW_PASSWORD":"your-master-password"}}'
# Qwen Code
qwen mcp add warden \
-e BW_HOST=https://vaultwarden.example.com \
-e BW_CLIENTID=user.xxxxx \
-e BW_CLIENTSECRET=xxxxx \
-e BW_PASSWORD=your-master-password \
npx -y @icoretech/warden-mcp --stdio
JSON config hosts
These hosts all use the same stdio payload shape. Only the config file location changes:
- Codex:
~/.codex/config.toml - Cursor:
~/.cursor/mcp.jsonor.cursor/mcp.json - Claude Desktop:
~/Library/Application Support/Claude/claude_desktop_config.json - Qwen Code:
~/.qwen/settings.jsonor.qwen/settings.json
Shared JSON shape:
{
"mcpServers": {
"warden": {
"command": "npx",
"args": ["-y", "@icoretech/warden-mcp", "--stdio"],
"env": {
"BW_HOST": "https://vaultwarden.example.com",
"BW_CLIENTID": "user.xxxxx",
"BW_CLIENTSECRET": "xxxxx",
"BW_PASSWORD": "your-master-password"
}
}
}
}
Codex uses TOML instead of JSON:
[mcp_servers.warden]
command = "npx"
args = ["-y", "@icoretech/warden-mcp", "--stdio"]
[mcp_servers.warden.env]
BW_HOST = "https://vaultwarden.example.com"
BW_CLIENTID = "user.xxxxx"
BW_CLIENTSECRET = "xxxxx"
BW_PASSWORD = "your-master-password"
Windsurf
Windsurf uses the same stdio idea but stores it in ~/.codeium/windsurf/mcp_config.json:
{
"mcpServers": {
"warden": {
"command": "npx",
"args": ["-y", "@icoretech/warden-mcp", "--stdio"],
"env": {
"BW_HOST": "https://vaultwarden.example.com",
"BW_CLIENTID": "user.xxxxx",
"BW_CLIENTSECRET": "xxxxx",
"BW_PASSWORD": "your-master-password"
}
}
}
}
Shared HTTP connections
If your MCP host supports Streamable HTTP with custom headers, you can connect to one long-lived warden-mcp service instead of spawning a local stdio process.
Start the shared server:
npx -y @icoretech/warden-mcp
Every MCP request must include:
X-BW-HostX-BW-Password- either
X-BW-ClientId+X-BW-ClientSecret, orX-BW-User
Example health check:
curl -fsS \
-H 'X-BW-Host: https://vaultwarden.example.com' \
-H 'X-BW-ClientId: user.xxxxx' \
-H 'X-BW-ClientSecret: xxxxx' \
-H 'X-BW-Password: your-master-password' \
http://localhost:3005/healthz
Example MCP endpoint:
http://localhost:3005/sse?v=2
This shared-server mode is useful when:
- one MCP gateway needs to front multiple Bitwarden profiles
- you want to rotate vault credentials per request instead of per process
- you are integrating from a custom client or agent host that can attach HTTP headers
- you want one always-on service instead of each editor spawning its own
bw-backed subprocess
Client examples for shared HTTP mode:
# Claude Code
claude mcp add-json warden '{"type":"http","url":"http://localhost:3005/sse?v=2","headers":{"X-BW-Host":"https://vaultwarden.example.com","X-BW-ClientId":"user.xxxxx","X-BW-ClientSecret":"xxxxx","X-BW-Password":"your-master-password"}}'
// Cursor (~/.cursor/mcp.json)
{
"mcpServers": {
"warden": {
"url": "http://localhost:3005/sse?v=2",
"headers": {
"X-BW-Host": "https://vaultwarden.example.com",
"X-BW-ClientId": "user.xxxxx",
"X-BW-ClientSecret": "xxxxx",
"X-BW-Password": "your-master-password"
}
}
}
}
// Qwen Code (~/.qwen/settings.json)
{
"mcpServers": {
"warden": {
"httpUrl": "http://localhost:3005/sse?v=2",
"headers": {
"X-BW-Host": "https://vaultwarden.example.com",
"X-BW-ClientId": "user.xxxxx",
"X-BW-ClientSecret": "xxxxx",
"X-BW-Password": "your-master-password"
}
}
}
}
// Windsurf (~/.codeium/windsurf/mcp_config.json)
{
"mcpServers": {
"warden": {
"serverUrl": "http://localhost:3005/sse?v=2",
"headers": {
"X-BW-Host": "https://vaultwarden.example.com",
"X-BW-ClientId": "user.xxxxx",
"X-BW-ClientSecret": "xxxxx",
"X-BW-Password": "your-master-password"
}
}
}
}
Codex currently fits better with stdio here, because its MCP config supports a bearer token env var for remote servers but not arbitrary custom X-BW-* header injection.
Verify bw is available
bw --version
If that fails after install, your environment likely skipped the optional @bitwarden/cli dependency. Install it explicitly:
npm install -g @bitwarden/cli
How It Works
The server executes bw commands on your behalf:
- In HTTP mode, Bitwarden/Vaultwarden connection + credentials are provided via HTTP headers per request. Env-var fallback is disabled by default; set
KEYCHAIN_ALLOW_ENV_FALLBACK=trueto enable it for single-tenant HTTP deployments. - In stdio mode, Bitwarden/Vaultwarden credentials are loaded once from
BW_*env vars at startup. - The server maintains per-profile
bwstate underKEYCHAIN_BW_HOME_ROOTto avoid session/config clashes. - Writes can optionally call
bw sync(internal; not exposed as an MCP tool).
Required Headers
X-BW-Host(must be an HTTPS origin, for examplehttps://vaultwarden.example.com)X-BW-Password(master password; required to unlock)- Either:
X-BW-ClientId+X-BW-ClientSecret(API key login), orX-BW-User(email for user/pass login; still usesX-BW-Password)
- Optional:
X-BW-Unlock-Interval(seconds)
Security Model
There is no built-in auth layer in v1. Run it only on a trusted network boundary (localhost, private subnet, VPN, etc.).
Credential resolution:
- HTTP mode requires
X-BW-*headers on every request by default. Without them, tools return an error. - Stdio mode reads
BW_*env vars at startup (single-tenant). - To allow HTTP mode to fall back to server env vars when headers are absent (single-tenant HTTP), set
KEYCHAIN_ALLOW_ENV_FALLBACK=true. Security warning: this means any client that can reach the HTTP endpoint gets full vault access without providing credentials. Only use this behind network-level access control.
Mutation control:
- Set
READONLY=trueto block all write operations (create/edit/delete/move/restore/attachments). - Set
NOREVEAL=trueto force allrevealparameters tofalseserver-side. Clients can still requestreveal: true, but the server will silently downgrade to redacted output. This prevents prompt injection from tricking an LLM agent into exfiltrating secrets. - Session guardrails:
KEYCHAIN_SESSION_MAX_COUNT(default32)KEYCHAIN_SESSION_TTL_MS(default900000)KEYCHAIN_SESSION_SWEEP_INTERVAL_MS(default60000)KEYCHAIN_MAX_HEAP_USED_MB(default1536, set0to disable memory fuse)KEYCHAIN_METRICS_LOG_INTERVAL_MS(default0, disabled)NOREVEAL/KEYCHAIN_NOREVEAL(defaultfalse; force all reveals to false)KEYCHAIN_ALLOW_ENV_FALLBACK(defaultfalse; HTTP env-var credential fallback)
Redaction defaults (item reads):
- Login:
password,totp - Card:
number,code - Identity:
ssn,passportNumber,licenseNumber - Custom fields: hidden fields (Bitwarden
type: 1) - Attachments:
attachments[].url(signed download URL token) - Password history:
passwordHistory[].password
Reveal rules:
- Tools accept
reveal: truewhere applicable (default isfalse). - Secret helper tools (
get_password,get_totp,get_notes,generate,get_password_history) returnstructuredContent.result = { kind, value, revealed }.- When
revealis omitted/false,valueisnull(or historic passwords arenull) andrevealed: false.
- When
Production Deployment Checklist
If you run warden-mcp beyond local development, review these items:
-
TLS everywhere. Always terminate TLS in front of the HTTP endpoint.
X-BW-*headers carry master passwords in cleartext — without TLS they are visible to anyone on the network. -
Network isolation. Bind the server to
127.0.0.1or place it behind an authenticated reverse proxy. The service has no built-in authentication; anyone who can reach/ssecan issue vault operations. -
Do not enable
KEYCHAIN_ALLOW_ENV_FALLBACKon shared networks. This flag makes the server's own vault credentials available to any HTTP client that omits headers. Only use it in single-tenant setups where the network is fully trusted. -
Enable
READONLY=truewhen writes are not needed. This blocks all mutating tools at the MCP layer, limiting blast radius if an agent or client is compromised. -
Restrict filesystem access to
/data/bw-profiles. ThebwCLI stores decrypted state under its HOME directory. Ensure the profile directory is not world-readable and is mounted with appropriate permissions (the Docker image runs as non-root by default). -
Disable debug logging in production.
KEYCHAIN_DEBUG_BWandKEYCHAIN_DEBUG_HTTPemit request details and CLI invocations to stdout. Debug logs may include session metadata and request structure. Keep them off unless actively troubleshooting. -
Set
NOREVEAL=truewhen secrets should never leave the server. This forces allrevealparameters tofalseserver-side, regardless of what the client requests. Use this when the MCP host is an LLM agent that could be influenced by prompt injection — it prevents tricked agents from exfiltrating passwords or TOTP codes. -
Monitor
/metricsz. The endpoint is intentionally unauthenticated (for scraper compatibility) but exposes session counts, heap usage, and rejection counters. If this data is sensitive in your environment, restrict access at the network level.
Quick Start
Minimal local run
Run the published package in HTTP mode and verify the server is up:
npx -y @icoretech/warden-mcp
curl -fsS http://localhost:3005/healthz
Local Development
Docker Compose
Starts a local Vaultwarden + HTTPS proxy (for bw), bootstraps a test user, and runs the MCP server.
cp .env.example .env
make up
curl -fsS http://localhost:3005/healthz
Run integration tests:
make test
Run session flood regression locally (guardrail sanity):
npm run test:session-regression
Local dev (host)
npm install
cp .env.example .env
npm run dev
Tool Reference (v1)
Vault/session:
keychain.statuskeychain.encode(base64-encode a string viabw encode)keychain.generate(returns a generated secret only whenreveal: true)
Items:
keychain.search_items,keychain.get_item,keychain.update_itemkeychain.create_login,keychain.create_note,keychain.create_card,keychain.create_identity,keychain.create_ssh_keykeychain.delete_item,keychain.restore_item
Folders:
keychain.list_folders,keychain.create_folder,keychain.edit_folder,keychain.delete_folder
Orgs/collections:
keychain.list_organizations,keychain.list_collectionskeychain.list_org_collections,keychain.create_org_collection,keychain.edit_org_collection,keychain.delete_org_collectionkeychain.move_item_to_organization
Attachments:
keychain.create_attachment,keychain.delete_attachment,keychain.get_attachment
Sends:
keychain.send_list,keychain.send_template,keychain.send_getkeychain.send_create(quick create viabw send)keychain.send_create_encoded,keychain.send_edit(advanced create/edit viabw send create|edit)keychain.send_remove_password,keychain.send_deletekeychain.receive
Direct “bw get …” helpers:
keychain.get_username(returns{ kind:"username", value, revealed:true })keychain.get_password/keychain.get_totp/keychain.get_notes(only return real values whenreveal: true)keychain.get_uri,keychain.get_exposedkeychain.get_folder,keychain.get_collection,keychain.get_organization,keychain.get_org_collectionkeychain.get_password_history(only returns historic passwords whenreveal: true)
Known Limitations
bw list items --search(and thuskeychain.search_items) does not reliably search inside custom field values.- SSH keys are stored as secure notes in v1 (until
bwsupports native SSH key item creation). - High-risk CLI features are intentionally not exposed yet (export/import).
Contributing
See AGENTS.md for repo guidelines, dev commands, and testing conventions.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.