crudkit
A metadata-driven CRUD framework for Django. Define models; get typed object IDs, soft delete, merge, change logging, an activity feed, saved views, AI-populated fields and a generic REST API — without writing per-model serializers, viewsets or routes.
Core concepts
Every CrudKit model inherits BaseCrudKitModel and declares a 3-letter
TYPE_ID. Object IDs are rendered as <TYPE_ID><pk> (e.g. CUS42) across
the API and UI.
from django.db import models
from crudkit.models import BaseCrudKitModel
class Customer(BaseCrudKitModel):
TYPE_ID = "CUS"
name = models.CharField(max_length=255)
class CrudKitSettings(BaseCrudKitModel.CrudKitSettings):
search_fields = ["name"]
Included with every model: created_by/updated_by/created_at/updated_at
audit columns, a deleted soft-delete flag (soft_delete()), merge support
(delete_and_merge_with()), a change log, an activity feed (FeedItem),
external-system sync (ExternalObject), saved views/layouts, and optional AI
fields (AISummaryField, AICategoryField, AIBooleanField, AITagsField,
AIForeignKeyField) populated asynchronously via Celery and pydantic-ai.
Installation
pip install crudkit[api] # REST API included
pip install crudkit[assistant] # + AI assistant sidebar (Channels)
pip install crudkit[mcp] # + remote MCP server with OAuth (e.g. a Claude connector)
INSTALLED_APPS = [
...,
"rest_framework",
"crudkit",
"crudkit_assistant", # optional
]
REST_FRAMEWORK = {
"DEFAULT_PERMISSION_CLASSES": [
"crudkit_api.permissions.CrudKitModelPermissions",
],
"DEFAULT_PAGINATION_CLASS": "crudkit_api.pagination.CrudKitPagination",
"DEFAULT_FILTER_BACKENDS": ["crudkit_api.filters.BasicFilter"],
"PAGE_SIZE": 50,
}
# urls.py — one include registers a full CRUD API for every TYPE_ID model
urlpatterns = [path("api/v1/", include("crudkit_api.urls"))]
The assistant sidebar talks over a WebSocket at ws/assistant/, next to the
realtime ws/changes/ socket; route both in your ASGI application (see
examples/demo/demo/asgi.py):
application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket": URLRouter(
crudkit_api.routing.websocket_urlpatterns + crudkit_assistant.routing.websocket_urlpatterns
),
})
The sidebar sees what the user has on screen — the open record, or the list
with its saved view, search, filters, visible and selected rows — and can
search and read across record types. It only proposes changes (to any record
the user may change); each one waits for Confirm. Conversations are stored
per user (AssistantConversation, ASC) and resume after a reload. The SPA
only shows the assistant when an AI model is configured (CRUDKIT_AI_MODEL or
CRUDKIT_AI_MODEL_FACTORY).
Company knowledge for the AI lives in AI context documents (AIContext,
AIC), which users edit in the UI at /AIC (add a saved view to the menu to link it).
Active documents without model_types go into the assistant's instructions,
joined by documents scoped to a type while a record or list of that type is
open; AI fields get both in a "Company context" section
of their prompt. Code can render them with crudkit.llm.ai_context(type_id).
The assistant proposes edits to these documents when the user teaches it
something durable about the company.
CrudKit requires the standard Django model permissions for every API and
assistant operation. Projects that need row-level rules can override
CrudKitSettings.get_authorized_queryset(user, queryset, action); action
is one of view, add, change, or delete. Model actions require change
permission and can be narrowed further with
CrudKitSettings.has_action_permission(user, instance, action_name).
Settings
| Setting | Purpose |
|---|---|
CRUDKIT_AI_MODEL |
pydantic-ai model string (e.g. "mistral:mistral-large-latest") enabling AI fields/assistant |
CRUDKIT_AI_MODEL_FACTORY |
dotted path to an async context manager yielding a pydantic-ai Model (advanced) |
CRUDKIT_USER_PROFILE_ADAPTER |
dotted path to a class supplying preferred language + avatar images for users |
CRUDKIT_EXTRA_GENERIC_RELATIONS |
project models surfaced as generic relations in object metadata |
CRUDKIT_DEFAULT_CURRENCY, CRUDKIT_CURRENCY_CHOICES |
currency configuration for MoneyField |
CRUDKIT_GRAVATAR_FALLBACK_EMAIL |
fallback avatar email (gravatar mp default otherwise) |
CRUDKIT_DASHBOARD_WIDGETS |
dotted path to a dashboard_for_user(user) widget provider |
CRUDKIT_ASSISTANT_NAME, CRUDKIT_ASSISTANT_SYSTEM_PROMPT, CRUDKIT_ASSISTANT_AVATAR_URL |
assistant branding |
CRUDKIT_FRONTEND_CONFIG |
dict injected into the bundled SPA at runtime (app_name, logo_url, ...) |
CRUDKIT_FRONTEND_LOGIN_REQUIRED |
redirect anonymous users of the SPA view to LOGIN_URL |
CRUDKIT_MCP_SERVER_NAME |
serverInfo.name reported by the MCP server (default "crudkit") |
CRUDKIT_MCP_WRITE_ENABLED |
offer the write OAuth scope: the create/update/action/note/undo tools write directly (default False) |
CRUDKIT_MCP_WRITE_RATE |
write tool calls allowed per token, as "<count>/<second|minute|hour|day>" (default "60/min"; None for no limit). Counted in the default Django cache: use a shared one (Redis, Memcached, database) with several processes; with CRUDKIT_MCP_WRITE_ENABLED, system check crudkit_mcp.W001 warns otherwise |
CRUDKIT_AI_CONTEXT_MAX_CHARS |
cap on the AI context text sent with every assistant step and AI-field run (default 20000) |
CRUDKIT_MCP_MODELS |
list of TYPE_IDs to expose over MCP (default: every project model) |
CRUDKIT_MCP_EXTRA_TOOLS |
dotted paths to crudkit_mcp.tools.Tool instances; added to, or replacing, the generated tools |
CRUDKIT_MCP_BASE_URL |
public origin for OAuth metadata URLs when the request's host/scheme is wrong (e.g. behind a proxy) |
MCP server
crudkit_mcp exposes the models as a remote MCP
server (streamable HTTP) with its own OAuth 2.1 authorization server
(dynamic client registration, PKCE, rotating refresh tokens), so it can be
added as e.g. a Claude connector by URL.
INSTALLED_APPS = [..., "crudkit_mcp"]
LOGIN_URL = "/login/" # the OAuth consent page needs a session login
urlpatterns = [
path("api/v1/", include("crudkit_mcp.urls")), # /api/v1/mcp, /api/v1/oauth/...
path("", include("crudkit_mcp.well_known_urls")), # /.well-known/oauth-* (site root)
...,
]
The tool set is fixed — records are addressed by TYPE_ID and CK-ID, so the same tools serve any project:
| Tool | |
|---|---|
describe_types |
the record types; for one type, its filters, writable fields, actions, permissions and saved views |
search |
free-text search across types, returning {id, label} |
list_records |
one type or saved view (its filters, ordering and columns), a filters object (keys from describe_types), query, order_by, limit, offset |
get_record |
one record by ID, with its feed, change log and available actions |
Five more tools change data: create_record, update_record, run_action,
add_note and undo. On the consent page the user picks what the client may
do:
- Read only — the
readscope; no write tools. - Propose changes — the
proposescope, offered whencrudkit_assistantis installed. The write tools validate their input as usual but change nothing: each files a proposal and returns{"status": "pending_approval", "proposal": "ASP12"}. The user confirms or skips it in the Inbox's Proposals tab. - Write directly — the
writescope, only withCRUDKIT_MCP_WRITE_ENABLED. Each write returns thechange_setit made;undoreverts one. Actions and fields that require approval (seedocs/concepts.md) are still only proposed.
Writes and proposals are recorded as made over MCP by the token's OAuth
client, and limited by CRUDKIT_MCP_WRITE_RATE.
Every call is filtered through the token user's model, row and action
permissions, so a type the user can't view isn't listed and can't be read.
CrudKit's own models and Django's (including User) are not exposed, except
background agents (AGT), which clients can propose; narrow the rest with CRUDKIT_MCP_MODELS = ["CMP", "PER", ...] or opt a
single model out with CrudKitSettings.mcp_exclude = True.
Background agents
With crudkit_assistant installed, users can save agents (TYPE_ID AGT):
instructions the assistant carries out on its own, on a record that was just
created or changed, on the records of a saved view on a schedule, or when
someone clicks Run now. Each go is an AgentRun (AGR) with the agent's
summary and the proposals it made. See docs/concepts.md for the details.
Runs are Celery tasks (crudkit_assistant.tasks.run_agent). For scheduled
agents, add the scheduler task to Celery beat:
CELERY_BEAT_SCHEDULE = {
"crudkit-scheduled-agents": {
"task": "crudkit_assistant.tasks.run_scheduled_agents",
"schedule": 300, # every 5 minutes; each agent runs when its interval has passed
},
}
Agents start from logged changes (the REST API, MCP, the assistant, actions), so plain ORM saves in project code do not trigger them. The enabled agents per type are cached in the Django cache for up to a minute.
Bundled frontend
The wheel ships the built CrudKit web SPA. To serve it, add the app, the context processor, a config dict, and a catch-all url include (last!):
INSTALLED_APPS = [..., "crudkit_frontend"]
TEMPLATES = [{
...,
"OPTIONS": {"context_processors": [
...,
"crudkit_frontend.context_processors.crudkit_config",
]},
}]
CRUDKIT_FRONTEND_CONFIG = {"app_name": "My App"} # injected into the SPA at runtime
CRUDKIT_FRONTEND_LOGIN_REQUIRED = False # True → redirect anonymous users to LOGIN_URL
# urls.py — must be the LAST pattern; everything unmatched serves the SPA
urlpatterns = [..., path("", include("crudkit_frontend.urls"))]
Static assets are served by django.contrib.staticfiles (or WhiteNoise et
al.) from crudkit_frontend/static/, under whatever STATIC_URL the project
uses — nothing assumes the default static/ prefix. Contributors hacking on
the SPA itself
run the Vite dev server from ../frontend against any CrudKit
backend; npm run build there regenerates the bundled assets.
Running the tests
cd backend
uv sync --all-extras
uv run manage.py test crudkit crudkit_api crudkit_assistant crudkit_frontend crudkit_mcp tests
License
MIT
Metadata
Release files for crudkit 0.6.5
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| crudkit-0.6.5.tar.gz | 736.0 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| crudkit-0.6.5-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.4 MB
Release files / crudkit-0.6.5.tar.gz
| Download URL | crudkit-0.6.5.tar.gz |
|---|---|
| Size | 736.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b99067c66f68e0c955965a06329fbdc920f7b4d0d8c2870b10721cb7240b1f9c
|
|
BLAKE2b-256 checksum How to use checksums |
ab1d4603fdb7816bf9d4cbd20eb74842589634309139971b103362bf06991c6e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 21, 2026.
Transparency logRelease files / crudkit-0.6.5-py3-none-any.whl
| Download URL | crudkit-0.6.5-py3-none-any.whl |
|---|---|
| Size | 702.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
31ad18bacffbd53cc234e77faab2bc0408dc9ba22142372f392280aa63081a52
|
|
BLAKE2b-256 checksum How to use checksums |
c5a6d3f0c79da6b4a1cffaba68384542e982f9217788cb0b2b247f77f9b773f0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 21, 2026.
Transparency logSecurity for crudkit 0.6.5
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for crudkit-0.6.5.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 21, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.