How Does mermaid_extract.py Work: Parsing Mermaid Diagrams into Intermediate Representation

mermaid_extract.py is a command-line tool in the cathrynlavery/diagram-design repository that parses Mermaid diagram source files into a normalized intermediate representation (IR) consisting of nodes and edges, supporting flowcharts, sequence diagrams, state diagrams, and ER diagrams with optional JSON or Markdown output.

The mermaid_extract.py script serves as the core parsing engine for the diagram-design project, transforming Mermaid syntax into structured data. Located at skills/diagram-design/scripts/mermaid_extract.py, this self-contained Python module converts both standalone .mmd files and Markdown-embedded diagrams into a machine-readable format. Understanding how mermaid_extract.py works reveals a sophisticated pipeline that handles multiple diagram types while maintaining strict security boundaries.

CLI Entry Point and Argument Parsing

The script operates as a standalone CLI tool invoked via python3 mermaid_extract.py <file>.

The main function (line 92) uses argparse to handle command-line arguments including --diagram, --json, --max-rows, and --out. After validating the target file path using Path.is_file, it delegates to load_blocks (lines 112-133) to extract Mermaid source blocks from the input file.

Loading and Preparing Source Blocks

The load_blocks function distinguishes between three input types to locate raw Mermaid syntax.

For .mmd or .mermaid extensions, the function returns a single SourceBlock containing the entire file contents (lines 28-35). For Markdown files, it scans for fenced ```mermaid blocks, capturing the delimiter, collecting content, and wrapping each discovered block in a SourceBlock instance (lines 36-61). The SourceBlock dataclass (lines 87-93) preserves the block index, raw text, and starting line number for accurate error reporting. If no valid blocks are found or a fence is unterminated, _fail triggers an error (lines 56-60).

Diagram Type Detection

Before parsing, _prepared_lines sanitizes the raw input (lines 81-89).

This preprocessing strips leading YAML front-matter delimited by --- (lines 66-78), removes Mermaid directives (%%{…}%%) and comments (%% …), and retains original line numbers for debugging. The cleaned lines pass to _kind_and_direction (lines 111-127), which scans for the first non-empty statement to identify the diagram type. The parser supports flowchart, sequenceDiagram, stateDiagram-v2, and erDiagram types, raising a clear error for unsupported variants (lines 128-132).

Core Data Structures

The intermediate representation relies on two primary dataclasses defined in the source.

The Node class (lines 66-78) stores the identifier, display label, shape, parent reference, depth level, container flag, children list, field definitions, and in/out degrees. The Edge class (lines 80-88) captures the edge identifier, source node, target node, label text, visual style, arrowhead type, and directionality flags. These aggregate into the Diagram class (lines 94-106), which maintains collections of nodes and edges alongside a private _nodes_by_id mapping for O(1) node lookup during parsing.

Parsing Pipeline

The parse_block function orchestrates the transformation from text to structured objects.

First, it invokes _prepared_lines to sanitize input (lines 93-95). Next, it determines the diagram kind and default direction via _kind_and_direction (lines 96-97). Finally, it delegates to a specialized parser based on the detected type: _parse_flowchart, _parse_sequence, _parse_state, or _parse_er (lines 98-104). Each parser operates on logical statements produced by _logical_statements, which merges multiline statements and splits on top-level semicolons (lines 111-128).

Flowchart Parsing

_parse_flowchart handles complex graph structures including subgraph containers and direction changes.

The function discards non-semantic directives such as style, classDef, and click via _discard_nonsemantic (lines 87-94). Edge detection occurs through _edge_operators (lines 100-165), which first masks quoted or bracketed content using _top_level_mask to avoid false positives, then identifies labeled and unlabeled operators with their associated styles. Nodes register via Diagram.add_node while edges connect via Diagram.add_edge, with parent-child relationships tracked for container hierarchies.

Sequence Diagram Parsing

_parse_sequence processes participant declarations, fragments (alt, opt, loop), notes, and messages.

Participant definitions using participant or actor keywords establish the node set. Message syntax (A-->B: label) is parsed using the message_re regex (lines 158-176), translating interactions into Edge objects with appropriate arrowhead metadata including cross and async variants.

State Diagram Parsing

_parse_state manages nested state declarations and transitions.

The parser supports state X { … } syntax for hierarchies and handles direction directives. Transition arrows (-->) connect states, with _state_endpoint generating special pseudo-nodes (__start_…, __end_…) to represent initial and final states within the IR.

ER Diagram Parsing

_parse_er processes entity-relationship models.

Entity blocks defined with entity {} syntax become nodes, while relationships (A --|> B : label) become edges. The parser normalizes cardinality notations (-- vs ..) and stores these values within edge labels for downstream analysis.

Post-Processing and Analysis

After constructing the initial graph, the script enriches the IR with structural metadata.

_finalize_degrees (lines 109-116) traverses all edges to compute each node's in_degree and out_degree counts. The _has_cycle function (lines 118-144) performs a depth-first search on the adjacency map to detect and flag cyclic graph structures. The analyze function (lines 149-176) aggregates high-level statistics including node counts, edge counts, container counts, leaf nodes, shape distributions, hub candidates (high-degree nodes), entry/exit points, and orphan nodes.

Output Generation

The tool provides two distinct output modes controlled by CLI flags.

The Markdown digest mode (lines 212-269) generates human-readable summaries with formatted tables for nodes and edges. The --max-rows parameter truncates output to prevent information overload in large diagrams. The JSON mode via to_json (lines 250-268) exports the complete machine-readable representation including the raw Diagram objects, analysis statistics, and discarded metadata. Output writes to stdout by default or to a file specified by --out (lines 219-227).

Security and Trust Boundaries

The script implements a strict trust boundary by treating all label and directive content as untrusted data.

The clean_label function sanitizes input through HTML unescaping and tag removal (lines 4-8). Click handlers and styling directives are counted for analysis but discarded from the IR, ensuring no executable content persists in the output. This design prevents code injection while preserving the structural integrity of the diagram.

Summary

  • mermaid_extract.py is located at skills/diagram-design/scripts/mermaid_extract.py in the cathrynlavery/diagram-design repository.
  • The tool accepts .mmd, .mermaid, and Markdown files, extracting fenced mermaid blocks via load_blocks.
  • It supports four diagram types: flowchart, sequenceDiagram, stateDiagram-v2, and erDiagram.
  • The IR uses Node and Edge dataclasses aggregated within a Diagram object.
  • Output formats include human-readable Markdown digests and machine-readable JSON.
  • Security measures sanitize all labels and discard executable directives like click handlers.

Frequently Asked Questions

What diagram types does mermaid_extract.py support?

The parser explicitly supports flowchart, sequenceDiagram, stateDiagram-v2, and erDiagram types as detected by _kind_and_direction (lines 111-127). Each type delegates to a specialized parser function that understands the specific syntax and semantics of that diagram variety. Unsupported diagram kinds trigger a clear error message during the detection phase.

How does mermaid_extract.py handle security when parsing untrusted diagrams?

The script treats all content as untrusted data through the clean_label function (lines 4-8), which performs HTML unescaping and removes HTML tags from labels. Additionally, the _discard_nonsemantic function strips potentially dangerous directives like click handlers and classDef styling commands, ensuring only structural data enters the intermediate representation.

Can mermaid_extract.py extract multiple diagrams from a single Markdown file?

Yes. The load_blocks function (lines 36-61) scans Markdown files for all fenced ```mermaid blocks and returns a list of SourceBlock objects. Each block maintains its original line number and index, allowing the parser to process multiple diagrams sequentially while preserving error context for each specific block.

What is the difference between the Markdown digest and JSON output modes?

The Markdown digest produces human-readable tables summarizing nodes, edges, and analysis statistics, with optional row limits via --max-rows (lines 212-269). The JSON mode exports the complete Diagram object structure including all nodes, edges, metadata, and analysis results in a machine-parseable format (lines 250-268), suitable for downstream automation or database ingestion.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →