Exposing Models¶
This guide explains how to expose Django models via the MCP protocol using MCPAdminMixin.
Basic Usage¶
Add MCPAdminMixin to any ModelAdmin class:
from django.contrib import admin
from django_admin_mcp import MCPAdminMixin
from .models import Article
@admin.register(Article)
class ArticleAdmin(MCPAdminMixin, admin.ModelAdmin):
mcp_expose = True
Two-Level Exposure System¶
Django Admin MCP uses a two-level exposure system:
Level 1: Discoverable Models¶
Models with MCPAdminMixin (but without mcp_expose = True) are discoverable:
class AuthorAdmin(MCPAdminMixin, admin.ModelAdmin):
pass # Discoverable via find_models, no direct tools
These models:
- Appear in
find_modelsresults - Show their field structure
- Do NOT expose direct CRUD tools
Level 2: Fully Exposed Models¶
Models with mcp_expose = True are fully exposed:
These models expose 12 tools:
| Tool | Permission | Description |
|---|---|---|
list_<model> |
view | List instances with pagination/filtering |
get_<model> |
view | Get single instance by ID |
create_<model> |
add | Create new instance |
update_<model> |
change | Update existing instance |
delete_<model> |
delete | Delete instance |
describe_<model> |
view | Get field definitions |
actions_<model> |
view | List available admin actions |
action_<model> |
varies | Execute admin action |
bulk_<model> |
varies | Bulk create/update/delete |
related_<model> |
view | Get related objects |
history_<model> |
view | View change history |
autocomplete_<model> |
view | Search suggestions |
Mixin Placement¶
Mixin Order Matters
MCPAdminMixin should come before admin.ModelAdmin in the inheritance chain:
# Correct
class ArticleAdmin(MCPAdminMixin, admin.ModelAdmin):
pass
# Also correct with other mixins
class ArticleAdmin(MCPAdminMixin, SomeOtherMixin, admin.ModelAdmin):
pass
Configuring Exposed Behavior¶
List Display and Serialization¶
Fields in list_display are included in list responses:
class ArticleAdmin(MCPAdminMixin, admin.ModelAdmin):
mcp_expose = True
list_display = ['title', 'author', 'published', 'created_at']
Search Configuration¶
search_fields enables the search parameter in list_* and powers autocomplete_*:
class ArticleAdmin(MCPAdminMixin, admin.ModelAdmin):
mcp_expose = True
search_fields = ['title', 'content', 'author__name']
Ordering¶
ordering sets the default order for list results:
class ArticleAdmin(MCPAdminMixin, admin.ModelAdmin):
mcp_expose = True
ordering = ['-created_at'] # Newest first
Readonly Fields¶
readonly_fields are excluded from create/update operations:
class ArticleAdmin(MCPAdminMixin, admin.ModelAdmin):
mcp_expose = True
readonly_fields = ['created_at', 'updated_at', 'view_count']
Field Filtering¶
Control which fields are exposed in MCP responses using field filtering. This is critical for preventing sensitive data exposure.
MCP-Specific Field Control¶
Use mcp_fields and mcp_exclude_fields for MCP-specific field visibility:
class UserAdmin(MCPAdminMixin, admin.ModelAdmin):
mcp_expose = True
# Only expose these fields via MCP
mcp_fields = ['id', 'username', 'email', 'first_name', 'last_name', 'is_active']
# Never expose password, even though it exists in the model
Alternatively, exclude specific fields:
class APIKeyAdmin(MCPAdminMixin, admin.ModelAdmin):
mcp_expose = True
# Exclude sensitive fields from MCP responses
mcp_exclude_fields = ['secret_key', 'api_token', 'private_data']
Django Admin Field Fallback¶
If mcp_fields or mcp_exclude_fields are not set, django-admin-mcp falls back to Django admin's fields and exclude attributes:
class ArticleAdmin(MCPAdminMixin, admin.ModelAdmin):
mcp_expose = True
fields = ['title', 'content', 'author', 'published'] # Used if mcp_fields not set
Field Filtering Rules¶
- MCP-specific takes precedence: If
mcp_fieldsis set, it overridesfields - MCP-specific exclusion takes precedence: If
mcp_exclude_fieldsis set, it overridesexclude - Exclusion wins over inclusion: If a field is in both
mcp_fieldsandmcp_exclude_fields, it's excluded - No configuration = all fields: If no field configuration is provided, all model fields are exposed
Auto-generated fields
Django's model_to_dict() automatically excludes fields with auto_now_add=True (like created_at)
from serialization. These fields are not editable and Django doesn't include them by default.
Regular date fields like expires_at and last_used_at are included normally.
Example: Protecting Sensitive Data¶
from django_admin_mcp import MCPAdminMixin
@admin.register(MCPToken)
class MCPTokenAdmin(MCPAdminMixin, admin.ModelAdmin):
mcp_expose = True
# Never expose token credentials via MCP
mcp_exclude_fields = ['token_key', 'token_hash', 'salt']
list_display = ['name', 'user', 'is_active', 'created_at']
readonly_fields = ['token_key', 'token_hash', 'salt'] # Also readonly in admin
When listing or getting tokens via MCP, sensitive fields are automatically filtered out:
{
"id": 1,
"name": "Production API Token",
"user": 1,
"is_active": true,
"expires_at": "2026-04-24T16:48:36Z",
"last_used_at": null,
"groups": [],
"permissions": []
// token_key, token_hash, and salt are NOT included
// created_at is also not included (auto_now_add fields are excluded by Django's model_to_dict)
}
Custom Actions¶
Admin actions are automatically exposed:
@admin.action(description='Mark as published')
def publish(modeladmin, request, queryset):
queryset.update(published=True)
@admin.action(description='Mark as draft')
def unpublish(modeladmin, request, queryset):
queryset.update(published=False)
class ArticleAdmin(MCPAdminMixin, admin.ModelAdmin):
mcp_expose = True
actions = [publish, unpublish]
Inline Models¶
Inline models are included in get_* responses:
class CommentInline(admin.TabularInline):
model = Comment
extra = 0
class ArticleAdmin(MCPAdminMixin, admin.ModelAdmin):
mcp_expose = True
inlines = [CommentInline]
When fetching an article, its comments are included:
{
"id": 1,
"title": "My Article",
"comments": [
{"id": 1, "text": "Great article!"},
{"id": 2, "text": "Thanks for sharing"}
]
}
Best Practices¶
Start Conservative¶
Begin with discoverable models, then expose as needed:
# Start with discovery only
class SensitiveDataAdmin(MCPAdminMixin, admin.ModelAdmin):
pass # Can see structure, but no direct access
# Later, if needed:
class SensitiveDataAdmin(MCPAdminMixin, admin.ModelAdmin):
mcp_expose = True
Use Meaningful Search Fields¶
Configure search fields for useful autocomplete:
class UserAdmin(MCPAdminMixin, admin.ModelAdmin):
mcp_expose = True
search_fields = ['username', 'email', 'first_name', 'last_name']
Protect Sensitive Fields¶
Use mcp_exclude_fields to prevent sensitive data exposure:
class UserAdmin(MCPAdminMixin, admin.ModelAdmin):
mcp_expose = True
# Never expose password or sensitive authentication data
mcp_exclude_fields = ['password', 'security_token', 'api_secret']
readonly_fields = ['password', 'last_login', 'date_joined']
For models with many fields, use mcp_fields to explicitly allowlist safe fields:
class CustomerAdmin(MCPAdminMixin, admin.ModelAdmin):
mcp_expose = True
# Only expose non-sensitive customer data
mcp_fields = ['id', 'name', 'email', 'company', 'created_at']
# This excludes: credit_card, ssn, internal_notes, etc.
Next Steps¶
- Token Management - Configure access tokens
- Permissions - Understand the permission system
- Tools Reference - Explore all available tools