Every documentation site has two failure modes the team never names out loud.
The first is drift. The README says the field is optional; the OpenAPI spec says it is required. The blog post promises a method that the SDK removed two minor versions ago. The Doxygen output cites a parameter that the C++ refactor renamed last week. Any docs system that maintains a parallel artifact next to the source of truth will eventually lie.
The second is rent. The polish floor moved years ago; what teams keep paying for is the proprietary MDX dialect that holds the polish in place. A hundred pages in a vendor dialect is a hundred migrations the day you want to leave. The contract is buried in the syntax of every page. The longer version of that argument lives in the essay.
The shape underneath
Sourcey takes the specs themselves as the documentation. OpenAPI, MCP server snapshots, Doxygen XML, Go modules, Rust crates, changelogs, MkDocs sites, and rich markdown guides flow into a single normalised representation; the renderer turns that representation into static HTML, the search index, and the llms.txt context files. Nothing runs in the browser at page-load time. The output is plain HTML you can put behind any static host, and the source is plain markdown next to the specs that already live in your repo.
What you publish is what you keep. If you ever want to leave, the markdown is markdown and the spec is the spec.
import { defineConfig } from "sourcey";
export default defineConfig({
name: "Cheese Store",
navigation: {
tabs: [
{ tab: "Documentation", groups: [
{ group: "Getting Started", pages: ["introduction", "quickstart"] },
]},
{ tab: "API Reference", openapi: "./openapi.yaml" },
{ tab: "MCP Server", mcp: "./mcp.json" },
{ tab: "C++ API", doxygen: { xml: "./doxygen/xml", language: "cpp" } },
{ tab: "Go API", godoc: { module: ".", packages: ["./..."] } },
],
},
});
A live build of the demo is at sourcey.com/cheesestore. The whole site builds from one config file and the specs it already references; no proprietary syntax, no admin panel, no hosted service in the loop.
One pipeline, eight sources
OpenAPI 3.x / Swagger 2.0 --> parsed --> dereferenced --> normalised --> HTML
MCP `mcp.json` snapshot -----------------------------> normalised --> HTML
Doxygen XML -----------------------------> normalised --> HTML
Go module (via host) -----------------------------> normalised --> HTML
rustdoc JSON -----------------------------> normalised --> HTML
Changelog markdown -----------------------------> normalised --> HTML
MkDocs site -----------------------------> normalised --> HTML
Markdown guides -----------------------------> normalised --> HTML
Every source format converges on the same internal representation before rendering. That convergence is the load-bearing decision. It is what lets a single theme, a single search index, and a single llms.txt generator cover REST, MCP, C++, Go, Rust, changelogs, MkDocs sites, and prose in the same site. Add a new adapter and everything downstream already works.
Pipelines that parse-and-render in one pass cannot grow new spec types without rewriting the renderer. Sourcey grows by adding normalisers, not by editing templates.
What goes in
| Source | What it produces |
|---|---|
| OpenAPI 3.x / Swagger 2.0 | REST API reference with method pills, schemas, and code samples |
MCP mcp.json | Tools, resources, prompts; annotation badges for read-only / destructive / idempotent |
| Doxygen XML | C++ class, namespace, and function reference via moxygen |
| Go module | Consts, vars, functions, types, fields, methods, examples from *_test.go, via host toolchain |
| rustdoc JSON | Rust crate reference with full type and module hierarchy |
| Changelog | Versioned release timeline with feed output |
| MkDocs site | Migration adapter: read an existing MkDocs config and render natively |
| Markdown + components | Prose guides with Steps, Cards, Accordions, tabbed code groups |
REST code samples generate in ten languages at build time: cURL, JavaScript, TypeScript, Python, Go, Ruby, Java, PHP, Rust, C#. MCP samples render across three transports: JSON-RPC, TypeScript SDK, Python SDK. Every sample is baked into the HTML so no client-side generator runs in the reader’s browser.
What comes out
Static HTML. A search index. llms.txt and llms-full.txt alongside the site. The documentation graph is the artifact; the HTML, the search index, and the llms files are alternate views of the same source. The reader gets a site that loads instantly; the model gets a context file built from the same data.
Theming
Everything visual lives under theme. Three presets cover the common page shapes: default is a sidebar with a right-hand table of contents and sticky code panels; minimal strips the sidebar to a single centred column for landing pages and changelogs; api-first is the persistent-example layout where the code stays visible beside the prose. Colours, fonts, and layout dimensions layer on top of any preset, and theme.css takes an array of stylesheets loaded last for anything the config does not reach.
theme: {
preset: "api-first",
colors: { primary: "#3b82f6", light: "#60a5fa", dark: "#2563eb" },
fonts: { sans: "'Inter', sans-serif", mono: "'Fira Code', monospace" },
layout: { sidebar: "16rem", content: "48rem" },
css: ["./brand.css"],
}
Guides and components
Prose guides are standard markdown with a small set of components for the shapes documentation actually needs:
<Steps>
<Step title="Install">Run `npm install -D sourcey`</Step>
<Step title="Configure">Create `sourcey.config.ts`</Step>
<Step title="Build">Run `sourcey build`</Step>
</Steps>
<CardGroup cols={2}>
<Card title="API Reference" icon="book" href="/api">Full endpoint docs</Card>
<Card title="Guides" icon="map" href="/docs">Step-by-step tutorials</Card>
</CardGroup>
Code samples
Every code sample is generated at build time, not by a script in the reader’s browser. The generator recurses each schema with cycle detection and takes the first usable value it finds: example, then default, then the first enum, then const, then a type-specific fallback. String formats get sensible defaults, so date-time becomes an ISO timestamp and email becomes [email protected]. An endpoint that never declared an example still renders a realistic call.
MCP reference
Point a tab at an mcp.json snapshot from mcp-parser and the server renders as first-class reference in the same design language as the REST tabs. Tools, resources, and prompts each take their own colour; annotation badges surface the read-only, destructive, and idempotent hints at a glance; connection-config cards show exactly how to wire the server into Claude, Cursor, or any MCP client. Samples render in JSON-RPC, the TypeScript SDK, and the Python SDK.
Client-side interactivity
All of it is vanilla JavaScript, six small modules concatenated in dependency order, no framework and no bundler runtime. Search opens on / or Ctrl+K and filters endpoints and models as you type. Dark mode respects the system preference and persists across loads. Language tabs sync, so switching one example to Python switches every example on the page. An IntersectionObserver highlights the current section in the sidebar as you scroll. Code blocks copy in one click, and the sidebar collapses to a drawer on narrow screens.
Go and godoc
Go documentation is extracted natively from source, with no Doxygen detour. Point a godoc tab at a module and Sourcey runs go list, go/parser, and go/doc against the host toolchain to pull consts, vars, functions, types, fields with tags, methods, and examples from *_test.go. Live mode runs the toolchain at build time; snapshot mode reads a committed godoc.json (from sourcey godoc --out godoc.json), so a docs host without Go installed renders the same pages. For Go-only teams, sourcey-godoc ships as a standalone binary on Homebrew, Scoop, and go install, no npm package required.
Owned, not rented
Sourcey is AGPL-3.0. Free to use, free to self-host, free to fork. If you run it as a hosted service, you open-source your stack.
Documentation is infrastructure. It should be owned, not rented.
Install
npm install -D sourcey
sourcey init
sourcey build
The CLI scaffolds a project, runs a Vite SSR dev server with hot reload across config, content, and specs, and produces a static HTML site you can put behind any CDN. Full reference at sourcey.com/docs.
