list_icons
Browse and filter available icons for AWS architecture diagrams. Use provider and service filters to efficiently find specific cloud infrastructure symbols for visualization needs.
Instructions
List available icons from the diagrams package, with optional filtering.
This tool dynamically inspects the diagrams package to find available providers, services, and icons that can be used in diagrams.
USAGE INSTRUCTIONS:
Call without filters to get a list of available providers
Call with provider_filter to get all services and icons for that provider
Call with both provider_filter and service_filter to get icons for a specific service
Example workflow:
First call: list_icons() → Returns all available providers
Second call: list_icons(provider_filter="aws") → Returns all AWS services and icons
Third call: list_icons(provider_filter="aws", service_filter="compute") → Returns AWS compute icons
This approach is more efficient than loading all icons at once, especially when you only need icons from specific providers or services.
Returns: Dictionary with available providers, services, and icons organized hierarchically
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| provider_filter | No | Filter icons by provider name (e.g., "aws", "gcp", "k8s") | |
| service_filter | No | Filter icons by service name (e.g., "compute", "database", "network") |
Implementation Reference
- Core handler function that dynamically scans the 'diagrams' package directory structure, imports provider service modules, and uses inspect to find icon classes (those with '_icon' attribute). Supports hierarchical filtering by provider and service, returning structured providers/services/icons data.def list_diagram_icons( provider_filter: Optional[str] = None, service_filter: Optional[str] = None ) -> DiagramIconsResponse: """List available icons from the diagrams package, with optional filtering. Args: provider_filter: Optional filter by provider name (e.g., "aws", "gcp") service_filter: Optional filter by service name (e.g., "compute", "database") Returns: DiagramIconsResponse: Dictionary with available providers, services, and icons """ logger.debug('Starting list_diagram_icons function') logger.debug(f'Filters - provider: {provider_filter}, service: {service_filter}') try: # If no filters provided, just return the list of available providers if not provider_filter and not service_filter: # Get the base path of the diagrams package diagrams_path = os.path.dirname(diagrams.__file__) providers = {} # List of provider directories to exclude exclude_dirs = ['__pycache__', '_template'] # Just list the available providers without their services/icons for provider_name in os.listdir(os.path.join(diagrams_path)): provider_path = os.path.join(diagrams_path, provider_name) # Skip non-directories and excluded directories if ( not os.path.isdir(provider_path) or provider_name.startswith('_') or provider_name in exclude_dirs ): continue # Add provider to the dictionary with empty services providers[provider_name] = {} return DiagramIconsResponse(providers=providers, filtered=False, filter_info=None) # Dictionary to store filtered providers and their services/icons providers = {} # Get the base path of the diagrams package diagrams_path = os.path.dirname(diagrams.__file__) # List of provider directories to exclude exclude_dirs = ['__pycache__', '_template'] # If only provider filter is specified if provider_filter and not service_filter: provider_path = os.path.join(diagrams_path, provider_filter) # Check if the provider exists if not os.path.isdir(provider_path) or provider_filter in exclude_dirs: return DiagramIconsResponse( providers={}, filtered=True, filter_info={'provider': provider_filter, 'error': 'Provider not found'}, ) # Add provider to the dictionary providers[provider_filter] = {} # Iterate through all service modules in the provider for service_file in os.listdir(provider_path): # Skip non-Python files and special files if not service_file.endswith('.py') or service_file.startswith('_'): continue service_name = service_file[:-3] # Remove .py extension # Import the service module module_path = f'diagrams.{provider_filter}.{service_name}' try: service_module = importlib.import_module( # nosem: python.lang.security.audit.non-literal-import.non-literal-import module_path # nosem: python.lang.security.audit.non-literal-import.non-literal-import ) # nosem: python.lang.security.audit.non-literal-import.non-literal-import # Find all classes in the module that are Node subclasses icons = [] for name, obj in inspect.getmembers(service_module): # Skip private members and imported modules if name.startswith('_') or inspect.ismodule(obj): continue # Check if it's a class and likely a Node subclass if inspect.isclass(obj) and hasattr(obj, '_icon'): icons.append(name) # Add service and its icons to the provider if icons: providers[provider_filter][service_name] = sorted(icons) except (ImportError, AttributeError, Exception) as e: logger.error(f'Error processing {module_path}: {str(e)}') continue return DiagramIconsResponse( providers=providers, filtered=True, filter_info={'provider': provider_filter} ) # If both provider and service filters are specified elif provider_filter and service_filter: provider_path = os.path.join(diagrams_path, provider_filter) # Check if the provider exists if not os.path.isdir(provider_path) or provider_filter in exclude_dirs: return DiagramIconsResponse( providers={}, filtered=True, filter_info={ 'provider': provider_filter, 'service': service_filter, 'error': 'Provider not found', }, ) # Add provider to the dictionary providers[provider_filter] = {} # Check if the service exists service_file = f'{service_filter}.py' service_path = os.path.join(provider_path, service_file) if not os.path.isfile(service_path): return DiagramIconsResponse( providers={provider_filter: {}}, filtered=True, filter_info={ 'provider': provider_filter, 'service': service_filter, 'error': 'Service not found', }, ) # Import the service module module_path = f'diagrams.{provider_filter}.{service_filter}' try: service_module = importlib.import_module( # nosem: python.lang.security.audit.non-literal-import.non-literal-import module_path # nosem: python.lang.security.audit.non-literal-import.non-literal-import ) # nosem: python.lang.security.audit.non-literal-import.non-literal-import # Find all classes in the module that are Node subclasses icons = [] for name, obj in inspect.getmembers(service_module): # Skip private members and imported modules if name.startswith('_') or inspect.ismodule(obj): continue # Check if it's a class and likely a Node subclass if inspect.isclass(obj) and hasattr(obj, '_icon'): icons.append(name) # Add service and its icons to the provider if icons: providers[provider_filter][service_filter] = sorted(icons) except (ImportError, AttributeError, Exception) as e: logger.error(f'Error processing {module_path}: {str(e)}') return DiagramIconsResponse( providers={provider_filter: {}}, filtered=True, filter_info={ 'provider': provider_filter, 'service': service_filter, 'error': f'Error loading service: {str(e)}', }, ) return DiagramIconsResponse( providers=providers, filtered=True, filter_info={'provider': provider_filter, 'service': service_filter}, ) # If only service filter is specified (not supported) elif service_filter: return DiagramIconsResponse( providers={}, filtered=True, filter_info={ 'service': service_filter, 'error': 'Service filter requires provider filter', }, ) # Original implementation for backward compatibility else: # Dictionary to store all providers and their services/icons providers = {} # Get the base path of the diagrams package diagrams_path = os.path.dirname(diagrams.__file__) logger.debug(f'Diagrams package path: {diagrams_path}') # Iterate through all provider directories for provider_name in os.listdir(os.path.join(diagrams_path)): provider_path = os.path.join(diagrams_path, provider_name) # Skip non-directories and excluded directories if ( not os.path.isdir(provider_path) or provider_name.startswith('_') or provider_name in exclude_dirs ): logger.debug(f'Skipping {provider_name}: not a directory or in exclude list') continue # Add provider to the dictionary providers[provider_name] = {} logger.debug(f'Processing provider: {provider_name}') # Iterate through all service modules in the provider for service_file in os.listdir(provider_path): # Skip non-Python files and special files if not service_file.endswith('.py') or service_file.startswith('_'): logger.debug( f'Skipping file {service_file}: not a Python file or starts with _' ) continue service_name = service_file[:-3] # Remove .py extension logger.debug(f'Processing service: {provider_name}.{service_name}') # Import the service module module_path = f'diagrams.{provider_name}.{service_name}' try: logger.debug(f'Attempting to import module: {module_path}') service_module = importlib.import_module( # nosem: python.lang.security.audit.non-literal-import.non-literal-import module_path # nosem: python.lang.security.audit.non-literal-import.non-literal-import ) # nosem: python.lang.security.audit.non-literal-import.non-literal-import # Find all classes in the module that are Node subclasses icons = [] for name, obj in inspect.getmembers(service_module): # Skip private members and imported modules if name.startswith('_') or inspect.ismodule(obj): continue # Check if it's a class and likely a Node subclass if inspect.isclass(obj) and hasattr(obj, '_icon'): icons.append(name) logger.debug(f'Found icon: {name}') # Add service and its icons to the provider if icons: providers[provider_name][service_name] = sorted(icons) logger.debug( f'Added {len(icons)} icons for {provider_name}.{service_name}' ) else: logger.warning(f'No icons found for {provider_name}.{service_name}') except ImportError as ie: logger.error(f'ImportError for {module_path}: {str(ie)}') continue except AttributeError as ae: logger.error(f'AttributeError for {module_path}: {str(ae)}') continue except Exception as e: logger.error(f'Unexpected error processing {module_path}: {str(e)}') continue logger.debug(f'Completed processing. Found {len(providers)} providers') return DiagramIconsResponse(providers=providers, filtered=False, filter_info=None) except Exception as e: logger.exception(f'Error in list_diagram_icons: {str(e)}') # Return empty response on error return DiagramIconsResponse(providers={}, filtered=False, filter_info={'error': str(e)})
- aws_diagram_mcp_server/server.py:227-227 (registration)MCP decorator that registers the 'list_icons' tool, binding it to the mcp_list_diagram_icons handler function.@mcp.tool(name='list_icons')
- aws_diagram_mcp_server/server.py:227-264 (handler)MCP tool handler wrapper that validates inputs via Pydantic Fields, calls the core list_diagram_icons function, and serializes the response.@mcp.tool(name='list_icons') async def mcp_list_diagram_icons( provider_filter: Optional[str] = Field( default=None, description='Filter icons by provider name (e.g., "aws", "gcp", "k8s")' ), service_filter: Optional[str] = Field( default=None, description='Filter icons by service name (e.g., "compute", "database", "network")', ), ): """List available icons from the diagrams package, with optional filtering. This tool dynamically inspects the diagrams package to find available providers, services, and icons that can be used in diagrams. USAGE INSTRUCTIONS: 1. Call without filters to get a list of available providers 2. Call with provider_filter to get all services and icons for that provider 3. Call with both provider_filter and service_filter to get icons for a specific service Example workflow: - First call: list_icons() → Returns all available providers - Second call: list_icons(provider_filter="aws") → Returns all AWS services and icons - Third call: list_icons(provider_filter="aws", service_filter="compute") → Returns AWS compute icons This approach is more efficient than loading all icons at once, especially when you only need icons from specific providers or services. Returns: Dictionary with available providers, services, and icons organized hierarchically """ # Extract the actual values from the parameters provider_filter_value = None if provider_filter is None else provider_filter service_filter_value = None if service_filter is None else service_filter result = list_diagram_icons(provider_filter_value, service_filter_value) return result.model_dump()
- Pydantic model defining the output schema for list_icons: hierarchical dict of providers -> services -> icons list, with filter metadata.class DiagramIconsResponse(BaseModel): """Response model for listing available diagram icons.""" providers: Dict[str, Dict[str, List[str]]] filtered: bool = False filter_info: Optional[Dict[str, str]] = None
- Pydantic model defining the input schema for list_icons tool (though primarily used via Field in handler).class DiagramIconsRequest(BaseModel): """Request model for listing available diagram icons.""" provider_filter: Optional[str] = Field( None, description='Filter icons by provider name (e.g., "aws", "gcp", "k8s")' ) service_filter: Optional[str] = Field( None, description='Filter icons by service name (e.g., "compute", "database", "network")' )