feat(docs): add MCP artifact generation to manage_docs CLI
- Introduced `build_mcp` command to generate docs-only MCP artifacts - Reuses existing MkDocs + mkdocstrings pipeline (no code introspection) - Extracts `:::` mkdocstrings directives from generated Markdown - Emits structured MCP output: - `mcp/index.json` (project metadata) - `mcp/nav.json` (page → module mapping) - `mcp/modules/*.json` (per-module references) - Preserves all existing commands and behavior (`generate`, `build`, `serve`) - Avoids source code exposure; MCP output is derived solely from documentation This enables a clean docs → MCP → MCP server workflow suitable for AI IDE integration.
This commit is contained in:
104
manage_docs.py
104
manage_docs.py
@@ -18,6 +18,7 @@ Usage:
|
||||
python manage_docs.py generate
|
||||
python manage_docs.py build
|
||||
python manage_docs.py serve
|
||||
python manage_docs.py build_mcp
|
||||
|
||||
Optional flags:
|
||||
--docs-dir PATH Path to docs directory (default: ./docs)
|
||||
@@ -27,7 +28,10 @@ Optional flags:
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Iterable
|
||||
|
||||
from mkdocs.commands import build as mkdocs_build
|
||||
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_PACKAGE_ROOT = "mail_intake"
|
||||
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(
|
||||
project_root: Path,
|
||||
@@ -116,10 +127,86 @@ def cmd_serve(_: argparse.Namespace) -> None:
|
||||
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:
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="manage_docs.py",
|
||||
description="Manage MkDocs documentation for the project",
|
||||
description="Manage MkDocs documentation and MCP exports for the project",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
@@ -128,12 +215,20 @@ def main() -> None:
|
||||
default=DEFAULT_DOCS_DIR,
|
||||
help="Path to the docs directory",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--package-root",
|
||||
default=DEFAULT_PACKAGE_ROOT,
|
||||
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.add_parser(
|
||||
@@ -151,6 +246,13 @@ def main() -> None:
|
||||
help="Serve the MkDocs site locally",
|
||||
).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.func(args)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user