django-admin-fastmcp

django-admin-fastmcp

Exposes the Django admin as an MCP server, letting authorized staff interact with admin models via natural language while delegating all permissions to the existing ModelAdmin.

Category
访问服务器

README

django-admin-fastmcp

A reusable Django app that exposes the Django admin as an MCP server, built on FastMCP.

Every tool call runs as the staff user who owns the bearer token. Every tool call asks the ModelAdmin for permission first. A superuser can do everything a superuser can do in the admin. A staff user can do exactly what that staff user can do in the admin, and nothing more.

SPEC.md is the full specification.

How it works

Three rules define the package:

  1. No parallel permission system. Authorization delegates to the ModelAdmin methods: has_view_permission, has_add_permission, has_change_permission, has_delete_permission, get_queryset, get_readonly_fields, and get_actions. A get_queryset override that hides rows hides them from MCP too.
  2. No parallel data surface. Writes go through the admin's own ModelForm and save_model, then record a LogEntry. The admin history page stays truthful.
  3. Fail closed. Every unresolved lookup, missing ModelAdmin, unknown action, unknown field, and unknown tool denies the call.

Installation

uv add django-admin-fastmcp

Add the app to your settings:

INSTALLED_APPS = [
    ...,
    "django.contrib.admin",
    "django_admin_fastmcp",
]

ADMIN_FASTMCP = {
    "SERVER_NAME": "acme-admin",
    "EXCLUDE_MODELS": ("auth.Permission", "auth.Group"),
    "WRITABLE_MODELS": (),          # empty means no writes at all
}

Mount the OAuth endpoints on the same site as the admin:

# urls.py
urlpatterns = [
    # RFC 8414 fixes this one at the site root.
    path("", include("django_admin_fastmcp.well_known_urls")),
    # This prefix is yours to choose. Match it to the path in MCP_URL.
    path("admin/mcp/", include("django_admin_fastmcp.urls")),
    path("admin/", admin.site.urls),
]

The package hardcodes no prefix. Every URL the metadata document advertises comes from reverse(), so a project that mounts the endpoints at /backoffice/oauth/ gets that in discovery and clients follow it. Two rules: the well-known document belongs at the root, because a client derives its URL from the issuer, and the endpoints should sit on the same site as the admin, because the consent page rides the admin session cookie.

Apply the migrations:

python manage.py migrate django_admin_fastmcp

Nothing else. No per-model registration, no mixin, no decorators. The server exposes whatever the admin already exposes.

Connect a client

Run the server (see Deployment), then register it:

# Claude Code
claude mcp add --transport http acme-admin https://<host>/admin/mcp

Use the path with no trailing slash. /admin/mcp/ answers a 307 redirect to /admin/mcp, and not every client follows a redirect on POST.

No token, no header. The first call starts the standard MCP OAuth flow:

  1. The client opens your browser at the authorize page on the Django site.
  2. Your admin session cookie identifies you. If you are logged out, the normal admin login appears first.
  3. A consent page shows the client name and what approval means. You approve.
  4. The client receives its tokens and connects. It refreshes them by itself.

Any MCP client that speaks streamable HTTP with OAuth works the same way, for example Cursor or a FastMCP Client.

Rules around access:

  • Any staff user can authorize a client, for themselves only.
  • A grant acts with your own admin permissions, never more. There is no separate permission system: whoever may change a model in the admin may change it over MCP, when the server lists that model in WRITABLE_MODELS.
  • Refresh tokens expire after REFRESH_TOKEN_TTL_DAYS (default 90), so re-consent happens that often. Revocation is an admin action on the grant changelist.

Tools

Eleven generic tools, mounted under the namespace admin, so wire names are admin_list_models and so on. Each takes model as "app_label.ModelName". The tool list is static. What varies per user is what each tool lets that user see and do.

Read

Tool Arguments Returns
list_models none Every exposed model this caller may view, with permission flags.
describe_model model Fields, list display, filters, search fields, readonly fields, and available actions.
search_objects model, q, filters, order_by, page, page_size Rows plus total. q uses the admin's own search. An unknown filter is an error.
get_object model, pk One serialized instance.
object_history model, pk Admin log entries for that object, newest first.
recent_actions limit Admin log entries, scoped to the caller unless the caller is a superuser.

Write

Write tools need the model listed in WRITABLE_MODELS; your own admin permissions decide the rest, per model and per object. A model outside the list refuses every write and every action, whoever calls. Leave sensitive models out, an event log for example, and no MCP client can ever write to them.

Tool Arguments Behavior
create_object model, data Validates through the admin form, then saves and logs.
update_object model, pk, data Partial update. Readonly fields are ignored.
delete_object model, pk, confirm Without confirm, returns the exact deletion cascade and changes nothing.
run_action model, action, pks, confirm Runs an admin action. Without confirm, returns a preview.
autocomplete model, field, q Resolves a foreign-key value to a primary key by searching the related model.

Every returned row carries pk as a string and an admin_url, so an agent can hand a person a link into the real admin.

Settings

All keys live in the ADMIN_FASTMCP dict. An unknown key is an error at startup.

Key Default Meaning
SERVER_NAME "django-admin" Name the MCP server advertises.
ADMIN_SITE "django.contrib.admin.site" Dotted path to the AdminSite.
MODELS () Allowlist of "app_label.ModelName". When non-empty, nothing else is exposed.
EXCLUDE_MODELS () Denylist. Supports "app_label.*".
WRITABLE_MODELS () Models that accept writes. Empty means no writes, whoever calls.
DISABLED_TOOLS () Tool names removed from the catalogue entirely.
REDACT_FIELDS ("password", "token", "secret", "api_key", "private_key") Substring match on field names. Values read "[redacted]".
MAX_PAGE_SIZE 200 Cap on search_objects page size.
MAX_PKS 1000 Cap on pks per run_action.
ACCESS_TOKEN_TTL_MINUTES 60 Access token lifetime. Clients renew with the refresh token.
REFRESH_TOKEN_TTL_DAYS 90 Refresh token lifetime. Re-consent happens this often.
SITE_URL "http://127.0.0.1:8000" Public URL of the Django site. It is the OAuth issuer, and the MCP server names it as its authorization server.
MCP_URL "http://127.0.0.1:8765/admin/mcp" Public URL of the MCP endpoint.

Set SITE_URL and MCP_URL for any real deployment. MCP_URL is the single source of three things that must agree: the path the endpoint is served on, the resource that discovery advertises, and the audience every token is bound to. Its path defaults to /admin/mcp. A startup check refuses a MCP_URL with no path, because then the whole origin would be advertised as the protected resource.

Per-ModelAdmin knobs

Set these on a ModelAdmin class, no mixin needed:

class InvoiceAdmin(admin.ModelAdmin):
    mcp_expose = False                     # hide this model from MCP entirely
    mcp_fields = ("number", "total")       # allowlist of serialized fields
    mcp_exclude_fields = ("internal_note",)  # denylist of serialized fields

REDACT_FIELDS wins over mcp_fields. Listing a password field explicitly does not reveal it.

Safety

An admin MCP server for a superuser is a remote shell over the production database, driven by a language model. The rails:

  • WRITABLE_MODELS defaults to empty, so no model accepts writes until the deployment names it. Everything else is your ordinary Django permissions, asked through the ModelAdmin on every call.
  • Access tokens are short-lived. Only salted hashes are stored, so a leaked database row cannot be replayed.
  • delete_object and run_action preview by default and change nothing until confirm=True.
  • Every mutation records a LogEntry attributed to the grant's user, with the client name in the change message, for example "Changed status. Via MCP (client: Claude Code).". A write that cannot record a LogEntry rolls back.
  • The package's own models, sessions.Session, and authtoken.Token are never exposed, whatever the settings say.
  • Keep auth.Permission and auth.Group out of WRITABLE_MODELS. An agent that can grant permissions can escape the permission model.

Deployment

Separate process. Run the MCP server next to your Django project:

python manage.py admin_mcp_serve

It serves the path from MCP_URL, which is /admin/mcp by default, on the port from MCP_URL, or 8765 when that URL names no port. Both are overridable with --host and --port. Nothing about your existing serving configuration changes. Route /admin/mcp through your ingress to that port, and make sure the Authorization header passes through.

Mounted (M3). Mount the server at /admin/mcp inside your project's asgi.py. One constraint: dispatch on the exact path. The OAuth endpoints live directly below the same prefix (/admin/mcp/authorize and friends) and Django must keep serving those, so a dispatcher that sends everything under /admin/mcp to FastMCP would swallow them. The recipe ships with milestone M3.

The server is stateless, so any instance behind a load balancer can serve any request.

Development

make install     # bootstrap uv, pin Python, install dependencies
make test        # run the permission matrix
make check       # format, lint, typecheck, and test
make migrate     # migrate the test project
make serve       # run the MCP server against the test project on :8765/admin/mcp
make help        # everything else

License

MIT

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选