Skip to main content
Glama

generate_markdown_from_json

Convert exported or edited WorkFlowy JSON files into clean Markdown format, removing metadata for readable documentation.

Instructions

Convert exported/edited JSON to Markdown format (without metadata).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
json_fileYes

Implementation Reference

  • The core handler function that loads a JSON file containing Workflowy node data (either a bare list or wrapped in a 'nodes' key), generates a hierarchical Markdown representation using the private _generate_markdown method, writes it to a .md file (same basename), and returns success status with the output path.
    def generate_markdown_from_json(self, json_file: str) -> dict[str, Any]: """Convert JSON file to Markdown (without metadata - for edited JSON review).""" try: # Read JSON file with open(json_file, 'r', encoding='utf-8') as f: data = json.load(f) # Handle both bare array AND metadata wrapper if isinstance(data, dict) and "nodes" in data: nodes = data["nodes"] elif isinstance(data, list): nodes = data else: return { "success": False, "markdown_file": None, "error": f"JSON must be array or dict with 'nodes' key. Got: {type(data).__name__}" } # Generate Markdown WITH metadata markdown_content = self._generate_markdown(nodes, level=1) # Write to .md file markdown_file = json_file.replace('.json', '.md') with open(markdown_file, 'w', encoding='utf-8') as f: f.write(markdown_content) return { "success": True, "markdown_file": markdown_file } except Exception as e: return { "success": False, "markdown_file": None, "error": f"Failed to generate markdown: {str(e)}" }
  • MCP tool registration decorator and wrapper function that exposes the client.generate_markdown_from_json method as the 'generate_markdown_from_json' tool.
    @mcp.tool( name="generate_markdown_from_json", description="Convert exported/edited JSON to Markdown format (without metadata)." ) def generate_markdown( json_file: str, ) -> dict: """Convert JSON file to Markdown format. Use after editing JSON with Quill scroll to create Markdown for PARALLAX review. Args: json_file: Absolute path to JSON file. Returns: Dictionary with success status and markdown file path. """ client = get_client() return client.generate_markdown_from_json(json_file)
  • Private helper method that recursively traverses the hierarchical node structure and generates Markdown output with headings (limited to h6), hidden metadata comments (UUIDs, full paths), and fenced note blocks. Called by generate_markdown_from_json to produce the final .md content.
    def _generate_markdown( self, nodes: list[dict[str, Any]], level: int = 1, parent_path_uuids: list[str] | None = None, parent_path_names: list[str] | None = None, root_node_info: dict[str, Any] | None = None ) -> str: """Convert hierarchical nodes to Markdown format with UUID metadata.""" if parent_path_uuids is None: parent_path_uuids = [] if parent_path_names is None: parent_path_names = [] markdown_lines = [] # Add root metadata at top of file (level 1 only) if level == 1 and root_node_info: root_uuid = root_node_info.get('id', '') root_name = root_node_info.get('name', 'Root') full_path_uuids = root_node_info.get('full_path_uuids', [root_uuid]) full_path_names = root_node_info.get('full_path_names', [root_name]) # Truncated UUID path for readability truncated_path = ' > '.join([uuid[:8] + '...' for uuid in full_path_uuids]) # Full name path for orientation name_path = ' > '.join(full_path_names) markdown_lines.append(f'<!-- EXPORTED_ROOT_UUID: {root_uuid} -->') markdown_lines.append(f'<!-- EXPORTED_ROOT_NAME: {root_name} -->') markdown_lines.append(f'<!-- EXPORTED_ROOT_PATH_UUIDS: {truncated_path} -->') markdown_lines.append(f'<!-- EXPORTED_ROOT_PATH_NAMES: {name_path} -->') markdown_lines.append('') for node in nodes: node_uuid = node.get('id', '') node_name = node.get('name', 'Untitled') # Build paths for this node current_path_uuids = parent_path_uuids + [node_uuid] current_path_names = parent_path_names + [node_name] # Create heading (limit to h6) heading_level = min(level, 6) heading = '#' * heading_level + ' ' + node_name markdown_lines.append(heading) # Add metadata (hidden in Obsidian reading view) markdown_lines.append(f'<!-- NODE_UUID: {node_uuid} -->') # Truncated UUID path truncated_uuids = [uuid[:8] + '...' for uuid in current_path_uuids] uuid_path = ' > '.join(truncated_uuids) markdown_lines.append(f'<!-- NODE_PATH_UUIDS: {uuid_path} -->') # Name path name_path = ' > '.join(current_path_names) markdown_lines.append(f'<!-- NODE_PATH_NAMES: {name_path} -->') markdown_lines.append('') # Add note content if present note = node.get('note') if note and note.strip(): # Quick detection: check if note looks like markdown is_markdown = any(line.strip().startswith('#') for line in note.split('\n')) language = 'markdown' if is_markdown else 'text' # Wrap in 12-backtick delimiter markdown_lines.append('````````````' + language) markdown_lines.append(note) markdown_lines.append('````````````') markdown_lines.append('') # Recursively process children children = node.get('children', []) if children: child_markdown = self._generate_markdown( children, level + 1, current_path_uuids, current_path_names ) markdown_lines.append(child_markdown) return '\n'.join(markdown_lines)

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/daniel347x/workflowy-mcp-fixed'

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