Skip to content

Keyholder MCP

Your AI agent uses the credential. It never sees it.

Keyholder MCP is an MCP server over OpenBao. The agent writes a reference; Keyholder puts the real value into the command only when it runs, and hands the output back with the value masked. Access ends on its own.

Get started Download

The agent asks keyholder_run

curl -sS \
  -H "Authorization: Bearer {{vault://my-project/dev/API_TOKEN}}" \
  {{vault://my-project/dev/API_URL}}

The API receives

Authorization: Bearer
tok-fake-not-a-real-secret

The agent reads back

api says: hello, caller with
header Authorization: Bearer
[hidden:vault://my-project/dev/API_TOKEN]

A recorded run with a fake token, checked on every CI build: see the quickstart.

Why not just give the agent the key

Agents need API keys, cloud credentials and tokens to do real work. Pasted into the chat, a .env file or a command the model writes, a credential ends up in the transcript, in logs and in places you will forget. The other OpenBao and Vault MCP servers we found have a tool that reads a secret for the model. Keyholder has none: the model only ever holds names.

A run, start to finish

  1. You store the secret

    In OpenBao, one path per project. The OpenBao web UI or the bao CLI, outside the chat.

  2. You issue a session

    A wrapped, single-use token with a TTL, given to keyholder-mcp through its environment.

  3. The agent runs a command

    It sends {{vault://…}}. Keyholder resolves it at execution, runs an allowed command without a shell, masks the output.

  4. Access ends

    At the TTL every call fails with a clear message. You renew outside the chat; the agent cannot.

How it compares

Keyholder MCP mcp-vault openbao-mcp vault-mcp-server
Backend OpenBao OpenBao, embedded OpenBao HashiCorp Vault
Model receives secret values Never Yes, documented Has a read tool Yes, documented
Value injected at run time Yes Not documented Not documented Not documented
Output masked Yes Not documented Not documented Not documented
Cloud credentials per run AWS, GCP; revoked after the run Not documented Not documented Not documented
Temporary access Wrapped token, TTL Wrapping, SSH CA, PKI Not documented Not documented
What the agent can do Run allowed commands 39 tools: secrets, SSH, PKI, policies Vault operations Mounts, KV, PKI

From each project's README on 2026-10-14. They manage the vault for the agent; Keyholder only lets the agent use it. Details and sources: How it compares.

What it protects, and what it does not

Protects

  • Values stay out of the chat, tool results, errors and logs.
  • A leaked session token dies with its TTL; a wrapped token works once.
  • Only commands you name run, and never through a shell.
  • References outside the project scope are refused.

Does not

  • Hide the value from the command it runs: that command needs it.
  • Catch an encoded value: an allowed interpreter can escape the mask, as the masking tutorial shows.
  • Stop a hostile agent running as your own OS user.

The full list is in the security model.

Start in five minutes

bao server -dev -dev-root-token-id=root-dev        # in-memory, for a first try
export BAO_ADDR=http://127.0.0.1:8200 BAO_TOKEN=root-dev
bao kv put -mount=secret my-project/dev API_TOKEN=your-api-token
bao policy write my-project-read deploy/policy-project-read.hcl
bao token create -policy=my-project-read -ttl=8h -explicit-max-ttl=8h -wrap-ttl=5m -field=wrapping_token

Then add keyholder-mcp to Claude Code, Cursor or Codex with that wrapped token. Every step, explained: Getting started. Every behaviour, with real output: Tutorials.

Version 0.3.0, MIT licensed. Source, issues and releases on GitHub. Cloud CLIs get a credential made for each run: cloud credentials. For real secrets, run a persistent vault. Next on the roadmap: access requests from the agent, SSH.