Skip to main content
Glama
stv-io

AWS Terraform MCP Server

by stv-io

SearchSpecificAwsIaModules

Find and retrieve detailed information about four specific AWS-IA Terraform modules for Bedrock, OpenSearch Serverless, SageMaker endpoints, and serverless Streamlit applications.

Instructions

Search for specific AWS-IA Terraform modules.

This tool checks for information about four specific AWS-IA modules:
- aws-ia/bedrock/aws - Amazon Bedrock module for generative AI applications
- aws-ia/opensearch-serverless/aws - OpenSearch Serverless collection for vector search
- aws-ia/sagemaker-endpoint/aws - SageMaker endpoint deployment module
- aws-ia/serverless-streamlit-app/aws - Serverless Streamlit application deployment

It returns detailed information about these modules, including their README content,
variables.tf content, and submodules when available.

The search is performed across module names, descriptions, README content, and variable
definitions. This allows you to find modules based on their functionality or specific
configuration options.

Examples:
    - To get information about all four modules:
      search_specific_aws_ia_modules()

    - To find modules related to Bedrock:
      search_specific_aws_ia_modules(query='bedrock')

    - To find modules related to vector search:
      search_specific_aws_ia_modules(query='vector search')

    - To find modules with specific configuration options:
      search_specific_aws_ia_modules(query='endpoint_name')

Parameters:
    query: Optional search term to filter modules (empty returns all four modules)

Returns:
    A list of matching modules with their details, including:
    - Basic module information (name, namespace, version)
    - Module documentation (README content)
    - Input and output parameter counts
    - Variables from variables.tf with descriptions and default values
    - Submodules information
    - Version details and release information

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYesOptional search term to filter modules (empty returns all four modules)

Implementation Reference

  • Registration of the 'SearchSpecificAwsIaModules' tool using @mcp.tool decorator. Defines input schema (query: str) and output type List[ModuleSearchResult]. Includes comprehensive docstring. Delegates execution to the implementation function.
    @mcp.tool(name='SearchSpecificAwsIaModules')
    async def search_specific_aws_ia_modules(
        query: str = Field(
            ..., description='Optional search term to filter modules (empty returns all four modules)'
        ),
    ) -> List[ModuleSearchResult]:
        """Search for specific AWS-IA Terraform modules.
    
        This tool checks for information about four specific AWS-IA modules:
        - aws-ia/bedrock/aws - Amazon Bedrock module for generative AI applications
        - aws-ia/opensearch-serverless/aws - OpenSearch Serverless collection for vector search
        - aws-ia/sagemaker-endpoint/aws - SageMaker endpoint deployment module
        - aws-ia/serverless-streamlit-app/aws - Serverless Streamlit application deployment
    
        It returns detailed information about these modules, including their README content,
        variables.tf content, and submodules when available.
    
        The search is performed across module names, descriptions, README content, and variable
        definitions. This allows you to find modules based on their functionality or specific
        configuration options.
    
        Examples:
            - To get information about all four modules:
              search_specific_aws_ia_modules()
    
            - To find modules related to Bedrock:
              search_specific_aws_ia_modules(query='bedrock')
    
            - To find modules related to vector search:
              search_specific_aws_ia_modules(query='vector search')
    
            - To find modules with specific configuration options:
              search_specific_aws_ia_modules(query='endpoint_name')
    
        Parameters:
            query: Optional search term to filter modules (empty returns all four modules)
    
        Returns:
            A list of matching modules with their details, including:
            - Basic module information (name, namespace, version)
            - Module documentation (README content)
            - Input and output parameter counts
            - Variables from variables.tf with descriptions and default values
            - Submodules information
            - Version details and release information
        """
        return await search_specific_aws_ia_modules_impl(query)
  • Core handler implementation `search_specific_aws_ia_modules_impl`. Fetches detailed info (README, variables.tf, submodules, outputs) for four specific AWS-IA modules (bedrock, opensearch-serverless, sagemaker-endpoint, serverless-streamlit-app) from Terraform Registry and GitHub. Filters results based on query matching names, descriptions, README, variables.
    async def search_specific_aws_ia_modules_impl(query: str) -> List[ModuleSearchResult]:
        """Search for specific AWS-IA Terraform modules.
    
        This tool checks for information about four specific AWS-IA modules:
        - aws-ia/bedrock/aws - Amazon Bedrock module for generative AI applications
        - aws-ia/opensearch-serverless/aws - OpenSearch Serverless collection for vector search
        - aws-ia/sagemaker-endpoint/aws - SageMaker endpoint deployment module
        - aws-ia/serverless-streamlit-app/aws - Serverless Streamlit application deployment
    
        It returns detailed information about these modules, including their README content,
        variables.tf content, and submodules when available.
    
        The search is performed across module names, descriptions, README content, and variable
        definitions. This allows you to find modules based on their functionality or specific
        configuration options.
    
        The implementation fetches module information directly from the Terraform Registry API
        and GitHub repositories to ensure the most up-to-date information. Results include
        comprehensive details about each module's structure, configuration options, and usage examples.
    
        Examples:
            - To get information about all four modules:
              search_specific_aws_ia_modules_impl(query='')
    
            - To find modules related to Bedrock:
              search_specific_aws_ia_modules_impl(query='bedrock')
    
            - To find modules related to vector search:
              search_specific_aws_ia_modules_impl(query='vector search')
    
            - To find modules with specific configuration options:
              search_specific_aws_ia_modules_impl(query='endpoint_name')
    
        Parameters:
            query: Optional search term to filter modules (empty returns all four modules)
    
        Returns:
            A list of matching modules with their details, including:
            - Basic module information (name, namespace, version)
            - Module documentation (README content)
            - Input and output parameter counts
            - Variables from variables.tf with descriptions and default values
            - Submodules information
            - Version details and release information
        """
        logger.info(f"Searching for specific AWS-IA modules with query: '{query}'")
    
        tasks = []
    
        # Create tasks for fetching module information
        for module_info in SPECIFIC_MODULES:
            tasks.append(get_specific_module_info(module_info))
    
        # Run all tasks concurrently
        module_results = await asyncio.gather(*tasks)
    
        # Filter out None results (modules not found)
        module_results = [result for result in module_results if result is not None]
    
        # If query is provided, filter results
        if query and query.strip():
            query_terms = query.lower().split()
            filtered_results = []
    
            for result in module_results:
                # Check if any query term is in the module name, description, readme, or variables
                matches = False
    
                # Build search text from module details and variables
                search_text = (
                    f'{result.name} {result.description} {result.readme_content or ""}'.lower()
                )
    
                # Add variables information to search text if available
                if result.variables:
                    for var in result.variables:
                        var_text = f'{var.name} {var.type or ""} {var.description or ""}'
                        search_text += f' {var_text.lower()}'
    
                # Add variables.tf content to search text if available
                if result.variables_content:
                    search_text += f' {result.variables_content.lower()}'
    
                # Add outputs information to search text if available
                if result.outputs:
                    for output in result.outputs:
                        output_text = f'{output.name} {output.description or ""}'
                        search_text += f' {output_text.lower()}'
    
                for term in query_terms:
                    if term in search_text:
                        matches = True
                        break
    
                if matches:
                    filtered_results.append(result)
    
            logger.info(
                f"Found {len(filtered_results)} modules matching query '{query}' out of {len(module_results)} total modules"
            )
            return filtered_results
        else:
            logger.info(f'Returning all {len(module_results)} specific modules (no query filter)')
            return module_results
  • Configuration list defining the four specific AWS-IA modules targeted by the tool.
    SPECIFIC_MODULES = [
        {'namespace': 'aws-ia', 'name': 'bedrock', 'provider': 'aws'},
        {'namespace': 'aws-ia', 'name': 'opensearch-serverless', 'provider': 'aws'},
        {'namespace': 'aws-ia', 'name': 'sagemaker-endpoint', 'provider': 'aws'},
        {'namespace': 'aws-ia', 'name': 'serverless-streamlit-app', 'provider': 'aws'},
    ]
  • Helper function to fetch comprehensive module details including API data, GitHub README/variables/submodules, outputs extraction.
    async def get_module_details(namespace: str, name: str, provider: str = 'aws') -> Dict:
        """Fetch detailed information about a specific Terraform module.
    
        Args:
            namespace: The module namespace (e.g., aws-ia)
            name: The module name (e.g., vpc)
            provider: The provider (default: aws)
    
        Returns:
            Dictionary containing module details including README content and submodules
        """
        logger.info(f'Fetching details for module {namespace}/{name}/{provider}')
    
        try:
            # Get basic module info via API
            details_url = f'https://registry.terraform.io/v1/modules/{namespace}/{name}/{provider}'
            logger.debug(f'Making API request to: {details_url}')
    
            response = requests.get(details_url)
            response.raise_for_status()
    
            details = response.json()
            logger.debug(
                f'Received module details. Status code: {response.status_code}, Content size: {len(response.text)} bytes'
            )
    
            # Debug log the version info we initially have
            initial_version = details.get('latest_version', 'unknown')
            if 'latest' in details and 'version' in details['latest']:
                initial_version = details['latest']['version']
            logger.debug(f'Initial version from primary API: {initial_version}')
    
            # Add additional API call to get the latest version if not in details
            if 'latest' not in details or 'version' not in details.get('latest', {}):
                versions_url = f'{details_url}/versions'
                logger.debug(f'Making API request to get versions: {versions_url}')
    
                versions_response = requests.get(versions_url)
                logger.debug(f'Versions API response code: {versions_response.status_code}')
    
                if versions_response.status_code == 200:
                    versions_data = versions_response.json()
                    logger.debug(
                        f'Received versions data with {len(versions_data.get("modules", []))} module versions'
                    )
    
                    if versions_data.get('modules') and len(versions_data['modules']) > 0:
                        latest_version = versions_data['modules'][0].get('version', '')
                        details['latest_version'] = latest_version
                        logger.debug(f'Updated latest version to: {latest_version}')
                    else:
                        logger.debug('No modules found in versions response')
                else:
                    logger.debug(
                        f'Failed to fetch versions. Status code: {versions_response.status_code}'
                    )
            else:
                logger.debug('Latest version already available in primary API response')
    
            # Try to get README content and version details, starting with direct API if available
            readme_content = None
            version_details = None
            version_from_github = ''
    
            # APPROACH 1: Try to see if the registry API provides README content directly
            logger.debug('APPROACH 1: Checking for README content in API response')
            if 'readme' in details and details['readme']:
                readme_content = details['readme']
                logger.info(
                    f'Found README content directly in API response: {len(readme_content)} chars'
                )
    
            # APPROACH 2: Try using the GitHub repo URL for README content and version details
            if 'source' in details:
                source_url = details.get('source')
                # Properly validate GitHub URL using regex to ensure it's actually from github.com domain
                if isinstance(source_url, str) and re.match(r'https://github.com/', source_url):
                    logger.info(f'Found GitHub source URL: {source_url}')
    
                    # Extract GitHub owner and repo
                    github_parts = re.match(r'https://github.com/([^/]+)/([^/]+)', source_url)
                    if github_parts:
                        owner, repo = github_parts.groups()
                        logger.info(f'Extracted GitHub repo: {owner}/{repo}')
    
                        # Get version details from GitHub
                        github_version_info = await get_github_release_details(owner, repo)
                        version_details = github_version_info['details']
                        version_from_github = github_version_info['version']
    
                        if version_from_github:
                            logger.info(f'Found version from GitHub: {version_from_github}')
                            details['latest_version'] = version_from_github
    
                        # Get variables.tf content and parsed variables
                        variables_content, variables = await get_variables_tf(owner, repo, 'main')
                        if variables_content and variables:
                            logger.info(f'Found variables.tf with {len(variables)} variables')
                            details['variables_content'] = variables_content
                            details['variables'] = [var.dict() for var in variables]
                        else:
                            # Try master branch as fallback if main didn't work
                            variables_content, variables = await get_variables_tf(
                                owner, repo, 'master'
                            )
                            if variables_content and variables:
                                logger.info(
                                    f'Found variables.tf in master branch with {len(variables)} variables'
                                )
                                details['variables_content'] = variables_content
                                details['variables'] = [var.dict() for var in variables]
    
                        # If README content not already found, try fetching it from GitHub
                        if not readme_content:
                            logger.debug(
                                f'APPROACH 2: Fetching README from GitHub source: {source_url}'
                            )
    
                            # Convert HTTPS URL to raw content URL
                            try:
                                # Try main branch first, then fall back to master if needed
                                found_readme_branch = None
                                for branch in ['main', 'master']:
                                    raw_readme_url = f'https://raw.githubusercontent.com/{owner}/{repo}/{branch}/README.md'
                                    logger.debug(f'Trying to fetch README from: {raw_readme_url}')
    
                                    readme_response = requests.get(raw_readme_url)
                                    if readme_response.status_code == 200:
                                        readme_content = readme_response.text
                                        found_readme_branch = branch
                                        logger.info(
                                            f'Successfully fetched README from GitHub ({branch}): {len(readme_content)} chars'
                                        )
                                        break
    
                                # Look for submodules now that we have identified the main branch
                                if found_readme_branch:
                                    logger.info(
                                        f'Fetching submodules using {found_readme_branch} branch'
                                    )
                                    start_time = time.time()
                                    submodules = await get_submodules(owner, repo, found_readme_branch)
                                    if submodules:
                                        logger.info(
                                            f'Found {len(submodules)} submodules in {time.time() - start_time:.2f} seconds'
                                        )
                                        details['submodules'] = [
                                            submodule.dict() for submodule in submodules
                                        ]
                                    else:
                                        logger.info('No submodules found')
                                else:
                                    # Try both main branches for submodules if readme wasn't found
                                    for branch in ['main', 'master']:
                                        logger.debug(f'Trying {branch} branch for submodules')
                                        start_time = time.time()
                                        submodules = await get_submodules(owner, repo, branch)
                                        if submodules:
                                            logger.info(
                                                f'Found {len(submodules)} submodules in {branch} branch in {time.time() - start_time:.2f} seconds'
                                            )
                                            details['submodules'] = [
                                                submodule.dict() for submodule in submodules
                                            ]
                                            break
                            except Exception as ex:
                                logger.error(f'Error fetching README from GitHub: {ex}')
                                logger.debug(f'Stack trace: {traceback.format_exc()}')
    
            # Process content we've gathered
    
            # Add readme_content to details if available
            if readme_content:
                logger.info(f'Successfully extracted README content ({len(readme_content)} chars)')
                logger.debug(f'First 100 characters of README: {readme_content[:100]}...')
    
                # Extract outputs from README content
                outputs = extract_outputs_from_readme(readme_content)
                if outputs:
                    logger.info(f'Extracted {len(outputs)} outputs from README')
                    details['outputs'] = outputs
                else:
                    logger.info('No outputs found in README')
    
                # Trim if too large
                if len(readme_content) > 8000:
                    logger.debug(
                        f'README content exceeds 8000 characters ({len(readme_content)}), truncating...'
                    )
                    readme_content = readme_content[:8000] + '...\n[README truncated due to length]'
                    logger.debug('README content truncated')
    
                details['readme_content'] = readme_content
            else:
                logger.warning('No README content found through any method')
    
            # Add version details if available
            if version_details:
                logger.info('Adding version details to response')
                logger.debug(f'Version details: {version_details}')
                details['version_details'] = version_details
    
            return details
    
        except Exception as e:
            logger.error(f'Error fetching module details: {e}')
            # Add stack trace for debugging
            logger.debug(f'Stack trace: {traceback.format_exc()}')
            return {}
  • Helper that wraps get_module_details to produce structured ModuleSearchResult for a single module.
    async def get_specific_module_info(module_info: Dict[str, str]) -> Optional[ModuleSearchResult]:
        """Get detailed information about a specific module.
    
        Args:
            module_info: Dictionary with namespace, name, and provider of the module
    
        Returns:
            ModuleSearchResult object with module details or None if module not found
        """
        namespace = module_info['namespace']
        name = module_info['name']
        provider = module_info['provider']
    
        try:
            # First, check if the module exists
            details_url = f'https://registry.terraform.io/v1/modules/{namespace}/{name}/{provider}'
            response = requests.get(details_url)
    
            if response.status_code != 200:
                logger.warning(
                    f'Module {namespace}/{name}/{provider} not found (status code: {response.status_code})'
                )
                return None
    
            module_data = response.json()
    
            # Get the description and clean it
            description = module_data.get('description', 'No description available')
            cleaned_description = clean_description(description)
    
            # Create the basic result
            result = ModuleSearchResult(
                name=name,
                namespace=namespace,
                provider=provider,
                version=module_data.get('latest_version', 'unknown'),
                url=f'https://registry.terraform.io/modules/{namespace}/{name}/{provider}',
                description=cleaned_description,
            )
    
            # Get detailed information including README
            details = await get_module_details(namespace, name, provider)
    
            if details:
                # Update the version if we got a better one from the details
                if 'latest_version' in details:
                    result.version = details['latest_version']
    
                # Add version details if available
                if 'version_details' in details:
                    result.version_details = details['version_details']
    
                # Get README content
                if 'readme_content' in details and details['readme_content']:
                    result.readme_content = details['readme_content']
    
                # Get input and output counts if available
                if 'root' in details and 'inputs' in details['root']:
                    result.input_count = len(details['root']['inputs'])
    
                if 'root' in details and 'outputs' in details['root']:
                    result.output_count = len(details['root']['outputs'])
    
                # Add submodules if available
                if 'submodules' in details and details['submodules']:
                    submodules = [
                        SubmoduleInfo(**submodule_data) for submodule_data in details['submodules']
                    ]
                    result.submodules = submodules
    
                # Add variables information if available
                if 'variables' in details and details['variables']:
                    from awslabs.terraform_mcp_server.models import TerraformVariable
    
                    variables = [TerraformVariable(**var_data) for var_data in details['variables']]
                    result.variables = variables
    
                # Add variables.tf content if available
                if 'variables_content' in details and details['variables_content']:
                    result.variables_content = details['variables_content']
    
                # Add outputs from README if available
                if 'outputs' in details and details['outputs']:
                    from awslabs.terraform_mcp_server.models import TerraformOutput
    
                    outputs = [
                        TerraformOutput(name=output['name'], description=output.get('description'))
                        for output in details['outputs']
                    ]
                    result.outputs = outputs
                    # Update output_count if not already set
                    if result.output_count is None:
                        result.output_count = len(outputs)
    
            return result
    
        except Exception as e:
            logger.error(f'Error getting info for module {namespace}/{name}/{provider}: {e}')
            return None

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/stv-io/aws-terraform-mcp-server'

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