mdjango
v0.1.0rc2.dev3+g175c8392a llms.txtllms-full.txt github

Reference

URLs#

Mount with path("docs/", include("mdjango.urls")). Any prefix works. The URLconf sets app_name = "mdjango", so names reverse as mdjango:<name>. Examples below assume the docs/ prefix.

The namespace is fixed. Templates and services reverse routes as mdjango:<name>, so passing a namespace= argument to include() or mounting the URLconf at two prefixes breaks every internal link.

Serve the docs at the site root#

If the site is the docs, mount at the root instead of under docs/:

urlpatterns = [
    # any other root routes (sitemap.xml, robots.txt, admin/, …) FIRST
    path("", include("mdjango.urls")),  # a catch-all — must come last
]

The <path:page_path>/ route matches any path, so the mdjango include has to be the last pattern: anything you serve at the root — sitemap.xml, robots.txt, the admin — must be declared above it or the page view will shadow it with a 404. Reversed URLs and the export drop the prefix accordingly (mdjango:index is /, a page is /how-to/deploy/). This is exactly how mdjango's own docs site is mounted.

Routes#

Path Name Response
/docs/ mdjango:index HTML. The Index page, or the first Page in navigation order when there is no root _index.md. 404 when the tree has no Pages.
/docs/<path>/ mdjango:page HTML. <path> is the Page path (how-to/deploy, how-to/hosts/harden). 404 for an unknown path.
/docs/search-index.json mdjango:search_index JSON array; see below. Always available.
/docs/llms.txt mdjango:llms_txt text/markdown. 404 when MDJANGO_LLM_DOCS is false.
/docs/llms-full.txt mdjango:llms_full text/markdown. 404 when MDJANGO_LLM_DOCS is false.
/docs/index.md mdjango:index_markdown text/markdown. The landing page's source. 404 when MDJANGO_LLM_DOCS is false.
/docs/<path>.md mdjango:page_markdown text/markdown. One Page's source. 404 when unknown or MDJANGO_LLM_DOCS is false.

File-shaped routes carry no trailing slash and the HTML page route requires one. That keeps a Page named llms from shadowing llms.txt. The index.md route is declared before <path>.md so it is not read as a Page named index.

Markdown responses are served as text/markdown; charset=utf-8. The per-page .md is the source body with front-matter stripped; a body that does not open with a heading gets # <title> prepended.

Reversing#

from django.urls import reverse

reverse("mdjango:index")                                    # /docs/
reverse("mdjango:page", args=["how-to/deploy"])             # /docs/how-to/deploy/
reverse("mdjango:page_markdown", args=["how-to/deploy"])    # /docs/how-to/deploy.md
reverse("mdjango:llms_txt")                                 # /docs/llms.txt

mdjango.views.page_url(page) and mdjango.views.page_markdown_url(page) do the same from a Page object, handling the Index page's empty path.

Sitemap#

sitemap.xml is not a route mdjango.urls mounts. mdjango ships the mdjango.sitemaps.DocsSitemap class; you mount the standard django.contrib.sitemaps view at your site root (not under the docs prefix — a sitemap is a root resource), and Django supplies the domain from the request. See Make your docs discoverable. robots.txt and /.well-known/ are your site root's concern too and mdjango ships neither.

Headers#

Every response carries a strong ETag (MD5 of the body). Cache-Control is public, max-age=<MDJANGO_CACHE_SECONDS> when caching is enabled and no-cache otherwise. A request with a matching If-None-Match receives 304 Not Modified. No Vary, Last-Modified or s-maxage is set. See How caching works.

Errors#

Situation Response
unknown Page path 404
no Pages in the tree, at the mount root 404
any markdown route with MDJANGO_LLM_DOCS = False 404
ContentError or ImproperlyConfigured while reading the tree 500 (uncaught)

Search index#

search-index.json is a JSON array with one object per visible Page, the Index page first, then navigation order:

Field Value
id the Page path ("" for the Index page)
title Page title
section ancestor trail as one string, "Section / Subsection"; empty for root Pages
text the rendered page reduced to plain text: tags stripped, entities unescaped, whitespace collapsed. The full body, not a summary.
url the Page's HTML URL

The client indexes title, section and text with MiniSearch: title boosted ×3, section ×2, prefix and fuzzy (0.2) matching on. Index and library are fetched on the first opening of the palette, not on page load. Open with / or Ctrl/⌘-K; close with Esc. Building the index renders every Page, so it is the most expensive build in the app.