Skip to main content
Glama

convert_workflow_to_ui

Convert API-format workflows to UI-compatible formats for editing in ComfyUI, assigning node IDs, creating visual links, and arranging layouts.

Instructions

Convert an API format workflow to UI/Litegraph format.

    Args:
        workflow: Workflow in API format (flat dict with class_type/inputs)

    Returns:
        Workflow in UI format compatible with ComfyUI editor.
        This format includes node positions, visual links, and metadata
        required for the workflow to be displayed and edited in the UI.

    The conversion:
    - Assigns integer IDs to nodes
    - Places nodes in a grid layout
    - Creates visual links from input connections
    - Sets appropriate node sizes based on type
    

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
workflowYesWorkflow in API format

Implementation Reference

  • The MCP tool handler for convert_workflow_to_ui, decorated with @mcp.tool(). Includes input schema via Field and delegates to api_to_ui_workflow helper.
    @mcp.tool()
    def convert_workflow_to_ui(
        workflow: dict = Field(description="Workflow in API format"),
        ctx: Context = None,
    ) -> dict:
        """Convert an API format workflow to UI/Litegraph format.
    
        Args:
            workflow: Workflow in API format (flat dict with class_type/inputs)
    
        Returns:
            Workflow in UI format compatible with ComfyUI editor.
            This format includes node positions, visual links, and metadata
            required for the workflow to be displayed and edited in the UI.
    
        The conversion:
        - Assigns integer IDs to nodes
        - Places nodes in a grid layout
        - Creates visual links from input connections
        - Sets appropriate node sizes based on type
        """
        if ctx:
            ctx.info("Converting workflow to UI format")
    
        return api_to_ui_workflow(workflow)
  • Core helper function that performs the actual conversion from API workflow dict to UI-compatible Litegraph format, including node positioning, link creation, and serialization.
    def api_to_ui_workflow(api_workflow: dict) -> dict:
        """Convert API format workflow to SerialisableGraph format (version 1).
    
        API format: { "node_id": {"class_type": "...", "inputs": {...}}, ... }
        UI format: { "version": 1, "nodes": [...], "links": [...], ... }
    
        Based on: ComfyUI_frontend/src/lib/litegraph/src/types/serialisation.ts
    
        Args:
            api_workflow: Workflow in API format
    
        Returns:
            Workflow in SerialisableGraph format compatible with ComfyUI editor
        """
        ui_nodes: list[UINode] = []
        ui_links: list[SerialisableLLink] = []
    
        # Map string node IDs to integer IDs
        node_id_map: dict[str, int] = {}
        for idx, node_id in enumerate(api_workflow.keys(), start=1):
            node_id_map[node_id] = idx
    
        # Track link ID
        link_id = 0
    
        # Compute node sizes for layout
        node_sizes = {
            node_id: get_node_size(data.get("class_type", "")) for node_id, data in api_workflow.items()
        }
    
        # Graph-aware layout using topological layers
        positions = compute_workflow_layout(api_workflow, node_sizes)
    
        for idx, (node_id, node_data) in enumerate(api_workflow.items()):
            int_id = node_id_map[node_id]
            class_type = node_data.get("class_type", "Unknown")
            inputs = node_data.get("inputs", {})
    
            # Get position from graph layout
            pos_x, pos_y = positions.get(node_id, (100, 100))
            size = node_sizes[node_id]
    
            # Separate connection inputs from widget values
            node_inputs: list[UINodeSlot] = []
            node_outputs: list[UINodeSlot] = []
            widgets_values: list = []
    
            for input_name, value in inputs.items():
                if isinstance(value, list) and len(value) == 2:
                    # This is a connection: [source_node_id, output_index]
                    source_node_str = str(value[0])
                    source_slot = value[1]
    
                    if source_node_str in node_id_map:
                        link_id += 1
                        source_int_id = node_id_map[source_node_str]
    
                        # Add input slot
                        node_inputs.append(
                            UINodeSlot(
                                name=input_name,
                                type="*",  # Wildcard type
                                link=link_id,
                                slot_index=len(node_inputs),
                            )
                        )
    
                        # Add link (object format for SerialisableGraph)
                        ui_links.append(
                            SerialisableLLink(
                                id=link_id,
                                origin_id=source_int_id,
                                origin_slot=source_slot,
                                target_id=int_id,
                                target_slot=len(node_inputs) - 1,
                                type="*",
                            )
                        )
                else:
                    # This is a widget value
                    widgets_values.append(value)
    
            # Add a default output for nodes that might be sources
            # (detected by checking if other nodes reference them)
            has_outgoing = any(
                isinstance(inp, list) and len(inp) == 2 and str(inp[0]) == node_id
                for n in api_workflow.values()
                for inp in n.get("inputs", {}).values()
            )
            if has_outgoing or "SaveImage" not in class_type:
                node_outputs.append(UINodeSlot(name="output", type="*", links=[], slot_index=0))
    
            # Create UI node
            ui_node = UINode(
                id=int_id,
                type=class_type,
                pos=(pos_x, pos_y),
                size=size,
                flags=UINodeFlags(),
                order=idx,
                mode=0,
                inputs=node_inputs,
                outputs=node_outputs,
                properties={"Node name for S&R": class_type},
                widgets_values=widgets_values if widgets_values else None,
            )
            ui_nodes.append(ui_node)
    
        # Update output links on source nodes
        for link in ui_links:
            for node in ui_nodes:
                if node.id == link.origin_id and node.outputs:
                    if node.outputs[link.origin_slot].links is None:
                        node.outputs[link.origin_slot].links = []
                    node.outputs[link.origin_slot].links.append(link.id)
    
        # Build UI workflow (SerialisableGraph version 1 format)
        max_node_id = max(node_id_map.values()) if node_id_map else 0
        ui_workflow = UIWorkflow(
            version=1,
            state=UIWorkflowState(
                lastNodeId=max_node_id,
                lastLinkId=link_id,
                lastGroupId=0,
                lastRerouteId=0,
            ),
            nodes=ui_nodes,
            links=ui_links,
            groups=[],
            extra=UIWorkflowExtra(),
        )
    
        return ui_workflow.to_dict()

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/IO-AtelierTech/comfyui-mcp'

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