Skip to main content

MCP server for Django REST Framework — auto-discovers DRF views and exposes them as MCP tools

Project description

django-rest-mcp

A thin wrapper around the official mcp Python SDK that lets DRF ViewSets show up as MCP tools. No new protocol, no re-implementation of the MCP server; this package is glue.

Concretely, it does four small things:

  1. Walks a DRF router (or individual ViewSets) and registers one tool per action with mcp.server.fastmcp.FastMCP.
  2. Converts each action's DRF serializer into a pydantic model so the tool has a typed input schema.
  3. Hosts the MCP streamable-HTTP transport behind a normal Django URL, passing the authenticated request.user through to your ViewSet so existing permission_classes and get_queryset() filtering still apply.
  4. Ships an IsOAuth2Authenticated DRF permission plus RFC 8414 / RFC 9728 .well-known/ metadata views, for the common case of fronting it with django-oauth-toolkit.

It owns no domain models, no business logic, no views of its own. If you can already hit your API with curl, this just lets an MCP client hit the same code.

Install

pip install django-rest-mcp

Requires Python 3.12+, Django 5.1+, DRF 3.14+, mcp>=1.26, pydantic>=2.

Quickstart

Assuming you already have a DRF router with your ViewSets registered, the minimum is three lines:

# myapp/urls.py
from drf_mcp import DRFMCP
from myapp.urls import router  # your existing DefaultRouter

mcp = DRFMCP("myapp"); mcp.autodiscover(router)

urlpatterns = [path("mcp/", mcp.as_view()), ...]

That exposes every standard action on every registered ViewSet as an MCP tool, with input schemas built from the matching serializer.

Production shape

Add OAuth discovery and a permission class:

# myapp/urls.py
from django.urls import path
from drf_mcp import (
    DRFMCP, IsOAuth2Authenticated,
    AuthorizationServerMetadataView, ProtectedResourceMetadataView,
)
from myapp.urls import router

mcp = DRFMCP("myapp")
mcp.autodiscover(router)

urlpatterns = [
    path("mcp/", mcp.as_view(permission_classes=[IsOAuth2Authenticated]), name="mcp"),
    path(".well-known/oauth-authorization-server", AuthorizationServerMetadataView.as_view()),
    path(".well-known/oauth-protected-resource",   ProtectedResourceMetadataView.as_view()),
]

For a BookViewSet registered as books, MCP clients see books_list, books_create, books_retrieve, books_update, books_partial_update, books_destroy.

Registering one action at a time

Skip autodiscover if you want finer control:

mcp = DRFMCP("myapp")

mcp.register_view(BookViewSet, action="list", name="list_books",
                  description="Return all books the current user can read.")
mcp.register_view(BookViewSet, action="create")
mcp.register_view(BookViewSet, action="retrieve")

include / exclude on autodiscover use the router basename:

mcp.autodiscover(router, include=["books"])
mcp.autodiscover(router, exclude=["internal_audit"])

How the input schema is built

For write actions (create, update, partial_update) the package reads the serializer returned by view.get_serializer_class() (falling back to view.serializer_class) and converts each writable field:

DRF field Python type
CharField, EmailField, URLField, ... str
IntegerField int
FloatField, DecimalField float
BooleanField bool
Date/DateTime/Time/DurationField str
ListField list
DictField dict
JSONField Any
nested Serializer typed submodel
Serializer(many=True) (ListSerializer) List[submodel]
PrimaryKeyRelatedField(many=True) List[int]
read-only / HiddenField skipped

required=False fields become Optional[...]. Callable defaults like default=list / default=dict are evaluated so the JSON schema has a concrete default. Unknown field types fall back to str with a debug log.

The generated pydantic model is named <Serializer>Input, for example BookSerializer becomes BookInput.

Authentication

IsOAuth2Authenticated is a thin DRF permission that accepts the request only if request.user is authenticated and request.auth looks like an OAuth2 access token (has a .scope attribute). It plays well with django-oauth-toolkit:

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "oauth2_provider.contrib.rest_framework.OAuth2Authentication",
    ],
}

Inside a tool, the authenticated request is available via:

from drf_mcp import get_current_request

request = get_current_request()
request.user        # the authenticated user
request.auth        # the OAuth2 access token

The package re-uses request.user when dispatching to your ViewSet, so your existing permission_classes, object-level permissions, and get_queryset() filtering all run as if the call came in over HTTP.

Customising the inner request

Pass a prepare_request callable if you need to attach extra state (tenant, feature flags, trace IDs, etc.) before the ViewSet runs:

def attach_tenant(fake_request, original_request):
    fake_request.tenant = original_request.tenant

mcp = DRFMCP("myapp", prepare_request=attach_tenant)

OAuth discovery metadata

The metadata views read their settings from DRF_MCP in Django settings:

DRF_MCP = {
    "RESOURCE_PATH": "/mcp/",
    "SCOPES": ["read", "write"],
}

The issuer, authorization_endpoint, token_endpoint, and registration_endpoint URLs are built from the incoming request host, so the same deployment serves correct metadata whether reached via localhost, a tunnel, staging, or production. You do not need to set absolute URLs per env.

Tool descriptions

Descriptions shown to the model come from, in order of preference:

  1. The description= kwarg to register_view.
  2. The docstring of the action method, if defined directly on the ViewSet (docstrings inherited from mixins like ListModelMixin are ignored).
  3. The ViewSet's class docstring, prefixed with the action name.
  4. A generated fallback like "List all Book".

Write action docstrings when you want to guide the model on when to use a tool:

class BookViewSet(viewsets.ModelViewSet):
    """Books in the catalogue."""

    def create(self, request):
        """Create a new book.

        Requires title and author_id. Use books_list first to check whether
        the book already exists before creating a duplicate.
        """
        ...

Running the tests

uv sync
uv run pytest tests/ -v

License

MIT. See LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_rest_mcp-0.1.4.tar.gz (47.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_rest_mcp-0.1.4-py3-none-any.whl (14.6 kB view details)

Uploaded Python 3

File details

Details for the file django_rest_mcp-0.1.4.tar.gz.

File metadata

  • Download URL: django_rest_mcp-0.1.4.tar.gz
  • Upload date:
  • Size: 47.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for django_rest_mcp-0.1.4.tar.gz
Algorithm Hash digest
SHA256 be8f977198e0dde8513828a23a3f1bb5977c8d23b09dd73941f1838eee80728b
MD5 0ce920a94bb304bba16ed5e340685ce3
BLAKE2b-256 ad211eab0a3dd136da0d2d7adad13944fbe0665c32541419fcf60ecd35b58261

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_rest_mcp-0.1.4.tar.gz:

Publisher: ci.yml on pescheckit/django-rest-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file django_rest_mcp-0.1.4-py3-none-any.whl.

File metadata

  • Download URL: django_rest_mcp-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 14.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for django_rest_mcp-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 9f699968eb81fbbe0c297b0a4da3743199bd1a8e7e7f713e30532d8e2caa4e01
MD5 52b06f1dfbf3aea41e0f6e10f01f4ad0
BLAKE2b-256 cbaea32c97072420b945fdfcd6f646ede71ecfca1675459e612bdff36badc30f

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_rest_mcp-0.1.4-py3-none-any.whl:

Publisher: ci.yml on pescheckit/django-rest-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page