Install
$ agentstack add mcp-abwaters-mcp-zero ✓ 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 No
- ✓ 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
[](https://github.com/abwaters/mcp-zero/actions/workflows/ci.yml) [](https://github.com/abwaters/mcp-zero/actions/workflows/codeql.yml) [](https://securityscorecards.dev/viewer/?uri=github.com/abwaters/mcp-zero) [](LICENSE) [](pyproject.toml)
An open-source gateway that sits between your enterprise AI tools and MCP servers to enforce the security controls that compliance teams require before approving MCP adoption.
Without it, AI tools can call any MCP server with no access control, no audit trail, and no data protection — a non-starter in regulated environments. mcp-zero adds the missing governance layer: it validates user identity, checks policy rules, masks sensitive data, and logs every action — all inline, before requests ever reach downstream servers.
Deploy it as a single Python service. Configure it with a YAML policy file. No agents to install, no SaaS dependency, no vendor lock-in.
Enterprise AI Tool ──► MCP Gateway ──► MCP Servers
│
┌──────────┴──────────┐
│ Hook Pipeline │
│ │
│ Identity (core) │
│ Governance (core) │
│ ◇ Plugins (ext) │
│ Audit (core) │
└─────────────────────┘
Features
- Identity — Okta OAuth2 JWT validation with configurable claim mapping (
user_id,email,groups) - Governance — YAML policy files with default-deny rules scoped to server, tool, user, and group
- Data Protection — Inline PII and secret masking via Microsoft Presidio on both inputs and outputs
- Auditing — Structured logs with user attribution, correlation IDs, and policy decisions
- Transport — Streamable HTTP (primary), legacy inbound SSE (optional), and upstream HTTP/SSE/stdio server connectivity under the same policy pipeline
- Pipeline — Hook-based request lifecycle with ordered execution and short-circuit support
- Plugins — Entry-point based plugin architecture for extending the pipeline with custom hooks (masking, rate limiting, metrics, etc.)
Quick Start
Prerequisites
- Python 3.12+
- An Okta tenant (for identity validation)
Security Notice
IMPORTANT: The gateway is designed with fail-closed defaults but requires proper configuration to enforce security:
- Policy file strongly recommended: Set
MCP_POLICY_FILEto enable governance and plugin configuration. - Fail-closed startup by default: If neither identity nor a policy file is configured, the gateway exits with code 78.
- Development-only bypasses:
MCP_RELAX_STARTUP_CHECKS=trueallows startup without security controls.MCP_SKIP_TLS_VALIDATION=trueallows non-HTTPS identity/server/OBO URLs.- Strict production hardening:
MCP_STRICT_SECURITY=truerequires both identity and governance to be active. - Masking dependencies: Presidio masking requires
presidio-analyzerandpresidio-anonymizer(included in default install).
Known Limitations (see [security review](docs/securityreviewmcp_gateway.md) for broader analysis):
- Legacy/development modes can still be enabled intentionally via
MCP_RELAX_STARTUP_CHECKS=trueand/orMCP_SKIP_TLS_VALIDATION=true; these must not be used in production. - OBO token exchange requires explicit environment variables (
OKTA_TOKEN_ENDPOINT,OKTA_CLIENT_ID,OKTA_CLIENT_SECRET) and per-server policy configuration. - Inbound SSE support remains available for compatibility but is deprecated; disable with
MCP_SSE_ENABLED=falseif not needed.
Never run production deployments with relaxed startup checks or TLS validation disabled.
Install
# Clone and install
git clone https://github.com/abwaters/mcp-zero.git
cd mcp-zero
python -m venv .venv
# Linux/macOS
source .venv/bin/activate
# Windows
.venv\Scripts\activate
pip install -e ".[dev]"
On Windows, convenience scripts are provided:
scripts\install.bat # Creates venv and installs everything
Configure
Create a policy file (e.g., policy.yaml):
version: 1
default: deny
identity:
provider: okta
issuer: https://your-org.okta.com
audience: your-app-audience
servers:
- name: my-mcp-server
transport: http
url: https://mcp-server.internal.corp/mcp
policies:
- id: allow-devs
description: Allow developers to use read tools
effect: allow
subjects:
groups:
- developers
mcp_servers:
- name: my-mcp-server
tools:
- read_*
- list_*
masking:
presidio:
enabled: true
entities:
- PERSON
- EMAIL_ADDRESS
- PHONE_NUMBER
- CREDIT_CARD
- API_KEY
- PASSWORD
Run
# Set the policy file path
export MCP_POLICY_FILE=policy.yaml
# Start the gateway
python -m mcp_zero
# or
mcp-zero
Or with environment variables for simple setups:
export MCP_UPSTREAM_URL=http://localhost:9000
export OKTA_ISSUER=https://your-org.okta.com
export OKTA_AUDIENCE=your-app-audience
python -m mcp_zero
The gateway starts on 0.0.0.0:8080 by default (configurable via MCP_HOST and MCP_PORT).
Configuration
Environment Variables
| Variable | Description | Default | |---|---|---| | MCP_POLICY_FILE | Path to YAML/JSON policy file | (none) | | MCP_UPSTREAM_URL | Single upstream MCP server URL (legacy fallback) | (none) | | MCP_HOST | Host to bind the gateway | 0.0.0.0 | | MCP_PORT | Port to bind the gateway | 8080 | | MCP_RELAX_STARTUP_CHECKS | Allow startup without required security controls (dev/testing only) | false | | MCP_SKIP_TLS_VALIDATION | Allow http:// issuer/server/OBO URLs (dev/testing only) | false | | MCP_STRICT_SECURITY | Require both identity and governance at startup | false | | MCP_SSE_ENABLED | Enable deprecated inbound SSE endpoints (/mcp/sse*) | true | | LOG_LEVEL | Logging level (DEBUG, INFO, WARNING, ERROR) | INFO | | LOG_FORMAT | Log output format (json or text) | json | | OKTA_ISSUER | Okta token issuer URL (fallback if no policy file) | (none) | | OKTA_AUDIENCE | Expected JWT audience claim (fallback if no policy file) | (none) | | OKTA_TOKEN_ENDPOINT | Okta token exchange endpoint (for OBO) | (none) | | OKTA_CLIENT_ID | Gateway client ID (for OBO) | (none) | | OKTA_CLIENT_SECRET | Gateway client secret (for OBO) | (none) | | ANALYTICS_REDIS_URL | Redis connection URL (enables analytics when set) | (none) | | ANALYTICS_REDIS_CLUSTER | Use Redis Cluster client | false | | ANALYTICS_REDIS_PASSWORD | Redis authentication password | (none) | | ANALYTICS_ENVIRONMENT | Analytics key namespace (e.g. production) | default | | ANALYTICS_GATEWAY_ID | Unique gateway instance ID | (auto-generated) | | ANALYTICS_KEY_PREFIX | Redis key prefix for analytics | mcpgw | | ANALYTICS_RETENTION_SECONDS | TTL for analytics keys in seconds | 3600 | | MCP_CORS_ORIGINS | Comma-separated allowed CORS origins (enables CORS when set) | (none) | | MCP_CORS_ALLOW_CREDENTIALS | Allow credentials in CORS requests | false | | MCP_CORS_MAX_AGE | Preflight cache duration in seconds | 600 |
When MCP_POLICY_FILE is set, its identity section takes precedence over OKTA_* env vars. CORS and analytics env vars override policy values.
Policy File
See [docs/enterprise_mcp_gateway_policy_schema_example.md](docs/enterprisemcpgatewaypolicyschema_example.md) for a full annotated example.
Working examples are available in the [policies/](policies/) directory:
| File | Description | |------|-------------| | [policies/everything.yaml](policies/everything.yaml) | stdio transport with @modelcontextprotocol/server-everything | | [policies/filesystem.yaml](policies/filesystem.yaml) | stdio transport with @modelcontextprotocol/server-filesystem | | [policies/filesystem-redacted.yaml](policies/filesystem-redacted.yaml) | Filesystem server with Presidio masking plugin | | [policies/time.yaml](policies/time.yaml) | stdio transport with @modelcontextprotocol/server-time | | [policies/all.yaml](policies/all.yaml) | Multiple stdio servers combined | | [policies/remote-github.yaml](policies/remote-github.yaml) | Remote GitHub MCP server via Streamable HTTP | | [policies/remote-github-readonly.yaml](policies/remote-github-readonly.yaml) | GitHub server with explicit read-only tool allowlist |
Key concepts:
version: Must be1default:deny(recommended) orallowservers: Downstream MCP server definitions (HTTP or stdio)policies: Ordered rules evaluated top-down; explicit deny overrides allowcors: CORS configuration for browser-based clients (disabled by default)masking: Presidio entity detection configuration (legacy — seepluginsbelow)plugins: Plugin declarations for extensible pipeline hooks (masking, rate limiting, etc.)
CORS Configuration
Add a cors section to the policy file to allow browser-based MCP clients:
cors:
allow_origins:
- https://web-ide.corp.com
- https://dashboard.corp.com
allow_methods: ["GET", "POST", "OPTIONS"]
allow_headers: ["Authorization", "Content-Type"]
allow_credentials: false
max_age: 600
CORS is disabled by default (fail-closed). Only allow_origins is required; all other fields have safe defaults. Environment variables (MCP_CORS_ORIGINS, MCP_CORS_ALLOW_CREDENTIALS, MCP_CORS_MAX_AGE) override policy file values.
Server Types
HTTP servers — remote MCP servers accessed over Streamable HTTP:
servers:
- name: remote-api
transport: http
url: https://mcp-server.corp/mcp
SSE servers — remote MCP servers over SSE:
servers:
- name: legacy-sse-server
transport: sse
url: https://mcp-server.corp/sse
stdio servers — local processes spawned and managed by the gateway (configured in policy files):
servers:
- name: local-filesystem
transport: stdio
command: npx
args:
- -y
- "@modelcontextprotocol/server-filesystem"
- /workspace
env:
NODE_ENV: production
Development
# Run tests
python -m pytest # all tests
python -m pytest tests/masking/ -v # specific module
python -m pytest tests/test_main.py -v # specific file
# Lint and format
ruff check src tests
ruff format src tests
# Run the gateway
python -m mcp_zero
On Windows, use the provided scripts:
scripts\test.bat # Run tests
scripts\lint.bat # Lint
scripts\format.bat # Format
scripts\run.bat # Run gateway
Project Structure
src/mcp_zero/
├── main.py # Application entry point
├── plugin.py # Plugin protocol and base class
├── plugin_manager.py # Plugin discovery and lifecycle
├── context.py # RequestContext, HookContext, UserIdentity
├── identity/ # Okta JWT validation, OBO token exchange
├── governance/ # Policy loading, evaluation, enforcement
├── masking/ # Masking engine interface and hook
├── plugins/ # Built-in plugins (Presidio masking, GitHub repo filter)
├── pipeline/ # Hook lifecycle, registry, execution
├── proxy/ # Starlette app, server management, tool routing
├── analytics/ # Optional Redis-based analytics
└── transport/ # HTTP, SSE, and stdio MCP transport clients
Architecture
The gateway uses a hook-based pipeline with a plugin system. Core hooks handle identity, governance, and audit. Everything else — masking, rate limiting, metrics — is a plugin loaded from the policy file.
Core hooks (always present):
| Priority | Hook | Phase | Purpose | |----------|------|-------|---------| | 10 | IdentityHook | PREVALIDATION | Validates JWT, resolves user identity | | 50 | GovernanceHook | POSTVALIDATION | Evaluates policy rules, allows or denies | | 145 | AnalyticsHook | PREAUDIT | Records metrics to Redis (when configured) | | 150 | AuditHook | PREAUDIT | Emits structured log with full request context |
Plugin hooks (loaded from policy file plugins: section):
| Priority Range | Slot | Examples | |----------------|------|---------| | 20–49 | Pre-governance | Rate limiting, request validation | | 70–99 | Post-governance | Masking (Presidio at 75), transformation | | 100–139 | General | Metrics, caching, custom hooks |
Hooks execute in priority order. Any hook can short-circuit the pipeline (e.g., governance denial stops processing immediately).
Plugin system: Plugins are discovered via Python entry points (mcp_zero.plugins group), configured in the policy file, and registered into the pipeline at startup. See [docs/plugin-architecture-design.md](docs/plugin-architecture-design.md) for details.
Documentation
| Document | Description | |---|---| | [docs/quickstart.md](docs/quickstart.md) | Step-by-step quickstart with Docker Compose and local paths | | [docs/prd.md](docs/prd.md) | Product requirements and acceptance criteria | | [docs/enterprise_mcp_gateway_architecture_diagram.md](docs/enterprisemcpgatewayarchitecturediagram.md) | Logical architecture with component diagram | | [docs/enterprise_mcp_gateway_implementation_plan_epics.md](docs/enterprisemcpgatewayimplementationplanepics.md) | Phased implementation plan | | [docs/enterprise_mcp_gateway_policy_schema_example.md](docs/enterprisemcpgatewaypolicyschemaexample.md) | Full annotated policy file example | | [docs/enterprise_mcp_gateway_security_compliance_positioning.md](docs/enterprisemcpgatewaysecuritycompliancepositioning.md) | Security controls and compliance alignment | | [docs/enterprise_mcp_gateway_threat_model_canvas.md](docs/enterprisemcpgatewaythreatmodelcanvas.md) | Threat model and mitigations | | [docs/okta_obo_for_an_enterprise_mcp_gateway.md](docs/oktaoboforanenterprisemcpgateway.md) | OBO token exchange deep-dive | | [docs/enterprise_mcp_gateway_leadership_explainer.md](docs/enterprisemcpgatewayleadershipexplainer.md) | Non-technical stakeholder overview | | [docs/configuration-architecture.md](docs/configuration-architecture.md) | Configuration loading, env vars, policy schema, precedence rules | | [docs/cors-configuration.md](docs/cors-configuration.md) | CORS setup for browser-based MCP clients | | [docs/plugins/](docs/plugins/) | Plugin quickstart and per-plugin documentation |
Comparison
How mcp-zero compares to other MCP gateways:
| Capability | mcp-zero | AgentGateway | MintMCP | Microsoft MCP Gateway | Lasso MCP Gateway | |---|---|---|---|---|---| | License | ✅ MIT | ✅ Apache 2.0 (Linux Foundation) | ❌ Commercial SaaS | ✅ MIT | ✅ MIT | | Language | Python | Rust / Go | Proprietary | .NET / C# | Python | | Transport | ✅ Streamable HTTP, stdio | ✅ Streamable HTTP, SSE, stdio | ✅ HTTP, SSE, stdio | Streamable HTTP only | stdio only | | Authentication | ✅ Okta OAuth2 JWT | ✅ JWT, API keys, OAuth (Auth0, Keycloak), MCP auth spec | ✅ OAuth 2.0, SAML, SSO (Okta, Azure AD) | Azure Entra ID / OAuth 2.0 | ❌ None built-in | | Governance | ✅ YAML/JSON policy files, default-deny, server/tool/user/group rules | ✅ RBAC, Cedar policy engine, rate limiting | RBAC/ABAC, Virtual MCP role-based endpoints | RBAC via Entra ID roles | ❌ Plugin-based only | | Data protection | ✅ Inline Presidio masking on inputs and outputs | ❌ None built-in | ✅ PII redaction, secrets scanning, content filtering | ❌ None built-in | Presidio PII + regex secret masking | | Auditing | ✅ Structured logs with user attribution, correlation IDs, policy decisions | ✅ OpenTelemetry met
…
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: abwaters
- Source: abwaters/mcp-zero
- License: MIT
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.