Releases: Streamlined-Analytics/django-stateless-mcp
Release list
0.1.4
django-stateless-mcp 0.1.4
Added
- Subscription streams (SEP-2575).
subscriptions/listen— the
2026-07-28 replacement for the standing GET stream, whose POST response is
itself the stream — is now served: under ASGI the response streams live SSE
frames (ack, then filtered, subscription-id-tagged events) until the client
disconnects; under WSGI, where a live stream would pin a worker, the view
answers an explicit501 unsupported_deploymentinstead of hanging. Routed
by the spec's requiredmcp-methodheader; every other request is
byte-identical to before. Wire an externalSubscriptionBus
(MCPServer(subscriptions=…)) for multi-replica event fan-out. The
conformance suite'sserver-statelessscenario now passes 30/30 and has
left the expected-failures baseline. (ADR-0020)
Fixed
- Worker-thread database connections are now recycled. Sync tools run
in the SDK's thread pool, which Django'srequest_finishedcleanup never
reaches — connections opened there were never aged out or health-checked,
a slow stale-connection leak under sustained load. The view now runs
Django's ownclose_old_connectionsinside that pool after each request,
honouringCONN_MAX_AGEexactly as request threads do. (ADR-0021)
Changed
mcp_view's published return annotation widens toHttpResponseBase
(streaming responses are notHttpResponsesubclasses).- The example project registers
test_trigger_tool_change/
test_trigger_prompt_changetools and an in-memory subscription bus.
0.1.3
django-stateless-mcp 0.1.3
Fixed
- The view now works in a default Django project: it is CSRF-exempt. Any
project running the standardCsrfViewMiddleware— which is to say, any
conventionally configured Django project — rejected every MCP POST with
403 "CSRF cookie not set". MCP clients authenticate with a bearer header
an attacker's page cannot set, not ambient cookies, so CSRF does not apply;
mcp_viewnow returns acsrf_exemptview (the same posture DRF takes for
token-authenticated APIs). Caught by CodeQL when the example project gained
a realistic middleware stack; a regression test now posts through
Client(enforce_csrf_checks=True).
Changed
- The example project carries the standard middleware a real project runs
(sessions, CSRF, auth), sorequest.userexists on every endpoint —
AnonymousUserwhen unauthenticated — instead of a tool touching.user
crashing withAttributeErroron the open endpoints. example/README.mdis now a reproduction-grade walkthrough of the full
manual test plan: MCP Inspector setup (including the Protocol Era gotcha),
the elicitation round-trip, the permission grant/revoke cycle, and the
statelessness curl proofs — plus the July 2026 client status: Claude Code
speaks the stateless transport but not SEP-2322 elicitation yet, so MCP
Inspector is the reference client.
0.1.2
Warning
Broken in default Django projects — upgrade to 0.1.3. With the standard CsrfViewMiddleware active (any conventionally configured project), every MCP POST is rejected with 403 CSRF cookie not set. 0.1.3 makes the view csrf_exempt (ADR-0018).
django-stateless-mcp 0.1.2
Fixed
- A single GET request could permanently peg a worker at 100% CPU. The
view routed every method into the SDK's transport, whose GET handler opens
an SSE listen stream and waits for client disconnect by polling the ASGI
receivecallable. The bridge's synthesizedreceivereplayed the request
body forever without ever yielding, so the disconnect listener became a hot
spin that starved the worker's event loop — one anonymous GET per worker
was a full denial of service. Two fixes, either sufficient: the view now
serves POST only (a stateless exchange is one request and one response;
GET/DELETE session machinery has nothing to attach to) and answers other
methods with405 Method Not Allowed; and the synthesizedreceivenow
follows the ASGI contract — the buffered body once, thenhttp.disconnect.
0.1.1
Warning
Denial-of-service defect — upgrade to 0.1.2. A single anonymous GET to any MCP endpoint permanently pegs one worker at ~100% CPU (the SSE disconnect listener spins against the bridge's synthesized receive). Fixed in 0.1.2 (ADR-0017).
django-stateless-mcp 0.1.1
Fixed
- A bare install could not be imported. 0.1.0's
__init__eagerly
importedStructlogRequestLogger, whose module imports structlog — an
optional dependency — soimport django_stateless_mcpraised
ModuleNotFoundErrorunless the[structlog]extra happened to be
installed. The logger is now imported lazily (PEP 562), and reaching it
without structlog raises anImportErrornaming the extra to install:
django-stateless-mcp[structlog]. 0.1.0 has been yanked on PyPI.
0.1.0
Warning
Yanked on PyPI — broken import on a bare install. import django_stateless_mcp raised ModuleNotFoundError: No module named 'structlog' unless the optional [structlog] extra happened to be installed. Use 0.1.1.
django-stateless-mcp 0.1.0
The first release: a stateless Model Context Protocol server for Django, built
on the 2026-07-28 spec.
uv add django-stateless-mcpFeatures
mcp_view(server)— serve anMCPServeras a stateless streamable-HTTP
Django view, under both WSGI and ASGI. Optional OAuth bearer authentication
via the SDK'sTokenVerifierprotocol, withrequired_scopes.mcp.pyautodiscovery — addingdjango_stateless_mcptoINSTALLED_APPS
imports each app'smcpmodule at startup, so apps register tools the way
they register admin models.request_state_security()— keys the SDK's elicitation-resume encryption
fromSECRET_KEY(andSECRET_KEY_FALLBACKS), so elicitation survives a
multi-worker deployment instead of failing when a retry lands on another
worker.django_request(ctx)— reach the authenticated Django request and ORM
from inside a tool, with no request-spanning global state.StructlogRequestLogger— optional flow-logging middleware
(django-stateless-mcp[structlog]), one structured event per request.
Supported versions
- Python 3.12, 3.13, 3.14
- Django 5.2 LTS and 6.0
mcp2.0.x
Verified against the spec
The official MCP conformance suite (modelcontextprotocol/conformance) runs in
CI against a live server; the full stateless-elicitation scenario set passes.
Contributors
@BenA-SA (Ben Atkinson) created django-stateless-mcp.