FastAPI & Django Blueprint: Deterministic /llms.txt, /pricing.md & /.well-known/mcp.json
Updated 2026-11-01 · Grounded in 2106 audited B2B SaaS domains (average HTML payload: 244.1 KB) · All guides: /guides/
Python-first AI, RAG, and MCP SaaS teams often serve docs and marketing frontends behind SPA catch-all routes that return HTML soft-404s when OAI-SearchBot, Claude-SearchBot, or PerplexityBot request /llms.txt, /pricing.md, or /.well-known/mcp.json. Expose explicit UTF-8 plain-text and JSON routes before any catch-all mount, and attach RFC 8288 Link discovery headers:
from pathlib import Path
from fastapi import FastAPI, Request
from fastapi.responses import FileResponse, PlainTextResponse
app = FastAPI()
ASO_DIR = Path("static/aso")
LINK_HDR = '</llms.txt>; rel="llms-txt", </pricing.md>; rel="pricing", </.well-known/mcp.json>; rel="mcp"'
@app.middleware("http")
async def add_agent_discovery_headers(request: Request, call_next):
resp = await call_next(request)
resp.headers.setdefault("Link", LINK_HDR)
return resp
@app.get("/llms.txt", response_class=PlainTextResponse)
async def llms_txt() -> str:
return (ASO_DIR / "llms.txt").read_text(encoding="utf-8")
@app.get("/pricing.md", response_class=PlainTextResponse)
async def pricing_md() -> str:
return (ASO_DIR / "pricing.md").read_text(encoding="utf-8")
@app.get("/.well-known/mcp.json")
async def mcp_json() -> FileResponse:
return FileResponse(ASO_DIR / "mcp.json", media_type="application/json")