104 lines
2.8 KiB
Python
104 lines
2.8 KiB
Python
#!/usr/bin/env python
|
|
"""Build the hand-authored "standards" MCP bundle from the wiki markdown.
|
|
|
|
This converter reads `standards_src/wiki/*.md` and writes the MCP bundle shape
|
|
that doc-forge's `MCPRenderer` emits, so the shared `MCPServer` can serve it:
|
|
|
|
mcp/standards/index.json
|
|
mcp/standards/nav.json
|
|
mcp/standards/modules/<module>.json
|
|
|
|
No doc-forge code is involved; the bundle is authored here so the spec and the
|
|
machine-readable resources never drift.
|
|
|
|
Usage:
|
|
python tools/build_standards.py
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import re
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
SRC = ROOT / "standards_src" / "wiki"
|
|
OUT = ROOT / "mcp" / "standards"
|
|
MODULES = OUT / "modules"
|
|
|
|
NUM_PREFIX = re.compile(r"^\d+[_-]?")
|
|
|
|
|
|
def module_name(stem: str) -> str:
|
|
"""Map a markdown filename to a module name (strip the NN_ prefix)."""
|
|
return NUM_PREFIX.sub("", stem) or stem
|
|
|
|
|
|
def read_title(path: Path) -> str:
|
|
"""Return the first markdown H1 title, or a prettified fallback."""
|
|
for line in path.read_text(encoding="utf-8").splitlines():
|
|
if line.startswith("# "):
|
|
return line[2:].strip()
|
|
return module_name(path.stem).replace("_", " ").title()
|
|
|
|
|
|
def write_module(name: str, body: str) -> None:
|
|
payload = {
|
|
"module": name,
|
|
"content": {
|
|
"path": name,
|
|
"docstring": body.strip() + "\n",
|
|
"objects": {},
|
|
},
|
|
}
|
|
(MODULES / f"{name}.json").write_text(
|
|
json.dumps(payload, indent=2, ensure_ascii=False), encoding="utf-8"
|
|
)
|
|
|
|
|
|
def build() -> int:
|
|
if not SRC.is_dir():
|
|
raise SystemExit(f"wiki source not found: {SRC}")
|
|
|
|
MODULES.mkdir(parents=True, exist_ok=True)
|
|
|
|
home = SRC / "index.md"
|
|
if home.exists():
|
|
write_module("index", home.read_text(encoding="utf-8"))
|
|
nav = [{"module": "index", "resource": "docs://modules/index", "title": "Home"}]
|
|
else:
|
|
nav = []
|
|
|
|
for path in sorted(SRC.glob("*.md")):
|
|
if path.name == "index.md":
|
|
continue
|
|
name = module_name(path.stem)
|
|
write_module(name, path.read_text(encoding="utf-8"))
|
|
nav.append({"module": name, "resource": f"docs://modules/{name}", "title": read_title(path)})
|
|
|
|
(OUT / "nav.json").write_text(
|
|
json.dumps(nav, indent=2, ensure_ascii=False), encoding="utf-8"
|
|
)
|
|
(OUT / "index.json").write_text(
|
|
json.dumps(
|
|
{
|
|
"project": "standards",
|
|
"type": "standards",
|
|
"modules_count": len(nav),
|
|
"source": "hand-authored",
|
|
},
|
|
indent=2,
|
|
ensure_ascii=False,
|
|
),
|
|
encoding="utf-8",
|
|
)
|
|
return len(nav)
|
|
|
|
|
|
def main() -> None:
|
|
count = build()
|
|
print(f"[OK] wrote {count} modules to {OUT.relative_to(ROOT)}/")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|