Production MCP gateway: JWT auth, rate limiting, spending controls, audit log. mcp-guard serve --config mcp-guard.yaml
Project description
mcp-guard
Put auth, rate limits, spend caps, and audit logs in front of any MCP server โ without changing the server.
pip install "bonanza-mcp-guard[yaml]"
mcp-guard scan
mcp-guard serve --config mcp-guard.yaml
PyPI: package
bonanza-mcp-guard(namemcp-guardwas taken). CLI is stillmcp-guard.
Sits in front of any MCP server over stdio. Zero required dependencies.
Complements static scanners like mcp-scan with a runtime stdio gateway (auth, limits, audit).
Why mcp-guard?
| Without mcp-guard | With mcp-guard |
|---|---|
| Any agent calls any tool | Agent must authenticate (API key / JWT) |
| No spend ceiling | Per-session spend caps, per-hour rate limits |
| No audit trail | Every request logged to JSONL |
| Server exposed directly | Gateway wraps server โ zero code changes |
Complements static scanners like mcp-scan with a runtime stdio gateway (auth, limits, audit).
The problem
Security research in 2026 reported 1,800+ internet-exposed MCP endpoints with no authentication. Any MCP client can invoke tools with no identity, no spend ceiling, and no audit trail.
mcp-guard adds a gateway layer โ Claude Desktop, Cursor, Windsurf, or custom agents talk to mcp-guard; it talks to your real MCP server.
30-second try
pip install "bonanza-mcp-guard[yaml]"
mcp-guard scan
Example output:
๐ ~/Library/Application Support/Claude/claude_desktop_config.json
โน๏ธ No mcpServers defined
Summary: 0 critical, 0 warnings
Then add a config and run the gateway:
mcp-guard serve --config mcp-guard.yaml
Wire into Claude Desktop:
{
"mcpServers": {
"guarded": {
"command": "mcp-guard",
"args": ["serve", "--config", "/path/to/mcp-guard.yaml"]
}
}
}
Quickstart
1. Install
pip install "bonanza-mcp-guard[yaml]"
1b. Audit your machine (no server needed)
mcp-guard scan
2. Configure (mcp-guard.yaml)
auth:
mode: api_key
keys:
- "sk-agent-1"
- "sk-agent-2"
servers:
filesystem:
command: npx @modelcontextprotocol/server-filesystem /data
policies:
max_spend_per_session: 10.00
audit_log: /var/log/mcp-guard.jsonl
rate_limit:
requests_per_minute: 100
spend_per_hour_usd: 50.00
3. Agents include auth in requests
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"_meta": { "api_key": "sk-agent-1" },
"name": "wallet_request",
"arguments": { "amount": 1.50 }
}
}
Architecture
MCP Client (Claude Desktop / Cursor / custom agent)
โ
โผ JSON-RPC over stdio
โโโโโโโโโโโโโโโโ
โ mcp-guard โ auth โ rate limit โ spend check โ audit
โโโโโโโโโโโโโโโโ
โ
โผ forwards allowed requests
Your MCP Server (filesystem, wallet, fetch, anything)
1,168 lines of Python. Zero required dependencies.
Config reference
| Key | Type | Default | Description |
|---|---|---|---|
auth.mode |
none | api_key | jwt |
none |
Authentication mode |
auth.keys |
list[str] |
[] |
Valid API keys (mode=api_key) |
auth.jwt_secret |
str |
โ | HMAC-SHA256 secret (mode=jwt) |
policies.rate_limit.requests_per_minute |
int |
โ |
Max requests per agent per minute |
policies.rate_limit.spend_per_hour_usd |
float |
โ |
Max spend per agent per hour |
policies.max_spend_per_session |
float |
โ |
Session spend cap |
policies.require_approval_above |
float |
โ |
Threshold for manual approval |
policies.audit_log |
str |
โ | JSONL audit log path |
servers |
map |
{} |
Backend MCP servers to proxy to |
Authentication modes
API Key (simplest)
auth:
mode: api_key
keys:
- "sk-agent-1"
Keys: _meta.api_key, _meta.token, Authorization: Bearer, or X-API-Key.
JWT (multi-agent)
auth:
mode: jwt
jwt_secret: "${JWT_SECRET}"
from mcp_guard.auth import create_jwt
token = create_jwt(secret="my-secret", agent_id="agent-007", ttl_seconds=3600)
None (dev/local)
auth:
mode: none
Rate limiting & spending
policies:
rate_limit:
requests_per_minute: 60
spend_per_hour_usd: 25.00
max_spend_per_session: 10.00
require_approval_above: 2.00
Spending tools: wallet_request, wallet_pay, x402_pay, etc. Blocked payments: JSON-RPC -32002.
Use with agent-budget (pip install bonanza-labs-agent-budget) for @budget on LLM calls.
Audit log
policies:
audit_log: /var/log/mcp-guard.jsonl
Programmatic use
from mcp_guard import MCPProxy, GuardConfig
config = GuardConfig.from_dict({
"auth": {"mode": "api_key", "keys": ["sk-abc"]},
"policies": {"max_spend_per_session": 25.0},
})
proxy = MCPProxy.from_config(config)
Related (Bonanza Labs)
| Project | Role |
|---|---|
| agent-budget | @budget(max_usd=โฆ) for LLM + x402 |
| bonanza-labs-fork-doctor | Repo health before you ship |
GitHub Action โ scan MCP configs on PRs
Automatically scan MCP config files in pull requests for security issues (missing auth, exposed remote URLs, no guard wrapper).
# .github/workflows/mcp-scan.yml โ already included in this repo
# Triggers on: **/claude_desktop_config.json, **/mcp.json, **/*mcp*.json/yaml
Add it to your repo:
uses: c6zks4gssn-droid/mcp-guard/.github/workflows/mcp-scan.yml@main
Or copy the workflow file directly. Fails PRs with critical findings and posts a comment with the full report.
Roadmap
v0.1.2 (next)
- Docker image (
docker run mcp-guard) - HTTP/SSE transport (
mcp-guard serve-http) - Prometheus metrics endpoint (
GET /metrics) - Approval queue โ hold tool calls above threshold for human approval
- HTTP/SSE transport (not just stdio)
- Tool allowlist/denylist per agent
- Prometheus metrics endpoint
Future
- Standalone GitHub Action (reusable workflow)
- Web dashboard for audit log visualization
- Multi-server routing (one gateway, many backends)
- OAuth2 provider for agent-to-agent auth
- Plugin system for custom policy checks
Launch: LAUNCH.md
Approval queue
When a spending tool call exceeds require_approval_above, the proxy holds it and returns error -32004 with the approval_id. A human reviews and approves/denies via the CLI:
policies:
require_approval_above: 5.00
# List pending
mcp-guard approvals list --db ~/.mcp-guard/approvals.db
# Approve (short ID works)
mcp-guard approvals approve abc12345 --by admin
# Deny with reason
mcp-guard approvals deny abc12345 --by admin --reason "vendor untrusted"
Pending requests are persisted in SQLite โ survive restarts.
HTTP/SSE transport
For remote agents that connect over HTTP instead of stdio:
mcp-guard serve-http --config mcp-guard.yaml --port 8080
Endpoints:
| Method | Path | Description |
|---|---|---|
POST |
/rpc |
JSON-RPC request โ response |
GET |
/health |
Health check |
GET |
/metrics |
Prometheus metrics |
Auth over HTTP: X-API-Key header or Authorization: Bearer header.
curl -X POST http://localhost:8080/rpc \
-H "Content-Type: application/json" \
-H "X-API-Key: sk-agent-1" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
Docker
# Build
DOCKER_BUILDKIT=1 docker build -t mcp-guard .
# Run with config
DOCKER_BUILDKIT=1 docker run --rm -i \
-v $(pwd)/mcp-guard.yaml:/etc/mcp-guard/config.yaml:ro \
-v $(pwd)/logs:/var/log/mcp-guard \
mcp-guard serve --config /etc/mcp-guard/config.yaml
Or with docker compose:
docker compose up
CI publishes to ghcr.io/c6zks4gssn-droid/mcp-guard on every tag.
Contributing
PRs welcome. The codebase is 1,168 lines across 8 modules โ small enough to hold in your head.
git clone https://github.com/c6zks4gssn-droid/mcp-guard
cd mcp-guard
pip install -e ".[yaml,dev]"
pytest
Project structure
mcp_guard/
โโโ __main__.py # CLI entry point (serve, scan)
โโโ config.py # GuardConfig + YAML/JSON loader
โโโ auth.py # API key + JWT providers
โโโ proxy.py # MCPProxy โ the intercept engine
โโโ ratelimit.py # Per-agent rate limiting
โโโ audit.py # JSONL audit logger
โโโ scan.py # Local config scanner
License
Apache 2.0 ยท Bonanza Labs
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file bonanza_mcp_guard-0.1.3.tar.gz.
File metadata
- Download URL: bonanza_mcp_guard-0.1.3.tar.gz
- Upload date:
- Size: 27.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
38d71669c7476307e89a75d210f6480027590cf799a4ae6c2693f14759c51d19
|
|
| MD5 |
fc441eae70c4fe65ef7a18497412c504
|
|
| BLAKE2b-256 |
9dbe34ba06edea520e4dbacc7ce5a0a3d313d72dec69c22d35c927e875ff24b3
|
File details
Details for the file bonanza_mcp_guard-0.1.3-py3-none-any.whl.
File metadata
- Download URL: bonanza_mcp_guard-0.1.3-py3-none-any.whl
- Upload date:
- Size: 26.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
260df446a585923f828f481ca06fdf3b647eea15ae06a7e42f000bf5e4443f38
|
|
| MD5 |
582fc0663c07e22da48fd1b4a57db1a3
|
|
| BLAKE2b-256 |
1ced6f6ce6c349b3d819d3001f6e67aedb22739c6c1a890f6616a3d514f33c68
|