MCP

vaulted mcp starts an MCP stdio server that exposes the local vault to AI coding agents. Secret values never enter the agent's context window. Listing tools return names and metadata only, and command output from the run tool is redacted against the injected secret values before it is returned.

The server orients itself exactly like the CLI. The vault lives in VAULTED_CONFIG_DIR, default ~/.config/vaulted, and the active project comes from the vaulted.toml discovered by walking up from the server's working directory. Launch it from your project directory, every client below does this by default.

Tools

ToolKindWhat it returns
statusread-onlyVault exists, locked or unlocked, active project from vaulted.toml
list-projectsread-onlyProjects with their environments and folders
list-secretsread-onlyKey names, scopes, value types, folder, and environment, never values
get-current-projectread-onlyThe vaulted.toml project discovered from the working directory
run-with-secretsdestructiveRuns a command through the hardened Rust runner, returns {exitCode, stdout, stderr, timedOut} with output redacted, then truncated to 50,000 chars per stream

run-with-secrets

ts
{command: string[], environment?, folder?, projectId?, timeoutSeconds?}

Injects server and shared scoped secrets, root level by default or one folder via folder, mirroring vaulted run. The command executes on a worker thread, so the server keeps answering other tool calls during a run. Once timeoutSeconds elapses, default 300 and max 3600, the command is terminated with SIGTERM, then SIGKILL after a 5 second grace period, and the captured output up to that point comes back with timedOut: true. Long-running commands such as dev servers are therefore not suitable for this tool.

The vault must be unlocked, through the keychain, the password file, or VAULTED_PASSWORD, before agents can use run-with-secrets. The server never prompts, because stdio is the protocol channel. Run vaulted unlock once beforehand.

Redacted output

A child process that echoes an injected secret gets its output rewritten before the agent sees it. The matcher covers the plain, base64, and URL-encoded forms of each value, and replacement happens before the 50,000 character truncation, never instead of it.

json
{
"exitCode": 0,
"stdout": "connecting with key [REDACTED:STRIPE_KEY]\n",
"stderr": "",
"timedOut": false
}

Client setup

The snippets assume the compiled vaulted binary is on PATH. When running from source, replace the command with bun and prepend run /path/to/vaulted/apps/cli/src/index.ts to the arguments.

Claude Code

One command from your project directory.

claude mcp add vaulted -- vaulted mcp

Cursor

.cursor/mcp.json in your project, or ~/.cursor/mcp.json globally.

.cursor/mcp.json
{
"mcpServers": {
"vaulted": {
"command": "vaulted",
"args": ["mcp"]
}
}
}

Codex

~/.codex/config.toml
[mcp_servers.vaulted]
command = "vaulted"
args = ["mcp"]

Zed

In settings.json, opened with zed: open settings.

settings.json
{
"context_servers": {
"vaulted": {
"source": "custom",
"command": "vaulted",
"args": ["mcp"]
}
}
}

OpenCode

opencode.json in your project.

opencode.json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"vaulted": {
"type": "local",
"command": ["vaulted", "mcp"],
"enabled": true
}
}
}

Claude Desktop

claude_desktop_config.json, found under Settings, Developer, Edit Config.

claude_desktop_config.json
{
"mcpServers": {
"vaulted": {
"command": "vaulted",
"args": ["mcp"]
}
}
}

Claude Desktop launches servers from its own working directory, not a project checkout. Start Claude Desktop from your project, or rely on the projectId and environment arguments to the tools.

Security model

What is guaranteed

  • Secret values are never in read tool responses. list-secrets is implemented in a module with no access to any decryption function, and a test asserts structurally that decryptSecret and unwrapProjectKey are not referenced there. status, list-projects, and get-current-project handle no ciphertext at all.
  • One hardened execution path. run-with-secrets calls the same Rust runner, via the same FFI bridge, as vaulted run. Core dumps disabled, environment cleared to a 10 variable allowlist plus the injected secrets, secret buffers zeroized, no disk writes. A test asserts that no JS level process API appears anywhere in the MCP sources or the run command.
  • Output is redacted before truncation. stdout and stderr are scanned with a multi-pattern matcher built from the injected secret values, in plain, base64 padded and unpadded, and URL-encoded forms, and matches are replaced with [REDACTED:KEY_NAME]. The 50,000 character truncation is applied after redaction, never instead of it.
  • Every access is audited. mcp.list_secrets and mcp.run_with_secrets rows, with argv and key names but never values, land in the local audit log. The run entry is written before the child starts, so interrupted runs still leave a record.
  • The server never prompts and never writes secrets to its own stdout or stderr. A locked vault is a clean tool error.

What is not guaranteed

  • Redaction is best effort, for the accidental echo case. It catches a child process printing an injected value in the three indexed encodings. It cannot catch values transformed in other ways, hashed, encrypted, hex-dumped, or split and reassembled, and secrets shorter than 6 characters are not indexed at all.
  • The agent still chooses the command in v0.3. run-with-secrets executes what it is told. A prompt-injected agent can run a program that reads a secret and sends it over the network, and redaction of the returned output does not prevent that.
  • Anything the target process does with the secrets is out of scope. Vaulted hardens delivery into the child process, it does not sandbox the child.
PreviousCommands