We built the same MCP server six times. Then we built 0-mcp.
0-mcp started life as a thin REST framework for Django โ born from the boredom of writing the same list view, the same auth check, the same cache wrapper on every project. Then agents arrived. Suddenly every team wanted to expose those same resources as MCP tools, and we found ourselves writing a second codebase to mirror the first. So we collapsed them. The MCP server became the headline. The REST API became the bonus. Same engine, sharper purpose.
Phase 1 โ The boring 90% of every Django API
We've built the same Django REST API ten times. List view, detail, create, update, delete, auth, rate limit, cache, tenant switch. Hundreds of lines per resource, copy-pasted across projects, drifting in slightly different ways every time. The original goal was simple: collapse that pattern into one class.
- A list view with pagination, filters, search, ordering โ written once, copy-pasted forever
- A detail view nobody remembers writing
- Create, update, delete handlers with the same field-whitelist code in slightly different shapes
- Auth checks duplicated at the top of every handler
- A rate limit decorator that someone added in 2021 and never tested
- A cache wrapper that invalidates wrong half the time
- A multi-tenant switch that lives in middleware, in views, and in random helpers
Every one of those is a few lines. Together they are hundreds of lines per resource. Across a SaaS with fifty resources, they are tens of thousands of lines that nobody owns and everybody is afraid to touch.
Phase 2 โ Agents changed the brief
When Claude Desktop, Cursor and the MCP ecosystem started showing up in production, every team had the same realization at the same time: the agent surface is just another view of the resources the REST API already exposes. Different protocol, same auth, same rate limit, same field whitelists, same ownership scoping. Writing it twice is insane.
We tried bolting an MCP layer on top of what we had. It worked. It also doubled the surface area: tool registrations had to mirror endpoint signatures, schemas drifted between OpenAPI and JSON Schema, auth wiring had to be re-implemented at the MCP boundary. The same problem we solved for REST was now back, in a new shape, on the agent side.
Phase 3 โ MCP became the headline
So we promoted MCP from a side feature to the main product. The framework's job is now: turn a Django model into an MCP server, with a REST API falling out of the same definition. Same engine. Different center of gravity. New name to match โ 0-mcp, because that's what you write to get one running.
We tried the alternatives
- FastMCP / MCP Python SDK โ great if you're starting from a blank file. Painful when you already have a Django app: you re-implement auth, rate limit, ownership and validation that already live in your codebase.
- DRF + custom MCP layer โ two codebases, two schemas, drift on day one.
- Django Ninja + custom MCP layer โ same problem, slightly less code.
- Hand-rolled MCP server โ what every team ends up with first. And what every team regrets.
- DRF for the API alone โ powerful but huge. New devs spend days learning serializers, viewsets, routers, permissions, throttles and authentication classes before writing their first line of product code.
- FastAPI โ beautiful, but it is not Django. You give up the ORM, the admin, the migrations, the auth โ half your stack โ to gain pretty docs.
Goals we set
- MCP-first. A Django model becomes an MCP server with no extra registration. Tools, schemas, transports โ generated.
- One source of truth. REST and MCP share the same fields, schemas, auth, rate limit and ownership rules. Drift is impossible because there is no second copy.
- Convention over configuration. A class with attributes covers the common case. No metaclasses, no descriptors, no magic.
- Async-first. Every handler is
async. The async ORM is used end-to-end. Nosync_to_asyncshims you have to remember. - Redis-native. Sessions, cache, rate limit and abuse blocking share one client. Connection pool tuning is not your problem.
- Security-by-default. Token validation, IP spoof prevention, scanner blocking, 4xx-flood detection โ all on. You opt out, not in.
- Pluggable, not magical. Every step of dispatch is an overridable hook. When the convention doesn't fit, escape hatches are obvious.
What we are not trying to be
- A general-purpose MCP framework โ we wrap Django models specifically
- A drop-in DRF replacement for projects that already use it
- A serverless-only library
- Database-agnostic โ we trust Django's ORM
- Working without Redis. Sessions, cache, rate limit and abuse all need it
0-mcp is the framework we wished we had the day Claude Desktop shipped. It is not trying to be everything. It is trying to be the right thing for Django apps that need an agent surface โ and ruthlessly good at it.
0-mcp by Stamatios Stamou Jr โ github.com/ssjunior/0-mcp