Django MCP Server is a Django extensions to easily enable AI Agents to interact with Django Apps through the Model Context Protocol it works equally well on WSGI and ASGI
Django MCP Server is an implementation of the Model Context Protocol (MCP) extension for Django. This module allows MCP Clients and AI agents to interact with any Django application seamlessly.
π Django-Style declarative style tools to allow AI Agents and MCP clients tool to interact with Django.
π Expose Django models for AI Agents and MCP Tools to query in 2 lines of code in a safe way.
π Convert Django Rest Framework APIs to MCP tools with one annotation.
β Working on all apps (WSGI and ASGI) without infrastructure change.
π€ Any MCP Client or AI Agent supporting MCP , (Google Agent Developement Kit, Claude Desktop β¦) can interact with your application.
Licensed under the MIT License.
pip install django-mcp-server
Or directly from GitHub:
pip install git+https://github.com/omarbenhamid/django-mcp-server.git
β
Add mcp_server
to your INSTALLED_APPS
:
INSTALLED_APPS = [
# your apps...
'mcp_server',
]
β
Add the MCP endpoint to your urls.py
:
from django.urls import path, include
urlpatterns = [
# your urls...
path("", include('mcp_server.urls')),
]
By default, the MCP endpoint will be available at /mcp
.
In mcp.py create a subclass of ModelQueryToolset
to give access to a model :
from mcp_server import ModelQueryToolset
from .models import *
class BirdQueryTool(ModelQueryToolset):
model = Bird
def get_queryset(self):
"""self.request can be used to filter the queryset"""
return super().get_queryset().filter(location__isnull=False)
class LocationTool(ModelQueryToolset):
model = Location
class CityTool(ModelQueryToolset):
model = City
Or create a sub class of MCPToolset
to publish generic methods (private _ methods are not published)
Example:
from mcp_server import MCPToolset
from django.core.mail import send_mail
class MyAITools(MCPToolset):
# This method will not be published as a tool because it starts with _
def add(self, a: int, b: int) -> list[dict]:
"""A service to add two numbers together"""
return a+b
def send_email(self, to_email: str, subject: str, body: str):
""" A tool to send emails"""
send_mail(
subject=subject,
message=body,
from_email='[email protected]',
recipient_list=[to_email],
fail_silently=False,
)
The mcp tool is now published on your Django App at /mcp
endpoint.
IMPORTANT For production setup, on non-public data, consider enabling
authorization through : DJANGO_MCP_AUTHENTICATION_CLASSES
You can test it with the python mcp SDK :
from mcp.client.streamable_http import streamablehttp_client
from mcp import ClientSession
async def main():
# Connect to a streamable HTTP server
async with streamablehttp_client("http://localhost:8000/mcp") as (
read_stream,
write_stream,
_,
):
# Create a session using the client streams
async with ClientSession(read_stream, write_stream) as session:
# Initialize the connection
await session.initialize()
# Call a tool
tool_result = await session.call_tool("get_alerts", {"state": "NY"})
print(tool_result)
if __name__ == "__main__":
import asyncio
asyncio.run(main())
Replace http://localhost:8000/mcp
by the acutal Django host and run this cript.
You can test MCP servers in Claude Desktop. As for now
claude desktop only supports local MCP Servers. So you need to have your app installed on the same machine, in a
dev setting probably.
For this you need :
claude_desktop_config.json
and setup your MCP server :{
"mcpServers": {
"test_django_mcp": {
"command": "/path/to/interpreter/python",
"args": [
"/path/to/your/project/manage.py",
"stdio_server"
]
}
}
NOTE /path/to/interpreter/
should point to a python interpreter you use (can be in your venv for example)
and /path/to/your/project/
is the path to your django project.
You can use drf_publish_create_mcp_tool
/ drf_publish_update_mcp_tool
/ drf_publish_delete_mcp_tool
/
drf_publish_list_mcp_tool
as annotations or method calls to register DRF CreateModelMixin / UpdateModelMixin
/ DestroyModelMixin / ListModelMixin based views to MCP tools seamlessly. Django MCP Server will generate the schemas
to allow MCP Clients to use them.
NOTE in some older DRF versions schema generation is not supported out of the box, you should then provide to the registration
annotation the
from mcp_server import drf_publish_create_mcp_tool
@drf_publish_create_mcp_tool
class MyModelView(CreateAPIView):
"""
A view to create MyModel instances
"""
serializer_class=MySerializer
notice that the docstring of the view is used as instructions for the model.
You can better tune this like :
@drf_publish_create_mcp_tool(instructions="Use this view to create instances of MyModel")
class MyModelView(CreateAPIView):
"""
A view to create MyModel instances
"""
serializer_class=MySerializer
Finally, you can register after hand in mcp.py for example with:
drf_publish_update_mcp_tool(MyDRFAPIView, instructions="Use this tool to update my model, but use it with care")
IMPORTANT Notice that builti-in authentication classes are disabled along with filter_backends, thatβs because
the MCP authentication is used.
You can annotate a tool with drf_serialize_output(...)
to serialize its output using
django rest framework, like :
from mcp_server import drf_serialize_output
from .serializers import FooBarSerializer
from .models import FooBar
class MyTools(MCPToolset):
@drf_serialize_output(FooBarSerializer)
def get_foo_bar():
return FooBar.objects.first()
You can import the DjangoMCP server instance and use FastMCP annotations to declare
mcp tools and resources :
from mcp_server import mcp_server as mcp
from .models import Bird
@mcp.tool()
async def get_species_count(name: str) -> int:
'''Find the ID of a bird species by name (partial match). Returns the count.'''
ret = await Bird.objects.filter(species__icontains=name).afirst()
if ret is None:
ret = await Bird.objects.acreate(species=name)
return ret.count
@mcp.tool()
async def increment_species(name: str, amount: int = 1) -> int:
'''
Increment the count of a bird species by a specified amount.
Returns the new count.
'''
ret = await Bird.objects.filter(species__icontains=name).afirst()
if ret is None:
ret = await Bird.objects.acreate(species=name)
ret.count += amount
await ret.asave()
return ret.count
β οΈ Important:
In settings.py
you can initialize the DJANGO_MCP_GLOBAL_SERVER_CONFIG
parameter. These will be
passed to the MCPServer
server during initialization
DJANGO_MCP_GLOBAL_SERVER_CONFIG = {
"name":"mymcp",
"instructions": "Some instructions to use this server",
"stateless": False
}
By default the server is statefull, and state is managed as Django session
request.session
object, so the session backend must thus be set up correctly. The
request object is available in self.request
for class based toolsets.
NOTE The session middleware is not required to be set up as MCP sessions are managed
independently and without cookies.
.
You can make the server stateless by defining : DJANGO_MCP_GLOBAL_SERVER_CONFIG
IMPORTANT state is managed by django sessions, if you use low level @mcp_server.tool()
annotation for example
the behaviour of preserving the server instance accross calls of the base python API is not preserved due to architecture
of django in WSGI deployments where requests can be served by different threads !
The MCP endpoint supports Django Rest Framework authorization classes
You can set them using DJANGO_MCP_AUTHENTICATION_CLASSES
in settings.py
ex. :
DJANGO_MCP_AUTHENTICATION_CLASSES=["rest_framework.authentication.TokenAuthentication"]
IMPORTANT Now the MCP Specification version 2025-03-26
advices to use an OAuth2 workflow, so you should integrate
django-oauth-toolkit with djangorestframework integration
setup, and use 'oauth2_provider.contrib.rest_framework.OAuth2Authentication'
in
DJANGO_MCP_AUTHENTICATION_CLASSES
. Refer to the official documentation of django-oauth-toolkit
You can in your urls.py mount the MCPServerStreamableHttpView.as_view() view and customize it with any extra parameters.
in mcp.py
second_mcp = DjangoMCP(name="altserver")
@second_mcp.tools()
async def my_tool():
...
in urls.py
...
path("altmcp", MCPServerStreamableHttpView.as_view(mcp_server=second_server))
...
IMPORTANT When you do this the DJANGO_MCP_AUTHENTICATION_CLASSES settings is ignored and
your view is unsecure. You SHOULD Setup DRF Authentication
for your view, for exemple :
...
MCPServerStreamableHttpView.as_view(permission_classes=[IsAuthenticated], authentication_classes=[TokenAuthentication])
...
You can setup you own app or use the mcpexample django app app.
By default, your MCP Server will be available as a
stateless streamable http transport
endpoint at <your_django_server>/mcp (ex. http://localhost:8000/mcp) (*without / at the end !).
There are many ways to test :
NOTE as of today the official google adk does not support StreamableHTTP Transport
but you could use this fork
Then you can use the test agent in test/test_agent with by
starting adk web
in the test
folder. Make sure first :
pip install git+https://github.com/omarbenhamid/google-adk-python.git
python manage.py runserver
in the examples/mcpexample
folder.test/test_agent/agent.py
the right endpoint location and authentication headertest
folder.adk web
You can easily plug your MCP server endpoint into any agentic framework supporting MCP streamable http servers.
Refer to this list of clients
DJANGO_MCP_GLOBAL_SERVER_CONFIG a configuration dictionnary for the global MCP server default to empty. It can include the following parmaters
DJANGO_MCP_AUTHENTICATION_CLASSES (default to no authentication) a list of reference to Django Rest Framework authentication classes to enfors in the main MCP view.
DJANGO_MCP_GET_SERVER_INSTRUCTIONS_TOOL (default=True) if true a tool will be offered to obtain global instruction and tools will instruct the agent o use it, as agents do not always have the MCP server glboal instructions incldued in their system prompt.
If you encounter bugs or have feature requests, please open an issue on GitHub Issues.
MIT License.