vault-dev-broker
Supported OIDC identity provider for signing in: the broker authenticates the developer against Auth0 using authorization code + PKCE and uses the token to log in to the Vault JWT auth mount.
Supported OIDC identity provider for signing in: the broker authenticates the developer against Keycloak using authorization code + PKCE and uses the token to log in to the Vault JWT auth mount.
Supported OIDC identity provider for signing in: the broker runs an authorization code + PKCE flow against Okta (requires the offline_access scope and an access policy on the authorization server) and exchanges the resulting ID token for a Vault token.
Core integration: the broker signs in to HashiCorp Vault (JWT auth mount bound to the user's OIDC identity) and resolves vault+kv2://mount/path#field references against a KV v2 secrets engine. Tools let the agent initialize a .env.vault file, check which secret references resolve, and run a command with the resolved values injected only into that process, with any secret that appears in the output redacted. Secrets can also be stored in the developer's personal Vault path. Short-lived Vault tokens scoped by policy mean the agent never receives a secret value.
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., "@vault-dev-brokerrun the deploy script with the API key from Vault"
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.
IBM Verify Vault for AI agents (VS Code)
Personal project. Not supported by IBM in any way. This is not an IBM product. It is not supported, endorsed, reviewed, or officially created by IBM, and there is no support of any kind from anyone: no SLA, no help desk, no guaranteed fixes. Use it at your own risk. It is a prototype, not production software: use it only in test environments with test credentials.
Requirements: an OIDC provider you control (IBM Verify, Okta, Entra, Auth0, Keycloak, ...) with an app registered for this tool, and a HashiCorp Vault instance you can configure (JWT auth mount, role, policy, KV v2). Neither is provided here.
New machine? Start with handoff.md.
Prototype extension. The developer signs in with IBM Verify. The agent in VS Code chat gets a local MCP server, the Vault dev broker, whose only tools run commands with secrets resolved from HashiCorp Vault. The agent never receives a secret value.
Why this is needed
AI coding agents run commands for you. Every command that talks to an LLM, Jira, Databricks or a database needs a credential, and today that credential usually sits in a .env file, a shell variable, or the chat itself. Once it is there, three things go wrong:
The agent can read it, and anything the agent reads can end up in a transcript, a log, a commit, or a prompt-injected exfiltration.
It never expires and nobody knows who used it. A leaked key works for everyone until someone notices.
Every project and every laptop gets its own copy, so rotating a key means finding every copy.
Vault fixes the model, not the symptom. Secrets live in one place with policy and audit. You prove who you are with your normal sign-in (IBM Verify or any OIDC provider). Vault gives you a short-lived token scoped to what you may read. The broker injects the values into the one process that needs them and redacts them from anything the agent sees. The agent asks for a command to be run, never for a secret.
Related MCP server: agent-vault
Before you start (critical)
You need all of the following. Missing any one means the sign-in or the resolve step fails.
Need | What exactly |
Skills | Working knowledge of HashiCorp Vault (auth methods, policies, KV v2) and of OIDC (issuer, client id, redirect URI, scopes). This is not a zero-config tool. |
A supported client | VS Code 1.104 or newer (the extension), or IBM Bob, Claude Code, Cursor, Windsurf (the standalone broker; Node 20 or newer). |
A browser that can reach your identity provider | The sign-in happens in the browser and returns to |
An OIDC identity provider with an app registered for the broker | See the identity provider table. |
A HashiCorp Vault (2.0 or newer; Community edition is enough) reachable from your machine (HTTPS in real use; the throwaway local Vault is plain http) | See the Vault table. |
A project that needs sensitive values | LLM keys, API tokens, personal access tokens, database passwords. |
Those values already stored in Vault, and their paths | The path is what you write in |
Identity provider
Setting | IBM Verify | Any other OIDC provider (Okta, Entra, Auth0, Keycloak) |
Discovery |
| same rule; the issuer is what the discovery document reports |
App type | OIDC, public client (no secret), PKCE required | native or public client, PKCE |
Grants | authorization code, refresh token | authorization code, refresh token; Okta also needs the |
Redirect URIs |
|
|
Token given to Vault | JWT access token with a custom audience ( | ID token ( |
Users | assigned to the app (birthright or a group) | assigned to the app, plus an access policy on the authorization server (an Okta trial has none by default) |
Vault
Item | Value |
Auth method |
|
Role |
|
Policy |
|
Secrets engine | KV v2 mounted at |
Scripts |
|
Store the values, note the paths
An admin (or you, on your own path) puts each value in once. Fake examples:
vault kv put secret/dev/shared/anthropic api_key=sk-ant-EXAMPLE-FAKE # pragma: allowlist secret
vault kv put secret/dev/shared/jira email=charlie@example.com api_token=ATATT-EXAMPLE-FAKE # pragma: allowlist secretA reference in .env.vault is vault+kv2://<mount>/<path>#<field>, so those become:
ANTHROPIC_API_KEY=vault+kv2://secret/dev/shared/anthropic#api_key
JIRA_EMAIL=vault+kv2://secret/dev/shared/jira#email
JIRA_API_TOKEN=vault+kv2://secret/dev/shared/jira#api_token
PORT=5177Personal values go under secret/dev/users/<your sub>/...; the VS Code command IBM Verify Vault: Store a secret in Vault writes one and copies the matching line for you.
Using it while coding
Tell the agent once, in the project's instructions file (CLAUDE.md, AGENTS.md, or Bob's rules):
Secrets come from Vault through the vault-dev-broker MCP server. Never ask me for an API key, never write one into .env, a shell variable or code, and never read .env.vault for values. When a command needs a credential, run it with the vault_run tool; the values are injected for that command only. Use vault_check to see which references resolve and vault_init to create .env.vault. If a tool answers "Not signed in", call vault_signin and tell me to finish in the browser; call vault_signout when I ask to sign out.A normal session then looks like this:
You: "Run the Jira smoke test."
Agent: calls
vault_check(seesok JIRA_API_TOKEN, no value), thenvault_runwithnpx tsx scripts/jira-smoke.ts.Result: the script's output, with any credential that leaked into it shown as
[redacted].
If the agent ever asks you to paste a key, that is the moment to point it at vault_run instead.
How it works
IBM Verify Vault: Sign inruns OIDC authorization code + PKCE against the tenant as a public client. The callback isvscode://ibm.verify-vault/callback. Tokens live in VS Code SecretStorage (the OS keychain). The callback authority is the extension id (publisher.name), the same for every user; it changes only for VS Code Insiders (vscode-insiders://) or a republish under another publisher, which must then be added to the Verify app.VS Code starts
dist/broker.jsover stdio with the developer's Verify JWT in its environment.The broker logs in to Vault at
auth/verify-dev/login(a standardjwtauth mount bound to the tenant issuer and the app'svault-dev-brokeraudience). Vault templates thedev-secretspolicy on the developer'ssub, so every read is attributed to that person in the Vault audit log.vault_runreads.env.vault, resolvesvault+kv2://mount/path#fieldreferences, spawns the command with the values injected, makes a best-effort attempt to revoke any dynamic leases, and redacts any value that shows up in the output: no secret value is returned in a tool result.vault_checkreports which references resolve.IBM Verify Vault: Store a secret in Vaultmoves a literal into the developer's personal path and copies the matching.env.vaultline.
Quick VS Code install
Nine steps on a machine that has VS Code, Node 20 or newer, a Verify PKCE app and a reachable Vault.
Install the extension. Download
verify-vault-0.3.4.vsixfrom the release page (https://github.com/iamidentity-ai/vscode-verify-vault/releases/tag/v0.3.4), then Extensions view, "Install from VSIX" (orcode --install-extension verify-vault-0.3.4.vsix). Reload.Check the Verify app once. It needs redirect URI
vscode://ibm.verify-vault/callback, JWT access tokens with audiencevault-dev-broker, refresh tokens on, and your user assigned. Note its client id.Point VS Code at your tenant and Vault. Settings, search "verifyVault": tenant
https://<your-tenant>, clientId from step 2, vaultAddrhttp://127.0.0.1:8200, mountverify-dev, roledeveloper.Teach Vault to trust Verify. With the
vaultCLI on PATH:VAULT_ADDR=http://127.0.0.1:8200 VAULT_TOKEN=<root> ISSUER=https://<your-tenant>/oauth2 BOUND_CLIENT_ID=<client id> bash scripts/vault-setup.sh(download the script from the repo). This creates theverify-devmount, thedeveloperrole and thedev-secretspolicy.Put the secrets where the policy can see them. The policy reads
secret/dev/shared/*and your ownsecret/dev/users/<you>/*. If your secrets live elsewhere, either copy them undersecret/dev/shared/or add their path to the policy in the script and re-run it.In your project, create
.env.vaultwith references, one per secret:ANTHROPIC_API_KEY=vault+kv2://secret/dev/shared/anthropic#api_key.Sign in. Cmd+Shift+P,
IBM Verify Vault: Sign in, finish in the browser.Tell the agent. Paste the instruction block from "Using it while coding" into the project's
.github/copilot-instructions.md(orCLAUDE.md).Use it. Open Chat in Agent mode (GitHub Copilot Chat is what VS Code's chat runs on), ask for
vault_check on .env.vault, thenvault_runwith a command. The broker starts on first use;MCP: List Serversshows it.
Installing with an AI agent instead? Point it at this repo; AGENTS.md tells it what to do.
Run outside VS Code (IBM Bob, Claude Code, Cursor, Windsurf)
The broker is a plain stdio MCP server. Without the extension it signs you in by itself: the first vault_signin opens the browser on your Verify tenant, the callback lands on http://127.0.0.1:41837/callback (loopback; set VERIFY_REDIRECT_HOST=vault-dev.localhost to use the .localhost alias instead), and the refresh token is kept in the macOS keychain (service verify-vault-broker) so later starts are silent. If no browser opens (Windows has no opener yet), vault_signin returns the URL to open by hand.
Download broker.js from the release (https://github.com/iamidentity-ai/vscode-verify-vault/releases/tag/v0.3.4), then:
node broker.js setup --vault-addr <url> --issuer https://<tenant>/oauth2 --client-id <client id> # writes the mcpServers entry into every client it finds (backup kept)
node broker.js signin --issuer https://<tenant>/oauth2 --client-id <client id> # optional: sign in now so the first use in the IDE is silentsignin takes the same flags as setup (--issuer, --client-id, --login-token, --scopes); both are required, from flags or from OIDC_ISSUER/OIDC_CLIENT_ID in the environment.
setup flags for another provider or Vault: --vault-addr, --issuer, --client-id, --login-token access|id, --scopes "openid email profile offline_access", --vault-mount, --vault-namespace, --workspace /abs/path (writes WORKSPACE_ROOT). Restart the client. Ask the agent to call vault_init (creates a starter .env.vault in the workspace) and then vault_check. Without WORKSPACE_ROOT the broker uses the client's working directory; that is the open project in Claude Code (proven) and should be set explicitly for Bob and Cursor until proven. vault_init refuses to write into your home directory or /.
Build once instead of downloading: npm install && npm run build; the only file you need afterwards is dist/broker.js.
Manual alternative
Edit the client's mcpServers JSON by hand instead of running setup.
IBM Bob (global ~/.bob/settings/mcp.json, the file Bob already creates; or .bob/mcp.json in a project, which wins). Merge the entry into the existing mcpServers object rather than adding a second file; Bob ignores other filenames:
{
"mcpServers": {
"vault-dev-broker": {
"command": "node",
"args": ["/ABSOLUTE/PATH/vscode-verify-vault/dist/broker.js"],
"env": { "VAULT_ADDR": "https://vault.example.com", "WORKSPACE_ROOT": "/ABSOLUTE/PATH/your-project" }
}
}
}Cursor ignores cwd in an MCP server entry; WORKSPACE_ROOT is what the broker actually reads to find .env.vault, so set it explicitly instead.
Claude Code:
claude mcp add vault-dev-broker -e VAULT_ADDR=https://vault.example.com -- node /ABSOLUTE/PATH/vscode-verify-vault/dist/broker.jsCursor (.cursor/mcp.json) uses the same mcpServers shape as Bob.
First use: ask the agent to call vault_signin, finish in the browser, then vault_check on your .env.vault. Optional env: OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_SCOPES, OIDC_LOGIN_TOKEN (access or id; default access), OIDC_REDIRECT_HOST, OIDC_REDIRECT_PORT, VAULT_JWT_MOUNT, VAULT_JWT_ROLE, VAULT_NAMESPACE, WORKSPACE_ROOT (defaults to the working directory). The broker discovers the token/authorize/revoke endpoints from <issuer>/.well-known/openid-configuration, so it works against any OIDC provider, not just IBM Verify; OIDC_LOGIN_TOKEN=id presents the ID token to Vault instead of the access token, for providers where the access token is not meant for a third party like Vault, either because it is scoped to the provider's own APIs (Entra, Auth0, Keycloak) or opaque to begin with (Okta's org-authorization-server tokens). The legacy VERIFY_TENANT/VERIFY_CLIENT_ID/VERIFY_REDIRECT_HOST/VERIFY_REDIRECT_PORT names are still read as fallbacks. Note: VERIFY_TENANT used to be the bare tenant host (for example https://<tenant>.verify.ibm.com); the OIDC issuer is that host plus /oauth2, and the broker normalizes a bare VERIFY_TENANT for you. A refresh token stored under the old bare-host key is migrated automatically on first use. Either OIDC_ISSUER (or the legacy VERIFY_TENANT) and OIDC_CLIENT_ID (or VERIFY_CLIENT_ID) must be set for the standalone broker to sign in; there is no built-in default tenant.
Sign out: ask the agent to call vault_signout (it revokes the refresh token at the provider and removes it from the keychain), or run security delete-generic-password -s verify-vault-broker -a "<issuer>|<client id>" on macOS, which removes it locally only; the account is needed once more than one provider is stored. On Linux and Windows the refresh token is not stored, so sign in again after every broker start and whenever the access token expires (about an hour). The keychain item is readable by other processes running as you (same ceiling as the gh CLI). The access token already issued is not revoked; a running broker keeps it until it expires (about an hour), and the manual keychain delete revokes nothing.
IBM Bob configuration
Install Node 20 or newer, download broker.js from the release (https://github.com/iamidentity-ai/vscode-verify-vault/releases/tag/v0.3.4), then either run node broker.js setup --vault-addr <url> --issuer https://<tenant>/oauth2 --client-id <client id> in the folder it landed in (writes ~/.bob/settings/mcp.json, merging into any existing mcpServers) or add the entry to ~/.bob/settings/mcp.json yourself. Open a project and ask Bob to call vault_signin.
Providers
setup/signin work against any OIDC provider via discovery; only the flags change.
Provider |
|
| Vault role ( | Notes |
IBM Verify |
|
|
| JWT access tokens with a custom audience |
Okta (custom authorization server, e.g. |
|
|
| proven 2026-09-16 with a local Vault on a trial tenant's default authorization server (a fresh trial's default server may have no access policy: create one, ALL_CLIENTS + authorization_code, or every sign-in fails); access tokens are JWTs too, |
Okta (org authorization server, |
|
|
| access tokens are opaque, not usable at Vault (untested); add |
Microsoft Entra |
|
|
| untested; Entra backdates |
Auth0, Keycloak |
|
|
| untested |
Where the guarantee stops
The redaction above only protects the tool-result channel back to the model. vault_run executes any shell command the model chooses, with the resolved value in that command's environment. A command the model asks for, such as piping the value through base64 or rev, or writing it to a file in the workspace that the editor then reads back, can move the value past the exact-substring match in redact.ts without it ever showing up as the literal string. The ponytail comment in redact.ts names the same ceiling for encoded forms; that comment covers the encoding half only, not the broader channel. Closing this needs two things, both planned rather than built: an agent-identity RAR scope that limits what the broker is even allowed to resolve on the agent's behalf, and running the child command in a sandbox (an OpenShell egress proxy, for example) that substitutes values at the network boundary instead of handing them to the process directly. The command's environment is an allowlist (PATH, HOME, USER, LOGNAME, SHELL, LANG, LC_ALL, LC_CTYPE, TERM, TMPDIR, TZ, PWD, XDG_RUNTIME_DIR, SSH_AUTH_SOCK) plus the resolved values; your shell's own tokens are not passed through. Corporate proxy variables (HTTP_PROXY, HTTPS_PROXY, NO_PROXY), DOCKER_HOST, PYTHONPATH and similar are dropped too; pass any you need as literal lines in .env.vault (for example HTTPS_PROXY=http://proxy.example:8080). SSH_AUTH_SOCK is kept on purpose so git can push as you; remove it from the allowlist in run.ts if that is not wanted.
Reference grammar (shared with the OpenShell Vault credential driver)
Reference | Meaning |
| KV v2 read |
| KV v1 read |
| dynamic secret; lease revocation attempted (best effort) when the command exits |
| force a literal that would otherwise parse as a reference |
anything else | literal |
#<field> defaults to value.
Lease revocation is best effort: the dev-secrets policy grants no sys/leases/revoke capability today, so revokeLeases() fails and the failure is swallowed. The lease TTL is the real backstop until that grant is added. No dynamic mount is readable under dev-secrets yet, so this has not been exercised end to end.
Setup
Verify app:
VERIFY_TENANT_URL=https://<tenant>.verify.ibm.com VERIFY_ADMIN_CLIENT_ID=<admin api client id> VERIFY_ADMIN_SECRET=... npm run register-app. All three are required; there is no default tenant. Optional env:APP_NAME(defaultVault dev broker),APP_URL(default this repo's URL),AUDIENCE(defaultvault-dev-broker),REDIRECT_URIS(comma-separated; default the four broker/extension callbacks),VERIFY_IDENTITY_SOURCE_IDS(comma-separated identitysource ids; default every enabled identitysource on the tenant, since a Verify app with none set blocks every sign-in),THEME_ID(defaultdefault),WRITE_APP_JSON=1(also writesverify-app.json, gitignored; not written by default). Re-running finds the app by name and re-applies the redirect URIs; it only touchesidentitySourceswhenVERIFY_IDENTITY_SOURCE_IDSis set, so a re-run never shrinks a working app's identity sources.Build and install:
npm run packagethencode --install-extension verify-vault-0.3.4.vsix.Settings:
verifyVault.tenant,verifyVault.clientId,verifyVault.vaultAddr,verifyVault.vaultJwtMount,verifyVault.vaultJwtRole,verifyVault.vaultNamespace(HCP Vault Dedicated usesadmin).
Not yet (next plan)
Agent identity: RFC 8693 exchange with an actor token (JWT-SVID from Vault's SPIFFE engine) and RAR
vault:path_access, presented to Vault's OAuth resource server. Today the broker acts as the developer only.The broker launched by the VS Code extension holds the access token it was started with. When that token expires (Verify's default is one hour), the tools answer with a message telling you to restart the server from
MCP: List Servers. The standalone broker (Bob, Claude Code, Cursor, Windsurf) refreshes itself from the stored refresh token and does not have this limit. Raising the access-token lifetime on the Verify app removes the restart, but it widens the window in which a token revoked at Verify still logs in to Vault, because Vault's jwt auth validates the token locally and never asks Verify whether it was revoked. A refresh channel from the extension into the running broker is the planned fix. A shorter access-token lifetime for this app, or Vault-side introspection, would close that window so a revoked Verify token stops working at Vault before it expires.IBM Verify Vault: Copy access token (dev aid)puts the human's own short-lived token on the human's clipboard for the Vault login probe. No agent stores it; never echo it in a terminal.Refresh tokens outside macOS (libsecret, Windows Credential Manager).
Why is this a Node file, and is that normal?
Yes. This is how MCP servers work everywhere.
Inside VS Code, extensions that do real work run a helper process: the TypeScript checker, ESLint, Copilot's agent. VS Code ships its own Node and its docs say to run MCP servers with it. Our extension does exactly that, so you never install Node for the VS Code path.
Outside VS Code (Bob, Claude Code, Cursor), every MCP server is a small program the client starts for you. Look at any config:
"command": "npx"or"command": "uvx". Ours is"command": "node"with one file,broker.js. Nonpm install, no downloads at run time.What is different here, and better: most MCP servers need a long-lived API key pasted into their config. This one has none. It signs you in through your identity provider and asks Vault for what it needs, when it needs it.
This server cannot be deployed
Maintenance
Related MCP Connectors
Secrets for developers and agents—secure context and workflows without exposing secret values.
- TAPOAuthtech.human
Credential isolation for AI agents: placeholder secrets, policy checks, optional human approval.
Security gateway for AI agents: policy, approval, and audited execution, no secrets shared.
Command your AI agents: verifiable passports, credential injection, full audit, revoke in 60s.
Related MCP Servers
- FlicenseNot gradedqualityCmaintenanceEnables AI agents to securely use secrets by running commands with environment-injected credentials and sanitizing output to prevent leakage.1-
- AlicenseNot gradedqualityDmaintenanceAgent Vault is a local credential vault for AI agents. It enables agents to use secrets by name without ever seeing their values, with host allowlists, output scrubbing, and audit logging.4MIT
- 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.20 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to securely use secrets from an Obsidian vault by injecting them into command environments without exposing the values, with auditing and approval controls.33 npmMIT