Features
gomddoc is a single Go binary that serves a folder of Markdown as a documentation website for people and as structured, queryable content for AI agents. This page lists what it does, grouped by area. The configuration reference covers how to change each behaviour.
Two audiences, one folder
Every page you write reaches two kinds of reader from the same source files:
- People get a themed website with navigation, full-text search, a table of contents, dark mode and SEO tags.
- AI agents get an MCP server with search, page and section reads, tag queries and the navigation tree, plus the raw Markdown of any page at its normal URL.
Both views come from the same indexes, built from the same files, so an agent sees exactly the pages, titles and search results a person does.
MCP server
gomddoc has a built-in Model Context Protocol server. Any MCP client, including Claude Desktop, Claude Code and Cursor, can query your docs directly.
It runs over two transports:
- stdio:
gomddoc mcp ./docs. The client launches gomddoc as a subprocess. - Streamable HTTP: every
serveandpreviewinstance answers at/_mcp/, behind the same Basic Auth as the rest of the site.
{
"mcpServers": {
"docs": {
"command": "gomddoc",
"args": ["mcp", "/absolute/path/to/docs"]
}
}
}
Tools
Six tools, all annotated read-only and idempotent so clients can approve them automatically:
| Tool | What it returns |
|---|---|
search_docs |
Ranked results with snippets, using the same engine as the website |
read_page |
A page as Markdown, with its title, description and tags on top |
read_section |
One section of a page, selected by heading anchor |
list_pages |
Every page with title, path, description and tags, or one tag's pages |
get_table_of_contents |
The site's navigation tree |
find_related |
Pages that share tags with a given page |
read_section lets an agent pull the one part of a long reference page it needs instead of the whole file.
Resources and prompts
Four resources: docs://site/index, docs://site/tags, docs://site/page/{+path} and docs://site/tag/{tag}.
Three prompts: explain_concept, troubleshoot and summarize_page. Each gathers the relevant pages first, then
asks the model to answer from them.
gomddoc's own manual, over stdio
gomddoc mcp also describes gomddoc itself, so the agent editing your site can configure it correctly:
gomddoc_guidelists, searches and reads the user guide that ships in the binary.gomddoc_capabilitiesreturns every setting with its config key, environment variable, flag and default.gomddoc_doctorchecks the site and reports problems with fixes, reloading from disk on every call.- Resources
gomddoc://capabilities,gomddoc://schema/configandgomddoc://guide/{+path}, and thelearn_gomddocprompt.
The HTTP endpoint at /_mcp/ leaves these out, so your readers' agents see your docs and nothing else.
Content negotiation
Every page URL serves HTML to browsers and Markdown to anything that asks for it:
curl -H "Accept: text/markdown" http://localhost:8080/guide
The response is the page's Markdown body under a generated frontmatter block that holds the page's metadata, the
pages that share its tags, and its previous and next pages in the navigation. An Accept header gomddoc can't satisfy
gets a 406 listing the types it can produce. Every negotiated response carries Vary: Accept, so caches keep the
two versions apart.
Writing
Markdown
Rendering uses goldmark with GitHub Flavored Markdown: tables, task lists, strikethrough and autolinks. Raw HTML passes through unchanged, and YAML frontmatter is stripped from the output.
Syntax highlighting
Code blocks are highlighted on the server by Chroma, so readers download coloured HTML rather than a highlighting script. Choose any of Chroma's 70-plus styles:
highlighting:
theme: dracula
Admonitions
GitHub's callout syntax works as written, with five types: NOTE, TIP, IMPORTANT, WARNING and CAUTION.
> [!WARNING]
> Rotate the key before you deploy.
Math and diagrams
KaTeX renders $inline$ and $$display$$ math, and fenced mermaid blocks render as diagrams that follow the
page's light or dark mode. Both run in the browser and load their libraries from the jsDelivr CDN.
Heading anchors and table of contents
Every heading gets a stable ID and a link that appears on hover. The table of contents is built from the page's headings and highlights the section you're reading as you scroll.
Color chips
A hex code in backticks, such as `#2563eb`, renders with a swatch beside it. Clicking it copies the value.
Review annotations
Review a rendered page the way you would mark up a printout. Select a passage, click the pen that appears in the margin and type a note. The passage stays highlighted, and notes on the same line share one margin icon.
The Annotations button in the header gathers every note on the page as editable Markdown, ready to copy into Claude Code, Codex or any other LLM harness. The export names the page's source file relative to the repository root and quotes each passage in bold with the words around it, under its section heading, so the agent knows which file to edit and where:
# Review notes: Deploying to Kubernetes
Source file: `docs/guides/deploy.md`
Page: https://docs.example.com/guides/deploy
Each note quotes the passage it refers to, with the words just before and after it to disambiguate.
## Rollout › Health checks
> …the probe waits **thirty seconds** before the first check…
Say why thirty seconds, and how to change it.
Notes stay in the browser's local storage, one set per page URL, and never reach the server. When the source changes,
a note whose passage no longer matches exactly once is dropped and the page says how many were removed. Turn the
feature off with annotations: false under theme.features, site-wide or in a page's frontmatter.
Feature toggles
Twelve theme features can be switched off for the whole site or for a single page: search, toc, dark_mode,
code_copy, katex, mermaid, admonitions, heading_anchors, color_chips, tag_chips, see_also and
annotations. All default to on.
Navigation and discovery
Sidebar and breadcrumbs
The sidebar mirrors your directory tree, with collapsible folders and the current page highlighted. Breadcrumbs,
previous/next links and the matching rel="prev" / rel="next" tags come from the same tree. There's no sidebar
file to maintain.
Tags
Frontmatter tags turn into several discovery features at once:
- Tag chips at the top of the page, each linking to that tag's page.
- A page per tag at
/tags/{tag}and an index of all tags at/tags/. - A "See also" list of up to ten pages that share a tag with the current one.
- A
tag:filter in search, and JSON at/api/tagsand/api/tags/{tag}.
Edit links and directory listings
Set edit_url and every page links to its source file in your repository. Set dir_index: true and a directory
without a README.md gets a generated listing instead of redirecting to its first page.
Search
Full-text search is built in and needs no external service. gomddoc indexes every page at startup and holds the index in memory.
- Ctrl+K (Cmd+K on macOS) opens the search dialog on any page. Results update as you type, and the arrow keys and Enter navigate them.
- Results are ranked with TF-IDF. A match in the title counts double that of one in the description, and both outrank matches in the body.
- All terms must match. Text in fenced code blocks isn't indexed; inline code is.
tag:narrows a query to tagged pages, and can be combined with other terms or tags:
tag:kubernetes every page tagged kubernetes
tag:kubernetes tag:aws pages carrying both tags
tag:kubernetes rollback "rollback", ranked, within that tag
The same query language works in the dialog, at /api/search and in the MCP search_docs tool:
curl 'http://localhost:8080/api/search?q=deployment&limit=5'
Search runs in serve, preview and mcp. A static build has no search backend.
URLs and redirects
Clean URLs
guide.md is served at /guide, and a request for /guide.md gets a 301 to the clean URL. Images, stylesheets and
other files keep their extensions. strip_extensions controls which extensions are removed, and an empty list turns
the behaviour off.
Redirects
When you move a page, list its old paths in frontmatter:
redirect_from:
- /old/install
- /setup
serve answers those paths with a 301. build writes a redirect page at each one.
Git sources
Point any command at a Git URL instead of a directory:
gomddoc serve "git+https://github.com/your-org/monorepo.git#main:docs"
- The
#branch:subdirfragment picks a branch and a subdirectory. Both are optional. - gomddoc makes a shallow, single-branch clone at startup and serves that commit until it restarts.
- The clone lives in memory by default.
--git-storage-dirputs it on disk for large repositories. - Private repositories use
git+ssh://with--git-key-file. The host must be inknown_hosts; there is no trust-on-first-use. HTTPS token authentication isn't supported. - A
.gomddoc/config.ymlinside the repository is ignored. Configure Git-sourced sites with environment variables.
Internationalization
A language is a directory named with a BCP 47 tag that includes a region or script, such as fr-FR, zh-Hans or
es-419:
docs/
README.md → /
guide.md → /guide
fr-FR/
README.md → /fr-FR/
guide.md → /fr-FR/guide
A bare fr/ or en/ isn't treated as a language, so ordinary folders such as api/ or doc/ stay content. Each
language gets its own navigation, search index, tag pages, sitemap, feed and 404 page. hreflang tags and a language
switcher appear once a second language exists.
The interface strings ship in English. To translate them, add .gomddoc/locales/<lang>.yml with the keys you want to
change; the rest fall back to English.
SEO
Set meta.domain (or pass --domain) and gomddoc adds:
- A canonical URL on every page, with
README.mdfolded into its directory. /sitemap.xmland an Atom feed at/feed.xml, per language on multilingual sites.- A
Sitemap:line in/robots.txt, which is always served and blocks/_assets/,/api/and/debug/. - JSON-LD:
TechArticleon every page,BreadcrumbListon nested pages, andWebSitewith aSearchActionon the home page.
Open Graph and Twitter Card tags are emitted with or without a domain, using the page's title and description.
Frontmatter robots: noindex sets the meta tag and drops the page from the sitemap and feed.
Themes
gomddoc has eight themes. default is built into the binary; the other seven live in
gomddoc-themes and are installed by copying one into
.gomddoc/assets/themes/<name>/.
| Theme | Style |
|---|---|
default |
Three columns (navigation, content, TOC), Inter font |
academic |
Merriweather serif, warm parchment palette |
gitbook |
Book-style reading, tinted navigation sidebar |
material |
Material Design 3, rounded corners |
midnight |
Dark-first, neon purple and cyan gradients |
minimal |
System fonts, no border radius |
nord |
Nord palette, frosted glass |
ocean |
Teal and navy gradients |
Every theme includes light and dark modes, the TOC, breadcrumbs, admonitions, copy buttons, KaTeX and Mermaid. A theme
name that isn't installed falls back to default with a warning.
Customizing
- Colours without code:
theme.varssets CSS custom properties. The default theme readsprimary,bg,textand theirdark-variants. - One partial: put a file such as
footer.html.tmplin.gomddoc/partials/and it replaces that partial in every theme. - A full theme: write
layouts/default.html.tmplas a Go template. You inherit the default theme's partials and override only the ones you need. Extra layouts are selected per page withlayout: <name>.
Static files in .gomddoc/static/ are served under /_assets/.
Production serving
serve is built to run in production, behind the reverse proxy that terminates TLS.
- Health checks:
/health/liveand/health/readyreturn JSON, skip authentication, and stay on the main port so load balancer probes keep working. - Prometheus metrics:
/metricsexportshttp_requests_totalandhttp_request_duration_seconds, plus the standard Go and process collectors. - Admin port:
--admin-port :9090moves metrics and pprof to a separate listener, bound to loopback when you give a bare port. pprof stays off unless you pass--pprof. - Compression: gzip for responses of 1 KB or more, skipping images, video, audio and archives. SVG is compressed.
- Caching:
ETagandCache-Controlon pages, files, tag pages, the sitemap, the feed androbots.txt, with304 Not Modifiedon a match. Theme assets are cached for a year. - Request IDs: every response carries an
X-Request-ID. A valid inbound ID from your proxy is kept. - Graceful shutdown:
SIGINTandSIGTERMlet in-flight requests finish within a configurable grace period. - Timeouts: read-header, write and idle timeouts and a header size limit, all adjustable by environment variable.
Security
- File access is confined to the content root. Dotfiles and directories such as
.git/and.envreturn 404, with one exception for.well-known/. excludepatterns hide files from every route, the navigation, search, MCP andbuildoutput. Hidden and excluded paths get the same 404 as a missing file, so a response never confirms that a file exists.- Content routes accept only GET and HEAD.
- Every response carries
X-Content-Type-Options,X-Frame-Options,Referrer-PolicyandPermissions-Policy. --basic-auth-filereads an htpasswd file. Only bcrypt hashes are accepted, and an unknown username costs the same time as a wrong password.- SSH host keys are checked against
known_hosts, with no trust-on-first-use.
gomddoc sets no Content-Security-Policy. Set one at the proxy, where your real origins are known.
Static builds
gomddoc build renders the whole site to files through the same pipeline as serve:
gomddoc build ./docs -o ./public -d docs.example.com
The output has a directory per page (guide.md becomes guide/index.html), tag pages, 404.html, robots.txt,
redirect pages for redirect_from, and sitemap.xml and feed.xml when a domain is set. It deploys to GitHub Pages,
Netlify, Cloudflare Pages, S3 or any file server without rewrite rules.
A static host serves files and nothing else, so a build has no search backend, no content negotiation, no MCP
endpoint and no metrics. Use serve when you need those.
Tooling
| Command | What it does |
|---|---|
gomddoc preview --open |
Dev mode on a free port from 8080, opened in your browser |
gomddoc init |
Writes .gomddoc/config.yml with the defaults spelled out |
gomddoc doctor --strict |
Reports every config and content problem with a fix; fails CI on warnings |
gomddoc info |
Lists every setting with its config key, env var, flag and default |
gomddoc schema |
Prints the JSON Schema for config.yml, for editor validation |
gomddoc help <topic> |
Reads or searches the user guide built into the binary |
doctor --json and info --json produce the same documents the MCP tools return, so scripts and agents read the
same data.
Distribution
gomddoc ships as a single static binary for Linux and macOS on amd64 and arm64, through an install script, Homebrew,
go install and GitHub Releases with cosign-signed checksums. The Docker image is distroless, multi-arch and runs as
a non-root user:
docker run --rm -p 8080:8080 -v "$PWD:/site" ghcr.io/monolithiclab/gomddoc serve /site
See installation options for details, and licensing for commercial use.