Skip to main content
Glama
generate_docs.py5.83 kB
import argparse import logging import re import sys from pathlib import Path from dbt_mcp.tools.toolsets import toolsets logging.basicConfig(level=logging.INFO, format="%(message)s") logger = logging.getLogger(__name__) README_PATH = Path(__file__).parents[1] / "README.md" DIAGRAM_PATH = Path(__file__).parents[1] / "docs" / "diagram.d2" def format_toolset_heading(toolset_value: str) -> str: """ Format toolset enum value into a readable heading using regex. Examples: sql -> SQL semantic_layer -> Semantic Layer dbt_cli -> dbt CLI admin_api -> Admin API """ text = toolset_value.replace("_", " ").title() # Uppercase common acronyms text = re.sub(r"\b(Sql|Api|Cli|Lsp)\b", lambda m: m.group(1).upper(), text) # "dbt" stylized as lowercase text = re.sub(r"\bDbt\b", "dbt", text) return text def generate_readme_tools_section() -> str: """Generate the Tools section markdown from toolsets.""" lines = ["## Tools", ""] for toolset, tool_names in toolsets.items(): heading = format_toolset_heading(toolset.value) lines.append(f"### {heading}") sorted_tools = sorted([tool.value for tool in tool_names]) for tool in sorted_tools: lines.append(f"- `{tool}`") lines.append("") # Empty line after each section return "\n".join(lines) def generate_diagram_tools_section() -> str: """Generate the tools section for diagram.d2 from toolsets.""" lines = ["tools: Tools {"] for toolset in toolsets.keys(): # Use enum value directly as key (e.g., "dbt_cli", "semantic_layer") key = toolset.value # Reuse same formatting as README headings label = format_toolset_heading(toolset.value) lines.append(f" {key}: {label} {{") lines.append(" style.border-radius: 8") lines.append(" }") lines.append("") # Empty line between tools lines.append("}") return "\n".join(lines) def update_readme(check_only: bool = False) -> bool: """ Update the Tools section in README.md. Args: check_only: If True, only check if update is needed without writing. Returns: True if README is up to date (or was updated), False if update is needed. """ if not README_PATH.exists(): logger.error(f"README.md not found at {README_PATH}") return False readme_content = README_PATH.read_text() new_tools_section = generate_readme_tools_section() # Replace Tools section (from "## Tools" to next "##" heading or end) pattern = r"(## Tools\n).*?(?=\n## |\Z)" if not re.search(pattern, readme_content, re.DOTALL): logger.error("Could not find '## Tools' section in README.md") return False updated_content = re.sub( pattern, new_tools_section + "\n", readme_content, flags=re.DOTALL ) is_up_to_date = readme_content == updated_content if check_only: if is_up_to_date: logger.info("✓ README.md tools section is up to date") else: logger.error("✗ README.md tools section is out of date") return is_up_to_date else: README_PATH.write_text(updated_content) status = "was already up to date" if is_up_to_date else "updated successfully" logger.info(f"README.md tools section {status}") return True def update_d2_diagram(check_only: bool = False) -> bool: """ Update the tools section in diagram.d2. Args: check_only: If True, only check if update is needed without writing. Returns: True if diagram is up to date (or was updated), False if update is needed. """ if not DIAGRAM_PATH.exists(): logger.error(f"diagram.d2 not found at {DIAGRAM_PATH}") return False diagram_content = DIAGRAM_PATH.read_text() new_tools_section = generate_diagram_tools_section() # Replace tools section (from "tools: Tools {" to closing "}") pattern = r"tools: Tools \{.*?\n\}" if not re.search(pattern, diagram_content, re.DOTALL): logger.error("Could not find 'tools: Tools {' section in diagram.d2") return False updated_content = re.sub( pattern, new_tools_section, diagram_content, flags=re.DOTALL ) is_up_to_date = diagram_content == updated_content if check_only: if is_up_to_date: logger.info("✓ diagram.d2 tools section is up to date") else: logger.error("✗ diagram.d2 tools section is out of date") return is_up_to_date else: DIAGRAM_PATH.write_text(updated_content) status = "was already up to date" if is_up_to_date else "updated successfully" logger.info(f"diagram.d2 tools section {status}") return True def update_all(check_only: bool = False) -> bool: """ Update both README.md and diagram.d2. Args: check_only: If True, only check if updates are needed without writing. Returns: True if all docs are up to date (or were updated), False if updates needed. """ readme_ok = update_readme(check_only) diagram_ok = update_d2_diagram(check_only) if check_only and not (readme_ok and diagram_ok): logger.error("\nRun: uv run scripts/generate_docs.py") return readme_ok and diagram_ok def main(): """Main entry point.""" parser = argparse.ArgumentParser( description="Generate or validate documentation from toolsets" ) parser.add_argument( "--check", action="store_true", help="Check if documentation is up to date without modifying it", ) args = parser.parse_args() success = update_all(check_only=args.check) sys.exit(0 if success else 1) if __name__ == "__main__": main()

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/dbt-labs/dbt-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server