# Show HN: An MCP Server for Manpages

> Source: <https://www.chainguard.dev/unchained/every-manual-page-in-chainguard-os-now-readable-by-your-agent>
> Published: 2026-11-03 21:24:51+00:00

# It’s a man’s, man’s, man’s world: Every manual page in Chainguard OS, now readable by your agent

- Dustin Kirkland SVP, Engineering Chainguard [View all articles](https://www.chainguard.dev/unchained/author/dustin-kirkland)

Dustin Kirkland SVP, Engineering

## **20 years of manual pages on the web**

In 2008, I was a brand-new engineer on the Ubuntu Server Team at Canonical, and my starter project was the Ubuntu Manpage Repository: a website that rendered every manual page in all of Ubuntu as static, searchable HTML. It is still live today, at [__manpages.ubuntu.com__](https://manpages.ubuntu.com).

That site was built for the era of search engine optimization. The reader we had in mind was a developer typing a question into a search box, and the goal was for the answer to be an Ubuntu manual page. It worked. It had a large, measurable effect on how accessible, usable, and developer-friendly Ubuntu felt — because the documentation for whatever you were running was one search away, instead of buried in a package you hadn’t installed.

So, I have been thinking about manual pages on the web for almost 20 years. And the reader has changed. Today, a human with a search box is the least likely thing to be looking for an answer in documentation. Rather, it’s an agent, in your editor or your terminal, working on your behalf, and it needs what that site gave people, in a form a machine can use.

James Brown told us it’s a man’s, man’s, man’s world. Twenty years on, it still is. Only now, the readers are agents.

That’s why we built [__man.cgr.dev__](https://man.cgr.dev). It has every manual page in [__Chainguard OS__](https://edu.chainguard.dev/chainguard/chainguard-os/overview/) rendered as HTML, as you’d expect. It also has the original roff source and a Markdown version of every page. More importantly, it has an MCP server, so the same documentation is machine-readable and searchable from within your own AI development environment and by your agents.

## **Every manual page in Chainguard OS**

[**__man.cgr.dev__**](https://man.cgr.dev) publishes **every manual page in Chainguard OS** — 59,419 documents from 1,286 packages across all three of our package repositories (and counting), rebuilt every night from the signed apks that actually make up the distribution.

The short form, `man.cgr.dev/man/bash.1`, takes you to the current page. Every page also has a permanent address, and from there you can access it in four ways:

| HTML | `man.cgr.dev/os/bash/5.3-r14/man1/bash.1` | 
| Markdown | Append `.md` | 
| Original roff | Append `.raw` | 
| JSON manifest — package, version, content digests | Append `.json` | 

Put `latest` in place of the version to follow the current release, or leave the version pinned and get the same answer next year.

Cross-references work. `git-commit(1)` mentions `git-add(1)`, and that mention is a link you can click. Links are resolved against the corpus itself rather than guessed when the page is built, and 91% of references land on a real page. A manual page that cites 12 others and links to none of them is a dead end, and the web solved that problem in 1993.

## **Manual pages, built for agents**

Here’s what makes this different from manpages.ubuntu.com, and from Debian’s excellent equivalent: **it is built for agents first.**

There is an MCP server at  with five tools:

```
search_docs find pages across the corpus
get_man_page fetch one, at an exact version
resolve_xref resolve a cross-reference the way a human would
list_package_docs everything a given package documents
get_package_info which versions of a package have docs
```

Point Claude, or anything else that speaks MCP, at it, and the model stops guessing about flags. Ask any assistant: “What does `--exclude` do for this tool?”, and you get an answer drawn from training data that may be two years stale, for a version you are not running, with no signal about which. Worse, assistants increasingly operate inside minimal container images. Those are exactly the environments where the documentation has been deliberately left out, because manual pages live in `-doc` subpackages that nobody installs into production.

So the agent is working precisely where the manual isn’t. `man.cgr.dev` puts the accurate answer, **for the exact version the image ships**, one query away.

## **Version-exactness is the whole point**

Most documentation on the internet describes *a* version of the software. Ours describes *the* version, because every document is extracted from a specific signed apk, and the package and version are recorded on every page and in every JSON manifest.

Documents enter the corpus one way only: as an apk, checked against a signed `APKINDEX` before anything parses it. There is no upload API, no scraper, no hand-curated content. If a page is on the site, it came out of a package we built and signed.

Exactness has to hold in both directions. Adding a page when a package gains one is easy. The harder case is when a package *stops* shipping a page. A build with no manual page has nothing to ingest, so a naive pipeline never notices and keeps serving the old page as current. Ours notices. When a build drops a page, `latest` stops serving it, and the older version stays readable at its pinned address — because some of you are still running it.

## **Complete, and only what we may publish**

“Every manual page” is a promise about packaging as much as about a website. Manual pages belong in a `-doc` subpackage. When a package ships them somewhere else, they are either missing from the site or duplicated across it. So we went through the distribution and fixed the packaging: more than 150 recipes now split their documentation properly. That includes `clang(1)` for every LLVM stream from 14 through 23, and the Rust toolchain. We also removed copies of man pages that had been vendored into unrelated packages.

Then we made sure it stays fixed. A lint now runs on every change to the package definitions, so a package that has been split correctly cannot quietly regress. We also fixed the man-page detection in [__melange__](https://github.com/chainguard-dev/melange), our package build tool, so the build itself can flag misplaced pages.

Publishing everything also means knowing what we are allowed to publish. Every document records its license, and we read all of them. There is **no GFDL anywhere in the corpus** — the classic manual-page redistribution trap. The handful of pages under proprietary or source-available licenses (BUSL, SSPL, Elastic, and vendors’ proprietary terms) are withheld, and that rule matches by license, not by package. A rule written by package class would have hidden roughly 450 openly licensed Azul Zulu OpenJDK pages to protect two proprietary ones.

## **We are letting the crawlers in. On purpose.**

`robots.txt`, sitemaps, `llms.txt`, canonical URLs, JSON-LD: all present, all deliberately permissive to AI crawlers.

That is roughly the opposite of where the rest of the web is heading, and it is a considered position, not an oversight. What is public, well-structured, and widely linked today is what models are trained on tomorrow. If we want assistants to give good answers about Chainguard OS in two years, now is the time to publish authoritative, machine-readable documentation.

We also made every page public and unauthenticated, including the manual pages for our enterprise packages. You do not need a Chainguard account to read how our software works. That was a deliberate call, and an easy one. Documentation you have to log in to read is documentation no model will ever learn from, and no engineer will ever find at 2 a.m.

## **How it is built, briefly**

 renders the HTML. The Markdown is derived from that HTML rather than generated separately, so the two cannot drift apart. The orchestration is written in Go. `manpages.debian.org` has used this same architecture since 2017, for the same reasons. We evaluated the cost of writing our own roff renderer and rejected it, mostly because 42.7% of the corpus defines its own macros, so a fixed macro table would not be enough. You need a roff interpreter, and a good one already exists.

The whole corpus renders in about 20 seconds on one core. The slow part is downloading packages, not parsing them. On the serving side, a cold instance answers its first request in about 0.2 seconds, because the nightly crawl ships a prebuilt snapshot of the catalog rather than making the first visitor wait while it is built.

## **Try it**

Trying out our agent-ready manual pages is easy:

- **Humans:** Visit[__man.cgr.dev__](https://man.cgr.dev)__.__
- **Agents:** Point your MCP client at`https://man.cgr.dev/mcp`
- Every page is also `.md` ,`.raw` , and`.json`

Share this article
