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. Every manually duplicated contract gives the documentation another way to drift.
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.
Stop maintaining the same interface twice
The endpoint is already in OpenAPI. The C++ signature is in the header. The MCP tool advertises its input schema. Retyping those contracts into a documentation page creates another edit that has to land whenever the code changes.
Sourcey reads those inputs and builds the reference around them. Guides can sit beside the generated pages in the same navigation and search. A change to the spec can travel through the same pull request and build as the prose explaining it.
The author still has to explain why an endpoint exists, which example is useful and what goes wrong in production. Those are the parts worth writing.
One build, from source to site
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. Reference and guide content renders at build time; browser scripts provide search and other interactions. 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. The specs and Markdown remain in your repository; Sourcey-specific components may need adaptation if you move them to another renderer.
import { defineConfig, markdown, openapi, mcp, doxygen, godoc } from "sourcey";
export default defineConfig({
name: "Cheese Store",
navigation: {
tabs: [
{ tab: "Documentation", source: markdown({ groups: [
{ group: "Getting Started", pages: ["introduction", "quickstart"] },
]}) },
{ tab: "API Reference", source: openapi("./openapi.yaml") },
{ tab: "MCP Server", source: mcp("./mcp.json") },
{ tab: "C++ API", source: doxygen({ xml: "./doxygen/xml", language: "cpp" }) },
{ tab: "Go API", source: godoc({ module: ".", packages: ["./..."] }) },
],
},
});
A live build of the demo is at sourcey.com/cheesestore. The whole site builds from one config file and the sources it references. The build can run in your own CI and the output can go to your existing static host.
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
The adapters do the format-specific work before the site is rendered. Go extraction still uses Go’s toolchain; Doxygen still supplies the C++ declarations. The site build can then place those pages in the same navigation, index them for search and produce the context exports.
You can add another interface to the product without introducing another documentation website for its readers.
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 support ten languages at build time: cURL, JavaScript, TypeScript, Python, Go, Ruby, Java, PHP, Rust, C#. The default REST set is cURL, JavaScript and Python; the others are configurable. MCP samples render in three forms: 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
The build writes static HTML, a search index, llms.txt and llms-full.txt. Serve the directory from your own web server or CDN. Pages are rendered during the build; browser scripts handle search, navigation and other interactions.
Theming
Everything visual lives under theme. Four themes 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; reader is designed for editorial specifications. 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: {
name: "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 user@example.com. An endpoint that never declared an example still gets a call illustrating its schema. The generated values are examples to explain the contract; they have not been executed against the service.
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
The browser interactions use vanilla JavaScript alongside the pre-rendered pages. 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.
Why We Care What the Docs Look Like walks through the design decisions in working sites, from API references to long specifications.
Owned, not rented
Sourcey is AGPL-3.0. You can self-host and modify it under that licence; the licence text sets the source-sharing obligations.
The config, source files and built site can stay with the project. Hosting the output does not require a Sourcey service. You Don’t Own Your Docs gives you a practical way to test that independence.
Install
npm install -D sourcey
npx sourcey init
npx 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.
