Skip to main content
Glama
gbrlpzz

zig-docs-mcp

by gbrlpzz
README.md
# zig-docs-mcp

**Local, open-source MCP server + agent skills** that serve **always-fresh
official Zig documentation for the latest release**, a curated
**high-performance lightweight-software guidance corpus**, and a safe,
**dry-run-first auto-update** for an out-of-date local Zig toolchain.

```
zig-docs-mcp
├── zigdocs                 MCP server (stdio, local, no accounts)
├── guidance/               curated performance guidance (12 topics)
├── skills/zig-docs         agent skill: operating rules for Zig work
└── skills/zig-docs-mcp     agent skill: Python integration (`zdoc` singleton)
```

Zig changes fast, and answers from training memory go stale between minor
releases — 0.16 replaced the entire I/O layer and moved `Dir` from `std.fs` to
`std.Io`. This server fetches official docs and released std sources per call
(with short-TTL revalidation), so every answer cites the version it came from.
When your local compiler falls behind the docs, the server says so and offers
a gated upgrade. It never mutates your system without explicit confirmation.

---

## Table of contents

1. [Why](#why)
2. [How it stays fresh](#how-it-stays-fresh)
3. [Requirements](#requirements)
4. [Install the server](#install-the-server)
5. [Connect an MCP client](#connect-an-mcp-client)
6. [Prime Agent integration](#prime-agent-integration)
7. [MCP tool reference](#mcp-tool-reference)
8. [Toolchain auto-update](#toolchain-auto-update)
9. [Guidance corpus](#guidance-corpus)
10. [Configuration](#configuration)
11. [Development](#development)
12. [Troubleshooting](#troubleshooting)
13. [License](#license)

---

## Why

- **Docs rot fast.** Zig's std library layout moves between minor releases.
  Serving the *latest release's* real sources is the only honest source of
  API truth. `zig_std` resolves symbols by walking actual re-exports in the
  released tree — not a scraped snapshot.
- **Performance advice should be mechanical.** The bundled corpus explains
  allocation strategy, data layout, comptime, binary size, startup latency,
  SIMD, concurrency, and benchmarking — grounded in how hardware and the
  runtime actually behave (cache lines, syscalls, page faults), not vibes.
- **A behind compiler quietly invalidates everything.** `zig_version_status`
  compares your toolchain against the upstream index on every check, and
  `zig_update` offers a concrete, reviewable upgrade plan.
- **Everything is local-first.** The server runs on your machine over stdio.
  No accounts, no tokens, no telemetry. Network goes only to ziglang.org for
  docs, release notes, and source tarballs.

## How it stays fresh

- Cached responses **revalidate against ziglang.org** when older than 6 hours
  (`force=true` revalidates immediately). Revalidation uses conditional GETs
  (`ETag` / `Last-Modified`), so it is cheap.
- **Offline-safe**: if the network is down, cached content is served with a
  `stale` flag instead of failing. (The very first run needs network once.)
- Std sources come from the **official per-release `src` tarball** — the
  canonical content even when GitHub release tags lag (0.16.0 was not tagged
  on GitHub when this was built). The tarball is downloaded once per version
  and only `lib/std/**` is extracted.
- The `channel` parameter selects `stable` (latest release, the default) or
  `master` (nightly), so you can preview next-release changes.

Cache layout (`~/.cache/zig-docs-mcp/`, override with `ZIG_DOCS_MCP_CACHE`):

```
~/.cache/zig-docs-mcp/
├── http/                    upstream bodies + ETag/Last-Modified metadata
├── langref-0.16.0.json      parsed reference sections (per version)
├── notes-0.16.0.json        release-notes digest
├── zig-0.16.0-src.tar.xz    source tarball cache
└── src/0.16.0/lib/std/      extracted std sources (550 files)
```

## Requirements

- Python ≥ 3.10 and [uv](https://docs.astral.sh/uv/)
- macOS or Linux (auto-update supports Homebrew and standalone installs;
  Windows gets a working plan printout but no tarball strategy yet)
- Network access to ziglang.org for first fetches and revalidation

## Install the server

```bash
git clone https://github.com/gbrlpzz/zig-docs-mcp
cd zig-docs-mcp
uv tool install .          # installs the `zigdocs` command on your PATH
zigdocs --help             # verify
```

Prefer not to install? Run it straight from the clone:

```bash
uv run --project ~/zig-docs-mcp zigdocs
```

## Connect an MCP client

Any MCP client that speaks stdio. Point it at the `zigdocs` command:

```json
{
  "mcpServers": {
    "zig-docs": {
      "command": "zigdocs"
    }
  }
}
```

Without a global install, use the clone directly:

```json
{
  "mcpServers": {
    "zig-docs": {
      "command": "uv",
      "args": ["run", "--project", "/path/to/zig-docs-mcp", "zigdocs"]
    }
  }
}
```

## Prime Agent integration

Two skills ship in this repo. Symlink them and restart the session (or run
`/reload`):

```bash
ln -sfn ~/zig-docs-mcp/skills/zig-docs     ~/.agents/skills/zig-docs
ln -sfn ~/zig-docs-mcp/skills/zig-docs-mcp ~/.agents/skills/zig-docs-mcp
```

Then, from the agent kernel:

```python
from zig_docs_mcp import zdoc

await zdoc.zig_version_status()                       # local vs latest upstream
await zdoc.zig_update()                               # dry-run upgrade plan
await zdoc.zig_update(dry_run=False, confirm=True)    # apply after user agrees
await zdoc.zig_langref(section="Errors")              # fresh language reference
await zdoc.zig_std(symbol="std.heap.ArenaAllocator")  # std docs from released source
await zdoc.zig_changelog()                            # what changed in the release
await zdoc.perf_guidance(topic="allocation-strategy") # curated guidance
await zdoc.zig_search(query="vectorization")          # search everything at once
```

Calls return their result as a JSON string (full-topic guidance reads return
raw markdown); parse with `json.loads(...)` when you need fields like
`version` or `docs`. Arguments are keyword-only. The server command is
resolved in order: `ZIG_DOCS_MCP_CMD`, a `zigdocs` on PATH, then
`uv run --project` against `ZIG_DOCS_MCP_REPO` (default `~/zig-docs-mcp`).

`skills/zig-docs/SKILL.md` contains the operating rules the agent follows:
version-gate first, docs before code, cite the doc version, and never apply
an update without the user's explicit go-ahead.

## MCP tool reference

### `zig_version_status`

Compares the local `zig version` with the latest upstream release.

```json
{
 "local_version": "0.16.0",
 "local_path": "/opt/homebrew/bin/zig",
 "latest_stable": "0.16.0",
 "master": "0.17.0-dev.1818+7051f8e73",
 "up_to_date": true
}
```

When the local toolchain is older, the response adds `behind` and a
`suggestion` pointing at `zig_update` (illustrative example):

```json
{
 "local_version": "0.15.2",
 "latest_stable": "0.16.0",
 "up_to_date": false,
 "behind": "local 0.15.2 < latest 0.16.0",
 "suggestion": "Call the zig_update tool (dry-run first) to upgrade the local toolchain to the latest stable release."
}
```

### `zig_update`

Upgrades the local toolchain. **Dry-run is the default** — it prints the exact
plan and changes nothing. Applying requires `dry_run=false, confirm=true`.
See [Toolchain auto-update](#toolchain-auto-update).

### `zig_langref`

Official Language Reference, fetched fresh for the channel version.

- `section="Errors"` → full section text (code blocks preserved):

```
### Error Set Type
An error set is like an enum. However, each error name across the entire
compilation gets assigned an unsigned integer greater than 0. ...
```

- `query="vector"` → ranked section hits:

```json
[{"section_id": "Vectors", "title": "Vectors§"},
 {"section_id": "Builtin-Functions", "title": "Builtin Functions§"}]
```

- no arguments → the list of all section ids.

### `zig_std`

Standard-library docs from the **exact released source**. Symbol resolution
walks real re-exports (`std.zig` → `heap.zig` → `heap/ArenaAllocator.zig`),
follows `@import` aliases, and returns the `///` docs plus the declaration
text from that release:

```json
{
 "symbol": "std.ArrayList",
 "version_source": "0.16.0",
 "file": "lib/std/std.zig",
 "line": 49,
 "declaration": "pub fn ArrayList(comptime T: type) type {\n    return array_list.Aligned(T, null);\n}",
 "docs": "A contiguous, growable list of items in memory. This is a wrapper around a\nslice of `T` values. ..."
}
```

If a name is not a plain top-level declaration in the walked namespace
(layouts move between releases), the tool falls back to a corpus-wide search
of top-level declarations, best match first — e.g. `std.fs.Dir` on 0.16
correctly surfaces `lib/std/Io/Dir.zig`. `query="arena"` searches std doc
comments directly.

### `zig_changelog`

Release-notes digest for the current channel version: section titles plus a
short summary each. Useful right after a release lands
(`zig_changelog(force=true)`).

### `perf_guidance`

Curated guidance for high-performance lightweight software. No arguments
lists topics; `topic="allocation-strategy"` returns the full guide (raw
markdown with Principle / Mechanics / Zig idiom / Anti-patterns / Rules of
thumb); `query=...` searches across all guides.

### `zig_search`

Unified search across langref, std doc comments, and guidance:

```json
{"query": "vectorization", "langref": [...], "guidance": [...], "std": [...], "std_version": "0.16.0"}
```

`scope` narrows it: `all` (default) | `langref` | `std` | `guidance`.

## Toolchain auto-update

`zig_update` picks a strategy automatically:

1. **Homebrew-managed zig** (binary resolves inside the brew prefix) →
   `brew upgrade zig`:

```json
{
 "mode": "dry-run (nothing changed). Re-run with confirm=true to apply.",
 "target_version": "0.16.0",
 "current": "0.16.0",
 "strategy": "homebrew",
 "command": ["brew", "upgrade", "zig"],
 "note": "Homebrew formula may lag the newest release slightly."
}
```

2. **Standalone install** (official tarball, any other location) → downloads
   the platform tarball from the upstream index, extracts to
   `~/.local/opt/zig-<version>`, and shims `~/.local/bin/zig`:

```json
{
 "strategy": "standalone-tarball",
 "download": "https://ziglang.org/download/0.16.0/zig-aarch64-macos-0.16.0.tar.xz",
 "install_dir": "~/.local/opt/zig-0.16.0",
 "steps": ["download ...", "extract ...", "symlink ~/.local/bin/zig -> .../zig/zig"],
 "activation": "~/.local/bin is first on PATH; new zig takes effect immediately"
}
```

If `~/.local/bin` is **not** first on PATH, the plan says so explicitly — the
old compiler would still win, and the tool tells you how to fix the order.

Safety rules:

- Default is a dry-run. Nothing is downloaded, moved, or linked.
- Applying requires `dry_run=false, confirm=true` together.
- Agents using this server are instructed to show the plan and get the
  user's explicit go-ahead before confirming.

## Guidance corpus

Twelve topics in `guidance/`, shipped inside the wheel and served by
`perf_guidance`. Principles are universal; snippets are Zig 0.16-era; exact
API truth always comes from `zig_std`, never from the corpus.

| Topic | One-line summary |
| --- | --- |
| `allocation-strategy` | Match allocator to lifetime; arena bump-pointer cost vs general allocator bookkeeping; hidden allocations. |
| `data-oriented-design` | SoA vs AoS byte math on 64-byte cache lines; hot/cold splitting; `MultiArrayList`. |
| `comptime-over-runtime` | Comptime results become rodata/immediates; runtime tables cost dirty pages. |
| `zero-copy-parsing` | Slices are 16 bytes; allocate-per-token costs an allocation, a memcpy, and cache lines per token. |
| `binary-size` | Size = reachability; strip, panic modes, dep hygiene; smaller text = fewer startup page faults. |
| `startup-latency` | No init_array, lazy text page faults, lazy init, no work before argv. |
| `memory-layout` | Padding math, field ordering, packed structs, `@sizeOf` comptime asserts. |
| `simd-and-vectorization` | Auto-vectorization blockers, lane-wise accumulate + single reduce, `@select` vs branches. |
| `concurrency-and-io` | MESI cost of shared writes, futex parking, syscall batching, false-sharing padding. |
| `error-handling-cost` | Errors are u16 values; `try` is a predicted branch; no unwinding. |
| `benchmarking-methodology` | Release builds, warmup, min/median over mean, sink to defeat DCE, counters. |
| `dependency-lightweightness` | Std-first; deps add linked code and build fragility; vendor tiny utilities. |

## Configuration

| Variable | Meaning | Default |
| --- | --- | --- |
| `ZIG_DOCS_MCP_CACHE` | cache directory | `~/.cache/zig-docs-mcp` |
| `ZIG_DOCS_MCP_CMD` | full server command line (skill override) | — |
| `ZIG_DOCS_MCP_REPO` | repo dir for the `uv run` fallback | `~/zig-docs-mcp` |

## Development

```bash
make sync    # deps
make test    # unit tests (offline; std-source tests skip without warm cache)
make e2e     # spawns the real server over stdio, calls every tool
make fmt     # ruff format + check
```

The e2e suite needs network on first run (it warms the cache). Unit tests
that exercise symbol resolution run against the warm std-source cache and are
skipped cleanly when it is absent.

## Troubleshooting

- **`zigdocs server not found`** (Prime Agent skill): install with
  `uv tool install .` from the clone, or set `ZIG_DOCS_MCP_REPO` to the clone
  path, or set `ZIG_DOCS_MCP_CMD` to a full command line.
- **First run fails while offline**: the cache starts empty; fetch once while
  online. After that, stale-cache fallback keeps every tool working.
- **Results look stale after a new release**: pass `force=true` (the 6-hour
  TTL otherwise applies).
- **`zig version` still old after an update**: a new shell is needed, and
  `~/.local/bin` must precede the previous install directory on PATH. The
  dry-run plan states the exact situation for your machine.
- **Homebrew zig lags the newest release**: brew formulas trail releases; use
  the standalone strategy (remove the brew formula, install standalone) if
  you need day-one versions.

## License

MIT — see [LICENSE](LICENSE).

TDQS

A3.7/5.0

Scored across 7 tools

Disambiguation5/5

Each tool targets a distinct purpose: version checking, updating, language reference, standard library docs, changelog, performance guidance, and a unified search that complements the others. No two tools appear to do the same thing, and descriptions clarify boundaries.

Naming Consistency4/5

Most tools follow a 'zig_' prefix, but the second element varies in style (noun, verb_noun, abbreviation). 'perf_guidance' breaks the prefix pattern. Still, names are short, descriptive, and predictable enough for an agent to infer purpose.

Tool Count5/5

Seven tools is well-scoped for a documentation and toolchain server. Each tool earns its place, covering version checks, updates, references, and search, without bloat or missing essentials.

Completeness5/5

The surface covers the core documentation lifecycle: version status, update, language reference, std docs, changelog, performance guidance, and a cross-cutting search. There are no obvious gaps for the stated purpose of providing Zig documentation and version management.

Maintenance

ActivityMaintained
ResponsivenessNo issues