🔮 MCP Django Unfold
A Model Context Protocol (MCP) server that gives AI agents complete, accurate knowledge of the Django Unfold admin theme — so they can implement it without hallucination.
Built with the MCP Python SDK (FastMCP), this server exposes 25 documentation tools covering every feature, configuration option, and third-party integration of Django Unfold. Compatible with Claude Desktop, VS Code GitHub Copilot, Cursor, and any MCP-compatible client.
📑 Table of Contents
- Why This Exists
- Architecture
- Project Structure
- Quick Start
- Client Configuration
- Available Tools
- How It Was Built
- Development Guide
- Production Deployment
- Dev → Prod Pipeline
- Contributing
- License
💡 Why This Exists
AI coding agents (Claude, Copilot, Cursor) frequently hallucinate when generating Django Unfold code — inventing non-existent settings, using wrong import paths, or missing critical ordering requirements in INSTALLED_APPS. This MCP server solves that by providing:
- Verified documentation — every code example is sourced directly from the official Django Unfold docs
- Complete coverage — 24 documentation sections spanning installation to advanced integrations
- Instant access — AI agents call tools and get authoritative answers in milliseconds
- Search — full-text search across all documentation sections
🏗️ Architecture
System Overview
%%{init: {'theme': 'dark', 'themeVariables': {'primaryColor': '#6366f1', 'primaryTextColor': '#ffffff', 'primaryBorderColor': '#818cf8', 'lineColor': '#a5b4fc', 'secondaryColor': '#1e1b4b', 'tertiaryColor': '#312e81', 'background': '#0f0d1a', 'mainBkg': '#1e1b4b', 'nodeBorder': '#818cf8', 'clusterBkg': '#1a1744', 'clusterBorder': '#4f46e5', 'titleColor': '#ffffff', 'edgeLabelBackground': '#1e1b4b', 'textColor': '#e0e7ff', 'noteTextColor': '#ffffff', 'noteBkgColor': '#312e81'}}}%%
flowchart TB
subgraph clients["🖥️ MCP Clients"]
direction LR
claude["Claude Desktop"]
vscode["VS Code Copilot"]
cursor["Cursor / Other IDE"]
custom["Custom MCP Client"]
end
subgraph transport["📡 Transport Layer"]
stdio["stdio (stdin/stdout)"]
end
subgraph server["⚙️ MCP Server — mcp-django-unfold"]
direction TB
fastmcp["FastMCP Runtime\n(mcp Python SDK)"]
subgraph tools["🔧 25 Documentation Tools"]
direction LR
core["Core Tools\n─────────\nunfold_get_started\nunfold_configuration\nunfold_actions\nunfold_filters\nunfold_decorators"]
ui["UI Tools\n─────────\nunfold_components\nunfold_inlines\nunfold_widgets\nunfold_tabs\nunfold_dashboard"]
adv["Advanced Tools\n─────────\nunfold_pages\nunfold_styles_scripts\nunfold_features_overview\nunfold_complete_example\nunfold_search_docs"]
integ["Integration Tools\n─────────\nimport_export\nguardian\nsimple_history\ncelery_beat\nmodeltranslation\nmoney · constance\nlocation · djangoql\njson_widget"]
end
subgraph docs["📚 Documentation Store"]
docsmod["docs.py\n─────────\nDOCS dict\n24 sections\nFull code examples"]
end
end
clients -->|"JSON-RPC over stdio"| transport
transport --> fastmcp
fastmcp --> tools
tools --> docs
style clients fill:#1e1b4b,stroke:#6366f1,stroke-width:2px,color:#e0e7ff
style transport fill:#312e81,stroke:#818cf8,stroke-width:2px,color:#e0e7ff
style server fill:#0f172a,stroke:#6366f1,stroke-width:2px,color:#e0e7ff
style tools fill:#1a1744,stroke:#4f46e5,stroke-width:1px,color:#e0e7ff
style docs fill:#1a1744,stroke:#4f46e5,stroke-width:1px,color:#e0e7ff
style claude fill:#312e81,stroke:#818cf8,color:#ffffff
style vscode fill:#312e81,stroke:#818cf8,color:#ffffff
style cursor fill:#312e81,stroke:#818cf8,color:#ffffff
style custom fill:#312e81,stroke:#818cf8,color:#ffffff
style stdio fill:#312e81,stroke:#818cf8,color:#ffffff
style fastmcp fill:#4f46e5,stroke:#818cf8,color:#ffffff
style core fill:#312e81,stroke:#6366f1,color:#e0e7ff
style ui fill:#312e81,stroke:#6366f1,color:#e0e7ff
style adv fill:#312e81,stroke:#6366f1,color:#e0e7ff
style integ fill:#312e81,stroke:#6366f1,color:#e0e7ff
style docsmod fill:#312e81,stroke:#6366f1,color:#e0e7ff
Request Flow
%%{init: {'theme': 'dark', 'themeVariables': {'primaryColor': '#6366f1', 'primaryTextColor': '#ffffff', 'primaryBorderColor': '#818cf8', 'lineColor': '#a5b4fc', 'secondaryColor': '#1e1b4b', 'tertiaryColor': '#312e81', 'background': '#0f0d1a', 'mainBkg': '#1e1b4b', 'nodeBorder': '#818cf8', 'clusterBkg': '#1a1744', 'clusterBorder': '#4f46e5', 'titleColor': '#ffffff', 'edgeLabelBackground': '#1e1b4b', 'textColor': '#e0e7ff'}}}%%
sequenceDiagram
participant C as 🖥️ AI Client
participant T as 📡 stdio Transport
participant S as ⚙️ FastMCP Server
participant TM as 🔧 Tool Manager
participant D as 📚 Docs Store
Note over C,D: Tool Discovery Phase
C->>T: initialize request
T->>S: JSON-RPC handshake
S-->>T: server capabilities (25 tools)
T-->>C: tool list + descriptions
Note over C,D: Tool Invocation Phase
C->>T: tools/call: unfold_configuration
T->>S: dispatch to handler
S->>TM: lookup "unfold_configuration"
TM->>D: DOCS["configuration"]
D-->>TM: full markdown content
TM-->>S: documentation string
S-->>T: tool result (content)
T-->>C: configuration docs
Note over C,D: Search Phase
C->>T: tools/call: unfold_search_docs("sidebar")
T->>S: dispatch to handler
S->>TM: lookup "unfold_search_docs"
TM->>D: scan ALL_SECTIONS for "sidebar"
D-->>TM: matching sections
TM-->>S: aggregated results
S-->>T: tool result (matches)
T-->>C: search results
📂 Project Structure
%%{init: {'theme': 'dark', 'themeVariables': {'primaryColor': '#6366f1', 'primaryTextColor': '#ffffff', 'primaryBorderColor': '#818cf8', 'lineColor': '#a5b4fc', 'secondaryColor': '#1e1b4b', 'tertiaryColor': '#312e81', 'background': '#0f0d1a', 'mainBkg': '#1e1b4b', 'nodeBorder': '#818cf8', 'clusterBkg': '#1a1744', 'clusterBorder': '#4f46e5', 'titleColor': '#ffffff', 'edgeLabelBackground': '#1e1b4b', 'textColor': '#e0e7ff'}}}%%
graph TB
subgraph root["📦 mcp-django-unfold/"]
pyproject["pyproject.toml\n─────────\nname · version\ndependencies\nentry point\nhatch build"]
dockerfile["Dockerfile\n─────────\npython:3.12-slim\npip install\nENTRYPOINT"]
readme["README.md"]
lock["uv.lock"]
subgraph src["src/mcp_django_unfold/"]
init["__init__.py\n─────────\n__version__"]
server_py["server.py\n─────────\nFastMCP init\n25 @mcp.tool()\nmain() entry"]
docs_py["docs.py\n─────────\nDOCS dict\n24 doc sections\nALL_SECTIONS list"]
end
end
pyproject -->|"entry point"| server_py
server_py -->|"imports"| docs_py
server_py -->|"imports"| init
dockerfile -->|"pip install ."| pyproject
style root fill:#0f172a,stroke:#6366f1,stroke-width:2px,color:#e0e7ff
style src fill:#1a1744,stroke:#4f46e5,stroke-width:2px,color:#e0e7ff
style pyproject fill:#312e81,stroke:#818cf8,color:#ffffff
style dockerfile fill:#312e81,stroke:#818cf8,color:#ffffff
style readme fill:#312e81,stroke:#818cf8,color:#ffffff
style lock fill:#312e81,stroke:#818cf8,color:#ffffff
style init fill:#4f46e5,stroke:#818cf8,color:#ffffff
style server_py fill:#4f46e5,stroke:#a78bfa,stroke-width:2px,color:#ffffff
style docs_py fill:#4f46e5,stroke:#a78bfa,stroke-width:2px,color:#ffffff
mcp-django-unfold/
├── pyproject.toml # Package metadata, dependencies, build config
├── Dockerfile # Container image for production
├── .dockerignore # Docker build exclusions
├── .gitignore # Git exclusions
├── README.md # This file
├── LICENSE # MIT license
├── uv.lock # Dependency lock file
└── src/
└── mcp_django_unfold/
├── __init__.py # Package init + version
├── server.py # FastMCP server + 25 tool definitions
└── docs.py # Complete documentation content (24 sections)
| File | Purpose |
|---|---|
server.py |
FastMCP server initialization, all 25 @mcp.tool() handlers, and main() entry point |
docs.py |
DOCS dictionary containing 24 full documentation sections with code examples |
pyproject.toml |
Package name, version, Python ≥3.11, mcp[cli] dependency, hatchling build |
Dockerfile |
Production-ready container using python:3.12-slim |
🚀 Quick Start
Option 1: uvx (recommended — zero install)
uvx mcp-django-unfold
Option 2: pip install
pip install mcp-django-unfold
mcp-django-unfold
Option 3: Docker
docker build -t mcp-django-unfold .
docker run -i --rm mcp-django-unfold
⚙️ Client Configuration
Claude Desktop
Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"django-unfold": {
"command": "uvx",
"args": ["mcp-django-unfold"]
}
}
}
VS Code (GitHub Copilot)
Add to your project's .vscode/mcp.json:
{
"servers": {
"django-unfold": {
"command": "uvx",
"args": ["mcp-django-unfold"]
}
}
}
Cursor
Add to ~/.cursor/mcp.json:
{
"mcpServers": {
"django-unfold": {
"command": "uvx",
"args": ["mcp-django-unfold"]
}
}
}
Docker-based Configuration
For any client that supports Docker:
{
"mcpServers": {
"django-unfold": {
"command": "docker",
"args": ["run", "-i", "--rm", "mcp-django-unfold"]
}
}
}
🔧 Available Tools
Core Documentation
| Tool | Description |
|---|---|
unfold_get_started |
Installation, INSTALLED_APPS ordering, ModelAdmin setup |
unfold_configuration |
Complete UNFOLD settings dict — every option with examples |
unfold_actions |
Global, row, detail & submit line actions with icons/variants |
unfold_filters |
Dropdown, numeric, date, text, autocomplete filters |
unfold_decorators |
@display decorator — labels, headers, dropdowns |
UI & Layout
| Tool | Description |
|---|---|
unfold_components |
Dashboard components — card, chart, button, table, progress... |
unfold_inlines |
Stacked, tabular, nonrelated & sortable inlines |
unfold_widgets |
Form widgets — ArrayWidget, switches, WYSIWYG, all input types |
unfold_tabs |
Changelist tab navigation |
unfold_dashboard |
Custom dashboard with DASHBOARD_CALLBACK |
unfold_pages |
Custom admin pages with class-based views |
unfold_styles_scripts |
Custom CSS/JS, Tailwind 3.x & 4.x setup |
Third-party Integrations
| Tool | Description |
|---|---|
unfold_integration_import_export |
django-import-export forms and admin setup |
unfold_integration_guardian |
django-guardian object-level permissions |
unfold_integration_simple_history |
django-simple-history model tracking |
unfold_integration_celery_beat |
django-celery-beat task scheduling admin |
unfold_integration_modeltranslation |
django-modeltranslation with language flags |
unfold_integration_money |
django-money (auto-styled) |
unfold_integration_constance |
django-constance dynamic settings |
unfold_integration_location_field |
django-location-field map widget |
unfold_integration_djangoql |
djangoql advanced search (auto-styled) |
unfold_integration_json_widget |
django-json-widget (auto-styled) |
Meta
| Tool | Description |
|---|---|
unfold_features_overview |
Complete feature list and technology stack |
unfold_complete_example |
Full working project — settings, models, admin, templates |
unfold_search_docs |
Full-text search across all 24 documentation sections |
🧱 How It Was Built
Technology Stack
| Layer | Technology | Purpose |
|---|---|---|
| Protocol | Model Context Protocol | Standardized AI ↔ tool communication |
| SDK | mcp Python SDK (FastMCP) | Server framework with @mcp.tool() decorator pattern |
| Transport | stdio (stdin/stdout) | Local process communication — fast, no network overhead |
| Build | Hatchling | Modern Python build backend |
| Runtime | Python ≥ 3.11 | Type hints, modern syntax |
| Package | uvx / pip | Zero-install execution via uvx |
| Container | Docker (python:3.12-slim) |
Production-ready deployment |
Design Decisions
-
Embedded documentation — All docs are stored as Python strings in
docs.pyrather than fetched at runtime. This ensures zero latency, offline operation, and version-locked accuracy. -
One tool per topic — Each documentation section gets its own MCP tool with a descriptive docstring. AI agents can discover and call exactly the tool they need.
-
Full-text search — The
unfold_search_docstool scans all sections by keyword, so agents can find relevant docs even when they don't know the exact tool name. -
stdio transport — Chosen for local-first usage. The server starts as a child process of the AI client — no ports, no network config, no auth needed.
-
src layout — Standard Python packaging layout (
src/mcp_django_unfold/) with hatchling build for clean wheel generation and PyPI publishing.
🛠️ Development Guide
Prerequisites
- Python 3.11+
- uv (recommended) or pip
Setup
# Clone the repository
git clone https://github.com/danangharissetiawan/mcp-django-unfold.git
cd mcp-django-unfold
# Install dependencies in a virtual environment
uv sync
Run Locally
# Run the server directly
uv run mcp-django-unfold
Test with MCP Inspector
The MCP Inspector lets you interactively browse and call tools:
npx @modelcontextprotocol/inspector uv run mcp-django-unfold
This opens a web UI where you can:
- See all 25 registered tools
- Read each tool's description and parameters
- Execute tools and inspect the returned documentation
Verify the Server
# Quick smoke test — check tools load
uv run python -c "
from mcp_django_unfold.server import mcp
print(f'Server: {mcp.name}')
print(f'Tools: {len(mcp._tool_manager._tools)}')
"
# Expected output:
# Server: django_unfold_mcp
# Tools: 25
Adding Documentation
- Add a new section to the
DOCSdictionary insrc/mcp_django_unfold/docs.py - Create a corresponding
@mcp.tool()function insrc/mcp_django_unfold/server.py - The search tool (
unfold_search_docs) will automatically index the new section
Code Style
# Format
uv run ruff format src/
# Lint
uv run ruff check src/
🚢 Production Deployment
Publish to PyPI
# Build the package
uv build
# This creates dist/mcp_django_unfold-0.1.0.tar.gz and .whl
# Upload to PyPI
uv publish
# Or with twine:
# twine upload dist/*
Once published, anyone can run it instantly:
uvx mcp-django-unfold
Docker
# Build
docker build -t mcp-django-unfold .
# Run (interactive mode required for stdio)
docker run -i --rm mcp-django-unfold
# Tag and push to a registry
docker tag mcp-django-unfold ghcr.io/danangharissetiawan/mcp-django-unfold:latest
docker push ghcr.io/danangharissetiawan/mcp-django-unfold:latest
Version Bump Workflow
- Update
versioninpyproject.toml - Update
__version__insrc/mcp_django_unfold/__init__.py - Commit, tag, and push:
git add -A
git commit -m "release: v0.2.0"
git tag v0.2.0
git push origin main --tags
- Build and publish:
uv build && uv publish
🔄 Dev → Prod Pipeline
%%{init: {'theme': 'dark', 'themeVariables': {'primaryColor': '#6366f1', 'primaryTextColor': '#ffffff', 'primaryBorderColor': '#818cf8', 'lineColor': '#a5b4fc', 'secondaryColor': '#1e1b4b', 'tertiaryColor': '#312e81', 'background': '#0f0d1a', 'mainBkg': '#1e1b4b', 'nodeBorder': '#818cf8', 'clusterBkg': '#1a1744', 'clusterBorder': '#4f46e5', 'titleColor': '#ffffff', 'edgeLabelBackground': '#1e1b4b', 'textColor': '#e0e7ff'}}}%%
graph LR
subgraph dev["🛠️ Development"]
code["Write Code"]
test["Test with\nMCP Inspector"]
local["Run Local\nuv run mcp-django-unfold"]
end
subgraph build["📦 Build & Publish"]
hatch["uv build\n.tar.gz + .whl"]
twine["uv publish\nto PyPI"]
docker_build["docker build\n-t mcp-django-unfold ."]
end
subgraph prod["🚀 Production Use"]
uvx["uvx mcp-django-unfold"]
pip_install["pip install\nmcp-django-unfold"]
docker_run["docker run -i --rm\nmcp-django-unfold"]
end
code --> test
test --> local
local -->|"ready"| hatch
hatch --> twine
hatch --> docker_build
twine --> uvx
twine --> pip_install
docker_build --> docker_run
style dev fill:#1a1744,stroke:#6366f1,stroke-width:2px,color:#e0e7ff
style build fill:#1a1744,stroke:#6366f1,stroke-width:2px,color:#e0e7ff
style prod fill:#1a1744,stroke:#6366f1,stroke-width:2px,color:#e0e7ff
style code fill:#312e81,stroke:#818cf8,color:#ffffff
style test fill:#312e81,stroke:#818cf8,color:#ffffff
style local fill:#312e81,stroke:#818cf8,color:#ffffff
style hatch fill:#4f46e5,stroke:#818cf8,color:#ffffff
style twine fill:#4f46e5,stroke:#818cf8,color:#ffffff
style docker_build fill:#4f46e5,stroke:#818cf8,color:#ffffff
style uvx fill:#059669,stroke:#34d399,stroke-width:2px,color:#ffffff
style pip_install fill:#059669,stroke:#34d399,stroke-width:2px,color:#ffffff
style docker_run fill:#059669,stroke:#34d399,stroke-width:2px,color:#ffffff
| Stage | Command | What Happens |
|---|---|---|
| Dev | uv sync |
Install deps locally in .venv |
| Dev | uv run mcp-django-unfold |
Run server for local testing |
| Dev | npx @modelcontextprotocol/inspector ... |
Interactive tool browser |
| Build | uv build |
Generate .tar.gz + .whl in dist/ |
| Publish | uv publish |
Upload to PyPI |
| Prod | uvx mcp-django-unfold |
Zero-install run from PyPI |
| Prod | docker run -i --rm mcp-django-unfold |
Containerized run |
🤝 Contributing
- Fork the repository
- Create a feature branch:
git checkout -b feat/new-docs-section - Make your changes to
docs.pyandserver.py - Test with MCP Inspector
- Submit a pull request
📄 License
MIT — see LICENSE for details.
Built with ❤️ for the Django Unfold community
Django Unfold · Model Context Protocol · MCP Python SDK
Release files for mcp-django-unfold 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 | |
|---|---|---|---|
| mcp_django_unfold-0.1.0.tar.gz | 64.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mcp_django_unfold-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 91.5 kB
Release files / mcp_django_unfold-0.1.0.tar.gz
| Download URL | mcp_django_unfold-0.1.0.tar.gz |
|---|---|
| Size | 64.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cba631ecab3ac263dbe396d932bb4a590a452b4c579e1ad0d5a19b6d95de2817
|
|
BLAKE2b-256 checksum How to use checksums |
2f7538ffe7f37a49bbf07e9b25f1f635358cc31f6767aa43e441430724f37f89
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.10.8 {"installer":{"name":"uv","version":"0.10.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / mcp_django_unfold-0.1.0-py3-none-any.whl
| Download URL | mcp_django_unfold-0.1.0-py3-none-any.whl |
|---|---|
| Size | 26.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ac2c5538794ac52acf5b41b9ba70b13ff48fb67fa0114297744f40a053883214
|
|
BLAKE2b-256 checksum How to use checksums |
1ca2b17584a6f52472ffd07549ff0c3c08ee1a21e52c580164d98445769e98ad
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.10.8 {"installer":{"name":"uv","version":"0.10.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|