Skip to content
plugmcp
DirectorySkillsCategoriesBlog
EN/中文
Sign inSign up
Home/Servers/Vaultshell

Vaultshell

I
by io.github.SoWhatI

Injects secrets into shell commands at exec time; plaintext never reaches the LLM context

Power your agent

Add this server

The publisher's own command. Not a PlugMCP endpoint.

Local command
docker run -i --rm ghcr.io/sowhati/vaultshell:0.1.2
Provider
Claude Code
claude mcp add io.github.sowhati-vaultshell -- docker run -i --rm ghcr.io/sowhati/vaultshell:0.1.2

Run in a terminal.

Claude Desktop
{
  "mcpServers": {
    "io.github.sowhati-vaultshell": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "ghcr.io/sowhati/vaultshell:0.1.2"
      ]
    }
  }
}

claude_desktop_config.json

Step-by-step: add this in Claude

Remote URL or a command on your machine? How the two installs differ

Run on PlugMCP

Host a copy with our keys and logs.

A new server you create. It is not this listing's official endpoint. Use the publisher install when you want their URL and their credentials.

Launch

Related MCP

View more
  • Firecrawl🔥 Official Firecrawl MCP Server - Adds powerful web scraping and search to Cursor, Claude and any other LLM clients.
  • N
    NotionToken-auth Notion MCP: pages, databases, blocks, comments, files. Runs headless in CI and agents.
  • Context7Context7 Platform -- Up-to-date code documentation for LLMs and AI code editors

Related skills

View all
  • reaReverse engineer anything with agents, from app behavior down to native binaries.
  • rea-changelog-updatePrepare or rewrite REA release changelogs and GitHub release notes from pinned Git history, with verified contributor thanks and Release Please synchronization.
  • rea-tool-designDesign or change REA investigation tools, CLI/MCP contracts, provider capabilities, and Evidence semantics.

Questions

How do I install Vaultshell?
Copy the stdio config for Cursor, Claude Desktop, Claude Code, or VS Code on this page. It uses the publisher's install, not a PlugMCP proxy.
Does Vaultshell need an API key?
No required environment variable is listed on this record.
What transport does Vaultshell use?
Vaultshell runs locally over stdio.
When was Vaultshell last checked?
Last checked 2026-10-16. Injects secrets into shell commands at exec time; plaintext never reaches the LLM context

Injects secrets into shell commands at exec time; plaintext never reaches the LLM context

vaultshell

English | 简体中文

Available on the official MCP Registry: io.github.SoWhatI/vaultshell

An MCP server that stores secrets in local secure storage, injects them into shell child processes at exec time based on rules, and guarantees secret plaintext never reaches the model context — not in tool responses, not in logs, not in audit records.

The closed loop: reference-based storage + conditional injection + mandatory output redaction.

Status: M3 — one-shot commands + persistent sessions + credential proxy + external CLI backends. See CHANGELOG and docs/design-spec.md.

The iron rule

  • No MCP tool returns secret plaintext — ever. Responses contain only names, masks ([REDACTED:NAME]), and redacted output.
  • Resolvers are only invoked inside the launcher; values go straight into the child process env and never pass through the tool layer.
  • shell_exec has no free-form env parameter. Secrets enter by rule reference only.
  • Redactor failures are fail-closed: output is discarded, never returned raw.

Quick start

Requires Node.js 20+.

Install from GitHub (no npm registry account needed)

# Run on demand directly from the repo (npm installs devDependencies and
# builds dist/ via the `prepare` script):
npx -y github:SoWhatI/vaultshell

# Or install globally:
npm i -g github:SoWhatI/vaultshell

This is the same code as the npm registry package, installed from git — npm clones the repo, runs prepare (→ npm run build) and links the vaultshell bin. Pin a tag for reproducibility: npx -y github:SoWhatI/vaultshell#v0.1.1.

Docker (ghcr.io)

docker run -i --rm \
  -e MASTER_KEY=<64 hex chars> \
  -v ~/.vaultshell:/home/node/.vaultshell \
  ghcr.io/sowhati/vaultshell:latest

MCP client config using Docker:

{
  "mcpServers": {
    "vaultshell": {
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "-e", "MASTER_KEY=<64 hex chars>",
        "-v", "~/.vaultshell:/home/node/.vaultshell",
        "ghcr.io/sowhati/vaultshell:latest"
      ]
    }
  }
}

The image runs as the non-root node user (HOME=/home/node), so the data volume mounts at /home/node/.vaultshell. node-pty is excluded from the image — sessions automatically fall back to pipe mode (pty: false) in containers; injection and redaction are unchanged. The web subcommand passes through (docker run … ghcr.io/sowhati/vaultshell web — loopback inside the container, of limited use).

No Smithery distribution: Smithery has gone hosted-only (it only accepts servers deployed to its own infrastructure). vaultshell's secrets must stay on the user's machine by design — third-party hosting is incompatible with its threat model, so we deliberately do not publish there.

From source

# Run via npx (after publish) or from source:
npm install && npm run build

# 1. Create a master key for the encrypted-file backend
export MASTER_KEY=$(openssl rand -hex 32)

# 2. Create ~/.vaultshell/config.yaml and ~/.vaultshell/rules.yaml
#    (full annotated examples: docs/user-guide.md)

# 3. Store a secret (never echoed back) and run with injection
#    via your MCP client's tools: secret_set, then shell_exec

Claude Desktop (claude_desktop_config.json):

{
  "mcpServers": {
    "vaultshell": {
      "command": "npx",
      "args": ["-y", "vaultshell"],
      "env": { "MASTER_KEY": "<64 hex chars>" }
    }
  }
}

Full setup, configuration reference, rule-writing guide, client integration, and FAQ: docs/user-guide.md.

MCP tools

ToolPurposeReturns
shell_execRun a one-shot command with per-rule injection. Params: command, cwd?, profile?, extraEnv? (allowlist), dryRun?, timeoutSeconds? (≤3600){exitCode, stdout, stderr} (redacted), injected:[names], ruleId, redactedCount, timedOut, truncated, warnings; dryRun → {dryRun:true, ruleId, injected, envKeys, denied}
shell_session_openOpen a persistent session; secrets injected once at creation{sessionId, cwd, injected:[names], pty, ttlSeconds}
shell_session_sendSend a command to a session{output} (merged, redacted), exitCode, redactedCount
shell_session_closeKill a session; secrets die with the process{ok}
shell_session_listList live sessionsmetadata only
shell_session_revokeKill and rebuild the session without secrets{sessionId} (new)
shell_proxy_startStart a credential-injecting reverse proxy on 127.0.0.1{proxyId, port, expiresAt, upstreamHost}
shell_proxy_stopStop a proxy{ok}
shell_proxy_listList running proxiesmetadata only (incl. secret name, never value)
secret_listList names + metadata (backend ref, resolvable)never values
secret_setWrite to backend; persisted, never echoed{name, ok}
secret_deleteDelete and unregister{name, ok}
secret_probeCheck a ref resolves{ok, masked:"****"}
rule_listRules + static warnings (e.g. inject-everywhere)—
rule_validateStatic findings: unknown secrets, unreachable rules, inject-everywhere, union risk, requireConfirm capability{ok, findings[]}
audit_queryRead audit entriesnames + redacted commands only

Execution limits (M3)

  • dryRun: true on shell_exec reports the would-be ruleId, injected names, final env key list (never values) and the deny-list verdict — without executing anything.
  • timeoutSeconds (default defaults.execTimeoutSeconds = 120, capped at 3600) kills the command and returns timedOut: true.
  • defaults.maxOutputBytes (default 1 MiB) truncates each output stream and sets truncated: true. Truncation happens after the Redactor — it can never bypass redaction.
  • defaults.maxConcurrentExecs (default 8) rejects excess concurrent shell_exec calls with a clear error (no queue).
  • All of the above are recorded in the audit log (dryRun / timedOut / truncated flags).

Web config UI

vaultshell web            # loopback-only HTTP UI on a random port
vaultshell web --port 5317

Prints a one-time-token URL like http://127.0.0.1:5317/?token=… — open it to manage secrets (write-only), edit rules with live static-validation warnings and a matcher dry-run, edit config, and browse the redacted audit log. The token dies with the process; every API call needs it, mutations require same-origin application/json requests, and no endpoint can ever return a secret value. Do not port-forward it. Design & threat model: docs/web-config-ui-design.md.

Credential proxy (M3)

For high-sensitivity API tokens, prefer not landing the secret in env at all. Declare a proxy in config.yaml:

proxies:
  - id: stripe
    upstreamHost: https://api.stripe.com
    secretRef: STRIPE_KEY                          # registry name or full ref
    headerTemplate: "Authorization: Bearer ${value}"

Then shell_proxy_start { "id": "stripe" } → { port, expiresAt }, and commands call http://127.0.0.1:<port>/v1/charges instead of https://api.stripe.com/v1/charges. The proxy injects the header in memory only and auto-stops after its TTL (default 300s). Audit records the host and secret name, never the header value.

Boundaries: http:///https:// upstreams only (proxy→upstream TLS is properly validated; no CONNECT tunneling, no TLS termination); the client→proxy leg is plaintext on 127.0.0.1, which inherits the same-UID local-process boundary from the threat model.

Persistent sessions (M2)

shell_session_open spawns a long-lived shell with the matched rule's secrets injected at creation. The session is recycled after an idle TTL (defaults.sessionTtlSeconds, default 900s, overridable per rule via ttlSeconds or per call) — when the process dies, the secrets die with it. shell_session_revoke kills a possibly-compromised session immediately and rebuilds one without any secrets.

Sessions use a real PTY via node-pty (an optional dependency — native build, needs Xcode CLT on macOS / build-essential + python3 on Linux). If node-pty cannot be loaded or spawned, vaultshell automatically falls back to a plain child_process shell and the shell_session_open response says pty: false; injection and redaction behave identically, only interactivity is degraded. Session output merges stdout/stderr (PTY semantics) and passes through the same Redactor.

Backend support matrix

BackendRef schemeStatusPlatformsNotes
Encrypted fileencfile://NAME✅ ImplementedallAES-256-GCM; master key from env:MASTER_KEY or macOS keychain; default
Local keychainkeychain://svc/acct⚠️ macOS onlymacOSspawns security CLI; Linux/Windows give a clear error
Process envenv://NAME✅ Implementedallread-only; good for CI
Filefile://path✅ Implementedallfirst line; refuses permissions > 0600
Inlineinline:⚠️ Restrictedallplaintext in config; disabled by default, startup warning when enabled
1Passwordop://vault/item/field✅ via CLIallneeds authenticated op CLI; read-only
Vaultvault://path#field✅ via CLIallneeds authenticated vault CLI; read-only
Infisicalinfisical://proj/env/KEY✅ via CLIallneeds authenticated infisical CLI; read-only
Dopplerdoppler://proj/config/KEY✅ via CLIallneeds authenticated doppler CLI; read-only

Details, per-backend setup, and how to register a plugin resolver: docs/backends.md.

Threat model

ThreatMitigation
Model-context leakageStructural: values never cross the tool layer; only names and masks
Command output echoing secretsMandatory redaction (exact + URL/Base64 variants + generic patterns), fail-closed
Agent dumping env (env, printenv, /proc/*/environ, …)Hard-blocked deny-list (configurable via security.dangerousCommands: extra patterns, or mode: warn to allow-with-audit) + shell_exec_blocked audit events
Same-UID local process reading /proc/PID/environOS-level limit, cannot be fully fixed; per-command injection shrinks the window. Explicit boundary.
Long secret residencyPer-command injection by default; sessions have idle-TTL recycling + instant revoke
Misconfigured rule causing inject-everywhererule_validate / rule_list static warnings; mergeStrategy defaults to override; requireConfirm rules demand interactive approval (fail-closed without elicitation)
Config files leakingConfig stores refs only, never values; inline: disabled by default; store files forced to 0600

Full discussion: docs/design-spec.md §7. Report vulnerabilities privately per SECURITY.md.

Data layout

~/.vaultshell/
  config.yaml     # main config
  rules.yaml      # injection rules + secret refs (refs only, never values)
  secrets.enc     # encrypted-file backend (0600)
  audit/          # JSONL audit, redacted

VAULTSHELL_HOME overrides the data directory (used by tests).

Development

npm run build   # tsc
npm test        # vitest — redactor property tests, backend round-trips,
                # rule matching, shell_exec end-to-end

See CONTRIBUTING.md — including the security red lines every PR must respect.

License

MIT © 2026 vaultshell contributors

How do I install Vaultshell?
Copy the stdio config for Cursor, Claude Desktop, Claude Code, or VS Code on this page. It uses the publisher's install, not a PlugMCP proxy.
Does Vaultshell need an API key?
No required environment variable is listed on this record.
What transport does Vaultshell use?
Vaultshell runs locally over stdio.
When was Vaultshell last checked?
Last checked 2026-10-16. Injects secrets into shell commands at exec time; plaintext never reaches the LLM context

No tool list yet. Remote servers get one after the first health check.

plugmcp

Plug any tool into any agent. MCP servers for people who do not write servers. Gateway for volume, n8n for apps.

  • github.com/TODO
  • x.com/TODO
  • linkedin.com/company/TODO

PlugMCP, 2026

Product
  • Browse
  • Install
  • Host your own
  • Pricing
  • Voice mode
  • Browser
Directory
  • All MCP servers
  • MCP Apps
  • Tools
  • Agent skills
  • Categories
  • New
  • Voice
  • Submit a server
Developers
  • Docs
  • Blog
  • Club
  • Work with us
  • What is MCP
  • What is WebMCP
  • Status
Company
  • About
  • Contact
  • Terms
  • Privacy
  • DPA