Metadata-Version: 2.4
Name: djangorestframework-mcp-server
Version: 0.5.1
Summary: Expose djangorestframework-services services and selectors as an MCP (Model Context Protocol) server.
Project-URL: Homepage, https://github.com/Artui/djangorestframework-mcp-server
Project-URL: Repository, https://github.com/Artui/djangorestframework-mcp-server
Project-URL: Issues, https://github.com/Artui/djangorestframework-mcp-server/issues
Author-email: Artur Veres <artur8118@gmail.com>
License: MIT
License-File: LICENSE
Keywords: ai,django,drf,llm,mcp,model-context-protocol,rest-framework
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Framework :: Django :: 4.2
Classifier: Framework :: Django :: 5.0
Classifier: Framework :: Django :: 5.1
Classifier: Framework :: Django :: 5.2
Classifier: Framework :: Django :: 6.0
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: django>=4.2
Requires-Dist: djangorestframework-dataclasses>=1.3.1
Requires-Dist: djangorestframework-services==0.14.0
Requires-Dist: djangorestframework>=3.14
Requires-Dist: typing-extensions>=4.6
Provides-Extra: filter
Requires-Dist: django-filter>=23; extra == 'filter'
Provides-Extra: jwt
Requires-Dist: djangorestframework-simplejwt>=5.3; extra == 'jwt'
Provides-Extra: oauth
Requires-Dist: django-oauth-toolkit>=2.3; extra == 'oauth'
Provides-Extra: otel
Requires-Dist: opentelemetry-api>=1.27; extra == 'otel'
Provides-Extra: redis
Requires-Dist: redis>=5.0; extra == 'redis'
Provides-Extra: spectacular
Requires-Dist: drf-spectacular>=0.27; extra == 'spectacular'
Provides-Extra: toon
Requires-Dist: python-toon>=0.1.3; extra == 'toon'
Description-Content-Type: text/markdown

# djangorestframework-mcp-server

[![CI](https://github.com/Artui/djangorestframework-mcp-server/workflows/tests/badge.svg)](https://github.com/Artui/djangorestframework-mcp-server/actions/workflows/tests.yml)
[![PyPI](https://img.shields.io/pypi/v/djangorestframework-mcp-server.svg)](https://pypi.org/project/djangorestframework-mcp-server/)
[![Python versions](https://img.shields.io/pypi/pyversions/djangorestframework-mcp-server.svg)](https://pypi.org/project/djangorestframework-mcp-server/)
[![Django versions](https://img.shields.io/pypi/djversions/djangorestframework-mcp-server.svg)](https://pypi.org/project/djangorestframework-mcp-server/)
[![Docs](https://img.shields.io/badge/docs-artui.github.io-blue.svg)](https://artui.github.io/djangorestframework-mcp-server/)
[![Coverage](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Artui/djangorestframework-mcp-server/gh-pages/coverage.json)](https://github.com/Artui/djangorestframework-mcp-server/actions/workflows/tests.yml)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![License](https://img.shields.io/pypi/l/djangorestframework-mcp-server.svg)](LICENSE)

Expose [`djangorestframework-services`](https://github.com/Artui/djangorestframework-services)
services and selectors as a [Model Context Protocol](https://modelcontextprotocol.io)
(MCP) server, conforming to MCP **2025-11-25** (Streamable HTTP).

## Idea

Register `ServiceSpec` instances directly — no DRF router or viewset
involvement. The unit of registration is the `ServiceSpec`, not a view.

```python
from django.urls import include, path
from rest_framework_services.types.selector_kind import SelectorKind
from rest_framework_services.types.selector_spec import SelectorSpec
from rest_framework_services.types.service_spec import ServiceSpec

from rest_framework_mcp import MCPServer

server = MCPServer(name="my-app")

server.register_service_tool(
    name="invoices.create",
    spec=ServiceSpec(
        service=create_invoice,
        input_serializer=InvoiceInputSerializer,
        output_selector_spec=SelectorSpec(
            kind=SelectorKind.RETRIEVE,
            output_serializer=InvoiceOutputSerializer,
        ),
    ),
)

server.register_resource(
    name="invoice",
    uri_template="invoices://{pk}",
    selector=SelectorSpec(
        kind=SelectorKind.RETRIEVE,
        selector=get_invoice,
        output_serializer=InvoiceOutputSerializer,
    ),
)

urlpatterns = [path("mcp/", include(server.urls))]
```

A decorator form is also supported (`@server.service_tool(...)` / `@server.resource(...)`).
See the [quickstart](docs/quickstart.md) for the full end-to-end recipe.

- **Services** (mutations) → MCP **tools**.
- **Selectors** (reads) → MCP **resources**.
- A single `/mcp` endpoint speaks Streamable HTTP. The
  `/.well-known/oauth-protected-resource` endpoint comes mounted alongside.

## What ships

- **Tools** — `tools/list`, `tools/call` for `register_service_tool`
  (mutations) and `register_selector_tool` (reads, with optional
  `FilterSet` + ordering + pagination).
- **Resources** — `resources/list`, `resources/templates/list`,
  `resources/read` against `SelectorSpec`-backed callables; RFC 6570
  templated URIs.
- **Prompts** — `prompts/list`, `prompts/get` against render callables
  returning strings, `PromptMessage`s, or async coroutines.
- **Pluggable auth** — `DjangoOAuthToolkitBackend` (default) and
  `AllowAnyBackend` (dev only). Per-binding `MCPPermission` classes
  (`ScopeRequired`, `DjangoPermRequired`) plus your own.
- **RFC 8707 audience binding** when `RESOURCE_URL` is configured;
  **RFC 9728 PRM** served from the configured backend.
- **Per-binding rate limits** — `MCPRateLimit` Protocol with
  `FixedWindowRateLimit`, `SlidingWindowRateLimit`, and
  `TokenBucketRateLimit` implementations shipped.
- **Output formats** — JSON (default) and TOON (token-oriented;
  optional extra with safe JSON fallback).
- **Async POST/DELETE + GET-side SSE push** — sync `urls` for WSGI,
  `async_urls` for ASGI; `MCPServer.notify(session_id, payload)`
  pushes JSON-RPC frames on the session's SSE stream. Per-worker
  `InMemorySSEBroker` or cross-worker `RedisSSEBroker` (behind
  `[redis]`); `Last-Event-ID` resume via
  `InMemorySSEReplayBuffer` / `RedisSSEReplayBuffer`.
- **OpenTelemetry instrumentation** — `mcp.tools.call`,
  `mcp.resources.read`, `mcp.prompts.get` spans (no-op without the
  `[otel]` extra installed).
- **Origin allowlist + protocol-version validation + session
  lifecycle** per the 2025-11-25 transport rules.

## Install

```bash
pip install djangorestframework-mcp-server                              # JSON only
pip install "djangorestframework-mcp-server[toon]"                      # +TOON encoder
pip install "djangorestframework-mcp-server[oauth]"                     # +django-oauth-toolkit backend
pip install "djangorestframework-mcp-server[redis]"                     # +Redis SSE broker for multi-worker ASGI
pip install "djangorestframework-mcp-server[otel]"                      # +OpenTelemetry instrumentation
pip install "djangorestframework-mcp-server[filter]"                    # +django-filter for selector-tool FilterSets
pip install "djangorestframework-mcp-server[spectacular]"               # +drf-spectacular schema overrides
pip install "djangorestframework-mcp-server[toon,oauth,redis,otel,filter,spectacular]"  # everything
```

…or with `uv`:

```bash
uv add djangorestframework-mcp-server                                   # JSON only
uv add "djangorestframework-mcp-server[toon]"                           # +TOON encoder
uv add "djangorestframework-mcp-server[oauth]"                          # +django-oauth-toolkit backend
uv add "djangorestframework-mcp-server[redis]"                          # +Redis SSE broker for multi-worker ASGI
uv add "djangorestframework-mcp-server[otel]"                           # +OpenTelemetry instrumentation
uv add "djangorestframework-mcp-server[filter]"                         # +django-filter for selector-tool FilterSets
uv add "djangorestframework-mcp-server[spectacular]"                    # +drf-spectacular schema overrides
uv add "djangorestframework-mcp-server[toon,oauth,redis,otel,filter,spectacular]"  # everything
```

Optional extras degrade gracefully: TOON falls back to JSON with a runtime
warning if `python-toon` is not installed, and the OAuth backend module
imports cleanly without `oauth2_provider` — the `ImportError` only fires when
you actually configure it.

## Try it

Install [mcp-inspector](https://github.com/modelcontextprotocol/inspector) and
point it at your dev server:

```bash
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](docs/quickstart.md) — copy-pasteable end-to-end.
- [Concepts](docs/concepts.md) — tools vs resources, sessions, output formats.
- [Authentication](docs/auth.md) — backends, permissions, audience binding,
  bring-your-own AS recipe.
- [Recipes](docs/recipes/index.md) — focused cookbook entries.
- [Reference](docs/reference/index.md) — autodocs for every public symbol.

## License

MIT.
