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