Settings & env vars
Everything the library reads from your environment or Django settings.
Settings layout — MCP bag
Every 0-mcp setting lives inside a single MCP = {...} dict in settings.py (DRF/Celery-style namespace):
# settings.py
MCP = {
'CACHE_TTL': 300,
'CACHE_TTL_ENABLE': True,
'ENFORCE_TOKEN': True,
'COOKIE_ID': 'sid',
'RATE_LIMITS': {...},
}Inside the bag the MCP_ prefix is redundant — 'MCP_API_KEY_RESOLVER' and 'API_KEY_RESOLVER' resolve to the same setting.
Environment variables
| Variable | Required | Description |
|---|---|---|
REDIS_SERVER | Yes | Redis hostname / address. Requires Redis 6.2 or newer (the framework uses GETEX for sliding session TTLs). |
REDIS_DB | Yes | Redis database number |
REDIS_PREFIX | No | Optional prefix applied to all Redis keys (use to isolate environments on a shared instance) |
Django settings
| Setting | Default | Description |
|---|---|---|
DEFAULT_AUTHENTICATED | True | Class-level default for BaseResource.authenticated. Generated projects (0-mcp init) ship with this set to False so the demo boots without a configured api-key resolver — flip back to True (or override per-resource) before deploying. |
COOKIE_ID | 'sessionid' | Cookie name carrying the session id. Used by AuthMiddleware, BaseResource and the OpenAPI security scheme — set once in the bag and every entry point reads the same value. |
ALLOWED_ORIGINS | [] | If set, requests must have an Origin (with Referer fallback) whose host ends with one of these origins |
TRUSTED_PROXIES | [] | CIDRs allowed to set X-Real-IP / X-Forwarded-For |
ENFORCE_TOKEN | False | Require X-Token anti-replay header on authenticated requests |
TOKEN_MAX_DRIFT_MS | 30000 | Maximum clock drift (milliseconds) accepted when validating X-Token. Tighter values reduce the replay window; looser values tolerate slow clocks and event-loop pauses on the client. |
CREDENTIAL_PATHS | ['/forgot', '/change_password', '/signup', '/reset', '/recover', '/register'] | Path-segment list that triggers the credential-path auto-defense (a tight per-IP rate-limit bucket on top of the regular API limits). Segment-based match — /forgot covers /login/forgot, /user/forgot/..., but not /forgotten. Setting this in your project replaces the framework default (no merge); copy the list to extend. Set to [] to disable. |
CREDENTIAL_RATE_LIMIT | {'limit': 5, 'window': 30} | Per-IP rate limit applied to CREDENTIAL_PATHS. window in seconds. Defaults to 5 hits / 30s — tight enough to break credential-stuffing, loose enough to absorb legitimate retries (resend-email button, NAT-shared offices). |
READ_ONLY | False | Global read-only gate. When True, every non-GET request returns 405 across all resources (REST + MCP) — overrides per-resource allowed_methods. Useful for safe-by-default deployments and projects generated by 0-mcp init (the default unless you pass --writable). |
RATE_LIMITS | sensible defaults | See Rate limiting page |
AUTO_SCOPE_CACHE_BY_ACCOUNT | True | Auto-fold self.account_id into the cache key so multi-tenant deployments don't leak cache across tenants. Set to False only on single-tenant deployments that want the legacy key shape. See Cache. |
CACHE_TTL | None | Project-level default for BaseResource.cache_ttl (seconds). When unset, the framework default of 120s applies. Resources that declare cache_ttl = N keep their explicit value. See Cache. |
CACHE_TTL_ENABLE | True | Master kill switch. When False, every cache=True resource is treated as cache=False at runtime — no read, no write to Redis. Useful for incident response without code edits. |
SESSION_TTL | 1800 | Sliding TTL (seconds) for cookie-based sessions. Renewed on every authenticated request via Redis GETEX (Redis 6.2+ required). |
API_SESSION_TTL | 300 | Sliding TTL (seconds) for the API-key → session mapping. Renewed on every authenticated request via Redis GETEX. |
MCP_LIST_OMIT_NULL | False | Strip null fields from MCP list_* tool responses to shrink payloads. Useful for agent surfaces where empty fields just consume tokens. |
MCP_EDIT_OMIT_NULL | False | Same as MCP_LIST_OMIT_NULL but for get_* / update_* tool responses. |
MAX_4XX_PER_MINUTE | 10 | Anti-scanner: when an IP returns more than this many 4xx responses in 60s, it is parked in the block list for BLOCK_DURATION_SECONDS. |
MAX_4XX_PER_HOUR | 30 | Same as MAX_4XX_PER_MINUTE but on a 1-hour window. Hitting either threshold triggers the block. |
BLOCK_DURATION_SECONDS | 86400 | How long an IP stays in the block list once tripped. Defaults to 24h. |
BLOCK_PATTERNS | None (framework baseline) | Regex list applied to request paths — known vulnerability scanners are blocked pre-emptively. Pass a list to replace the baseline, [] to disable. |
BLOCK_USER_AGENTS | None (framework baseline) | Same as BLOCK_PATTERNS but for the User-Agent header. |
API_KEY_RESOLVER | None | Dotted path (or callable) to a custom API key resolver. Receives the raw key, returns a session dict or None. When unset, the default four-segment-hash resolver is used. (MCP_API_KEY_RESOLVER is also accepted — the prefix is redundant inside the bag.) |
DEBUG | False | When False, unhandled exceptions become sanitized 500s |
Middleware order
In MIDDLEWARE:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# ... your other middleware ...
'zeromcp.SecurityMiddleware', # blocks scanners early
'zeromcp.AuthMiddleware', # handled by dispatch, but kept for non-resource views
'zeromcp.ExceptionMiddleware', # catches HTTPException
]Database router (multi-tenant)
DATABASE_ROUTERS = ['zeromcp.DBRouter']
Required only if you use multi-tenant.
0-mcp by Stamatios Stamou Jr — github.com/ssjunior/0-mcp