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")'
        )
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden. It discloses that the tool 'dynamically inspects the diagrams package,' describes the hierarchical return structure, and explains efficiency benefits. However, it doesn't mention potential limitations like rate limits, error conditions, or authentication needs, which could be relevant for a dynamic inspection tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with clear sections (purpose, usage instructions, example workflow, returns) and uses bullet points effectively. It's appropriately sized for a tool with two parameters and complex usage patterns. Minor verbosity in the example workflow could be tightened, but overall it's efficient and front-loaded with key information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's moderate complexity (2 parameters, no output schema, no annotations), the description is largely complete. It explains the purpose, usage, parameters, and return format. The main gap is lack of output schema, but the description compensates by describing the return structure. It could benefit from more behavioral context (e.g., error handling), but covers core needs adequately.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents both parameters thoroughly. The description adds value by explaining how parameters work together in a workflow (e.g., 'Call with provider_filter to get all services and icons for that provider'), but doesn't provide additional semantic details beyond what's in the schema. This meets the baseline for high schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'List available icons from the diagrams package, with optional filtering.' It specifies the verb ('list'), resource ('icons from the diagrams package'), and scope ('available providers, services, and icons'). It distinguishes from sibling tools like 'generate_diagram' and 'get_diagram_examples' by focusing on listing rather than generating or retrieving examples.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit usage instructions with three scenarios (no filters, provider_filter only, both filters) and an example workflow. It explains when to use this tool ('more efficient than loading all icons at once') and implies alternatives by distinguishing from sibling tools. The guidance is clear and actionable for an AI agent.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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