Less code. Faster ships. Production-grade defaults.

Here is what you stop writing the moment you adopt 0-mcp — and what you get for free in return.

10x less code per resource

A typical Django REST resource done by hand: a list view (filters, search, ordering, pagination, serialization), a detail view, three write handlers (create, update, delete with field whitelists), session auth, rate limit, cache invalidation. That is 200–400 lines.

In 0-mcp the same resource is a class with attributes. Usually under 30 lines. Including comments.

Async by default. Always.

Every handler is async. The dispatch chain uses async Redis (redis.asyncio) and async ORM (afirst, aget, aupdate, acreate). You never have to think about sync_to_async boundaries unless you wrote blocking code on purpose. Your event loop stays free.

Redis is the engine, not an afterthought

One shared Redis client at import time. Reused for sessions, cache, rate limit, abuse blocking and security middleware. Zero per-request connection pool churn. Zero "did I close the pool" bugs.

Cache that invalidates correctly

Opt-in per resource — flip cache = True and you're done. But the part that actually matters: invalidation that doesn't blow up the world.

Cache keys live under namespaces. List responses go to list:<model>. Detail responses go to detail:<model>:<id>. When user 5 updates their profile, only their detail cache and the user list are dropped. User 7's cached detail stays warm. Your Redis stops being a stampede waiting to happen.

Security middleware that bites

SecurityMiddleware matches request paths against the patterns scanners actually use — WordPress probes, traversal, SQLi, XSS, dotfile fishing. It checks user agents against the names you'd find in any honeypot. One match and the IP is blocked for 24 hours.

A separate sliding window catches slow probers — IPs that stay quiet but rack up 4xx responses get the same 24-hour timeout.

Your logs get cleaner. Your DB stops processing junk. Your real users never notice.

Multi-tenant routing that just works

aset_tenant(account_id) switches the active database connection for the rest of the request. Combined with DBRouter, every ORM query is automatically scoped to the right tenant DB. Your model code does not change. Your business logic does not know there are multiple databases. Your queries can't accidentally cross tenants because the connection is gone.

Pydantic when you want it. Never forced.

Set create_schema, update_schema, list_schema and the library validates the request body and shapes the response through your schema. Skip those attributes and the library falls back to Django field introspection. Adopt schemas one resource at a time. No big migration.

OpenAPI that doesn't lie

You get /openapi.json (3.0.3) plus /docs rendered with Scalar — beautiful two-column layout, dark mode, search, try-it-out. Pydantic schemas are exported as JSON Schema and referenced from the spec. No manual annotations. The spec is generated from the same source of truth as the validation.

Production-grade defaults

What you get on day oneWhere it would normally live
Per-IP rate limit + abuse blockingA custom middleware you forgot to write
Scanner pattern blockingA WAF rule someone pays for
4xx-flood detectionA SIEM alert someone ignores
Sanitized 500 responses in productionA bug where you leak stack traces
IP spoof prevention via TRUSTED_PROXIESA CVE waiting to happen
Session cookie validation against a regexYour Redis getting probed
Field whitelists enforced on every writeMass-assignment bugs

Every feature is opt-in or opt-out at the resource level. Adopt 0-mcp for one endpoint, leave the rest of your project untouched, and grow from there. There is no rewrite.

The bottom line

Less code to write. Less code to maintain. Less code to drift. Less code that breaks. More time on the part of your product that customers actually pay for.

0-mcp by Stamatios Stamou Jr — github.com/ssjunior/0-mcp