Django: Django-mcpz, for making MCP servers Developer Simon Willison released django-mcpz, a Django package for building Model Context Protocol (MCP) servers, targeting the latest MCP specification version 2026-07-28. The package uses that version's stateless transport, which removes the initialization handshake, session ID, and streaming event stream, allowing an MCP server to run inside a single synchronous Django view on a standard WSGI deployment without Channels or separate ASGI infrastructure. django-mcpz also works with the 2025 MCP versions that offered a stateless mode, and requires an auth argument because MCP servers expose application internals to network callers. Django: introducing django-mcpz, for making MCP servers I made another package Say hello to django-mcpz https://django-mcpz.readthedocs.io/en/latest/ , for building Model Context Protocol MCP servers in your Django project. The package tagline is easy peasy MCP servers in Django. MCP, the place to be? MCP https://modelcontextprotocol.io/ specifies a way for LLMs to interact with external data sources. MCP servers can expose a set of tools , which are essentially functions that an LLM can call on behalf of a user. So, for example, a user can ask “where’s my pizza at?” and the LLM can call one or more tools on your pizza shop server to navigate your data and return the answer. Users can ask questions in natural language, and LLMs can interpret messy data in your system to return a clean, hopefully-correct answer. MCP is date-versioned, and the latest version, 2026-07-28 https://modelcontextprotocol.io/specification/2026-07-28/changelog , made the protocol much simpler by making it stateless, like ye olde HTTP APIs. There’s no longer an initialization handshake, no session ID, and no streaming event stream. Each request is a plain HTTP POST with a JSON body that gets one JSON response, like any other HTTP API. This new version is way easier to deploy for a typical synchronous, WSGI Django project. The previous default transport required the server to hold open streaming responses server-sent events and track sessions, which would entail a separate ASGI deployment using Channels https://channels.readthedocs.io/en/stable/ . Now, you can make an MCP server within a single synchronous Django view and keep it inside your normal WSGI deployment, no extra infrastructure required. One of my clients wants to deploy an MCP server for their app, and so I took it upon myself to take advantage of this new MCP version and build a ground-up implementation, rather than use the existing ASGI-based packages. The goal was to make it “easy peasy” to build an MCP server in your Django project, and so I named it django-mcpz “pz” read in the American way is “pea-zee”, as in “easy peasy” or maybe I should stick to calling it “pea-zed”? . django-mcpz targets the latest MCP version, 2026-07-28, with its stateless-by-default transport, but it still works with last year’s 2025 versions too, which also had a stateless mode. Client support seems widespread, and anyway, this is an ecosystem that moves fast. The basics Here’s the example from the README, a server for a shop with one tool that counts orders: python from typing import Literal import msgspec from django mcpz.server import MCPServer from django mcpz.bearer tokens.auth import token auth from example.models import Order server = MCPServer name="shop", version="1.0.0", instructions="Query the shop’s order database.", auth=token auth, class CountOrdersParams msgspec.Struct : status: Literal "pending", "shipped", "cancelled" | None = None class CountOrdersResult msgspec.Struct : count: int @server.tool description="Count Order rows, optionally filtered by status.", read only=True, def count orders request, params: CountOrdersParams - CountOrdersResult: qs = Order.objects.all if params.status is not None: qs = qs.filter status=params.status return CountOrdersResult count=qs.count Some notes: - MCPServer represents one server and its registry of tools. It works a bit like Django’s admin.site : you create one, register things on it, and route it like a plain old view function. The instructions are natural-language guidance for the LLM on how to use the server. - The auth argument is required, since an MCP server exposes your application’s internals to network callers. More on that below. - server.tool registers a plain function as a tool. The function receives the HttpRequest first, then its parameters, and Django’s request/response cycle applies as usual, so it can use the ORM, request.user , and anything else a view can. - Parameters and results are declared as msgspec https://msgspec.dev/ Struct classes. django-mcpz generates the tool’s JSON Schemas from the type annotations, and validates each call’s arguments before the tool runs. Mistyped or unknown arguments are rejected with in-band errors that the calling LLM can read and correct from. msgspec also handles all the JSON serialization and deserialization, via my recently-released django-msgspec https://adamj.eu/tech/2026/08/07/introducing-django-msgspec/ , so it’s fast. - read only=True sets one of the specification’s tool annotations , hints for client user interfaces about what a tool does. For example, an UI might not prompt for confirmation before calling a read-only tool, but it will for a write tool. The server is its own view function, so you route it directly in your urls.py : python from django.urls import path from example.mcp import server urlpatterns = path "mcp", server , And that’s all Deploy as normal, and your MCP server is live at /mcp . My editor, Zed , has MCP support https://zed.dev/docs/ai/mcp . I hooked it up to a test project running the above server code and had this brief conversation with an LLM: User count the number of orders in my shop Assistant