- Go 95.5%
- Dockerfile 4.5%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .forgejo/workflows | ||
| internal | ||
| .gitignore | ||
| alt-man-mcp.container | ||
| alt-man-mcp.network | ||
| Containerfile | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| main.go | ||
| README.md | ||
alt-man-mcp
A read-only Go MCP server for live access to man pages served by any
manpages site in the manpages.altlinux.team
layout — a per-branch names.json index plus per-page HTML. It exposes three
tools (get_manpage, search_manpages, list_sections) over stdio or
streamable-http and never writes anything back.
The source site is configurable with --man-base-url (default
https://manpages.altlinux.team): point it at any compatible mirror or
site that serves the same names.json + HTML format.
This is not RAG: no embeddings are built and no vector index is used. The server resolves a page against a lightweight in-memory name index and does a targeted live-fetch of a single HTML page, extracting readable text from it.
Data source
1. Name index — one JSON object per branch:
https://manpages.altlinux.team/<branch>/names.json (~6 MB)
Format: {"<name>": [{"s":"<section>","u":"<path>","d":"<short description>"}], ...}.
The key is the lowercased page name; one name may yield several records
(different packages/sections/languages). Example: "ls":[{"s":"1","u":"c/coreutils/ls.1.html","d":"list directory contents"}, ...].
From the path u are derived: the package (second-to-last segment,
c/coreutils/ls.1.html → coreutils) and the language (a .ru.html suffix
→ Russian page, otherwise English; there is no separate names.ru.json).
The index is loaded once per branch and cached in memory (TTL 1 hour, concurrent loads deduplicated via singleflight). Search and name resolution filter this in-memory map, without large per-request downloads.
2. Page content — HTML by the path from the index:
https://manpages.altlinux.team/<branch>/<u> (~16 KB)
The man-page text is extracted from the HTML (golang.org/x/net/html): the main
container is div.manual-text (mandoc output); sections h1.Sh → ## …,
subsections h2.Ss → ### …, paragraphs p.Pp, option lists dl.Bl-tag
(dt — the option, dd — the indented explanation), literal pre blocks in
triple backticks. The site header/nav/footer live outside div.manual-text and
are discarded. Pages are cached by URL with a short TTL (5 min).
3. Section manifest (small) for list_sections:
https://manpages.altlinux.team/<branch>/exports/index.json
The heavy ndjson.zst exports (tens of MB per request) are no longer used — the
only large download is reduced to a single names.json per branch.
Tools
| Tool | Purpose |
|---|---|
get_manpage(name, section?, package?, branch?, lang?) |
Full readable text of a single man page by name (extracted from HTML). section and package disambiguate. If the exact name is not found — similar names are returned. |
search_manpages(query, branch?, section?, limit?) |
Search pages by a name/description substring in the name index (in memory, fast). Ranking: exact name → prefix → name substring → description only. |
list_sections(branch?) |
Which sections and languages are available for a branch and how many pages each holds (from index.json). |
Defaults: branch=sisyphus, lang=en (for get_manpage — falls back to en
if there is no ru translation), limit=20.
Build and run
go build ./...
go test ./... -count=1
# stdio (for a local agent / debugging)
./alt-man-mcp --transport stdio
# streamable-http (endpoint /mcp, health /healthz)
./alt-man-mcp --transport http --addr :8092
Flags:
--transport stdio|http(defaultstdio)--addr(default:8092)--man-base-url— base URL of the manpages site (defaulthttps://manpages.altlinux.team). Set it to any compatible mirror or site that serves the samenames.json+ per-page HTML format.
Usage with MCP clients
The server speaks MCP over stdio; the binary is alt-man-mcp. Point
--man-base-url at the manpages site you want (omit it to use the default).
Claude — .mcp.json:
{
"mcpServers": {
"man": {
"command": "alt-man-mcp",
"args": ["--transport=stdio", "--man-base-url=https://manpages.altlinux.team"]
}
}
}
Codex — ~/.codex/config.toml:
[mcp_servers.man]
command = "alt-man-mcp"
args = ["--transport=stdio"]
opencode — opencode.json:
{
"mcp": {
"man": {
"type": "local",
"command": ["alt-man-mcp", "--transport=stdio"],
"enabled": true
}
}
}
Container / quadlet
podman build -t alt-man-mcp .
Quadlet units: alt-man-mcp.container + alt-man-mcp.network
(Options=mtu=1500 is required for rootless podman). The default port is :8092
(rdb=8090, bugzilla=8091).