Graph-integrity tooling for Architecture Decision Records — an MCP server and a CLI (same logic, one entry point) over a directory of ADR markdown files.
It treats the ADR corpus as a typed knowledge graph built from three reference channels:
- frontmatter typed edges —
supersedes,superseded_by,related - body wikilinks —
[[ADR-123]],[[123-slug|ADR-123]] - body markdown links —
[ADR-123](./123-slug.md)
This server standardizes on Google's Open Knowledge Format (OKF) v0.1 — an open, vendor-neutral format for representing knowledge as a directory of markdown files with YAML frontmatter. OKF is designed to be authored by people, generated by agents, exchanged across organizations, and consumed by both.
Required frontmatter: type (freeform — adr, playbook, metric, …)
Recommended frontmatter: title, description, resource, tags, timestamp
Every tool in this server reads and writes OKF-conformant documents. The migrate_okf
tool upgrades a legacy corpus to full conformance, and okf_conformance reports field
coverage and spec violations.
An incomplete graph is not automatically a broken one. Every "incomplete" finding is dispositioned as either an intentional frontier (a decision you'll connect later) or genuine rot:
| Finding | Intentional (signal) | Accidental (defect) |
|---|---|---|
| Singleton | status: proposed/draft/seed, standalone: true, or a seed tag |
status: accepted but wired to nothing |
| Dead link | target listed in the node's planned: / forward_refs: |
undeclared reference to a missing ADR |
validate returns ok: false only on genuine rot — undeclared dead links, broken
reciprocity, or OKF violations (missing type field). Intentional singletons and planned
forward-references are reported under signals, never as failures. This is what lets the
agent "spread out nodes as it works" without the validator fighting unfinished thinking.
Point it at a corpus with ADR_GRAPH_ROOT, then register the stdio server:
{
"mcpServers": {
"adr-graph": {
"command": "adr-graph",
"env": { "ADR_GRAPH_ROOT": "/path/to/docs/adr" }
}
}
}Tools:
validate(root?): Full topology report.okf_conformance(root?): OKF v0.1 conformance report — violations, warnings, field coverage.find_singletons(root?): Intended frontiers vs orphan suspects.find_dead_links(root?): Planned vs broken dead links.check_reciprocity(root?): Mirroring checks for supersedes edges.neighbors(adr, depth?, root?): Authored-link neighborhood context.export(fmt?, root?): Render asjson,mermaid, orokf(bundle summary).supersede(superseding, superseded, root?): Atomically write supersession edges.reconcile_related(adr?, apply?, root?): Derive frontmatter related links from body refs.read(adr, root?): Read ADR body and metadata (includesdescription,resource).list(status?, tag?, limit?, offset?, root?): Filtered catalog of ADRs with pagination.search(query, status?, limit?, offset?, root?): Title substring search with pagination.path(from_adr, to_adr, root?): BFS shortest path.set_status(adr, status, root?): Single-field frontmatter update.rename(old, new, dry_run?, root?): Renumber/rename ADR and update all references.drift(root?): Disagreements between body links and frontmatter relations.blast_radius(adr, root?): Downstream transitive dependencies.propose_adr(title, status?, context?, tags?, root?): Scaffold a new ADR file.hover_context(file_path, root?): Return architectural context for a file path (matches againstcode_pathsglobs in ADR frontmatter).migrate_okf(dry_run?, root?): Migrate corpus to OKF v0.1 — adds type, converts date→timestamp, synthesizes descriptions, generatesindex.md. Dry-run by default.
Also exposes the adr://{adr_id} resource to get raw markdown content natively.
Same binary, with a subcommand. Exits non-zero on rot, so it drops straight into CI or a pre-commit hook:
adr-graph validate /path/to/docs/adr # exit 1 if broken links or reciprocity breaks
adr-graph okf-conformance # OKF v0.1 field coverage and violations
adr-graph singletons # intentional frontier vs orphan suspects
adr-graph neighbors ADR-401 2 # authored-link neighbourhood (grounding context)
adr-graph reconcile ADR-3 --apply # derive frontmatter `related` from body links
adr-graph export mermaid # render for visualization
adr-graph export okf # OKF bundle metadata summary
adr-graph read ADR-3 # retrieve body text and frontmatter of ADR-3
adr-graph list --status proposed --limit 20 --offset 10 # list ADRs matching filters with pagination
adr-graph search "auth" --status accepted # search ADRs by title
adr-graph path ADR-1 ADR-5 # get BFS shortest path from ADR-1 to ADR-5
adr-graph set-status ADR-3 deprecated # update ADR status field
adr-graph rename ADR-3 ADR-30 --apply # cascade renumber ADR-3 to ADR-30
adr-graph drift # find nodes where yaml edges and body links disagree
adr-graph blast-radius ADR-3 # find all transitive dependents of ADR-3
adr-graph propose "Use PostgreSQL" "PostgreSQL spec" # scaffold a new ADR file
adr-graph migrate-okf --apply # migrate corpus to OKF (dry-run without --apply)Root resolution: explicit arg → ADR_GRAPH_ROOT → ./docs/adr.
pip install -e .