Skip to main content

I Built My Own Documentation Site Builder — Here's Why (and How)

· 6 min read
Eduardo J. Barrios
AI & Software Engineer · Music Producer (EyeMad)

A few months ago I found myself in a familiar situation: I needed to document a side project quickly. I had used Docusaurus before, loved it in theory, but every time I bootstrapped a new doc site I ended up fighting Node.js toolchains, React component boilerplate, and config files before I had written a single line of actual documentation. MkDocs was simpler, but it felt dated and lacked the AI-native features I increasingly wanted in my workflow.

So I did what every engineer does when the existing tools annoy them enough — I built my own. The result is ncmds (No Code Markdown Documentation Sites), and this post walks through the thinking, the architecture, and the lessons learned.

The Problem with Common Doc Builders

Popular documentation frameworks are fantastic pieces of software, but they all share a common tax:

ToolThe Tax
DocusaurusYou need a working Node.js + npm setup, React knowledge to customize, and a non-trivial config surface.
MkDocsPython-native (great!) but Material theme aside, the defaults feel like 2015 and AI integration is a plugin afterthought.
Read the Docs / SphinxRST-first, heavyweight config, slow build times for anything non-trivial.
NextraElegant but tightly coupled to Next.js — overkill if you just want to ship docs.

The pattern I noticed: none of these are local-first by default. They all assume you want to deploy to a CDN immediately. I wanted something that felt like python main.py — start the server, drop a .md file in a folder, refresh the browser, done.

The Design Philosophy

Before writing a line of code I wrote down three hard constraints:

  1. Zero configuration to get started. Clone, pip install -r requirements.txt, python ncmds.py, open localhost:5000. That's it. No YAML required until you want it.
  2. Markdown first, always. Not MDX, not RST, not AsciiDoc. Plain .md files. Ordering via numeric prefixes (01-intro.md, 02-guide.md) — something any file manager can sort.
  3. AI as a first-class citizen, not a plugin. Every documentation site should be able to answer questions about itself.

How It's Built

The Core: Flask + Python-Markdown

I chose Flask as the server layer for one reason: it's boring in the best possible way. There are no build steps, no compilation, no hot-module-replacement shenanigans. A request comes in, the server reads the .md file, converts it to HTML, injects it into a Jinja2 template, and sends it back. The entire render pipeline is synchronous and predictable.

from ncmds import app, DocumentationSite, config_manager

Python-Markdown handles the conversion, extended with the fenced_code, tables, toc, and attr_list extensions. This gives you syntax-highlighted code blocks, tables, and a navigable table of contents out of the box.

Configuration: YAML When You Need It

The config/config.yaml file is entirely optional. When present it unlocks:

site_name: "My Documentation"
hero:
enabled: true
project_name: "My Project"
tagline: "Your tagline here"

ai_chat:
enabled: true
model: "gpt-4o-mini"

PyYAML reads the config and merges it with sensible defaults, so you only ever specify what you want to override.

The AI Chat Module

This was the feature I was most excited about. Each documentation page carries its rendered content as context. When a user opens the chat sidebar and asks a question, the AI receives both the conversation history and the text of the page they are currently reading.

The integration targets LLM7.io — a free, OpenAI-compatible endpoint — so you get model switching (GPT-4o, Claude, Mistral, and more) without a per-query bill for small projects. The model selector is a live dropdown: you can switch models mid-conversation without reloading the page.

Extended Frontmatter Metadata

One thing that bothered me about most doc builders was that metadata lived in sidebar config files, not inside the documents themselves. ncmds supports a rich YAML frontmatter block:

---
title: "API Authentication"
tags:
- api
- auth
difficulty: "intermediate"
owner: "backend-team"
writer: "alice"
---

These fields render as chips on the page and are indexed by the internal search engine. You can filter by tag:api, difficulty:beginner, or owner:docs-team directly in the search bar — no external search service required.

Export: PDF and Quarto Markdown

A documentation site that can only live in a browser is incomplete. ncmds ships a /export module powered by WeasyPrint that generates professional PDFs with an automatic cover page and table of contents. It also exports to QMD (Quarto Markdown) for teams that use Quarto for scientific or academic publishing.

Cloud Deployment (Optional)

ncmds started as a localhost tool, but adding Vercel support was straightforward because Flask serves WSGI — Vercel's Python runtime handles the rest. A one-click deploy button in the README lets you publish your docs publicly in under two minutes:

Deploy with Vercel

Comparing ncmds to the Alternatives

FeaturencmdsDocusaurusMkDocs
Setup time~30 seconds5–10 min2–5 min
RuntimePython / FlaskNode.js / ReactPython
AI Chat✅ Built-in❌ Plugin needed❌ Plugin needed
Local-first✅ Default⚠️ Dev server only⚠️ Dev server only
PDF Export✅ Built-in❌ Third-party⚠️ Plugin needed
Metadata search✅ Built-in⚠️ Plugin needed
Cloud deploy✅ Vercel✅ Any CDN✅ Any CDN

What I Learned

Boring technology wins. Flask, Jinja2, and vanilla Markdown processing are not exciting. They are also not broken, have no breaking changes between minor versions, and require zero framework knowledge from documentation contributors.

AI context is everything. The page-aware chat only works well because we pass the full rendered page text as system context. Half-hearted AI integrations that just hook up a generic chatbot miss the point entirely.

Defaults are a product decision. Choosing localhost-first, dark theme-first, and numeric prefix ordering-first meant opinionating heavily on the user experience. That's fine — ncmds is not trying to be all things to all teams.

Try It

git clone https://github.com/edujbarrios/ncmds.git
cd ncmds
pip install -r requirements.txt
python ncmds.py
# → open http://localhost:5000

Drop your .md files in docs/, reload, and you have a documentation site. The project is on active development — contributions, issues, and feedback are very welcome.


If you found this interesting, check out my earlier comparison of Nextra vs Docusaurus for a look at the JavaScript side of the documentation landscape.