django-spec-mcp
Serve django-service-specs
services and selectors as a Model Context Protocol
(MCP) server, conforming to MCP 2026-07-28 and 2025-11-25 (Streamable HTTP).
Coming from djangorestframework-mcp-server (drf-mcp)? Read
Moving from drf-mcp before the first deploy:
the settings key, the stored names and the registration calls all change.
Idea
Register kernel specs directly. The unit of registration is a ServiceSpec
or a SelectorSpec from django-service-specs, not a view: the spec says what
an operation takes, who may run it and what it returns, and this package serves
it over MCP. The same spec can be served by any other transport on the kernel.
from dataclasses import dataclass
from django.urls import path
from django_service_specs import (
DataclassPresenter,
DataclassValidator,
Parameter,
Parameters,
SelectorKind,
SelectorSpec,
ServiceSpec,
Unrestricted,
)
from django_spec_mcp import MCPServer
@dataclass
class InvoiceInput:
number: str
amount_cents: int
@dataclass
class InvoiceOutput:
id: int
number: str
amount_cents: int
server = MCPServer(name="my-app", auth_backend=my_auth_backend)
server.register_tool(
ServiceSpec(
service=create_invoice, # def create_invoice(*, number, amount_cents)
validator=DataclassValidator(InvoiceInput),
presenter=DataclassPresenter(InvoiceOutput),
permissions=[Unrestricted()],
),
name="invoices.create",
description="Create an invoice.",
)
server.register_resource(
SelectorSpec(
kind=SelectorKind.RETRIEVE,
selector=get_invoice, # def get_invoice(*, pk)
reads=Parameters([Parameter("pk", "integer", required=True)]),
presenter=DataclassPresenter(InvoiceOutput),
permissions=[Unrestricted()],
),
name="invoice",
uri_template="invoices://{pk}",
)
urlpatterns = [path("mcp/", server.urls)]
See the quickstart for the full end-to-end recipe.
- Services (mutations) and selectors (reads) → MCP tools, through
register_tool; the spec's class says which kind of tool it is. - A
RETRIEVEselector → an MCP resource, throughregister_resource. - A single
/mcpendpoint speaks Streamable HTTP. The/.well-known/oauth-protected-resourceendpoint comes mounted alongside.
What ships
- Tools —
tools/list,tools/callforregister_tool, over a kernelServiceSpec(mutations) orSelectorSpec(reads, with an optionalFilterSet, ordering and pagination).register_tools(registry, overrides=...)registers every spec in a kernelSpecRegistry. - Server-supplied arguments —
server_arguments=ServerArguments(values=..., withheld=...)at registration answers a declared argument for the caller: the name leaves the tool's schema, a caller's value for it is dropped and logged by name, and the binding is checked when it is registered. Route and query values a tool asks for are read from the spec's ownreads. - Resources —
resources/list,resources/templates/list,resources/readagainst a kernelRETRIEVESelectorSpec, presented by its own presenter; RFC 6570 templated URIs whose variables are the spec's reads. - Prompts —
prompts/list,prompts/getagainst render callables returning strings,PromptMessages, or async coroutines. - In-process transport surface — call tools without an HTTP round-trip:
MCPServer.call_tool/acall_toolandlist_tools/alist_toolsdrive the same dispatch and permission checks as the wire path, for embedding in agent bridges, toolsets, or management commands.unavailable_tools/aunavailable_toolsname each tool the listing leaves out with the reason, so a bridge can tell its model why one is missing. - Tool annotations — pass
annotations=at registration (or rely on the read/mutation default) to advertise MCP hints likereadOnlyHint/destructiveHintontools/list. - Generic
_meta— passmeta=at registration to populate the base protocol's free-form_metaobject on a tool, resource, or prompt's listing entry (and on thecontentsofresources/read). Passed through verbatim, so protocol extensions have somewhere to live. - Interactive views (MCP Apps) —
register_ui_resource(...)declares an HTML view with typed CSP / permission metadata;ui=UIToolMeta(...)on a tool links its result to that view, and a host renders it inline in the chat. The render payload is thestructuredContentyou already emit, and a view's owntools/calls inherit your auth, permissions and rate limits. An extension over base MCP, so no protocol bump. We declare; the host sandboxes and renders. - Pluggable auth — an
MCPAuthBackendverifies the bearer token;AllowAnyBackend(dev only) ships, andbearer_challengebuilds the 401 challenge a backend of your own answers with. A tool or resource is guarded by its spec's own kernelpermissions, andscopes=at registration adds an OAuth scope gate. - RFC 8707 audience binding through
audience_matches, which a backend calls; RFC 9728 PRM served from the configured backend. - Per-binding rate limits —
MCPRateLimitProtocol withFixedWindowRateLimit,SlidingWindowRateLimit, andTokenBucketRateLimitimplementations shipped. - Async POST/DELETE + GET-side SSE push — sync
urlsfor WSGI,async_urlsfor ASGI;MCPServer.notify(session_id, payload)pushes JSON-RPC frames on the session's SSE stream. Per-workerInMemorySSEBrokeror cross-workerRedisSSEBroker(behind[redis]);Last-Event-IDresume viaInMemorySSEReplayBuffer/RedisSSEReplayBuffer. - OpenTelemetry instrumentation —
mcp.tools.call,mcp.resources.read,mcp.prompts.getspans (no-op without the[otel]extra installed). - Origin allowlist + protocol-version validation + session
lifecycle per the transport rules of both revisions.
2026-07-28and later declare version, identity and capabilities on every request and hold no session;2025-11-25and earlier negotiate once throughinitialize. Both are served concurrently on one endpoint, because a legacy client has no fall-forward mechanism.
Install
pip install django-spec-mcp # base install
pip install "django-spec-mcp[redis]" # +Redis SSE broker for multi-worker ASGI
pip install "django-spec-mcp[otel]" # +OpenTelemetry instrumentation
pip install "django-spec-mcp[redis,otel]" # everything
…or with uv:
uv add django-spec-mcp # base install
uv add "django-spec-mcp[redis]" # +Redis SSE broker for multi-worker ASGI
uv add "django-spec-mcp[otel]" # +OpenTelemetry instrumentation
uv add "django-spec-mcp[redis,otel]" # everything
Try it
Install mcp-inspector and point it at your dev server:
npx @modelcontextprotocol/inspector --url http://localhost:8000/mcp/
Inspector lists tools, fills in arguments from the generated JSON Schema, and walks the OAuth auth flow against your configured Authorization Server.
Documentation
- Quickstart — copy-pasteable end-to-end.
- Moving from drf-mcp — what a project on drf-mcp changes to move across.
- Concepts — tools vs resources, sessions, tool results.
- Authentication — backends, permissions, audience binding, bring-your-own AS recipe.
- Recipes — focused cookbook entries.
- Reference — autodocs for every public symbol.
License
MIT.
Metadata
Release files for django-spec-mcp 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| django_spec_mcp-0.1.0.tar.gz | 913.1 kB | With provenance | Details for django_spec_mcp-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| django_spec_mcp-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for django_spec_mcp-0.1.0-py3-none-any.whl |
Total release size: 1.3 MB
Release files / django_spec_mcp-0.1.0.tar.gz
| Download URL | django_spec_mcp-0.1.0.tar.gz |
|---|---|
| Size | 913.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7a41e68e57d0b056bf0fe442336d24374588d09ce6d2e39255070729165878a8
|
|
BLAKE2b-256 checksum How to use checksums |
930be88b4faff00dfd1756420710865e06b82dc60d05a50ef45786c7e536dc3b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.
Transparency logRelease files / django_spec_mcp-0.1.0-py3-none-any.whl
| Download URL | django_spec_mcp-0.1.0-py3-none-any.whl |
|---|---|
| Size | 393.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
73df0635a0277cf044c8034552d6ae3e60a227b42d2a43b74db92eaf10217f72
|
|
BLAKE2b-256 checksum How to use checksums |
26157f8a67e3a436d5ef172333487a56d55b42ae2e9e61defa25c41565af98fa
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.
Transparency logSecurity for django-spec-mcp 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for django_spec_mcp-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.