Skip to main content
Glama

create_diagram_with_custom_icons

Create infrastructure diagrams by adding custom icons from URLs or local files to represent services, brands, or logos not available in standard diagram libraries.

Instructions

Create diagrams with custom icons from web URLs or local files.

USE WHEN: Brand logos (Stripe, Vercel, Supabase, Fly.io) not in diagrams library. GitHub avatars work well: https://avatars.githubusercontent.com/u/{org_id}

Examples: URL: custom_nodes=[{"id":"stripe","icon_source":"url","icon_path":"https://avatars.githubusercontent.com/u/856813"}] Mixed: nodes=[{...AWS nodes...}], custom_nodes=[{...}], connections=[...]

HTTPS-only for URLs, 5MB limit, PNG/JPG supported. Automatic caching.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
nameYesDiagram title
custom_nodesYesCustom nodes with icon URLs/paths
connectionsYesConnections between nodes
nodesNoOptional standard provider nodes to mix with custom
clustersNoOptional clusters
directionNoLayout directionLR
curvestyleNoEdge styleortho
output_formatNoOutput format(s): png, pdf, jpg, dotpng
output_dirNoOutput directory (default: current directory). Auto-created if missing.
graph_attrNoGraph attributes
return_base64NoReturn base64 images

Implementation Reference

  • Main handler function implementing the tool: loads custom icons via IconManager, validates nodes/connections, generates diagram using Diagrams library with Custom nodes, handles mixed standard/custom nodes, outputs files with metadata and optional base64 encoding.
    async def create_diagram_with_custom_icons( name: Annotated[str, Field(description="Diagram title")], custom_nodes: Annotated[ List[CustomNodeDef], Field(description="Custom nodes with icon URLs/paths") ], connections: Annotated[List[ConnectionDef], Field(description="Connections between nodes")], nodes: Annotated[ Optional[List[NodeDef]], Field(description="Optional standard provider nodes to mix with custom"), ] = None, clusters: Annotated[Optional[List[ClusterDef]], Field(description="Optional clusters")] = None, direction: Annotated[ Literal["LR", "RL", "TB", "BT"], Field(description="Layout direction") ] = "LR", curvestyle: Annotated[Literal["ortho", "curved"], Field(description="Edge style")] = "ortho", output_format: Annotated[ str | List[str], Field(description="Output format(s): png, pdf, jpg, dot") ] = "png", output_dir: Annotated[ Optional[str], Field( description="Output directory (default: current directory). Auto-created if missing." ), ] = None, graph_attr: Annotated[Optional[Dict[str, Any]], Field(description="Graph attributes")] = None, return_base64: Annotated[bool, Field(description="Return base64 images")] = False, ) -> str: """Generate diagram with custom node icons.""" start_time = time.time() try: from diagrams.custom import Custom # Initialize icon manager icon_manager = IconManager() # Validate all custom icons and get paths custom_icon_paths = {} for custom_node in custom_nodes: try: icon_path = icon_manager.get_icon_path( custom_node.icon_source, custom_node.icon_path, cache=custom_node.cache_icons, ) custom_icon_paths[custom_node.id] = icon_path except Exception as e: return format_error( f"Failed to load icon for node '{custom_node.id}': {str(e)}", suggestion="Check icon_path is valid HTTPS URL or existing local file", ) # Build combined node ID set node_ids = {node.id for node in custom_nodes} if nodes: node_ids.update({node.id for node in nodes}) # Validate connections for conn in connections: if conn.from_node not in node_ids: raise ValueError(f"Connection references unknown node '{conn.from_node}'") targets = [conn.to_node] if isinstance(conn.to_node, str) else conn.to_node for target in targets: if target not in node_ids: raise ValueError(f"Connection references unknown node '{target}'") # Prepare output formats = [output_format] if isinstance(output_format, str) else output_format # Reject SVG - it's buggy and unsupported if any("svg" in fmt.lower() for fmt in formats): raise ValueError("SVG output is not supported. Use png, pdf, jpg, or dot instead.") # Change directory if needed original_dir = os.getcwd() if output_dir: os.makedirs(output_dir, exist_ok=True) os.chdir(output_dir) try: # Hide diagram title by setting label to empty string default_graph_attr = {"label": ""} merged_graph_attr = {**default_graph_attr, **(graph_attr or {})} with Diagram( name=name, show=False, direction=direction, curvestyle=curvestyle, outformat=formats, graph_attr=merged_graph_attr, ) as _: # Create all nodes node_objects = {} # Create custom nodes for custom_node in custom_nodes: icon_path = custom_icon_paths[custom_node.id] node_obj = Custom(custom_node.label, icon_path) node_objects[custom_node.id] = node_obj # Create standard nodes if provided if nodes: for node in nodes: NodeClass = import_node_class(node.provider, node.category, node.type) node_obj = NodeClass(node.label) node_objects[node.id] = node_obj # Create connections edge_count = 0 for conn in connections: from_obj = node_objects[conn.from_node] targets = [conn.to_node] if isinstance(conn.to_node, str) else conn.to_node for target in targets: to_obj = node_objects[target] if conn.label or conn.color or conn.style: edge = Edge( label=conn.label or "", color=conn.color or "black", style=conn.style or "solid", ) if conn.direction == "forward": _ = from_obj >> edge >> to_obj elif conn.direction == "reverse": _ = from_obj << edge << to_obj else: _ = from_obj - edge - to_obj else: if conn.direction == "forward": _ = from_obj >> to_obj elif conn.direction == "reverse": _ = from_obj << to_obj else: _ = from_obj - to_obj edge_count += 1 # Get file paths diagram_filename = name.replace(" ", "_").replace("-", "_").lower() file_paths = [] for fmt in formats: file_path = f"{diagram_filename}.{fmt}" if output_dir: file_path = os.path.join(output_dir, file_path) file_paths.append(os.path.abspath(file_path)) # Build metadata generation_time_ms = (time.time() - start_time) * 1000 total_nodes = len(custom_nodes) + (len(nodes) if nodes else 0) metadata = build_diagram_metadata( file_paths, node_count=total_nodes, edge_count=edge_count, cluster_count=len(clusters) if clusters else 0, generation_time_ms=generation_time_ms, ) # Base64 encoding if requested base64_images = None if return_base64: base64_images = {} for path in file_paths: ext = Path(path).suffix[1:] if ext != "dot": try: base64_images[ext] = encode_file_base64(path) except Exception: pass return format_diagram_result(file_paths, metadata, base64_images) finally: if output_dir: os.chdir(original_dir) except Exception as e: return format_error(f"Failed to generate diagram with custom icons: {str(e)}")
  • Tool registration decorator (@mcp.tool) that registers the function with MCP server, including name, description, and parameter annotations defining the input schema.
    @mcp.tool( name="create_diagram_with_custom_icons", description="""Create diagrams with custom icons from web URLs or local files. USE WHEN: Brand logos (Stripe, Vercel, Supabase, Fly.io) not in diagrams library. GitHub avatars work well: https://avatars.githubusercontent.com/u/{org_id} Examples: URL: custom_nodes=[{"id":"stripe","icon_source":"url","icon_path":"https://avatars.githubusercontent.com/u/856813"}] Mixed: nodes=[{...AWS nodes...}], custom_nodes=[{...}], connections=[...] HTTPS-only for URLs, 5MB limit, PNG/JPG supported. Automatic caching.""", annotations={ "readOnlyHint": False, "destructiveHint": False, "idempotentHint": True, }, )
  • Input schema defined via Pydantic Field annotations in the tool decorator and function signature, referencing validator models like CustomNodeDef, ConnectionDef.
    @mcp.tool( name="create_diagram_with_custom_icons", description="""Create diagrams with custom icons from web URLs or local files. USE WHEN: Brand logos (Stripe, Vercel, Supabase, Fly.io) not in diagrams library. GitHub avatars work well: https://avatars.githubusercontent.com/u/{org_id} Examples: URL: custom_nodes=[{"id":"stripe","icon_source":"url","icon_path":"https://avatars.githubusercontent.com/u/856813"}] Mixed: nodes=[{...AWS nodes...}], custom_nodes=[{...}], connections=[...] HTTPS-only for URLs, 5MB limit, PNG/JPG supported. Automatic caching.""", annotations={ "readOnlyHint": False, "destructiveHint": False, "idempotentHint": True, }, )
  • Imports IconManager used for handling custom icon loading, downloading, caching from URLs or local paths.
    from ..core.icon_manager import IconManager
  • Documentation reference in available_tools resource recommending the tool for custom icons (e.g., brands).
    create_diagram_with_custom_icons: Create diagrams with custom node icons

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/apetta/diagrams-mcp'

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