The Problem: When APIs Meet AI Assistants

A few weeks ago, I was experimenting with Model Context Protocol (MCP) for a Kafka integration project. The experience was eye-opening - being able to interact with complex systems through natural language felt like a glimpse into the future of development workflows.

After successfully getting MCP working with Kafka, I started thinking about other systems I could connect. That’s when I looked at one of my old Django projects.

The question hit me: “What if I could expose my existing Django APIs to Claude through MCP or any other MCP client?”

My first instinct was to start mapping out individual MCP tools for each endpoint. But as I began sketching out the implementation, I realized I was about to rebuild everything I already had: well-tested ViewSets, comprehensive serializers, validated business logic.

Then it occurred to me - what if this could be completely automatic? What if any Django developer could add MCP support to their existing project with minimal changes? The more I thought about it, the more convinced I became that this should be a easy solution.

After some experimentation, I built exactly that: a universal bridge that transforms any Django REST Framework project into an MCP-compatible server in under five minutes, without changing a single line of existing code.

What You’ll Achieve

This comprehensive guide will show you how to add Model Context Protocol support to your existing Django DRF project, enabling AI assistants to:

  • Interact with all your models through your existing ViewSets
  • Execute custom business logic you’ve defined with @action decorators
  • Understand your data structures automatically from DRF serializers
  • Respect your validation rules using your existing DRF validation logic

By the end of this tutorial, you’ll have a fully functional MCP server that works with Claude Desktop, VS Code, Warp Terminal, and any other MCP-compatible client.

Prerequisites

This guide assumes you have:

  • A Django project with Django REST Framework installed
  • At least one ModelViewSet defined in your application
  • Basic familiarity with Django URL configuration
  • Python 3.8+ and Django 3.2+ installed

Here’s an example of what we’ll be working with:

# Your existing ViewSet (remains completely unchanged)
class UserViewSet(ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    @action(detail=True, methods=['post'])
    def activate(self, request, pk=None):
        user = self.get_object()
        user.is_active = True
        user.save()
        return Response({'status': 'activated'})

Step 1: Add the MCP View

Download the MCP view class and add it to your Django project:

# In your Django project root
wget https://raw.githubusercontent.com/Joel-hanson/django-drf-mcp/main/mcp_view.py

This single file contains everything needed to convert your ViewSets to MCP tools. The implementation is lightweight and follows Django best practices for maximum compatibility.

Note: You can also manually download the file from the django-drf-mcp repository if you prefer.

Step 2: Update Your URL Configuration

Add the MCP endpoint to your main urls.py:

# urls.py
from django.contrib import admin
from django.urls import path, include
from mcp_view import MCPView  # Add this import

urlpatterns = [
    path('admin/', admin.site.urls),
    path('mcp/', MCPView.as_view(), name='mcp'),  # Add this line
    path('api/', include('your_app.urls')),  # Your existing URLs
]

That’s it. No configuration files, no setup scripts, no database changes. The system will automatically discover all your ViewSets.

Step 3: Test Your MCP Endpoint

Start your Django development server and verify the MCP endpoint:

python manage.py runserver

Test the tools discovery:

curl -X POST http://127.0.0.1:8000/mcp/ \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {}}'

You should see tools automatically generated for all your ViewSets.

What Gets Auto-Discovered

The system automatically finds and converts:

All Your ViewSets

# These ViewSets automatically become MCP tools:
class UserViewSet(ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

class ProductViewSet(ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

class OrderViewSet(ModelViewSet):
    queryset = Order.objects.all()
    serializer_class = OrderSerializer

CRUD Operations

Each ViewSet automatically gets these MCP tools:

  • list_{app}_{model} - List all objects
  • create_{app}_{model} - Create new object
  • retrieve_{app}_{model} - Get object by ID
  • update_{app}_{model} - Update existing object
  • destroy_{app}_{model} - Delete object

Custom Actions

Your @action methods become MCP tools automatically:

class UserViewSet(ModelViewSet):
    # ... standard ViewSet code ...

    @action(detail=True, methods=['post'])
    def activate(self, request, pk=None):
        # This becomes: activate_users_user
        pass

    @action(detail=False)
    def active_users(self, request):
        # This becomes: active_users_users_user
        pass

Schema Generation

JSON schemas are automatically created from your DRF serializers:

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['username', 'email', 'first_name', 'last_name']

# Automatically generates:
# {
#   "type": "object",
#   "properties": {
#     "username": {"type": "string", "maxLength": 150},
#     "email": {"type": "string", "format": "email"},
#     "first_name": {"type": "string", "maxLength": 30},
#     "last_name": {"type": "string", "maxLength": 30}
#   },
#   "required": ["username", "email"]
# }

Complete Example: Before and After

Let’s examine a typical Django application to see exactly what changes:

Before: Standard DRF API

# models.py
class User(models.Model):
    username = models.CharField(max_length=150, unique=True)
    email = models.EmailField()
    is_active = models.BooleanField(default=True)

# serializers.py
class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'username', 'email', 'is_active']

# views.py
class UserViewSet(ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    @action(detail=True, methods=['post'])
    def activate(self, request, pk=None):
        user = self.get_object()
        user.is_active = True
        user.save()
        return Response({'status': 'activated'})

# urls.py
router = DefaultRouter()
router.register(r'users', UserViewSet)
urlpatterns = [path('api/', include(router.urls))]

After: Same Code + MCP Support

# models.py - UNCHANGED
class User(models.Model):
    username = models.CharField(max_length=150, unique=True)
    email = models.EmailField()
    is_active = models.BooleanField(default=True)

# serializers.py - UNCHANGED
class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'username', 'email', 'is_active']

# views.py - UNCHANGED
class UserViewSet(ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    @action(detail=True, methods=['post'])
    def activate(self, request, pk=None):
        user = self.get_object()
        user.is_active = True
        user.save()
        return Response({'status': 'activated'})

# urls.py - ONE LINE ADDED
from mcp_view import MCPView  # Added

router = DefaultRouter()
router.register(r'users', UserViewSet)
urlpatterns = [
    path('api/', include(router.urls)),
    path('mcp/', MCPView.as_view(), name='mcp'),  # Added
]

Result: Auto-Generated MCP Tools

{
  "tools": [
    {
      "name": "list_your_app_user",
      "description": "List User in your_app"
    },
    {
      "name": "create_your_app_user",
      "description": "Create User in your_app"
    },
    {
      "name": "retrieve_your_app_user",
      "description": "Retrieve User in your_app"
    },
    {
      "name": "update_your_app_user",
      "description": "Update User in your_app"
    },
    {
      "name": "destroy_your_app_user",
      "description": "Destroy User in your_app"
    },
    {
      "name": "activate_your_app_user",
      "description": "Activate a user"
    }
  ]
}

Step 4: Configure Claude Desktop

Create or update your Claude Desktop configuration file:

{
  "mcpServers": {
    "my-django-project": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "http://127.0.0.1:8000/mcp/"]
    }
  }
}

Save this as claude_desktop_config.json in:

  • macOS: ~/Library/Application Support/Claude/
  • Windows: %APPDATA%/Claude/
  • Linux: ~/.config/Claude/

Important Notes:

  • Install mcp-remote first: npm install -g mcp-remote
  • Restart Claude Desktop after configuration changes
  • Ensure your Django server is running on http://127.0.0.1:8000
  • Make sure your Django project has CORS_ALLOW_ALL_ORIGINS = True in settings for local development

For production deployments, consider using django-cors-headers for proper CORS configuration.

Warp Terminal Configuration

Warp Terminal supports MCP through its AI features. Configure it by adding this to your Warp MCP configuration:

{
  "django-project": {
    "command": "npx",
    "args": ["-y", "mcp-remote", "http://127.0.0.1:8000/mcp/"],
    "env": {},
    "working_directory": null
  }
}

VS Code Configuration

Configure VS Code to work with your MCP server using the official MCP extension:

Create VS Code settings (.vscode/mcp.json):

{
  "servers": {
    "django-project": {
      "type": "http",
      "url": "http://127.0.0.1:8000/mcp/"
    }
  }
}

Alternative: Direct HTTP Configuration

For any MCP client that supports HTTP endpoints directly:

{
  "servers": {
    "django-project": {
      "type": "http",
      "url": "http://127.0.0.1:8000/mcp/",
      "headers": {
        "Content-Type": "application/json"
      }
    }
  }
}

Testing Your Configuration

Verify your setup works across all clients:

# Test the endpoint directly
curl -X POST http://127.0.0.1:8000/mcp/ \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {}}'

You should see a list of your auto-generated Django tools.

Step 5: Start Using AI with Your Data

Restart Claude Desktop and begin interacting with your Django data through natural language:

List your data:

“Show me all users in the system”

Create records:

“Create a new user with username ‘johndoe’ and email ‘john@example.com’”

Execute custom actions:

“Activate the user with ID 5”

Complex operations:

“Show me all inactive users and then activate them one by one”

Claude will automatically use your MCP tools to interact with your Django data while respecting all your existing validation rules and business logic.

Advanced Configuration Options

ViewSet Compatibility

The current implementation works specifically with ModelViewSet classes. Here’s the compatibility breakdown:

View TypeSupport StatusNotes
ModelViewSet✅ Full supportAutomatic CRUD + custom actions
ReadOnlyModelViewSet⚠️ PartialOnly list/retrieve operations
ViewSet❌ Not supportedNo standard CRUD pattern
APIView❌ Not supportedNo auto-discovery possible
GenericAPIView❌ Not supportedWould need custom implementation

Extending Support for Other ViewSet Types

You can extend the implementation to support other ViewSet types:

from rest_framework.viewsets import ViewSet, ReadOnlyModelViewSet
from mcp_view import CustomMCPView

class ExtendedMCPView(CustomMCPView):
    def get_viewset_classes(self):
        """Support multiple ViewSet types."""
        viewset_classes = []

        for app_config in apps.get_app_configs():
            if app_config.name.startswith("django."):
                continue

            try:
                views_module = importlib.import_module(f"{app_config.name}.views")

                for attr_name in dir(views_module):
                    attr = getattr(views_module, attr_name)

                    if inspect.isclass(attr):
                        # Support ModelViewSet (full CRUD)
                        if issubclass(attr, ModelViewSet) and attr != ModelViewSet:
                            viewset_classes.append((attr, app_config.name, 'full'))

                        # Support ReadOnlyModelViewSet (list/retrieve only)
                        elif issubclass(attr, ReadOnlyModelViewSet) and attr != ReadOnlyModelViewSet:
                            viewset_classes.append((attr, app_config.name, 'readonly'))

                        # Support custom ViewSet with @action methods
                        elif issubclass(attr, ViewSet) and attr != ViewSet:
                            viewset_classes.append((attr, app_config.name, 'custom'))

            except (ImportError, AttributeError):
                continue

        return viewset_classes

    def generate_tools_for_viewset(self, viewset_class, app_name, viewset_type):
        """Generate tools based on ViewSet type."""
        tools = []

        if viewset_type == 'full':
            # Full CRUD for ModelViewSet
            tools.extend(self.get_crud_tools(viewset_class, app_name))
        elif viewset_type == 'readonly':
            # Only list and retrieve for ReadOnlyModelViewSet
            tools.extend(self.get_readonly_tools(viewset_class, app_name))
        elif viewset_type == 'custom':
            # Only @action methods for custom ViewSet
            tools.extend(self.get_action_tools(viewset_class, app_name))

        return tools

Working with APIView Classes

For APIView classes, you’d need a different approach since they don’t follow standard patterns:

class APIViewMCPView(CustomMCPView):
    def get_custom_tools(self):
        """Manually define tools for APIView endpoints."""
        return [
            {
                "name": "custom_api_endpoint",
                "description": "Call a custom API endpoint",
                "inputSchema": {
                    "type": "object",
                    "properties": {
                        "data": {"type": "object", "description": "Request data"}
                    }
                }
            }
        ]

    def handle_custom_tool(self, tool_name: str, arguments: dict) -> str:
        """Handle custom APIView calls."""
        if tool_name == "custom_api_endpoint":
            # Create a request and call your APIView manually
            factory = RequestFactory()
            request = factory.post('/custom/', data=arguments.get('data', {}))

            from myapp.views import MyCustomAPIView
            view = MyCustomAPIView()
            response = view.post(request)

            return json.dumps(response.data)

Adding Custom Business Logic Tools

Extend the system with your own custom tools:

class MyProjectMCPView(CustomMCPView):
    def get_custom_tools(self):
        return [
            {
                "name": "user_statistics",
                "description": "Get user statistics for the platform",
                "inputSchema": {
                    "type": "object",
                    "properties": {
                        "period": {
                            "type": "string",
                            "enum": ["day", "week", "month"],
                            "description": "Statistics period"
                        }
                    }
                }
            }
        ]

    def handle_custom_tool(self, tool_name: str, arguments: dict) -> str:
        if tool_name == "user_statistics":
            period = arguments.get("period", "month")
            active_users = User.objects.filter(is_active=True).count()
            total_users = User.objects.count()

            return f"Statistics for {period}: {active_users}/{total_users} users active"

Common Use Cases

Content Management Systems

“Show me all blog posts published this month” “Create a new blog post with title ‘Django MCP Integration’”

Similar to how we automated Markdown publishing workflows, this approach streamlines content management through AI.

Data Analysis and Reporting

“How many orders were placed yesterday?” “Show me users who haven’t logged in for 30 days”

For complex data processing tasks, consider combining this with real-time processing solutions or RAG systems for enhanced analytics.

Administrative Tasks

“Deactivate all users with invalid email addresses” “Generate a report of recent product orders”

Customer Support Operations

“Find user with email john@example.com” “Show all orders for user ID 123”

Troubleshooting Common Issues

Tool Not Found Error

Error: Tool 'list_myapp_user' not found

Solution: Verify that your ViewSet inherits from ModelViewSet and has a queryset attribute properly defined.

Schema Validation Error

Error: Required field 'username' missing

Solution: Your DRF serializer validation rules are working correctly. Ensure all required fields are provided when creating or updating records.

Import Error

ImportError: cannot import name 'MCPView'

Solution: Confirm that mcp_view.py is in your project root directory or accessible in your Python path.

Production Considerations

Performance Optimization

  • Caching: ViewSet discovery occurs once at startup, minimizing runtime overhead
  • Security: Consider adding authentication middleware if your MCP endpoint needs access control. Learn more about Django authentication
  • Rate Limiting: Implement rate limiting for production APIs to prevent abuse using django-ratelimit
  • Database Optimization: Use select_related() and prefetch_related() in your ViewSets for better query performance

Scaling for Large Projects

# For projects with many ViewSets, consider selective exposure
class OptimizedMCPView(CustomMCPView):
    def get_viewset_classes(self):
        # Only expose ViewSets you actually want as MCP tools
        from myapp.views import UserViewSet, ProductViewSet
        return [
            (UserViewSet, 'myapp'),
            (ProductViewSet, 'myapp'),
        ]

Why This Approach Works

Zero Breaking Changes

Your existing DRF APIs continue to function exactly as before. MCP support is purely additive, requiring no modifications to your existing codebase.

Leverages Existing Infrastructure

  • Uses your existing models, serializers, and ViewSets without modification
  • Respects your validation rules and business logic
  • Works seamlessly with your existing authentication and permission systems

Automatic Scaling

  • Adding new ViewSets automatically creates new MCP tools
  • Custom actions become MCP tools instantly
  • No configuration files to maintain or update

Production Ready

  • Type-safe implementation with comprehensive error handling
  • Follows Django and DRF best practices
  • Minimal performance impact on your existing APIs

Developer Experience Transformation

Before MCP Integration

# Manual API testing required multiple steps
curl -X POST http://localhost:8000/api/users/ \
  -H "Content-Type: application/json" \
  -d '{"username":"john","email":"john@example.com"}'

After MCP Integration

Developer: “Create a user named John with email john@example.com
Claude: “I’ll create that user for you right now.”

User created successfully with ID 42

Complex Operations Comparison

Before (Multiple API calls):

curl http://localhost:8000/api/users/
curl -X POST http://localhost:8000/api/users/5/activate/
curl http://localhost:8000/api/orders/?user=5

After (Natural language):

Developer: “Show me user 5, activate them, then show their orders”
Claude: “Here’s user 5’s details… Now activating them… Here are their 3 orders…”

Real Project Examples

E-commerce Platform

# Existing ViewSets automatically become:
# - list_products_product
# - create_orders_order
# - update_inventory_item
# - activate_users_user

# AI can now:
# "Show me low-stock products"
# "Create an order for user@example.com with product ID 123"
# "Find all orders from the last week"

Content Management System

# Existing ViewSets:
# - BlogPostViewSet
# - CategoryViewSet
# - CommentViewSet

# AI workflows:
# "Create a blog post about Django MCP integration"
# "Show me all unpublished posts from this month"
# "Moderate comments on post 'MCP Tutorial'"

Customer Support Platform

# Existing ViewSets:
# - TicketViewSet
# - CustomerViewSet
# - AgentViewSet

# AI assistance:
# "Show me all open tickets assigned to John"
# "Create a high-priority ticket for customer@email.com"
# "Find tickets about billing issues"

Conclusion

Converting your Django DRF project to support MCP takes less than 5 minutes and opens up powerful AI-assisted workflows. You don’t need to change any existing code - just add one file and one URL pattern.

The beauty of this approach is that it:

  • Respects your existing architecture
  • Scales with your project growth
  • Requires zero maintenance
  • Enables powerful AI workflows

Whether you’re managing users, processing orders, or handling content, AI assistants can now interact with your Django data using natural language - while respecting all your existing business logic and validation rules.

This integration represents a significant step forward in making APIs more accessible and user-friendly through natural language interfaces. As AI continues to evolve, having your Django applications MCP-ready positions you at the forefront of this technological shift.


Ready to give your Django project AI superpowers? Download the MCP view and let me know how it transforms your development workflow!


AI & Development Integration

API Development & Integration

Automation & Productivity

Data Engineering & Processing


Stay Connected

Found this tutorial helpful? Share it with your fellow developers and help them supercharge their Django projects with AI capabilities!