Skip to main content
Glama
lukeburciu

AWS Diagram MCP Server

by lukeburciu

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:

  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

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
provider_filterNoFilter icons by provider name (e.g., "aws", "gcp", "k8s")
service_filterNoFilter 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)})
  • MCP decorator that registers the 'list_icons' tool, binding it to the mcp_list_diagram_icons handler function.
    @mcp.tool(name='list_icons')
  • 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")'
        )

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/lukeburciu/aws-diagrams-mcp-server'

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