docs-and-mcps #1
104
manage_docs.py
104
manage_docs.py
@@ -18,6 +18,7 @@ Usage:
|
|||||||
python manage_docs.py generate
|
python manage_docs.py generate
|
||||||
python manage_docs.py build
|
python manage_docs.py build
|
||||||
python manage_docs.py serve
|
python manage_docs.py serve
|
||||||
|
python manage_docs.py build_mcp
|
||||||
|
|
||||||
Optional flags:
|
Optional flags:
|
||||||
--docs-dir PATH Path to docs directory (default: ./docs)
|
--docs-dir PATH Path to docs directory (default: ./docs)
|
||||||
@@ -27,7 +28,10 @@ Optional flags:
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import json
|
||||||
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Iterable
|
||||||
|
|
||||||
from mkdocs.commands import build as mkdocs_build
|
from mkdocs.commands import build as mkdocs_build
|
||||||
from mkdocs.commands import serve as mkdocs_serve
|
from mkdocs.commands import serve as mkdocs_serve
|
||||||
@@ -38,7 +42,14 @@ PROJECT_ROOT = Path(__file__).resolve().parent
|
|||||||
DEFAULT_DOCS_DIR = PROJECT_ROOT / "docs"
|
DEFAULT_DOCS_DIR = PROJECT_ROOT / "docs"
|
||||||
DEFAULT_PACKAGE_ROOT = "mail_intake"
|
DEFAULT_PACKAGE_ROOT = "mail_intake"
|
||||||
MKDOCS_YML = PROJECT_ROOT / "mkdocs.yml"
|
MKDOCS_YML = PROJECT_ROOT / "mkdocs.yml"
|
||||||
|
DEFAULT_MCP_DIR = PROJECT_ROOT / "mcp"
|
||||||
|
|
||||||
|
MKDOCSTRINGS_DIRECTIVE = re.compile(r"^:::\s+([a-zA-Z0-9_.]+)", re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
|
# -------------------------
|
||||||
|
# Existing functionality
|
||||||
|
# -------------------------
|
||||||
|
|
||||||
def generate_docs_from_nav(
|
def generate_docs_from_nav(
|
||||||
project_root: Path,
|
project_root: Path,
|
||||||
@@ -116,10 +127,86 @@ def cmd_serve(_: argparse.Namespace) -> None:
|
|||||||
config_file=str(MKDOCS_YML)
|
config_file=str(MKDOCS_YML)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# -------------------------
|
||||||
|
# MCP generation
|
||||||
|
# -------------------------
|
||||||
|
|
||||||
|
def iter_markdown_files(docs_root: Path) -> Iterable[Path]:
|
||||||
|
yield from docs_root.rglob("*.md")
|
||||||
|
|
||||||
|
|
||||||
|
def extract_modules(md_file: Path) -> list[str]:
|
||||||
|
content = md_file.read_text(encoding="utf-8")
|
||||||
|
return MKDOCSTRINGS_DIRECTIVE.findall(content)
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_build_mcp(args: argparse.Namespace) -> None:
|
||||||
|
docs_root = args.docs_dir
|
||||||
|
mcp_root = args.mcp_dir
|
||||||
|
|
||||||
|
modules_dir = mcp_root / "modules"
|
||||||
|
modules_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
nav = []
|
||||||
|
modules = []
|
||||||
|
|
||||||
|
for md in iter_markdown_files(docs_root):
|
||||||
|
rel = md.relative_to(docs_root)
|
||||||
|
|
||||||
|
module_refs = extract_modules(md)
|
||||||
|
if not module_refs:
|
||||||
|
continue
|
||||||
|
|
||||||
|
nav.append({
|
||||||
|
"page": str(rel),
|
||||||
|
"modules": module_refs,
|
||||||
|
})
|
||||||
|
|
||||||
|
for module in module_refs:
|
||||||
|
module_entry = {
|
||||||
|
"module": module,
|
||||||
|
"doc_page": str(rel),
|
||||||
|
}
|
||||||
|
modules.append(module_entry)
|
||||||
|
|
||||||
|
out = modules_dir / f"{module}.json"
|
||||||
|
out.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
out.write_text(
|
||||||
|
json.dumps(module_entry, indent=2),
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
mcp_root.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
(mcp_root / "nav.json").write_text(
|
||||||
|
json.dumps(nav, indent=2),
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
(mcp_root / "index.json").write_text(
|
||||||
|
json.dumps(
|
||||||
|
{
|
||||||
|
"project": "Aetoskia Mail Intake",
|
||||||
|
"type": "docs-only",
|
||||||
|
"modules_count": len(modules),
|
||||||
|
},
|
||||||
|
indent=2,
|
||||||
|
),
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f"MCP artifacts written to: {mcp_root}")
|
||||||
|
|
||||||
|
|
||||||
|
# -------------------------
|
||||||
|
# CLI
|
||||||
|
# -------------------------
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
prog="manage_docs.py",
|
prog="manage_docs.py",
|
||||||
description="Manage MkDocs documentation for the project",
|
description="Manage MkDocs documentation and MCP exports for the project",
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@@ -128,12 +215,20 @@ def main() -> None:
|
|||||||
default=DEFAULT_DOCS_DIR,
|
default=DEFAULT_DOCS_DIR,
|
||||||
help="Path to the docs directory",
|
help="Path to the docs directory",
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--package-root",
|
"--package-root",
|
||||||
default=DEFAULT_PACKAGE_ROOT,
|
default=DEFAULT_PACKAGE_ROOT,
|
||||||
help="Root Python package name",
|
help="Root Python package name",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--mcp-dir",
|
||||||
|
type=Path,
|
||||||
|
default=DEFAULT_MCP_DIR,
|
||||||
|
help="Output directory for MCP artifacts",
|
||||||
|
)
|
||||||
|
|
||||||
subparsers = parser.add_subparsers(dest="command", required=True)
|
subparsers = parser.add_subparsers(dest="command", required=True)
|
||||||
|
|
||||||
subparsers.add_parser(
|
subparsers.add_parser(
|
||||||
@@ -151,6 +246,13 @@ def main() -> None:
|
|||||||
help="Serve the MkDocs site locally",
|
help="Serve the MkDocs site locally",
|
||||||
).set_defaults(func=cmd_serve)
|
).set_defaults(func=cmd_serve)
|
||||||
|
|
||||||
|
subparsers.add_parser(
|
||||||
|
"build_mcp",
|
||||||
|
help="Generate MCP artifacts (docs-only)",
|
||||||
|
).set_defaults(
|
||||||
|
func=cmd_build_mcp
|
||||||
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
args.func(args)
|
args.func(args)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user