mcpField
Z
zn-gate

zn-gate

Local
usezn.comSecurity2Updated today

About zn-gate

zn-gate is an open-source, local security layer for developers using coding agents or building agents with Python. It checks for prompt injection, redacts secrets in supported tool flows, audits prompts and configuration files, and can proxy local MCP servers.

Key features

  1. 01Detect prompt injection locally
  2. 02Redact secrets from tool output
  3. 03Audit prompts, skills, and configs
  4. 04Wrap local MCP servers with a shield
  5. 05Store .env values in an encrypted vault
  6. 06Review local security activity

Use cases

  1. 01Protect supported coding-agent tool flows
  2. 02Check prompts and skills before use
  3. 03Add a security proxy to local MCP servers
  4. 04Keep .env values out of agent-readable files

MCP support

zn-gate can run as an MCP server with tools for checking tool calls and results. It can also wrap local stdio MCP servers; remote URL servers are left unchanged.

Configuration

Local server config is the same for every client. Copy it into your client’s MCP config file.

mcp.json · Universal config
{
"mcpServers": {
"usezn-zn-gate": {
"command": "npx",
"args": [
"-y",
"zn-gate"
],
"env": {
"ZN_LOCAL_ONLY": "<ZN_LOCAL_ONLY>",
"ZN_CLOUD": "<ZN_CLOUD>",
"ZN_API_KEY": "<ZN_API_KEY>"
}
}
}
}
Run commandnpx -y zn-gate
Environment variables to set
ZN_LOCAL_ONLYSet to true to force local-only mode, even if ZN_CLOUD=1.
ZN_CLOUDSet to 1 to let prompt analysis also query the optional cloud classifier. Off by default.
ZN_API_KEYSecretAPI key for the optional cloud check. Has no effect without ZN_CLOUD=1.

Local servers need a runtime on your machine (e.g. Node.js 18+). Config file locations differ by client; see your client’s docs.

Packages
npmzn-gate1.14.1

Tools

4

Saved tool list · updated today

ParameterTypeRequiredDescription
textstringYesThe user input, system prompt, or external message to inspect.

Details

From the project README

zn

zn-gate

CI npm version License: MIT

The local security layer for coding agents. zn-gate guards your secrets, your data and your systems while you use Claude Code, Codex CLI, Cursor or OpenCode.

zn-gate is an open-source security layer that runs on your machine, between your agent and the tools and content it touches. It checks what the agent reads and calls for prompt injection, redacts secrets from tool output before the model sees it, and audits prompts, skills and configs. No account, no network by default, and no runtime dependencies except age-encryption (and its @noble/@scure dependencies) for the secret vault, bundled in the package at the versions pinned in package-lock.json.

For: developers using Claude Code, Codex CLI, Cursor, OpenCode or any MCP client. Python SDK: zn-gate on PyPI.


Install

Everything runs on your machine; no account or API key is needed. You need Node.js 18 or later (the LTS version from nodejs.org). With an older Node.js, the zn-gate and zn commands stop and say how to install it. The Claude Code plugin needs Node.js 20.12 or later.

Getting started: four steps, one command each. Run them in your own terminal.

  1. npx -y zn-gate reach shows what your coding agents can reach on this machine: secrets in your .env files (by name only), credential files and MCP servers. It is read only and sends nothing. Details in docs/reach.md.
  2. npx -y zn-gate init sets up protection for the coding agents it finds, one step at a time, and asks before each change.
  3. npx -y zn-gate check tests that protection with a fake secret in a temporary folder. init runs it at the end too.
  4. npx -y zn-gate env shadow .env moves the real values of your .env into the local vault and leaves markers in the file. The first time, run npx -y zn-gate unlock to create the vault.

One command for the coding agents on your machine:

npx -y zn-gate init

init does this:

  • It finds Claude Code, Codex CLI, Cursor and OpenCode on your machine.
  • For each one that is not protected yet, it shows the setup: the zn-gate plugin for Claude Code (or zn's hooks when your Node.js is older than 20.12), and zn's hooks for the others.
  • It runs each step only after you confirm it.
  • It ends with zn-gate check.

Run it yourself, in your own terminal. When an AI agent or a script runs it, it only prints the steps.

Windows PowerShell: npm and npx can fail with "running scripts is disabled on this system", because the default execution policy blocks npm.ps1. Use npm.cmd, npx.cmd and zn.cmd instead (for example npm.cmd install -g zn-gate), run the commands from Command Prompt, or allow local scripts for your account with Set-ExecutionPolicy -Scope CurrentUser RemoteSigned.

If something fails:

  • npx: command not found, or a message about the Node.js version: install the LTS version of Node.js, open a new terminal and run the command again.
  • "running scripts is disabled on this system" on Windows: see the PowerShell note above.
  • init only prints steps and changes nothing: an AI agent or a script ran it. Run it yourself in your own terminal.
  • check shows NOT protected: that line says why and what to run. What the output means.
  • You want to undo what init changed: npx -y zn-gate init --revert.

Then keep your .env values in the vault. With the plugin or the hooks, the agent cannot read .env files with real values; move them into zn-gate's encrypted vault, and the file keeps markers such as __ZN_STRIPE_KEY__:

npx -y zn-gate unlock            # creates the vault the first time
npx -y zn-gate env shadow .env

After this, your own app also reads the markers in .env: start it with zn-gate's run command once you allow it (docs/secrets.md). env shadow tells you how to undo it.

Five-minute walkthrough: docs/secrets.md. Limits: docs/threat-model.md. Words you do not know: glossary.

Other ways to install: one agent at a time, other editors, Python, MCP server only

One agent at a time (the same steps init runs):

Claude Code: the plugin. You also need Git and Claude Code 2.1.295 or later. The plugin needs Node.js 20.12 or later. With an older Node.js, npx -y zn-gate init skips the plugin and sets up zn's hooks for Claude Code instead (zn-gate check says so if the plugin is installed); update Node.js to use the plugin.

claude plugin marketplace add usezn/zn
claude plugin install zn-gate@usezn

Already ran zn hooks install claude-code before? Run zn hooks uninstall claude-code first, otherwise both sets of hooks run.

Start a new Claude Code session. Claude Code is then blocked from reading .env files with real values and credential files, secrets in what its tools return are replaced with markers or redacted before the model sees them, a secret pasted into a prompt is blocked, and Claude can check untrusted text for prompt injection. Not covered: Claude Code sessions in the cloud, Cowork and chat. Details: plugins/zn-gate.

Codex CLI, Cursor or OpenCode: the hooks.

npm install -g zn-gate
zn hooks install codex           # or: cursor, opencode

In Codex, approve the new hooks with /hooks. In Cursor, shell output and file contents are not scrubbed. What each agent supports: docs/compat.md.

Other editors (VS Code, Antigravity, Windsurf, Cline, Zed, JetBrains, Continue): zn-gate works there as an MCP server only. Setup for each one: usezn.com/docs/integrations/editors.

Python: for agents you build yourself (rules engine and secret redaction for LangChain, CrewAI, LlamaIndex or plain tool calling). It does not protect Claude Code, Codex, Cursor or other coding agents. Install it in a virtual environment; many recent Linux systems, and Homebrew's Python on macOS, refuse pip install outside one ("externally-managed-environment"):

python3 -m venv .venv
.venv/bin/pip install zn-gate      # Windows: .venv\Scripts\pip install zn-gate

zn-gate as an MCP server only (the agent calls it when it decides to; not automatic protection):

claude mcp add zn-gate -- npx -y zn-gate mcp     # Claude Code
codex mcp add zn-gate -- npx -y zn-gate mcp      # Codex CLI

Cursor (~/.cursor/mcp.json) and other editors: { "mcpServers": { "zn-gate": { "command": "npx", "args": ["-y", "zn-gate", "mcp"] } } }. OpenCode (~/.config/opencode/opencode.json): { "mcp": { "zn-gate": { "type": "local", "command": ["npx", "-y", "zn-gate", "mcp"], "enabled": true } } }.

Wrap the MCP servers you already use

init finds MCP servers in your agent config files, writes a backup, and wraps each local (stdio) server with zn-gate shield:

npx -y zn-gate init --dry-run   # preview, changes nothing
npx -y zn-gate init --wrap-mcp  # wrap (a .bak.<timestamp> copy is written first)
npx -y zn-gate init --shadow    # log detections without blocking
npx -y zn-gate init --revert    # restore the latest backup
AgentFileFormat
Claude Desktopclaude_desktop_config.json (macOS ~/Library/Application Support/Claude/, Windows %APPDATA%\Claude\, Linux ~/.config/Claude/)mcpServers JSON
Claude Code~/.claude.json (user scope)mcpServers JSON
Cursor~/.cursor/mcp.jsonmcpServers JSON
Codex CLI~/.codex/config.toml (or $CODEX_HOME/config.toml)[mcp_servers.<name>] TOML
OpenCode~/.config/opencode/opencode.jsonmcp JSON, command array
Antigravity~/.gemini/antigravity/mcp_config.json (Google's MCP guides) and ~/.gemini/config/mcp_config.json (shared settings, used by some setups); each one only if it existsmcpServers JSON
Hermes / PiAgent~/.hermes/mcp.jsonmcpServers JSON (path not verified against official docs)

Remote (URL) servers are left as they are. init never rewrites a file it cannot round-trip safely: OpenCode JSONC files with comments and Codex entries with multi-line args are reported as "wrap manually".

To wrap a single server by hand:

npx -y zn-gate shield -- uvx mcp-server-fetch

See what zn-gate did

npx -y zn-gate report

A short summary of the last 7 days on this computer: blocked actions, secrets hidden, new protected MCP servers and tool changes waiting for your approval. It reads only local files, needs no account and sends nothing. --since 30d changes the period, --json prints it for scripts. What it shows.

Turn on the optional cloud check

npx -y zn-gate connect

Optional, and you do it yourself in your own terminal (an agent cannot). It opens usezn.com so you can create an analysis-only API key (it cannot read your usage or manage keys), then asks you to paste it at a hidden prompt. A full-access key is refused. The key is kept in your system keychain (or the zn vault when there is no keychain), never written to a file, and zn's hooks refuse the direct ways for your agent to read it. After that, the text you or your agent send through analyze_prompt, with detected secrets removed first, is also checked by the Galvanize 2 classifier at usezn.com. Until you run it, nothing is sent. zn-gate disconnect removes the key from this computer and asks whether to revoke it on usezn.com too (default no). How it works.

Check that it works

npx -y zn-gate check

For each agent set up for your user, zn-gate asks its hook to let the agent read a .env file with a fake key and an ordinary file, in a temporary folder. It shows what your agent would be told, and a line that says NOT protected says how to fix it. Nothing is sent over the network and no setting changes. What the output means.

npx -y zn-gate test

This runs the local self-test (51 curated vectors: 26 attacks, 25 benign) and shows per-decision latency on your machine. It is a smoke test, not a detection rate. Nothing is sent over the network.

For developers: see the redaction by hand. Send a tool output with a fake AWS key (AWS's documentation example) and a database password through zn-gate, exactly as your agent would, and look for sanitized_content in the output; that field is what the model would see:

printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"check_tool_result","arguments":{"tool_name":"read_file","content":"AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE\nDATABASE_URL=postgres://app:hunter2@db:5432/main"}}}' \
  | npx -y zn-gate@latest mcp

What each piece does

Scrubber: secrets stay out of the model's context

  • Today: redacts AWS keys, OpenAI and Anthropic keys, GitHub tokens, JWTs, private keys and database URL passwords in everything that passes through zn-gate (check_tool_result, check_tool_call, and output of servers wrapped by shield / init).
  • Not covered today: your agent's built-in tools (for example a native file read in Claude Code) unless their output goes through zn-gate.
  • Roadmap: personal data (PII) detection; hooks for agents' built-in tools; stable placeholders backed by a local encrypted vault.

Galvanize: prompt-injection detection

  • Today, local: a deterministic rules engine with normalization against homoglyphs, zero-width characters, BiDi overrides (CVE-2021-42574), comment splicing and Base64. It checks prompts, tool arguments, tool output and MCP tool descriptions.
  • Today, optional cloud: the Galvanize 2 classifier (open weights, Apache-2.0) can also check prompts sent to analyze_prompt, only with ZN_CLOUD=1 and ZN_API_KEY.
  • Roadmap: running Galvanize locally inside zn-gate.

Scan: audit what your agent is about to trust

  • Today: npx -y zn-gate scan <path> audits prompt templates, AGENTS.md / SKILL.md files and configs for injection traps and leaked secrets. It exits with code 1 on findings, for CI. MCP servers can be checked on the web at usezn.com/scan.
  • Roadmap: auditing MCP servers and skills from the CLI before you add them.
MCP tools
ToolPhaseWhat it doesNetwork
analyze_promptBefore the LLMChecks a prompt for injection, jailbreak and exfiltration patterns.Only with ZN_CLOUD=1 + key, redacted
check_tool_callBefore executionChecks tool name and arguments; returns sanitized_arguments with secrets masked.Never
check_tool_resultBefore ingestionChecks tool output for indirect injection; returns it with secrets redacted, or neutralized if blocked.Never
zn_statusAnyEngine, rules version, and what (if anything) leaves the machine.Never

What leaves your machine

Nothing, by default.

DataEvaluatedSent anywhere?
Tool output (check_tool_result, shield)LocallyNever, in any configuration
Tool-call arguments (check_tool_call, shield)LocallyNever, in any configuration
MCP tool and parameter descriptions (shield)LocallyNever, in any configuration
Counts of what zn-gate did (zn-gate stats: checks, blocks, secrets replaced, per secret type)Written locally to ~/.zn/stats.jsonNever, unless you turn on zn-gate stats share on: then one report a day with a random installation id, the day, the zn-gate, OS and Node.js versions, the agent and part of zn-gate used most, and the number of secrets found per built-in type (never values, prompts, variable or secret names, paths, hosts or emails). zn-gate stats share show shows it first.
Prompts (analyze_prompt, zn-gate analyze)LocallyOnly if you set both ZN_CLOUD=1 and ZN_API_KEY, or run zn-gate connect yourself. Secrets are redacted locally before sending.

ZN_API_KEY alone enables nothing. zn-gate connect is the other way to opt in: you run it, in your own terminal, and it keeps the key in your system keychain or the zn vault, never in a file (zn-gate disconnect undoes it and can revoke the key on usezn.com when you say yes; details). ZN_LOCAL_ONLY=true (or --local-only) overrides ZN_CLOUD=1 and a connected key. npx -y zn-gate status shows the active detection engine and whether the optional cloud check is on. Sharing of usage statistics is off by default and has its own status: npx -y zn-gate stats share status. Data-flow diagrams: repository README.

1.4.0 and earlier: with ZN_API_KEY set, tool output and tool-call arguments not blocked locally were sent to api.usezn.com without redaction. 1.4.1 removes that path. See the CHANGELOG.


Performance (local rules engine, no network)

Measured 2026-10-11 on a 2-vCPU Intel Xeon Platinum 8259CL (AWS), Node 24, zn-gate 1.4.1, uncached inputs. p50 is the median of 3 runs; p99 is the worst of 3 runs.

Input sizeevaluate() p50evaluate() p99check_tool_result p50check_tool_result p99
200 B (typical prompt)0.012 ms0.09 ms0.045 ms0.19 ms
4 KB (typical tool output)0.16 ms0.45 ms0.30 ms0.81 ms
64 KB (large file)2.4 ms4.8 ms3.8 ms6.7 ms

check_tool_result includes secret redaction. Latency grows roughly linearly with input size. The MCP stdio round-trip for a 4 KB check_tool_result was 0.5 ms p50 on the same host.

Reproduce with node bench/bench_by_size.js from the repository. Please send us your numbers.

Accuracy: what the local rules catch, and what they don't

The local engine is a deterministic signature and normalization layer. It is fast and explainable, but it is not a semantic classifier.

  • Self-test: 51 curated vectors pass. That is a smoke test, not a detection or false-positive rate.
  • Known limitation: on whole source files, as seen by check_tool_result, the rules can flag ordinary code comments. 1.5.0 reduces these on code and documentation, at the cost of letting some loosely worded override attempts through the local rules. Prefer check_tool_result for web and third-party content rather than your own repository files.
  • Detection rates: on a private held-out set of 3,309 examples, Galvanize 2 has 83.4% recall at a 0.81% false positive rate (serving threshold 0.662); these numbers are for the cloud classifier, not the local rules engine. Method, per-language results, known weak spots and a reproducible benchmark are in the model card and the announcement. It is one layer of defense, not a guarantee.

Configuration

  • ZN_CLOUD: 1 lets analyze_prompt / zn-gate analyze also query the cloud classifier. Requires ZN_API_KEY. Off by default.
  • ZN_API_KEY: API key from usezn.com/dashboard. No effect without ZN_CLOUD=1. (zn-gate connect keeps a key for you instead and turns the cloud check on.)
  • ZN_LOCAL_ONLY: true forces local-only, even with ZN_CLOUD=1.
  • ZN_STRICT: 1 also blocks the rules that only warn by default (see below).
  • ZN_LONG_INPUT: how a long text (over 1,000 characters) is sent to the cloud classifier. windows (default) asks it to check overlapping 512-token windows, up to 20, so an injection in the middle or at the end of a long document is not missed; off checks only the first 512 tokens. Also --long-input windows|off. Windows are slower and slightly more likely to flag a long benign text, and cloud checks are billed by text size with or without windows, so off does not lower the count (1 call up to 2,000 characters, 1 more per further 1,000; billed_calls in the result); see the changelog.
  • ZN_STAGE: v30 (default, current production API) or prod (legacy endpoint). ZN_API_URL overrides both.
  • NO_COLOR (any non-empty value) or TERM=dumb: command output has no colors, symbols or box-drawing lines; the status words (ALLOWED, BLOCKED, PASSED, ...) stay. Colors are also off when the output is not a terminal.

To enable the cloud check for one MCP server, add to its entry: "env": { "ZN_CLOUD": "1", "ZN_API_KEY": "zn_live_..." }.

Custom rules (.znrules or zn.config.json)
# .znrules
path:.env.production
path:/etc/secrets
CONFIDENTIAL_INTERNAL_PROJECT_[A-Z0-9]+
Local audit log

zn-gate shield appends every decision to ~/.zn/evidence.jsonl as a hash-chained (SHA-256) record with a secret-redacted preview. It detects accidental or partial edits. It is not tamper-proof against someone with write access to the file, who can recompute the whole chain.

npx -y zn-gate evidence --verify
npx -y zn-gate evidence --ui        # local dashboard on http://localhost:3100
npx -y zn-gate evidence --tail 20

What is blocked and what only warns

zn-gate blocks what is almost certainly an attack or a leak: reading credential files (.env, ~/.aws/credentials, SSH keys), asking for API keys or passwords, sending data out through hidden image links, hidden instructions in Base64 or invisible characters, "ignore all previous instructions", and dangerous commands.

Some other patterns also appear in ordinary text, such as system: keys in YAML, "bypass the cache" or an article that explains what a jailbreak is. For those, zn-gate lets the text through and adds a short warning (advisory in the result) so you or your agent can take a look.

To block those too, turn on strict mode: set ZN_STRICT=1, or add "strict": true to zn.config.json in your project (or a line strict in .znrules).

FAQ

Do I need an account or API key? No. Everything local works without one.

Will it block legitimate content? Sometimes. Use --shadow with init or shield to log detections without blocking while you evaluate it.

How do I remove it? npx -y zn-gate init --revert restores your configs from the backup. For a standalone server, remove the zn-gate entry from your agent config.

Security

Report vulnerabilities to [email protected]. Policy and safe harbor: SECURITY.md.

License

MIT © usezn

The package bundles age-encryption (BSD-3-Clause) and its @noble / @scure dependencies (MIT); see THIRD_PARTY_NOTICES.md.

Comments

Markdown supported

New to MCP?

From what the protocol is to installing your first server, these answers cover most first questions.

An open protocol that defines how AI applications connect to external tools and data in a uniform way. Clients talk to the model; MCP servers provide tools, resources and prompt templates. They exchange standard messages, so one server can be reused by many clients.