Install
$ agentstack add mcp-686f6c61-mcp-zero-trust-layer ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
MCP Zero Trust Layer
MCP Zero Trust Layer, usually shortened to MCPZT, is an open-source and self-hosted security layer for MCP servers. It sits between an MCP client and one or more real MCP servers, then decides what the client can discover, call, send, receive, approve and audit.
The purpose is not to create a new MCP ecosystem or a hosted control plane. The purpose is to give developers and platform teams a practical enforcement point they can put in front of existing MCP servers without rewriting those servers.
MCP client / agent
|
v
MCP Zero Trust Layer
|
| identity
| policy
| argument validators
| human approvals
| output enforcement
| audit
v
real MCP server
|
v
database / repository / filesystem / CRM / payment API / internal system
This README is the public practical entry point: what the project does, how to run it, how to configure it, and how to reason about common policy patterns. It intentionally avoids linking to internal planning material so the published package stays focused on usage, operations and security.
The Short Version
An MCP server can expose very powerful capabilities. A tool named search_issues may be harmless, while a tool named merge_pull_request, delete_repository, run_sql, send_email, read_secret or create_refund may have real operational impact. If a client can connect directly to the MCP server, the client may be able to list and call more tools than the user or organization intended.
MCPZT adds a policy layer in front of that server. It can hide capabilities from tools/list, block calls before they reach the upstream, validate arguments, require human approval for high-risk actions, redact sensitive output, and write audit logs explaining every important decision.
The important design point is that MCPZT is not a SaaS requirement. It runs locally, in Docker, in CI, as an internal gateway, as a sidecar, or as a stdio wrapper around command-based MCP servers. Configuration is plain YAML, so it can be reviewed in pull requests and versioned with the project it protects.
Status
Current line: 0.x developer preview. Use mcpzt version, the PyPI project page or the GitHub releases page to confirm the exact installed version.
The core path is implemented. The package has a CLI, YAML config validation, HTTP proxy mode, stdio wrapper mode, multi-MCP routing, policy evaluation, policy explanation, policy coverage analysis, parameter-level controls, validators, approvals, a self-hosted approval UI, file and SQLite approval storage, output enforcement, capability discovery, onboarding config generation, deterministic scanning, searchable audit logs with hash-chain verification, Prometheus metrics, authentication modes, secret references, client config generation, examples, deployment recipes, Docker packaging and PyPI-ready build metadata.
The HTTP runtime supports MCP Streamable HTTP POST with JSON responses. GET SSE streams are not implemented in this release; the endpoint returns HTTP 405 for GET, which is allowed when a server does not offer an SSE stream. Request-scoped upstream SSE passthrough is intentionally left for a later release because streaming needs a separate security design.
Security hardening is already part of the preview. Production configs reject fail-open dry_run by default, require default deny, require a public base URL or trusted hosts, and require issuer/audience for JWT and OIDC. Shared-key auth does not trust caller-supplied identity headers unless explicitly configured. Request and upstream response sizes are bounded. Upstream error bodies are truncated and redacted. Output policies apply to JSON-RPC result and error payloads. Approval decisions are auditable. Production disables FastAPI docs and OpenAPI routes.
Why This Project Exists
MCP makes it easy for agents and AI applications to reach real systems. That is the point, and it is powerful. The risk is that many MCP servers are created as local developer tools first, then later connected to systems that deserve more careful authorization. A filesystem MCP may move from reading a scratch directory to reading a repository. A database MCP may move from exploring a sample database to touching production-like data. A GitHub MCP may expose both search and merge operations.
Traditional security controls still matter. OAuth can identify a user or client. Firewalls can restrict network access. Upstream credentials can be scoped. None of that automatically answers MCP-specific questions such as "should this agent see this tool?", "is this SQL query read-only?", "does this tool call need approval?", "did a new tool appear in the upstream?", or "should this returned customer email be redacted before the model sees it?".
MCPZT is built for that missing layer. It treats every MCP interaction as something to evaluate. It understands MCP methods such as tools/list, tools/call, resources/list, resources/read, prompts/list and prompts/get, and it maps those messages into a consistent policy context.
The result is a boring but useful control point. Boring is good here. You want a deterministic layer that can explain why it allowed a call, why it denied a call, why it created an approval, and whether the upstream server was actually contacted.
Installation
For most users, install MCPZT from PyPI into an isolated environment. This gives you the mcpzt command without cloning the repository.
python -m pip install mcp-zero-trust-layer
mcpzt version
For one-off usage without keeping a permanent environment, use uvx or pipx run. This is a good fit when you want to initialize a config, validate a repository, generate client config or run a local wrapper from a clean install.
uvx mcp-zero-trust-layer version
pipx run mcp-zero-trust-layer version
For local development from this repository, create a virtual environment and install the package in editable mode.
python -m venv .venv
. .venv/bin/activate
python -m pip install -e ".[dev]"
The CLI exposes two command names. mcpzt is the short name for daily use, and mcp-zero-trust-layer is the full package-style name.
mcpzt version
mcp-zero-trust-layer version
Docker is also supported. Use the published GHCR image for release deployments, or build a local image when testing changes from a checkout. The Dockerfile installs the package with constraints.txt, which keeps image builds reproducible while leaving PyPI dependency ranges flexible for library users.
docker run --rm ghcr.io/686f6c61/mcp-zero-trust-layer: version
docker build -t mcpzt:local .
docker run --rm mcpzt:local version
You can validate an example config inside Docker without giving the container write access to the project.
docker run --rm \
-v "$PWD/examples/multi-mcp:/cfg:ro" \
mcpzt:local config validate --config /cfg/mcpzt.yaml
After any install path, run a small verification sequence. It proves that the CLI starts, config generation works, the schema validates and the client config generator can produce JSON without touching a real MCP server.
mcpzt init --config /tmp/mcpzt.yaml --force
mcpzt config validate --config /tmp/mcpzt.yaml
mcpzt config lint --config /tmp/mcpzt.yaml
mcpzt client config --config /tmp/mcpzt.yaml --kind json
If you want to see the gateway behave end to end before connecting a real MCP, generate the local demo. It creates a fake upstream MCP server, a policy file, a tiny client and a runner script. The demo shows capability filtering, an allowed call, a denied call and output redaction.
mcpzt demo --output mcpzt-demo
./mcpzt-demo/run_demo.sh
How To Think About MCPZT
MCPZT is its own system, but it is normally used to protect MCP servers that already exist. You usually do not import it into the upstream MCP server or rewrite that server around MCPZT. Instead, you run MCPZT as a separate enforcement layer and point the MCP client at MCPZT instead of pointing it directly at the real server.
The simplest mental model is:
MCP client / agent
|
v
MCP Zero Trust Layer
|
v
existing MCP server
That means MCPZT has a product identity of its own, with its own CLI, config file, audit log, approvals, metrics and deployment lifecycle. At the same time, its job is not to replace GitHub MCP, Postgres MCP, filesystem MCP, CRM MCP or any other upstream. Its job is to govern access to them.
There are three common ways to use it.
For local or development use, install MCPZT with uvx, pipx, pip or an editable install. This is the easiest way to create configs, test policies, inspect explanations, run discovery and wrap local stdio MCP servers.
For gateway use, run MCPZT as a service. MCP clients call routes such as /mcp/github, /mcp/postgres or /mcp/crm, and MCPZT forwards only allowed traffic to private upstream MCP servers. This is the shape for teams that want one control point protecting several MCPs.
For sidecar use, run MCPZT next to one MCP server, often in the same host, container group or Kubernetes pod. The real MCP server listens only on localhost or a private interface, and MCPZT is the only process exposed to clients. This is the cleanest shape when a team owns one sensitive MCP and wants policy, approval and audit around it without changing the upstream server code.
Docker is one way to run the gateway or sidecar shapes. It is not required for local CLI use, and it does not mean MCPZT must become part of the upstream MCP server image. Docker gives you a reproducible, isolated runtime for the enforcement layer.
First Run
Start by creating a config file. The generated file is meant to be readable rather than magical. It gives you a project, runtime settings, an auth mode, one upstream server, starter capability mappings, starter policies, audit output and approval storage.
mcpzt init
Validate the config before running a proxy or wrapper. This catches structural errors early, and in production it also catches intentionally unsafe settings.
mcpzt config validate --config mcpzt.yaml
Run the linter next. Validation answers "is this config structurally valid?". Linting answers the more operational question: "does this config look too permissive, fragile or easy to misuse?". A development config with auth.mode: none may produce warnings; a production config should be clean under strict mode.
mcpzt config lint --config mcpzt.yaml
mcpzt config lint --strict --config mcpzt.yaml
The JSON Schema can be exported for editor integration, CI validation or documentation.
mcpzt config schema --output mcpzt.schema.json
doctor is the practical sanity check. It validates the config, checks configured commands, checks secret environment references, warns about weak local choices, and fails when it sees dangerous production posture. For CI or release preparation, use strict mode so warnings become a failing signal.
mcpzt doctor --config mcpzt.yaml
mcpzt doctor --strict --config mcpzt.yaml
mcpzt doctor --production --strict --config mcpzt.yaml
For a real MCP server, you can start with onboarding instead of writing every mapping by hand. mcpzt onboard discovers the upstream capabilities, infers conservative metadata from names and descriptions, writes a complete starter config, and saves discovery snapshots for review. The generated file is meant to be read and edited by humans. It is a starting point, not a promise that every inferred risk label is perfect.
mcpzt onboard \
--server github=http://127.0.0.1:3001/mcp \
--server crm=http://127.0.0.1:3002/mcp \
--output mcpzt.yaml
If you already have a config with servers, run onboarding against that file. MCPZT will preserve the declared servers and runtime shape, then generate mappings and initial policies from the discovered capabilities.
mcpzt onboard --config existing-mcpzt.yaml --output onboarded-mcpzt.yaml
The onboarding policy set is deliberately cautious. Low-risk reads are allowed, high and critical capabilities require approval, destructive capabilities are hidden, SQL-like tools get a read-only validator, and confidential-looking outputs get a redaction policy. Before using the result in enforcement, run coverage and risk analysis and review the YAML in a pull request.
mcpzt policy coverage --config mcpzt.yaml
mcpzt policy risks --config mcpzt.yaml
mcpzt policy unused --config mcpzt.yaml
Before connecting a real MCP client, test policy decisions from the CLI. This is one of the fastest ways to understand whether a policy is matching what you think it is matching.
mcpzt policy test \
--config examples/github-readonly/mcpzt.yaml \
--server github \
--method tools/call \
--capability github.search_issues
When a decision is surprising, use policy explain instead of guessing. It prints the normalized request context, mapped capability metadata, the policies that matched, the policies that did not match, the reason each policy was discarded, and the final selected decision. This is especially useful when a semantic mapping such as risk: critical or an identity claim such as group: security is not doing what you expected.
mcpzt policy explain \
--config examples/github-readonly/mcpzt.yaml \
--server github \
--method tools/call \
--capability github.merge_pull_request \
--arguments '{"branch":"main"}'
For validators, pass representative arguments. This command simulates a destructive SQL query against the Postgres example. The expected decision is deny, and the output includes validation errors explaining why.
mcpzt policy test \
--config examples/postgres-readonly/mcpzt.yaml \
--server postgres \
--method tools/call \
--capability postgres.query \
--arguments '{"query":"delete from users"}'
Once the config behaves as expected, run MCPZT as an HTTP proxy.
mcpzt run --config examples/github-readonly/mcpzt.yaml --host 127.0.0.1 --port 8765
If your MCP client expects a JSON config, generate one after the proxy is reachable. The generator points each logical MCP server at MCPZT, not at the upstream. For clients that use mcp-remote, this keeps the client setup small while preserving MCPZT as the enforcement point.
mcpzt client config \
--config examples/multi-mcp/mcpzt.yaml \
--base-url http://127.0.0.1:8765 \
--kind claude-desktop
Choose the output kind for the client you are configuring. claude-desktop, cursor and vscode produce JSON client config shapes. claude-code produces ready-to-run claude mcp add commands because that client is commonly configured from its CLI. json produces the raw neutral object, which is useful for scripts, tests and custom client generators.
mcpzt client config --config mcpzt.yaml --kind cursor
mcpzt client config --config mcpzt.yaml --kind vscode
mcpzt client config --config mcpzt.yaml --kind claude-code
mcpzt client config --config mcpzt.yaml --kind json
For command-based MCP servers, use the stdio wrapper. This mode keeps stdout reserved for MCP protocol traffic, so audit output must go to a file rather than stdout.
mcpzt wrap --config examples/filesystem-safe/mcpzt.yaml --server filesystem
If you already have MCP servers configured in Claude Desktop, Cursor or VS Code, you do not need to rewrite those entries by hand. Import the existing client config and let MCPZT generate two local files: an MCPZT policy config and a wrapped client config. With --discover, MCPZT starts the real upstream MCP servers, performs the MCP initialization handshake, lists their tools/resources/prompts, infers starter metadata and writes reviewable policies.
mcpzt client import \
--source "$HOME/Library/Application Support/Claude/claude_desktop_config.json" \
--mcpzt-config .mcpzt/clien
…
## Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [686f6c61](https://github.com/686f6c61)
- **Source:** [686f6c61/mcp-zero-trust-layer](https://github.com/686f6c61/mcp-zero-trust-layer)
- **License:** Apache-2.0
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.