django-admin-mcp-api 0.1.0a0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_admin_mcp_api-0.1.0a0/LICENSE +21 -0
- django_admin_mcp_api-0.1.0a0/PKG-INFO +281 -0
- django_admin_mcp_api-0.1.0a0/README.md +248 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/README.md +30 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/__init__.py +22 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/apps.py +30 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/conf.py +57 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/server/README.md +33 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/server/__init__.py +17 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/server/dispatch.py +223 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/server/errors.py +41 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/server/jsonrpc.py +98 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/server/manifest.py +51 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/server/views.py +193 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/tools/README.md +45 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/tools/__init__.py +71 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/tools/action.py +53 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/tools/add_form.py +37 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/tools/autocomplete.py +42 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/tools/base.py +65 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/tools/bulk_update.py +47 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/tools/create.py +44 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/tools/delete_preview.py +42 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/tools/destroy.py +38 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/tools/history.py +38 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/tools/list_objects.py +48 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/tools/panel.py +47 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/tools/recent_actions.py +28 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/tools/registry.py +28 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/tools/retrieve.py +39 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/tools/schema.py +28 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/tools/set_password.py +50 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/tools/update.py +44 -0
- django_admin_mcp_api-0.1.0a0/django_admin_mcp_api/urls.py +35 -0
- django_admin_mcp_api-0.1.0a0/pyproject.toml +222 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 django-admin-mcp contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: django-admin-mcp-api
|
|
3
|
+
Version: 0.1.0a0
|
|
4
|
+
Summary: MCP (Model Context Protocol) adapter for django-admin-rest-api. A wire-protocol-only layer that lets agents reach the existing REST API — no new functionality, permissions, or validation.
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: django,admin,mcp,model-context-protocol,ai,llm
|
|
7
|
+
Author: django-admin-mcp contributors
|
|
8
|
+
Requires-Python: >=3.10,<4.0
|
|
9
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
10
|
+
Classifier: Environment :: Web Environment
|
|
11
|
+
Classifier: Framework :: Django
|
|
12
|
+
Classifier: Framework :: Django :: 5.0
|
|
13
|
+
Classifier: Framework :: Django :: 5.1
|
|
14
|
+
Classifier: Framework :: Django :: 5.2
|
|
15
|
+
Classifier: Framework :: Django :: 6.0
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Site Management
|
|
25
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
26
|
+
Requires-Dist: django (>=5.0,<7.0)
|
|
27
|
+
Requires-Dist: django-admin-rest-api (>=0.1.0a0)
|
|
28
|
+
Project-URL: Documentation, https://github.com/MartinCastroAlvarez/django-admin-mcp#readme
|
|
29
|
+
Project-URL: Homepage, https://github.com/MartinCastroAlvarez/django-admin-mcp
|
|
30
|
+
Project-URL: Repository, https://github.com/MartinCastroAlvarez/django-admin-mcp
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# django-admin-mcp-api
|
|
34
|
+
|
|
35
|
+
[](https://pypi.org/project/django-admin-mcp-api/)
|
|
36
|
+
[](https://pypi.org/project/django-admin-mcp-api/)
|
|
37
|
+
[](https://pypi.org/project/django-admin-mcp-api/)
|
|
38
|
+
[](LICENSE)
|
|
39
|
+
[](https://github.com/MartinCastroAlvarez/django-admin-mcp/actions/workflows/ci.yml)
|
|
40
|
+
|
|
41
|
+
> **An MCP (Model Context Protocol) adapter for the Django admin.**
|
|
42
|
+
> Lets AI agents reach every operation of your `ModelAdmin` — list, retrieve,
|
|
43
|
+
> create, update, delete, run admin actions, autocomplete — through the
|
|
44
|
+
> standard MCP wire protocol, with the **same** authentication, permissions,
|
|
45
|
+
> and validation as the rest of your admin.
|
|
46
|
+
|
|
47
|
+
`django-admin-mcp-api` is a thin **wire-protocol adapter** sitting on top of
|
|
48
|
+
[`django-admin-rest-api`](https://github.com/MartinCastroAlvarez/django-admin-rest-api).
|
|
49
|
+
It introduces **no new functionality, no parallel permission system, no extra
|
|
50
|
+
validation, and no new business logic** — it is the MCP face on the REST API
|
|
51
|
+
your admin already speaks.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## The three-repo family
|
|
56
|
+
|
|
57
|
+
`django-admin-mcp-api` is one of three sibling packages that share the same
|
|
58
|
+
admin core. Each one exposes the same surface in a different protocol:
|
|
59
|
+
|
|
60
|
+
| Repo | Protocol | PyPI | Status |
|
|
61
|
+
| ------------------------------------------------------------------------------------- | ---------------- | --------------------------------------------------------- | --------------------- |
|
|
62
|
+
| [`django-admin-react`](https://github.com/MartinCastroAlvarez/django-admin-react) | React SPA over HTTP/JSON | [`django-admin-react`](https://pypi.org/project/django-admin-react/) | Published |
|
|
63
|
+
| [`django-admin-rest-api`](https://github.com/MartinCastroAlvarez/django-admin-rest-api) | HTTP REST/JSON | _to be published_ | Extraction in progress |
|
|
64
|
+
| **`django-admin-mcp`** (this repo) | **MCP (JSON-RPC)** | [`django-admin-mcp-api`](https://pypi.org/project/django-admin-mcp-api/) | Pre-alpha (this is the v0) |
|
|
65
|
+
|
|
66
|
+
All three reuse your existing `ModelAdmin` as the **only** source of truth
|
|
67
|
+
for querysets, permissions, forms, and serialization.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Why this exists
|
|
72
|
+
|
|
73
|
+
LLM agents speak the [Model Context Protocol](https://modelcontextprotocol.io)
|
|
74
|
+
natively. Today they cannot reach a Django admin without a custom integration
|
|
75
|
+
per project. `django-admin-mcp-api` gives them a standard MCP endpoint that
|
|
76
|
+
exposes every admin endpoint as a tool — `admin.list`, `admin.retrieve`,
|
|
77
|
+
`admin.create`, `admin.update`, `admin.destroy`, `admin.action`,
|
|
78
|
+
`admin.autocomplete`, `admin.history`, and ten more — using the same
|
|
79
|
+
authentication mechanism, permissions, validation, and serialization that the
|
|
80
|
+
REST API and the React admin already use.
|
|
81
|
+
|
|
82
|
+
What this package is **not**:
|
|
83
|
+
|
|
84
|
+
- ❌ A new permission system.
|
|
85
|
+
- ❌ A new ORM layer.
|
|
86
|
+
- ❌ A bypass for CSRF or session auth.
|
|
87
|
+
- ❌ An owner of any admin behaviour.
|
|
88
|
+
|
|
89
|
+
If a behaviour is not in `django-admin-rest-api`, it is not in
|
|
90
|
+
`django-admin-mcp-api`. Period.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Install (plug-and-play)
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
pip install django-admin-mcp-api
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
In `settings.py`:
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
INSTALLED_APPS = [
|
|
104
|
+
# ...your apps...
|
|
105
|
+
"django.contrib.admin",
|
|
106
|
+
"django_admin_rest_api", # the REST layer (mandatory at v0.1+)
|
|
107
|
+
"django_admin_mcp_api", # the MCP adapter
|
|
108
|
+
]
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
In your root `urls.py`:
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from django.urls import include, path
|
|
115
|
+
|
|
116
|
+
urlpatterns = [
|
|
117
|
+
path("admin/", admin.site.urls),
|
|
118
|
+
path("api/v1/", include("django_admin_rest_api.urls")), # REST
|
|
119
|
+
path("mcp/", include("django_admin_mcp_api.urls")), # MCP (this package)
|
|
120
|
+
]
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
That is the entire integration. There is nothing else to configure.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## What you get
|
|
128
|
+
|
|
129
|
+
Two endpoints, both gated by the **same** auth your admin already has
|
|
130
|
+
(Django session + CSRF + `AdminSite.has_permission`):
|
|
131
|
+
|
|
132
|
+
- `POST /mcp/` — the MCP JSON-RPC 2.0 entry point. Speaks `initialize`,
|
|
133
|
+
`tools/list`, and `tools/call`.
|
|
134
|
+
- `GET /mcp/manifest/` — a read-only catalogue (server info + every tool's
|
|
135
|
+
name, description, JSON-Schema) for humans and dashboards.
|
|
136
|
+
|
|
137
|
+
### The tool catalogue
|
|
138
|
+
|
|
139
|
+
| MCP tool | What it does | rest-api endpoint |
|
|
140
|
+
| ----------------------- | --------------------------------------------------------------------- | ------------------------------------------------------- |
|
|
141
|
+
| `admin.registry` | List every model the user can see | `GET /api/v1/registry/` |
|
|
142
|
+
| `admin.schema` | The full admin metadata schema | `GET /api/v1/schema/` |
|
|
143
|
+
| `admin.recent_actions` | The user's own LogEntry feed | `GET /api/v1/recent-actions/` |
|
|
144
|
+
| `admin.list` | A page of list-view results | `GET /api/v1/<app>/<model>/` |
|
|
145
|
+
| `admin.retrieve` | A single object's detail view | `GET /api/v1/<app>/<model>/<pk>/` |
|
|
146
|
+
| `admin.add_form` | Create-page field descriptors | `GET /api/v1/<app>/<model>/add/` |
|
|
147
|
+
| `admin.create` | Create one object | `POST /api/v1/<app>/<model>/` |
|
|
148
|
+
| `admin.update` | Partial-update one object | `PATCH /api/v1/<app>/<model>/<pk>/` |
|
|
149
|
+
| `admin.destroy` | Delete one object | `DELETE /api/v1/<app>/<model>/<pk>/` |
|
|
150
|
+
| `admin.bulk_update` | Apply the same patch to many objects | `PATCH /api/v1/<app>/<model>/bulk/` |
|
|
151
|
+
| `admin.autocomplete` | Autocomplete a related model | `GET /api/v1/<app>/<model>/autocomplete/` |
|
|
152
|
+
| `admin.action` | Run a `ModelAdmin.actions` action | `POST /api/v1/<app>/<model>/actions/<name>/` |
|
|
153
|
+
| `admin.history` | One object's LogEntry timeline | `GET /api/v1/<app>/<model>/<pk>/history/` |
|
|
154
|
+
| `admin.delete_preview` | Cascade preview before a destroy | `GET /api/v1/<app>/<model>/<pk>/delete-preview/` |
|
|
155
|
+
| `admin.set_password` | Set/change a user-like password | `POST /api/v1/<app>/<model>/<pk>/password/` |
|
|
156
|
+
| `admin.panel` | A custom panel registered on the ModelAdmin | `GET /api/v1/<app>/<model>/<pk>/panel/<name>/` |
|
|
157
|
+
|
|
158
|
+
Every tool is a 1:1 mirror of a `django-admin-rest-api` endpoint.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Quick tour
|
|
163
|
+
|
|
164
|
+
### Discover the catalogue
|
|
165
|
+
|
|
166
|
+
```console
|
|
167
|
+
$ curl -s http://localhost:8000/mcp/manifest/ \
|
|
168
|
+
--cookie "sessionid=…" | jq '.tools[].name'
|
|
169
|
+
"admin.registry"
|
|
170
|
+
"admin.schema"
|
|
171
|
+
"admin.recent_actions"
|
|
172
|
+
"admin.list"
|
|
173
|
+
...
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Initialize an MCP session
|
|
177
|
+
|
|
178
|
+
```console
|
|
179
|
+
$ curl -s http://localhost:8000/mcp/ \
|
|
180
|
+
-H "Content-Type: application/json" \
|
|
181
|
+
-H "X-CSRFToken: $(grep csrftoken ~/.cookies)" \
|
|
182
|
+
--cookie "sessionid=…" \
|
|
183
|
+
-d '{"jsonrpc":"2.0","id":1,"method":"initialize"}' | jq .
|
|
184
|
+
{
|
|
185
|
+
"jsonrpc": "2.0",
|
|
186
|
+
"id": 1,
|
|
187
|
+
"result": {
|
|
188
|
+
"protocolVersion": "2024-11-05",
|
|
189
|
+
"serverInfo": { "name": "django-admin", "version": "0.1.0" },
|
|
190
|
+
"capabilities": { "tools": { "listChanged": false } }
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Call a tool
|
|
196
|
+
|
|
197
|
+
```console
|
|
198
|
+
$ curl -s http://localhost:8000/mcp/ \
|
|
199
|
+
-H "Content-Type: application/json" -H "X-CSRFToken: …" \
|
|
200
|
+
--cookie "sessionid=…" \
|
|
201
|
+
-d '{
|
|
202
|
+
"jsonrpc": "2.0",
|
|
203
|
+
"id": 2,
|
|
204
|
+
"method": "tools/call",
|
|
205
|
+
"params": {
|
|
206
|
+
"name": "admin.list",
|
|
207
|
+
"arguments": {"app_label": "auth", "model_name": "user", "page": 1}
|
|
208
|
+
}
|
|
209
|
+
}' | jq .
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
The response is the rest-api response, wrapped in an MCP `content` envelope.
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Screenshots
|
|
217
|
+
|
|
218
|
+
> Screenshots are generated by `scripts/screenshots.sh` against a local
|
|
219
|
+
> Django dev server. They will be regenerated and committed when
|
|
220
|
+
> `django-admin-rest-api` ships and the dispatcher is wired through.
|
|
221
|
+
|
|
222
|
+
- `docs/screenshots/manifest-curl.png` — `GET /mcp/manifest/` rendered
|
|
223
|
+
- `docs/screenshots/tools-list.png` — `tools/list` from Claude Desktop
|
|
224
|
+
- `docs/screenshots/agent-driving-admin.png` — an agent picking and
|
|
225
|
+
running an admin action via `admin.action`
|
|
226
|
+
|
|
227
|
+
(Placeholder until the dispatcher is live — tracked in [#3](https://github.com/MartinCastroAlvarez/django-admin-mcp/issues).)
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Security
|
|
232
|
+
|
|
233
|
+
Defaults are deliberately strict and match the rest of the family:
|
|
234
|
+
|
|
235
|
+
- **Staff-only.** Anonymous requests get `401`, non-staff get `403`. The
|
|
236
|
+
staff gate is the *minimum* — the real permission check happens inside
|
|
237
|
+
`django-admin-rest-api` per tool.
|
|
238
|
+
- **CSRF always on.** No view in this package is `@csrf_exempt`. The
|
|
239
|
+
pre-commit hook fails any PR that introduces one.
|
|
240
|
+
- **No new permission code.** This package never calls `user.has_perm` or
|
|
241
|
+
`objects.all()` — those checks belong to rest-api.
|
|
242
|
+
- **No secrets in code or commits.** `gitleaks` + a pygrep hook block any
|
|
243
|
+
token-shaped string from reaching the index.
|
|
244
|
+
|
|
245
|
+
See [SECURITY.md](SECURITY.md) for the full set of invariants and how to
|
|
246
|
+
report a vulnerability.
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Status
|
|
251
|
+
|
|
252
|
+
| Component | Status |
|
|
253
|
+
| ------------------------------------------ | -------------------------------------------- |
|
|
254
|
+
| MCP wire protocol (initialize, tools/list, tools/call) | ✅ implemented + tested |
|
|
255
|
+
| 16-tool catalogue | ✅ implemented + tested |
|
|
256
|
+
| Default dispatcher | ⏳ placeholder until `django-admin-rest-api` is on PyPI |
|
|
257
|
+
| PyPI release | ⏳ blocked on `django-admin-rest-api` |
|
|
258
|
+
|
|
259
|
+
Tracked in the [GitHub project board](https://github.com/MartinCastroAlvarez/django-admin-mcp/projects).
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## Contributing
|
|
264
|
+
|
|
265
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). The TL;DR:
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
poetry install
|
|
269
|
+
poetry run pytest
|
|
270
|
+
poetry run bash scripts/lint.sh
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
PRs go through review per [CLAUDE.md](CLAUDE.md). The same linters,
|
|
274
|
+
formatters, and security gates as `django-admin-react` run on every PR.
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## License
|
|
279
|
+
|
|
280
|
+
MIT — see [LICENSE](LICENSE).
|
|
281
|
+
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# django-admin-mcp-api
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/django-admin-mcp-api/)
|
|
4
|
+
[](https://pypi.org/project/django-admin-mcp-api/)
|
|
5
|
+
[](https://pypi.org/project/django-admin-mcp-api/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://github.com/MartinCastroAlvarez/django-admin-mcp/actions/workflows/ci.yml)
|
|
8
|
+
|
|
9
|
+
> **An MCP (Model Context Protocol) adapter for the Django admin.**
|
|
10
|
+
> Lets AI agents reach every operation of your `ModelAdmin` — list, retrieve,
|
|
11
|
+
> create, update, delete, run admin actions, autocomplete — through the
|
|
12
|
+
> standard MCP wire protocol, with the **same** authentication, permissions,
|
|
13
|
+
> and validation as the rest of your admin.
|
|
14
|
+
|
|
15
|
+
`django-admin-mcp-api` is a thin **wire-protocol adapter** sitting on top of
|
|
16
|
+
[`django-admin-rest-api`](https://github.com/MartinCastroAlvarez/django-admin-rest-api).
|
|
17
|
+
It introduces **no new functionality, no parallel permission system, no extra
|
|
18
|
+
validation, and no new business logic** — it is the MCP face on the REST API
|
|
19
|
+
your admin already speaks.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## The three-repo family
|
|
24
|
+
|
|
25
|
+
`django-admin-mcp-api` is one of three sibling packages that share the same
|
|
26
|
+
admin core. Each one exposes the same surface in a different protocol:
|
|
27
|
+
|
|
28
|
+
| Repo | Protocol | PyPI | Status |
|
|
29
|
+
| ------------------------------------------------------------------------------------- | ---------------- | --------------------------------------------------------- | --------------------- |
|
|
30
|
+
| [`django-admin-react`](https://github.com/MartinCastroAlvarez/django-admin-react) | React SPA over HTTP/JSON | [`django-admin-react`](https://pypi.org/project/django-admin-react/) | Published |
|
|
31
|
+
| [`django-admin-rest-api`](https://github.com/MartinCastroAlvarez/django-admin-rest-api) | HTTP REST/JSON | _to be published_ | Extraction in progress |
|
|
32
|
+
| **`django-admin-mcp`** (this repo) | **MCP (JSON-RPC)** | [`django-admin-mcp-api`](https://pypi.org/project/django-admin-mcp-api/) | Pre-alpha (this is the v0) |
|
|
33
|
+
|
|
34
|
+
All three reuse your existing `ModelAdmin` as the **only** source of truth
|
|
35
|
+
for querysets, permissions, forms, and serialization.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Why this exists
|
|
40
|
+
|
|
41
|
+
LLM agents speak the [Model Context Protocol](https://modelcontextprotocol.io)
|
|
42
|
+
natively. Today they cannot reach a Django admin without a custom integration
|
|
43
|
+
per project. `django-admin-mcp-api` gives them a standard MCP endpoint that
|
|
44
|
+
exposes every admin endpoint as a tool — `admin.list`, `admin.retrieve`,
|
|
45
|
+
`admin.create`, `admin.update`, `admin.destroy`, `admin.action`,
|
|
46
|
+
`admin.autocomplete`, `admin.history`, and ten more — using the same
|
|
47
|
+
authentication mechanism, permissions, validation, and serialization that the
|
|
48
|
+
REST API and the React admin already use.
|
|
49
|
+
|
|
50
|
+
What this package is **not**:
|
|
51
|
+
|
|
52
|
+
- ❌ A new permission system.
|
|
53
|
+
- ❌ A new ORM layer.
|
|
54
|
+
- ❌ A bypass for CSRF or session auth.
|
|
55
|
+
- ❌ An owner of any admin behaviour.
|
|
56
|
+
|
|
57
|
+
If a behaviour is not in `django-admin-rest-api`, it is not in
|
|
58
|
+
`django-admin-mcp-api`. Period.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Install (plug-and-play)
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install django-admin-mcp-api
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
In `settings.py`:
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
INSTALLED_APPS = [
|
|
72
|
+
# ...your apps...
|
|
73
|
+
"django.contrib.admin",
|
|
74
|
+
"django_admin_rest_api", # the REST layer (mandatory at v0.1+)
|
|
75
|
+
"django_admin_mcp_api", # the MCP adapter
|
|
76
|
+
]
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
In your root `urls.py`:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from django.urls import include, path
|
|
83
|
+
|
|
84
|
+
urlpatterns = [
|
|
85
|
+
path("admin/", admin.site.urls),
|
|
86
|
+
path("api/v1/", include("django_admin_rest_api.urls")), # REST
|
|
87
|
+
path("mcp/", include("django_admin_mcp_api.urls")), # MCP (this package)
|
|
88
|
+
]
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
That is the entire integration. There is nothing else to configure.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## What you get
|
|
96
|
+
|
|
97
|
+
Two endpoints, both gated by the **same** auth your admin already has
|
|
98
|
+
(Django session + CSRF + `AdminSite.has_permission`):
|
|
99
|
+
|
|
100
|
+
- `POST /mcp/` — the MCP JSON-RPC 2.0 entry point. Speaks `initialize`,
|
|
101
|
+
`tools/list`, and `tools/call`.
|
|
102
|
+
- `GET /mcp/manifest/` — a read-only catalogue (server info + every tool's
|
|
103
|
+
name, description, JSON-Schema) for humans and dashboards.
|
|
104
|
+
|
|
105
|
+
### The tool catalogue
|
|
106
|
+
|
|
107
|
+
| MCP tool | What it does | rest-api endpoint |
|
|
108
|
+
| ----------------------- | --------------------------------------------------------------------- | ------------------------------------------------------- |
|
|
109
|
+
| `admin.registry` | List every model the user can see | `GET /api/v1/registry/` |
|
|
110
|
+
| `admin.schema` | The full admin metadata schema | `GET /api/v1/schema/` |
|
|
111
|
+
| `admin.recent_actions` | The user's own LogEntry feed | `GET /api/v1/recent-actions/` |
|
|
112
|
+
| `admin.list` | A page of list-view results | `GET /api/v1/<app>/<model>/` |
|
|
113
|
+
| `admin.retrieve` | A single object's detail view | `GET /api/v1/<app>/<model>/<pk>/` |
|
|
114
|
+
| `admin.add_form` | Create-page field descriptors | `GET /api/v1/<app>/<model>/add/` |
|
|
115
|
+
| `admin.create` | Create one object | `POST /api/v1/<app>/<model>/` |
|
|
116
|
+
| `admin.update` | Partial-update one object | `PATCH /api/v1/<app>/<model>/<pk>/` |
|
|
117
|
+
| `admin.destroy` | Delete one object | `DELETE /api/v1/<app>/<model>/<pk>/` |
|
|
118
|
+
| `admin.bulk_update` | Apply the same patch to many objects | `PATCH /api/v1/<app>/<model>/bulk/` |
|
|
119
|
+
| `admin.autocomplete` | Autocomplete a related model | `GET /api/v1/<app>/<model>/autocomplete/` |
|
|
120
|
+
| `admin.action` | Run a `ModelAdmin.actions` action | `POST /api/v1/<app>/<model>/actions/<name>/` |
|
|
121
|
+
| `admin.history` | One object's LogEntry timeline | `GET /api/v1/<app>/<model>/<pk>/history/` |
|
|
122
|
+
| `admin.delete_preview` | Cascade preview before a destroy | `GET /api/v1/<app>/<model>/<pk>/delete-preview/` |
|
|
123
|
+
| `admin.set_password` | Set/change a user-like password | `POST /api/v1/<app>/<model>/<pk>/password/` |
|
|
124
|
+
| `admin.panel` | A custom panel registered on the ModelAdmin | `GET /api/v1/<app>/<model>/<pk>/panel/<name>/` |
|
|
125
|
+
|
|
126
|
+
Every tool is a 1:1 mirror of a `django-admin-rest-api` endpoint.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Quick tour
|
|
131
|
+
|
|
132
|
+
### Discover the catalogue
|
|
133
|
+
|
|
134
|
+
```console
|
|
135
|
+
$ curl -s http://localhost:8000/mcp/manifest/ \
|
|
136
|
+
--cookie "sessionid=…" | jq '.tools[].name'
|
|
137
|
+
"admin.registry"
|
|
138
|
+
"admin.schema"
|
|
139
|
+
"admin.recent_actions"
|
|
140
|
+
"admin.list"
|
|
141
|
+
...
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Initialize an MCP session
|
|
145
|
+
|
|
146
|
+
```console
|
|
147
|
+
$ curl -s http://localhost:8000/mcp/ \
|
|
148
|
+
-H "Content-Type: application/json" \
|
|
149
|
+
-H "X-CSRFToken: $(grep csrftoken ~/.cookies)" \
|
|
150
|
+
--cookie "sessionid=…" \
|
|
151
|
+
-d '{"jsonrpc":"2.0","id":1,"method":"initialize"}' | jq .
|
|
152
|
+
{
|
|
153
|
+
"jsonrpc": "2.0",
|
|
154
|
+
"id": 1,
|
|
155
|
+
"result": {
|
|
156
|
+
"protocolVersion": "2024-11-05",
|
|
157
|
+
"serverInfo": { "name": "django-admin", "version": "0.1.0" },
|
|
158
|
+
"capabilities": { "tools": { "listChanged": false } }
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Call a tool
|
|
164
|
+
|
|
165
|
+
```console
|
|
166
|
+
$ curl -s http://localhost:8000/mcp/ \
|
|
167
|
+
-H "Content-Type: application/json" -H "X-CSRFToken: …" \
|
|
168
|
+
--cookie "sessionid=…" \
|
|
169
|
+
-d '{
|
|
170
|
+
"jsonrpc": "2.0",
|
|
171
|
+
"id": 2,
|
|
172
|
+
"method": "tools/call",
|
|
173
|
+
"params": {
|
|
174
|
+
"name": "admin.list",
|
|
175
|
+
"arguments": {"app_label": "auth", "model_name": "user", "page": 1}
|
|
176
|
+
}
|
|
177
|
+
}' | jq .
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
The response is the rest-api response, wrapped in an MCP `content` envelope.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Screenshots
|
|
185
|
+
|
|
186
|
+
> Screenshots are generated by `scripts/screenshots.sh` against a local
|
|
187
|
+
> Django dev server. They will be regenerated and committed when
|
|
188
|
+
> `django-admin-rest-api` ships and the dispatcher is wired through.
|
|
189
|
+
|
|
190
|
+
- `docs/screenshots/manifest-curl.png` — `GET /mcp/manifest/` rendered
|
|
191
|
+
- `docs/screenshots/tools-list.png` — `tools/list` from Claude Desktop
|
|
192
|
+
- `docs/screenshots/agent-driving-admin.png` — an agent picking and
|
|
193
|
+
running an admin action via `admin.action`
|
|
194
|
+
|
|
195
|
+
(Placeholder until the dispatcher is live — tracked in [#3](https://github.com/MartinCastroAlvarez/django-admin-mcp/issues).)
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Security
|
|
200
|
+
|
|
201
|
+
Defaults are deliberately strict and match the rest of the family:
|
|
202
|
+
|
|
203
|
+
- **Staff-only.** Anonymous requests get `401`, non-staff get `403`. The
|
|
204
|
+
staff gate is the *minimum* — the real permission check happens inside
|
|
205
|
+
`django-admin-rest-api` per tool.
|
|
206
|
+
- **CSRF always on.** No view in this package is `@csrf_exempt`. The
|
|
207
|
+
pre-commit hook fails any PR that introduces one.
|
|
208
|
+
- **No new permission code.** This package never calls `user.has_perm` or
|
|
209
|
+
`objects.all()` — those checks belong to rest-api.
|
|
210
|
+
- **No secrets in code or commits.** `gitleaks` + a pygrep hook block any
|
|
211
|
+
token-shaped string from reaching the index.
|
|
212
|
+
|
|
213
|
+
See [SECURITY.md](SECURITY.md) for the full set of invariants and how to
|
|
214
|
+
report a vulnerability.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Status
|
|
219
|
+
|
|
220
|
+
| Component | Status |
|
|
221
|
+
| ------------------------------------------ | -------------------------------------------- |
|
|
222
|
+
| MCP wire protocol (initialize, tools/list, tools/call) | ✅ implemented + tested |
|
|
223
|
+
| 16-tool catalogue | ✅ implemented + tested |
|
|
224
|
+
| Default dispatcher | ⏳ placeholder until `django-admin-rest-api` is on PyPI |
|
|
225
|
+
| PyPI release | ⏳ blocked on `django-admin-rest-api` |
|
|
226
|
+
|
|
227
|
+
Tracked in the [GitHub project board](https://github.com/MartinCastroAlvarez/django-admin-mcp/projects).
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Contributing
|
|
232
|
+
|
|
233
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). The TL;DR:
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
poetry install
|
|
237
|
+
poetry run pytest
|
|
238
|
+
poetry run bash scripts/lint.sh
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
PRs go through review per [CLAUDE.md](CLAUDE.md). The same linters,
|
|
242
|
+
formatters, and security gates as `django-admin-react` run on every PR.
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## License
|
|
247
|
+
|
|
248
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# `django_admin_mcp_api/`
|
|
2
|
+
|
|
3
|
+
The shipped package. A Django app + an MCP wire-protocol adapter over
|
|
4
|
+
[`django-admin-rest-api`](https://github.com/MartinCastroAlvarez/django-admin-rest-api).
|
|
5
|
+
|
|
6
|
+
## What lives here
|
|
7
|
+
|
|
8
|
+
| File / dir | Purpose |
|
|
9
|
+
| --------------------- | ------------------------------------------------------------- |
|
|
10
|
+
| `__init__.py` | Version + default app config pointer. |
|
|
11
|
+
| `apps.py` | `DjangoAdminMcpApiConfig` — registered via `INSTALLED_APPS`. |
|
|
12
|
+
| `conf.py` | The one place to read `DJANGO_ADMIN_MCP_API` settings from. |
|
|
13
|
+
| `urls.py` | Two URLs: `POST /` (MCP JSON-RPC) and `GET /manifest/`. |
|
|
14
|
+
| [`server/`](server/) | The wire layer (views, JSON-RPC, dispatcher, manifest). |
|
|
15
|
+
| [`tools/`](tools/) | One module per MCP tool (16 tools total). |
|
|
16
|
+
|
|
17
|
+
## What must NOT live here
|
|
18
|
+
|
|
19
|
+
- Database queries.
|
|
20
|
+
- Permission checks (the staff gate in `server/views.py` is the only
|
|
21
|
+
exception, and it is a baseline — the real check is in rest-api).
|
|
22
|
+
- Serialization.
|
|
23
|
+
- Any new admin behaviour.
|
|
24
|
+
|
|
25
|
+
## Pointers
|
|
26
|
+
|
|
27
|
+
- [`../README.md`](../README.md) — user-facing docs.
|
|
28
|
+
- [`../ARCHITECTURE.md`](../ARCHITECTURE.md) — request flow + dispatcher seam.
|
|
29
|
+
- [`../SECURITY.md`](../SECURITY.md) — security invariants.
|
|
30
|
+
- [`../CLAUDE.md`](../CLAUDE.md) — agent contract.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""django-admin-mcp-api: an MCP (Model Context Protocol) server for the Django admin.
|
|
2
|
+
|
|
3
|
+
The package exposes every operation of a consumer's ``ModelAdmin`` as an MCP
|
|
4
|
+
tool, reusing Django's session + CSRF auth and the consumer's existing
|
|
5
|
+
``AdminSite`` registry. See ``ARCHITECTURE.md`` for the full design and
|
|
6
|
+
``SECURITY.md`` for the non-negotiable security rules.
|
|
7
|
+
|
|
8
|
+
This is the public surface; everything else under ``server/`` and
|
|
9
|
+
``tools/`` is implementation. The Django app config is auto-discovered
|
|
10
|
+
via ``apps.py`` once ``"django_admin_mcp_api"`` is added to
|
|
11
|
+
``INSTALLED_APPS``.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
__version__ = "0.1.0a0"
|
|
17
|
+
|
|
18
|
+
# Default Django app config — consumers add ``"django_admin_mcp_api"`` to
|
|
19
|
+
# INSTALLED_APPS and Django picks this up automatically.
|
|
20
|
+
default_app_config = "django_admin_mcp_api.apps.DjangoAdminMcpApiConfig"
|
|
21
|
+
|
|
22
|
+
__all__ = ["__version__", "default_app_config"]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Django app config for django-admin-mcp-api."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from django.apps import AppConfig
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class DjangoAdminMcpApiConfig(AppConfig):
|
|
9
|
+
"""App config for ``django_admin_mcp_api``.
|
|
10
|
+
|
|
11
|
+
Registered via ``INSTALLED_APPS``. We deliberately do not perform any
|
|
12
|
+
side-effects in ``ready()`` other than configuration validation —
|
|
13
|
+
every endpoint is opt-in through the consumer's URL conf.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
name = "django_admin_mcp_api"
|
|
17
|
+
label = "django_admin_mcp_api"
|
|
18
|
+
verbose_name = "Django Admin MCP API"
|
|
19
|
+
default_auto_field = "django.db.models.BigAutoField"
|
|
20
|
+
|
|
21
|
+
def ready(self) -> None:
|
|
22
|
+
"""Validate settings on app startup.
|
|
23
|
+
|
|
24
|
+
Kept light to avoid slow Django boot in test suites. Any future
|
|
25
|
+
signal wiring goes here.
|
|
26
|
+
"""
|
|
27
|
+
# Importing the conf module triggers settings validation via
|
|
28
|
+
# ``django.core.checks``-friendly accessors. See
|
|
29
|
+
# ``django_admin_mcp_api.conf`` for the actual checks.
|
|
30
|
+
from django_admin_mcp_api import conf # noqa: F401
|