|
10 | 10 |
|
11 | 11 | from __future__ import annotations |
12 | 12 |
|
| 13 | +import importlib.util |
13 | 14 | import pathlib |
14 | 15 | import shutil |
| 16 | +import sys |
15 | 17 |
|
16 | 18 | import pdoc |
| 19 | +import pdoc.render_helpers |
| 20 | + |
| 21 | + |
| 22 | +def _regenerate_mcp_markdown() -> None: |
| 23 | + """Regenerate `docs/mcp-generated/` before pdoc runs. |
| 24 | +
|
| 25 | + The `airbyte.mcp.{cloud,local,registry,prompts}` modules pull the |
| 26 | + per-module Markdown files from `docs/mcp-generated/` via pdoc's |
| 27 | + `.. include::` directive. That directory is git-ignored, so on a clean |
| 28 | + checkout pdoc would fail to resolve the include unless we regenerate it |
| 29 | + here. Running the generator from inside `docs-generate` makes the full |
| 30 | + docs build reproducible from a fresh clone (and matches the standalone |
| 31 | + `poe mcp-docs-md` task). |
| 32 | +
|
| 33 | + If generation fails (e.g. `fastmcp` is not installed, or the MCP server |
| 34 | + import fails), we print a warning and continue: pdoc will still build, |
| 35 | + and the include directive will just surface the missing file. |
| 36 | +
|
| 37 | + We load the generator via `importlib.util` from its on-disk path rather |
| 38 | + than a plain `from generate_mcp_markdown import ...`: the generator |
| 39 | + lives under `scripts/` (not on `sys.path`), and a static import would |
| 40 | + also trip `deptry` into flagging `generate_mcp_markdown` as a missing |
| 41 | + external dependency. |
| 42 | + """ |
| 43 | + script = pathlib.Path(__file__).parent.parent / "scripts" / "generate_mcp_markdown.py" |
| 44 | + if not script.exists(): |
| 45 | + print(f"[docs-generate] MCP markdown generator not found at {script}; skipping.") |
| 46 | + return |
| 47 | + try: |
| 48 | + spec = importlib.util.spec_from_file_location("_mcp_markdown_gen", script) |
| 49 | + if spec is None or spec.loader is None: |
| 50 | + msg = f"Could not load spec for {script}" |
| 51 | + raise RuntimeError(msg) # noqa: TRY301 |
| 52 | + module = importlib.util.module_from_spec(spec) |
| 53 | + spec.loader.exec_module(module) |
| 54 | + print("[docs-generate] Regenerating docs/mcp-generated/ ...") |
| 55 | + module.generate( |
| 56 | + server_spec=module.DEFAULT_SERVER_SPEC, |
| 57 | + output=module.DEFAULT_OUTPUT, |
| 58 | + ) |
| 59 | + except Exception as ex: |
| 60 | + print( |
| 61 | + f"[docs-generate] WARNING: failed to regenerate MCP Markdown docs: {ex}. " |
| 62 | + "pdoc will continue, but module pages may show missing include warnings.", |
| 63 | + file=sys.stderr, |
| 64 | + ) |
17 | 65 |
|
18 | 66 |
|
19 | 67 | def run() -> None: |
20 | 68 | """Generate docs for all public modules in PyAirbyte and save them to docs/generated.""" |
21 | 69 | public_modules = ["airbyte", "airbyte/cli/pyab.py"] |
22 | 70 |
|
| 71 | + # Regenerate MCP Markdown first so the `.. include::` directives in the |
| 72 | + # MCP module docstrings resolve on a clean checkout (docs/mcp-generated/ |
| 73 | + # is git-ignored). |
| 74 | + _regenerate_mcp_markdown() |
| 75 | + |
23 | 76 | # recursively delete the docs/generated folder if it exists |
24 | 77 | if pathlib.Path("docs/generated").exists(): |
25 | 78 | shutil.rmtree("docs/generated") |
26 | 79 |
|
| 80 | + # pdoc's default sidebar TOC depth is 2 (H1 + H2 only), which hides the |
| 81 | + # per-tool H3 anchors produced by our MCP Markdown generator. Bump to 3 so |
| 82 | + # individual tools / prompts / resources show up in the left nav. This |
| 83 | + # monkey-patches the module-level `markdown_extensions` dict because pdoc |
| 84 | + # 16's `configure()` does not expose markdown extension options. |
| 85 | + # pyrefly: ignore[unsupported-operation] |
| 86 | + pdoc.render_helpers.markdown_extensions["toc"] = {"depth": 3} |
| 87 | + |
27 | 88 | pdoc.render.configure( |
28 | 89 | template_directory=pathlib.Path("docs/templates"), |
29 | 90 | show_source=True, |
|
0 commit comments