This is page 381 of 384. Use http://codebase.md/awslabs/mcp?page={x} to view the full context.
# Directory Structure
```
├── .devcontainer
│ └── devcontainer.json
├── .github
│ ├── actions
│ │ ├── build-and-push-container-image
│ │ │ └── action.yml
│ │ └── clear-space-ubuntu-latest-agressively
│ │ └── action.yml
│ ├── codecov.yml
│ ├── CODEOWNERS
│ ├── dependabot.yml
│ ├── ISSUE_TEMPLATE
│ │ ├── bug_report.yml
│ │ ├── documentation.yml
│ │ ├── feature_request.yml
│ │ ├── rfc.yml
│ │ └── support_awslabs_mcp_servers.yml
│ ├── pull_request_template.md
│ ├── SECURITY
│ ├── SUPPORT
│ └── workflows
│ ├── aws-api-mcp-upgrade-version.yml
│ ├── bandit-requirements.txt
│ ├── bandit.yml
│ ├── cfn_nag.yml
│ ├── check-gh-pages-builds.yml
│ ├── check-license-header-hash.txt
│ ├── check-license-header.json
│ ├── check-license-header.yml
│ ├── checkov.yml
│ ├── codeql.yml
│ ├── dependency-review-action.yml
│ ├── detect-secrets-requirements.txt
│ ├── gh-pages.yml
│ ├── merge-prevention.yml
│ ├── powershell.yml
│ ├── pre-commit-requirements.txt
│ ├── pre-commit.yml
│ ├── pull-request-lint.yml
│ ├── python.yml
│ ├── RELEASE_INSTRUCTIONS.md
│ ├── release-initiate-branch.yml
│ ├── release-merge-tag.yml
│ ├── release.py
│ ├── release.yml
│ ├── scanners.yml
│ ├── scorecard-analysis.yml
│ ├── semgrep-requirements.txt
│ ├── semgrep.yml
│ ├── stale.yml
│ ├── trivy.yml
│ └── typescript.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .python-version
├── .ruff.toml
├── .secrets.baseline
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── DESIGN_GUIDELINES.md
├── DEVELOPER_GUIDE.md
├── docs
│ └── images
│ └── root-readme
│ ├── cline-api-provider-filled.png
│ ├── cline-chat-interface.png
│ ├── cline-custom-instructions.png
│ ├── cline-select-aws-profile.png
│ ├── cline-select-bedrock.png
│ ├── configure-mcp-servers.png
│ ├── install-cline-extension.png
│ ├── mcp-servers-installed.png
│ └── select-mcp-servers.png
├── docusaurus
│ ├── .gitignore
│ ├── docs
│ │ ├── installation.md
│ │ ├── intro.md
│ │ ├── samples
│ │ │ ├── index.md
│ │ │ ├── mcp-integration-with-kb.md
│ │ │ ├── mcp-integration-with-nova-canvas.md
│ │ │ └── stepfunctions-tool-mcp-server.md
│ │ ├── servers
│ │ │ ├── amazon-bedrock-agentcore-mcp-server.md
│ │ │ ├── amazon-keyspaces-mcp-server.md
│ │ │ ├── amazon-mq-mcp-server.md
│ │ │ ├── amazon-neptune-mcp-server.md
│ │ │ ├── amazon-qbusiness-anonymous-mcp-server.md
│ │ │ ├── amazon-qindex-mcp-server.md
│ │ │ ├── amazon-sns-sqs-mcp-server.md
│ │ │ ├── aurora-dsql-mcp-server.md
│ │ │ ├── aws-api-mcp-server.md
│ │ │ ├── aws-appsync-mcp-server.md
│ │ │ ├── aws-bedrock-custom-model-import-mcp-server.md
│ │ │ ├── aws-bedrock-data-automation-mcp-server.md
│ │ │ ├── aws-dataprocessing-mcp-server.md
│ │ │ ├── aws-diagram-mcp-server.md
│ │ │ ├── aws-documentation-mcp-server.md
│ │ │ ├── aws-healthomics-mcp-server.md
│ │ │ ├── aws-iot-sitewise-mcp-server.md
│ │ │ ├── aws-knowledge-mcp-server.md
│ │ │ ├── aws-location-mcp-server.md
│ │ │ ├── aws-msk-mcp-server.md
│ │ │ ├── aws-pricing-mcp-server.md
│ │ │ ├── aws-serverless-mcp-server.md
│ │ │ ├── aws-support-mcp-server.md
│ │ │ ├── bedrock-kb-retrieval-mcp-server.md
│ │ │ ├── billing-cost-management-mcp-server.md
│ │ │ ├── ccapi-mcp-server.md
│ │ │ ├── cdk-mcp-server.md
│ │ │ ├── cfn-mcp-server.md
│ │ │ ├── cloudtrail-mcp-server.md
│ │ │ ├── cloudwatch-appsignals-mcp-server.md
│ │ │ ├── cloudwatch-mcp-server.md
│ │ │ ├── code-doc-gen-mcp-server.md
│ │ │ ├── core-mcp-server.md
│ │ │ ├── cost-explorer-mcp-server.md
│ │ │ ├── documentdb-mcp-server.md
│ │ │ ├── dynamodb-mcp-server.md
│ │ │ ├── ecs-mcp-server.md
│ │ │ ├── eks-mcp-server.md
│ │ │ ├── elasticache-mcp-server.md
│ │ │ ├── finch-mcp-server.md
│ │ │ ├── frontend-mcp-server.md
│ │ │ ├── git-repo-research-mcp-server.md
│ │ │ ├── healthlake-mcp-server.md
│ │ │ ├── iam-mcp-server.md
│ │ │ ├── kendra-index-mcp-server.md
│ │ │ ├── lambda-tool-mcp-server.md
│ │ │ ├── memcached-mcp-server.md
│ │ │ ├── mysql-mcp-server.md
│ │ │ ├── nova-canvas-mcp-server.md
│ │ │ ├── openapi-mcp-server.md
│ │ │ ├── postgres-mcp-server.md
│ │ │ ├── prometheus-mcp-server.md
│ │ │ ├── redshift-mcp-server.md
│ │ │ ├── s3-tables-mcp-server.md
│ │ │ ├── stepfunctions-tool-mcp-server.md
│ │ │ ├── syntheticdata-mcp-server.md
│ │ │ ├── terraform-mcp-server.md
│ │ │ ├── timestream-for-influxdb-mcp-server.md
│ │ │ ├── valkey-mcp-server.md
│ │ │ └── well-architected-security-mcp-server.mdx
│ │ └── vibe_coding.md
│ ├── docusaurus.config.ts
│ ├── package-lock.json
│ ├── package.json
│ ├── README.md
│ ├── sidebars.ts
│ ├── src
│ │ ├── components
│ │ │ ├── HomepageFeatures
│ │ │ │ └── styles.module.css
│ │ │ └── ServerCards
│ │ │ ├── index.tsx
│ │ │ └── styles.module.css
│ │ ├── css
│ │ │ ├── custom.css
│ │ │ └── doc-override.css
│ │ └── pages
│ │ ├── index.module.css
│ │ └── servers.tsx
│ ├── static
│ │ ├── .nojekyll
│ │ ├── assets
│ │ │ ├── icons
│ │ │ │ ├── activity.svg
│ │ │ │ ├── book-open.svg
│ │ │ │ ├── cpu.svg
│ │ │ │ ├── database.svg
│ │ │ │ ├── dollar-sign.svg
│ │ │ │ ├── help-circle.svg
│ │ │ │ ├── key.svg
│ │ │ │ ├── server.svg
│ │ │ │ ├── share-2.svg
│ │ │ │ ├── tool.svg
│ │ │ │ └── zap.svg
│ │ │ └── server-cards.json
│ │ └── img
│ │ ├── aws-logo.svg
│ │ └── logo.png
│ └── tsconfig.json
├── LICENSE
├── NOTICE
├── README.md
├── samples
│ ├── mcp-integration-with-kb
│ │ ├── .env.example
│ │ ├── .python-version
│ │ ├── assets
│ │ │ └── simplified-mcp-flow-diagram.png
│ │ ├── clients
│ │ │ └── client_server.py
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── user_interfaces
│ │ │ └── chat_bedrock_st.py
│ │ └── uv.lock
│ ├── mcp-integration-with-nova-canvas
│ │ ├── .env.example
│ │ ├── .python-version
│ │ ├── clients
│ │ │ └── client_server.py
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── user_interfaces
│ │ │ └── image_generator_st.py
│ │ └── uv.lock
│ ├── README.md
│ └── stepfunctions-tool-mcp-server
│ ├── README.md
│ └── sample_state_machines
│ ├── customer-create
│ │ └── app.py
│ ├── customer-id-from-email
│ │ └── app.py
│ ├── customer-info-from-id
│ │ └── app.py
│ └── template.yml
├── src
│ ├── amazon-bedrock-agentcore-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── amazon_bedrock_agentcore_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── config.py
│ │ │ ├── server.py
│ │ │ └── utils
│ │ │ ├── __init__.py
│ │ │ ├── cache.py
│ │ │ ├── doc_fetcher.py
│ │ │ ├── indexer.py
│ │ │ ├── text_processor.py
│ │ │ └── url_validator.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── SECURITY.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── test_cache.py
│ │ │ ├── test_config.py
│ │ │ ├── test_doc_fetcher.py
│ │ │ ├── test_indexer.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_server.py
│ │ │ ├── test_text_processor.py
│ │ │ └── test_url_validator.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── amazon-kendra-index-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── amazon_kendra_index_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── server.py
│ │ │ └── util.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── amazon-keyspaces-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── amazon_keyspaces_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── client.py
│ │ │ ├── config.py
│ │ │ ├── consts.py
│ │ │ ├── llm_context.py
│ │ │ ├── models.py
│ │ │ ├── server.py
│ │ │ └── services.py
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── run_tests.sh
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── test_client.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_query_analysis_service.py
│ │ │ ├── test_server.py
│ │ │ └── test_services.py
│ │ └── uv.lock
│ ├── amazon-mq-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── amazon_mq_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── aws_service_mcp_generator.py
│ │ │ ├── consts.py
│ │ │ ├── rabbitmq
│ │ │ │ ├── __init__.py
│ │ │ │ ├── admin.py
│ │ │ │ ├── connection.py
│ │ │ │ ├── doc
│ │ │ │ │ ├── rabbitmq_broker_sizing_guide.md
│ │ │ │ │ ├── rabbitmq_performance_optimization_best_practice.md
│ │ │ │ │ ├── rabbitmq_production_deployment_guidelines.md
│ │ │ │ │ ├── rabbitmq_quorum_queue_migration_guide.md
│ │ │ │ │ └── rabbitmq_setup_best_practice.md
│ │ │ │ ├── handlers.py
│ │ │ │ └── module.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── example
│ │ │ └── sample_mcp_q_cli.json
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── .gitignore
│ │ │ ├── rabbitmq
│ │ │ │ ├── __init__.py
│ │ │ │ ├── conftest.py
│ │ │ │ ├── test_admin.py
│ │ │ │ ├── test_connection.py
│ │ │ │ ├── test_handlers.py
│ │ │ │ └── test_module.py
│ │ │ ├── test_aws_service_mcp_generator.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── amazon-neptune-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── amazon_neptune_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── exceptions.py
│ │ │ ├── graph_store
│ │ │ │ ├── __init__.py
│ │ │ │ ├── analytics.py
│ │ │ │ ├── base.py
│ │ │ │ └── database.py
│ │ │ ├── models.py
│ │ │ ├── neptune.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── test_analytics.py
│ │ │ ├── test_database.py
│ │ │ ├── test_exceptions.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_models.py
│ │ │ ├── test_neptune.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── amazon-qbusiness-anonymous-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── amazon_qbusiness_anonymous_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── clients.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── amazon-qindex-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── amazon_qindex_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── clients.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── test_clients.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ └── test_server.py
│ │ └── uv.lock
│ ├── amazon-sns-sqs-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── amazon_sns_sqs_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── common.py
│ │ │ ├── consts.py
│ │ │ ├── generator.py
│ │ │ ├── server.py
│ │ │ ├── sns.py
│ │ │ └── sqs.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── print_tools.py
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── run_tests.sh
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── .gitignore
│ │ │ ├── README.md
│ │ │ ├── test_common.py
│ │ │ ├── test_generator.py
│ │ │ ├── test_server.py
│ │ │ ├── test_sns.py
│ │ │ └── test_sqs.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── aurora-dsql-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aurora_dsql_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── consts.py
│ │ │ ├── mutable_sql_detector.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── test_connection_reuse.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_profile_option.py
│ │ │ ├── test_readonly_enforcement.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── aws-api-mcp-server
│ │ ├── .gitattributes
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aws_api_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── core
│ │ │ │ ├── __init__.py
│ │ │ │ ├── agent_scripts
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── manager.py
│ │ │ │ │ ├── models.py
│ │ │ │ │ └── registry
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── application-failure-troubleshooting.script.md
│ │ │ │ │ ├── cloudtral-mutli-region-setup.script.md
│ │ │ │ │ ├── create_amazon_aurora_db_cluster_with_instances.script.md
│ │ │ │ │ ├── lambda-timeout-debugging.script.md
│ │ │ │ │ ├── scripts_format.md
│ │ │ │ │ └── troubleshoot-permissions-with-cloudtrail-events.script.md
│ │ │ │ ├── aws
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── driver.py
│ │ │ │ │ ├── pagination.py
│ │ │ │ │ ├── regions.py
│ │ │ │ │ ├── service.py
│ │ │ │ │ └── services.py
│ │ │ │ ├── common
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── command_metadata.py
│ │ │ │ │ ├── command.py
│ │ │ │ │ ├── config.py
│ │ │ │ │ ├── errors.py
│ │ │ │ │ ├── file_operations.py
│ │ │ │ │ ├── file_system_controls.py
│ │ │ │ │ ├── helpers.py
│ │ │ │ │ ├── models.py
│ │ │ │ │ └── py.typed
│ │ │ │ ├── data
│ │ │ │ │ └── api_metadata.json
│ │ │ │ ├── metadata
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ └── read_only_operations_list.py
│ │ │ │ ├── parser
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── custom_validators
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── botocore_param_validator.py
│ │ │ │ │ │ ├── ec2_validator.py
│ │ │ │ │ │ └── ssm_validator.py
│ │ │ │ │ ├── interpretation.py
│ │ │ │ │ ├── lexer.py
│ │ │ │ │ └── parser.py
│ │ │ │ ├── py.typed
│ │ │ │ └── security
│ │ │ │ ├── __init__.py
│ │ │ │ ├── aws_api_customization.json
│ │ │ │ └── policy.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── CONTRIBUTING.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── agent_scripts
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_manager.py
│ │ │ │ └── test_registry
│ │ │ │ ├── another_valid_script.script.md
│ │ │ │ ├── test_script.script.md
│ │ │ │ └── valid_script.script.md
│ │ │ ├── aws
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_driver.py
│ │ │ │ ├── test_pagination.py
│ │ │ │ ├── test_service.py
│ │ │ │ └── test_services.py
│ │ │ ├── common
│ │ │ │ ├── test_command.py
│ │ │ │ ├── test_config.py
│ │ │ │ ├── test_file_operations.py
│ │ │ │ ├── test_file_system_controls.py
│ │ │ │ ├── test_file_validation.py
│ │ │ │ └── test_helpers.py
│ │ │ ├── fixtures.py
│ │ │ ├── history_handler.py
│ │ │ ├── metadata
│ │ │ │ ├── __init__.py
│ │ │ │ └── test_read_only_operations_list.py
│ │ │ ├── parser
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_file_path_detection.py
│ │ │ │ ├── test_lexer.py
│ │ │ │ ├── test_parser_customizations.py
│ │ │ │ └── test_parser.py
│ │ │ ├── test_security_policy.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── aws-appsync-mcp-server
│ │ ├── .dockerignore
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aws_appsync_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── decorators.py
│ │ │ ├── helpers.py
│ │ │ ├── operations
│ │ │ │ ├── __init__.py
│ │ │ │ ├── create_api_cache.py
│ │ │ │ ├── create_api_key.py
│ │ │ │ ├── create_api.py
│ │ │ │ ├── create_channel_namespace.py
│ │ │ │ ├── create_datasource.py
│ │ │ │ ├── create_domain_name.py
│ │ │ │ ├── create_function.py
│ │ │ │ ├── create_graphql_api.py
│ │ │ │ ├── create_resolver.py
│ │ │ │ └── create_schema.py
│ │ │ ├── server.py
│ │ │ ├── tools
│ │ │ │ ├── __init__.py
│ │ │ │ ├── create_api_cache.py
│ │ │ │ ├── create_api_key.py
│ │ │ │ ├── create_api.py
│ │ │ │ ├── create_channel_namespace.py
│ │ │ │ ├── create_datasource.py
│ │ │ │ ├── create_domain_name.py
│ │ │ │ ├── create_function.py
│ │ │ │ ├── create_graphql_api.py
│ │ │ │ ├── create_resolver.py
│ │ │ │ └── create_schema.py
│ │ │ └── validators.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── test_all_create_tools_write_protection.py
│ │ │ ├── test_create_api_cache.py
│ │ │ ├── test_create_api_key.py
│ │ │ ├── test_create_api.py
│ │ │ ├── test_create_channel_namespace.py
│ │ │ ├── test_create_datasource_tool.py
│ │ │ ├── test_create_datasource.py
│ │ │ ├── test_create_domain_name.py
│ │ │ ├── test_create_function.py
│ │ │ ├── test_create_graphql_api.py
│ │ │ ├── test_create_resolver.py
│ │ │ ├── test_create_schema_tool.py
│ │ │ ├── test_create_schema.py
│ │ │ ├── test_helpers.py
│ │ │ ├── test_server.py
│ │ │ ├── test_validators.py
│ │ │ └── test_write_operation.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── aws-bedrock-custom-model-import-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aws_bedrock_custom_model_import_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── client.py
│ │ │ ├── llm_context.py
│ │ │ ├── models.py
│ │ │ ├── prompts.py
│ │ │ ├── server.py
│ │ │ ├── services
│ │ │ │ ├── __init__.py
│ │ │ │ ├── imported_model_service.py
│ │ │ │ └── model_import_service.py
│ │ │ ├── tools
│ │ │ │ ├── create_model_import_job.py
│ │ │ │ ├── delete_imported_model.py
│ │ │ │ ├── get_imported_model.py
│ │ │ │ ├── get_model_import_job.py
│ │ │ │ ├── list_imported_models.py
│ │ │ │ └── list_model_import_jobs.py
│ │ │ └── utils
│ │ │ ├── __init__.py
│ │ │ ├── aws.py
│ │ │ ├── config.py
│ │ │ ├── consts.py
│ │ │ └── matching.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── services
│ │ │ │ ├── test_imported_model_service.py
│ │ │ │ └── test_model_import_service.py
│ │ │ ├── test_client.py
│ │ │ ├── test_init.py
│ │ │ ├── test_llm_context.py
│ │ │ ├── test_prompts.py
│ │ │ ├── test_server.py
│ │ │ ├── tools
│ │ │ │ ├── test_create_model_import_job.py
│ │ │ │ ├── test_delete_imported_model.py
│ │ │ │ ├── test_get_imported_model.py
│ │ │ │ ├── test_get_model_import_job.py
│ │ │ │ ├── test_list_imported_models.py
│ │ │ │ └── test_list_model_import_jobs.py
│ │ │ └── utils
│ │ │ ├── test_aws.py
│ │ │ ├── test_config.py
│ │ │ └── test_matching.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── aws-bedrock-data-automation-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aws_bedrock_data_automation_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── helpers.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── test_helpers.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── aws-dataprocessing-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aws_dataprocessing_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── core
│ │ │ │ ├── __init__.py
│ │ │ │ └── glue_data_catalog
│ │ │ │ ├── __init__.py
│ │ │ │ ├── data_catalog_database_manager.py
│ │ │ │ ├── data_catalog_handler.py
│ │ │ │ └── data_catalog_table_manager.py
│ │ │ ├── handlers
│ │ │ │ ├── __init__.py
│ │ │ │ ├── athena
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── athena_data_catalog_handler.py
│ │ │ │ │ ├── athena_query_handler.py
│ │ │ │ │ └── athena_workgroup_handler.py
│ │ │ │ ├── commons
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ └── common_resource_handler.py
│ │ │ │ ├── emr
│ │ │ │ │ ├── emr_ec2_cluster_handler.py
│ │ │ │ │ ├── emr_ec2_instance_handler.py
│ │ │ │ │ └── emr_ec2_steps_handler.py
│ │ │ │ └── glue
│ │ │ │ ├── __init__.py
│ │ │ │ ├── crawler_handler.py
│ │ │ │ ├── data_catalog_handler.py
│ │ │ │ ├── glue_commons_handler.py
│ │ │ │ ├── glue_etl_handler.py
│ │ │ │ ├── interactive_sessions_handler.py
│ │ │ │ └── worklows_handler.py
│ │ │ ├── models
│ │ │ │ ├── __init__.py
│ │ │ │ ├── athena_models.py
│ │ │ │ ├── common_resource_models.py
│ │ │ │ ├── data_catalog_models.py
│ │ │ │ ├── emr_models.py
│ │ │ │ └── glue_models.py
│ │ │ ├── server.py
│ │ │ └── utils
│ │ │ ├── __init__.py
│ │ │ ├── aws_helper.py
│ │ │ ├── consts.py
│ │ │ ├── logging_helper.py
│ │ │ └── mutable_sql_detector.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── core
│ │ │ │ ├── __init__.py
│ │ │ │ └── glue_data_catalog
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_data_catalog_database_manager.py
│ │ │ │ ├── test_data_catalog_handler.py
│ │ │ │ └── test_data_catalog_table_manager.py
│ │ │ ├── handlers
│ │ │ │ ├── __init__.py
│ │ │ │ ├── athena
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── test_athena_data_catalog_handler.py
│ │ │ │ │ ├── test_athena_query_handler.py
│ │ │ │ │ ├── test_athena_workgroup_handler.py
│ │ │ │ │ └── test_custom_tags_athena.py
│ │ │ │ ├── commons
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ └── test_common_resource_handler.py
│ │ │ │ ├── emr
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── test_custom_tags_emr.py
│ │ │ │ │ ├── test_emr_ec2_cluster_handler.py
│ │ │ │ │ ├── test_emr_ec2_instance_handler.py
│ │ │ │ │ └── test_emr_ec2_steps_handler.py
│ │ │ │ └── glue
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_crawler_handler.py
│ │ │ │ ├── test_custom_tags_glue.py
│ │ │ │ ├── test_data_catalog_handler.py
│ │ │ │ ├── test_glue_commons_handler.py
│ │ │ │ ├── test_glue_etl_handler.py
│ │ │ │ ├── test_glue_interactive_sessions_handler.py
│ │ │ │ └── test_glue_workflows_handler.py
│ │ │ ├── models
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_athena_models.py
│ │ │ │ ├── test_common_resource_models.py
│ │ │ │ ├── test_data_catalog_models.py
│ │ │ │ ├── test_emr_models.py
│ │ │ │ ├── test_glue_models.py
│ │ │ │ ├── test_interactive_sessions_models.py
│ │ │ │ └── test_workflows_models.py
│ │ │ ├── test_init.py
│ │ │ ├── test_server.py
│ │ │ └── utils
│ │ │ ├── __init__.py
│ │ │ ├── test_aws_helper.py
│ │ │ ├── test_custom_tags.py
│ │ │ └── test_logging_helper.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── aws-diagram-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aws_diagram_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── diagrams_tools.py
│ │ │ ├── models.py
│ │ │ ├── scanner.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── .gitignore
│ │ │ ├── conftest.py
│ │ │ ├── README.md
│ │ │ ├── resources
│ │ │ │ ├── __init__.py
│ │ │ │ └── example_diagrams
│ │ │ │ ├── __init__.py
│ │ │ │ ├── aws_example.py
│ │ │ │ ├── flow_example.py
│ │ │ │ └── sequence_example.py
│ │ │ ├── test_diagrams.py
│ │ │ ├── test_models.py
│ │ │ ├── test_sarif_fix.py
│ │ │ ├── test_scanner.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── aws-documentation-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aws_documentation_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── models.py
│ │ │ ├── server_aws_cn.py
│ │ │ ├── server_aws.py
│ │ │ ├── server_utils.py
│ │ │ ├── server.py
│ │ │ └── util.py
│ │ ├── basic-usage.gif
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── constants.py
│ │ │ ├── resources
│ │ │ │ └── lambda_sns_raw.html
│ │ │ ├── test_aws_cn_get_available_services_live.py
│ │ │ ├── test_aws_cn_read_documentation_live.py
│ │ │ ├── test_aws_read_documentation_live.py
│ │ │ ├── test_aws_recommend_live.py
│ │ │ ├── test_aws_search_live.py
│ │ │ ├── test_metadata_handling.py
│ │ │ ├── test_models.py
│ │ │ ├── test_server_aws_cn.py
│ │ │ ├── test_server_aws.py
│ │ │ ├── test_server_utils.py
│ │ │ ├── test_server.py
│ │ │ └── test_util.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── aws-healthomics-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aws_healthomics_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── consts.py
│ │ │ ├── models.py
│ │ │ ├── server.py
│ │ │ ├── tools
│ │ │ │ ├── __init__.py
│ │ │ │ ├── helper_tools.py
│ │ │ │ ├── run_analysis.py
│ │ │ │ ├── troubleshooting.py
│ │ │ │ ├── workflow_analysis.py
│ │ │ │ ├── workflow_execution.py
│ │ │ │ ├── workflow_linting.py
│ │ │ │ └── workflow_management.py
│ │ │ └── utils
│ │ │ ├── __init__.py
│ │ │ ├── aws_utils.py
│ │ │ ├── s3_utils.py
│ │ │ └── validation_utils.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── docs
│ │ │ └── workflow_linting.md
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── conftest.py
│ │ │ ├── test_aws_utils.py
│ │ │ ├── test_consts.py
│ │ │ ├── test_helper_tools.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_models.py
│ │ │ ├── test_run_analysis.py
│ │ │ ├── test_s3_utils.py
│ │ │ ├── test_server.py
│ │ │ ├── test_troubleshooting.py
│ │ │ ├── test_workflow_analysis.py
│ │ │ ├── test_workflow_execution.py
│ │ │ ├── test_workflow_linting.py
│ │ │ ├── test_workflow_management.py
│ │ │ └── test_workflow_tools.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── aws-iot-sitewise-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aws_iot_sitewise_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── client.py
│ │ │ ├── prompts
│ │ │ │ ├── __init__.py
│ │ │ │ ├── asset_hierarchy.py
│ │ │ │ ├── data_exploration.py
│ │ │ │ └── data_ingestion.py
│ │ │ ├── server.py
│ │ │ ├── tool_metadata.py
│ │ │ ├── tools
│ │ │ │ ├── __init__.py
│ │ │ │ ├── sitewise_access.py
│ │ │ │ ├── sitewise_asset_models.py
│ │ │ │ ├── sitewise_assets.py
│ │ │ │ ├── sitewise_data.py
│ │ │ │ └── sitewise_gateways.py
│ │ │ └── validation.py
│ │ ├── CHANGELOG.md
│ │ ├── DEVELOPMENT.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── examples
│ │ │ └── wind_farm_example.py
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── run_server.py
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_server.py
│ │ │ ├── test_sitewise_access.py
│ │ │ ├── test_sitewise_asset_models.py
│ │ │ ├── test_sitewise_assets.py
│ │ │ ├── test_sitewise_data.py
│ │ │ ├── test_sitewise_gateways.py
│ │ │ └── test_validation.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── aws-knowledge-mcp-server
│ │ └── README.md
│ ├── aws-location-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aws_location_server
│ │ │ ├── __init__.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── test_server_integration.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── aws-msk-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aws_msk_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── server.py
│ │ │ └── tools
│ │ │ ├── __init__.py
│ │ │ ├── common_functions
│ │ │ │ ├── __init__.py
│ │ │ │ ├── client_manager.py
│ │ │ │ └── common_functions.py
│ │ │ ├── logs_and_telemetry
│ │ │ │ ├── __init__.py
│ │ │ │ ├── cluster_metrics_tools.py
│ │ │ │ ├── list_customer_iam_access.py
│ │ │ │ └── metric_config.py
│ │ │ ├── mutate_cluster
│ │ │ │ ├── __init__.py
│ │ │ │ ├── batch_associate_scram_secret.py
│ │ │ │ ├── batch_disassociate_scram_secret.py
│ │ │ │ ├── create_cluster_v2.py
│ │ │ │ ├── put_cluster_policy.py
│ │ │ │ ├── reboot_broker.py
│ │ │ │ ├── update_broker_count.py
│ │ │ │ ├── update_broker_storage.py
│ │ │ │ ├── update_broker_type.py
│ │ │ │ ├── update_cluster_configuration.py
│ │ │ │ ├── update_monitoring.py
│ │ │ │ └── update_security.py
│ │ │ ├── mutate_config
│ │ │ │ ├── __init__.py
│ │ │ │ ├── create_configuration.py
│ │ │ │ ├── tag_resource.py
│ │ │ │ ├── untag_resource.py
│ │ │ │ └── update_configuration.py
│ │ │ ├── mutate_vpc
│ │ │ │ ├── __init__.py
│ │ │ │ ├── create_vpc_connection.py
│ │ │ │ ├── delete_vpc_connection.py
│ │ │ │ └── reject_client_vpc_connection.py
│ │ │ ├── read_cluster
│ │ │ │ ├── __init__.py
│ │ │ │ ├── describe_cluster_operation.py
│ │ │ │ ├── describe_cluster.py
│ │ │ │ ├── get_bootstrap_brokers.py
│ │ │ │ ├── get_cluster_policy.py
│ │ │ │ ├── get_compatible_kafka_versions.py
│ │ │ │ ├── list_client_vpc_connections.py
│ │ │ │ ├── list_cluster_operations.py
│ │ │ │ ├── list_nodes.py
│ │ │ │ └── list_scram_secrets.py
│ │ │ ├── read_config
│ │ │ │ ├── __init__.py
│ │ │ │ ├── describe_configuration_revision.py
│ │ │ │ ├── describe_configuration.py
│ │ │ │ ├── list_configuration_revisions.py
│ │ │ │ └── list_tags_for_resource.py
│ │ │ ├── read_global
│ │ │ │ ├── __init__.py
│ │ │ │ ├── list_clusters.py
│ │ │ │ ├── list_configurations.py
│ │ │ │ ├── list_kafka_versions.py
│ │ │ │ └── list_vpc_connections.py
│ │ │ ├── read_vpc
│ │ │ │ ├── __init__.py
│ │ │ │ └── describe_vpc_connection.py
│ │ │ └── static_tools
│ │ │ ├── __init__.py
│ │ │ └── cluster_best_practices.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── test_client_manager.py
│ │ │ ├── test_cluster_metrics_tools.py
│ │ │ ├── test_common_functions.py
│ │ │ ├── test_create_cluster_v2.py
│ │ │ ├── test_create_configuration.py
│ │ │ ├── test_create_vpc_connection.py
│ │ │ ├── test_delete_vpc_connection.py
│ │ │ ├── test_describe_cluster_operation.py
│ │ │ ├── test_describe_cluster.py
│ │ │ ├── test_describe_configuration_revision.py
│ │ │ ├── test_describe_configuration.py
│ │ │ ├── test_describe_vpc_connection.py
│ │ │ ├── test_get_bootstrap_brokers.py
│ │ │ ├── test_get_cluster_policy.py
│ │ │ ├── test_get_compatible_kafka_versions.py
│ │ │ ├── test_init.py
│ │ │ ├── test_list_client_vpc_connections.py
│ │ │ ├── test_list_cluster_operations.py
│ │ │ ├── test_list_clusters.py
│ │ │ ├── test_list_configuration_revisions.py
│ │ │ ├── test_list_configurations.py
│ │ │ ├── test_list_customer_iam_access.py
│ │ │ ├── test_list_kafka_versions.py
│ │ │ ├── test_list_nodes.py
│ │ │ ├── test_list_scram_secrets.py
│ │ │ ├── test_list_tags_for_resource.py
│ │ │ ├── test_list_vpc_connections.py
│ │ │ ├── test_logs_and_telemetry.py
│ │ │ ├── test_main.py
│ │ │ ├── test_mutate_cluster_init.py
│ │ │ ├── test_mutate_cluster_success_cases.py
│ │ │ ├── test_mutate_cluster.py
│ │ │ ├── test_mutate_config_init.py
│ │ │ ├── test_mutate_vpc_init.py
│ │ │ ├── test_read_cluster_init_updated.py
│ │ │ ├── test_read_cluster_init.py
│ │ │ ├── test_read_config_init.py
│ │ │ ├── test_read_global_init.py
│ │ │ ├── test_read_vpc_init.py
│ │ │ ├── test_reject_client_vpc_connection.py
│ │ │ ├── test_server.py
│ │ │ ├── test_static_tools_init.py
│ │ │ ├── test_tag_resource.py
│ │ │ ├── test_tool_descriptions.py
│ │ │ ├── test_untag_resource.py
│ │ │ └── test_update_configuration.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── aws-pricing-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aws_pricing_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── cdk_analyzer.py
│ │ │ ├── consts.py
│ │ │ ├── helpers.py
│ │ │ ├── models.py
│ │ │ ├── pricing_client.py
│ │ │ ├── pricing_transformer.py
│ │ │ ├── report_generator.py
│ │ │ ├── server.py
│ │ │ ├── static
│ │ │ │ ├── __init__.py
│ │ │ │ ├── COST_REPORT_TEMPLATE.md
│ │ │ │ └── patterns
│ │ │ │ ├── __init__.py
│ │ │ │ └── BEDROCK.md
│ │ │ └── terraform_analyzer.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── test_cdk_analyzer.py
│ │ │ ├── test_helpers.py
│ │ │ ├── test_pricing_client.py
│ │ │ ├── test_pricing_transformer.py
│ │ │ ├── test_report_generator.py
│ │ │ ├── test_server.py
│ │ │ └── test_terraform_analyzer.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── aws-serverless-mcp-server
│ │ ├── .pre-commit.config.yaml
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aws_serverless_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── models.py
│ │ │ ├── resources
│ │ │ │ ├── __init__.py
│ │ │ │ ├── deployment_details.py
│ │ │ │ ├── deployment_list.py
│ │ │ │ ├── template_details.py
│ │ │ │ └── template_list.py
│ │ │ ├── server.py
│ │ │ ├── template
│ │ │ │ ├── __init__.py
│ │ │ │ ├── registry.py
│ │ │ │ ├── renderer.py
│ │ │ │ └── templates
│ │ │ │ ├── backend.j2
│ │ │ │ ├── frontend.j2
│ │ │ │ ├── fullstack.j2
│ │ │ │ └── README.md
│ │ │ ├── tools
│ │ │ │ ├── common
│ │ │ │ │ └── base_tool.py
│ │ │ │ ├── guidance
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── deploy_serverless_app_help.py
│ │ │ │ │ ├── get_iac_guidance.py
│ │ │ │ │ ├── get_lambda_event_schemas.py
│ │ │ │ │ ├── get_lambda_guidance.py
│ │ │ │ │ └── get_serverless_templates.py
│ │ │ │ ├── sam
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── sam_build.py
│ │ │ │ │ ├── sam_deploy.py
│ │ │ │ │ ├── sam_init.py
│ │ │ │ │ ├── sam_local_invoke.py
│ │ │ │ │ └── sam_logs.py
│ │ │ │ ├── schemas
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── describe_schema.py
│ │ │ │ │ ├── list_registries.py
│ │ │ │ │ └── search_schema.py
│ │ │ │ └── webapps
│ │ │ │ ├── __init__.py
│ │ │ │ ├── configure_domain.py
│ │ │ │ ├── deploy_webapp.py
│ │ │ │ ├── get_metrics.py
│ │ │ │ ├── update_webapp_frontend.py
│ │ │ │ ├── utils
│ │ │ │ │ ├── deploy_service.py
│ │ │ │ │ ├── frontend_uploader.py
│ │ │ │ │ └── startup_script_generator.py
│ │ │ │ └── webapp_deployment_help.py
│ │ │ └── utils
│ │ │ ├── __init__.py
│ │ │ ├── aws_client_helper.py
│ │ │ ├── cloudformation.py
│ │ │ ├── const.py
│ │ │ ├── deployment_manager.py
│ │ │ ├── github.py
│ │ │ └── process.py
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── README.md
│ │ │ ├── test_cloudformation.py
│ │ │ ├── test_configure_domain.py
│ │ │ ├── test_deploy_serverless_app_help.py
│ │ │ ├── test_deploy_service.py
│ │ │ ├── test_deploy_webapp.py
│ │ │ ├── test_deployment_details.py
│ │ │ ├── test_deployment_help.py
│ │ │ ├── test_deployment_list.py
│ │ │ ├── test_deployment_manager.py
│ │ │ ├── test_frontend_uploader.py
│ │ │ ├── test_get_iac_guidance.py
│ │ │ ├── test_get_lambda_event_schemas.py
│ │ │ ├── test_get_lambda_guidance.py
│ │ │ ├── test_get_metrics.py
│ │ │ ├── test_get_serverless_templates.py
│ │ │ ├── test_github.py
│ │ │ ├── test_models.py
│ │ │ ├── test_process.py
│ │ │ ├── test_sam_build.py
│ │ │ ├── test_sam_deploy.py
│ │ │ ├── test_sam_init.py
│ │ │ ├── test_sam_local_invoke.py
│ │ │ ├── test_sam_logs.py
│ │ │ ├── test_schemas.py
│ │ │ ├── test_server.py
│ │ │ ├── test_startup_script_generator.py
│ │ │ ├── test_template_details.py
│ │ │ ├── test_template_list.py
│ │ │ ├── test_template_registry.py
│ │ │ ├── test_template_renderer.py
│ │ │ └── test_update_webapp_frontend.py
│ │ └── uv.lock
│ ├── aws-support-mcp-server
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aws_support_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── client.py
│ │ │ ├── consts.py
│ │ │ ├── debug_helper.py
│ │ │ ├── errors.py
│ │ │ ├── formatters.py
│ │ │ ├── models.py
│ │ │ └── server.py
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftests.py
│ │ │ ├── test_aws_support_mcp_server.py
│ │ │ └── test_models.py
│ │ └── uv.lock
│ ├── bedrock-kb-retrieval-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── bedrock_kb_retrieval_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── knowledgebases
│ │ │ │ ├── __init__.py
│ │ │ │ ├── clients.py
│ │ │ │ ├── discovery.py
│ │ │ │ └── retrieval.py
│ │ │ ├── models.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── run_tests.sh
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── .gitignore
│ │ │ ├── conftest.py
│ │ │ ├── README.md
│ │ │ ├── test_clients.py
│ │ │ ├── test_discovery.py
│ │ │ ├── test_env_config.py
│ │ │ ├── test_models.py
│ │ │ ├── test_retrieval.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── billing-cost-management-mcp-server
│ │ ├── __init__.py
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── billing_cost_management_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── models.py
│ │ │ ├── prompts
│ │ │ │ ├── __init__.py
│ │ │ │ ├── decorator.py
│ │ │ │ ├── graviton_migration.py
│ │ │ │ ├── README.md
│ │ │ │ ├── savings_plans.py
│ │ │ │ └── types.py
│ │ │ ├── server.py
│ │ │ ├── templates
│ │ │ │ └── recommendation_templates
│ │ │ │ ├── ebs_volume.template
│ │ │ │ ├── ec2_asg.template
│ │ │ │ ├── ec2_instance.template
│ │ │ │ ├── ecs_service.template
│ │ │ │ ├── idle.template
│ │ │ │ ├── lambda_function.template
│ │ │ │ ├── rds_database.template
│ │ │ │ ├── reserved_instances.template
│ │ │ │ └── savings_plans.template
│ │ │ ├── tools
│ │ │ │ ├── __init__.py
│ │ │ │ ├── aws_pricing_operations.py
│ │ │ │ ├── aws_pricing_tools.py
│ │ │ │ ├── budget_tools.py
│ │ │ │ ├── compute_optimizer_tools.py
│ │ │ │ ├── cost_anomaly_tools.py
│ │ │ │ ├── cost_comparison_tools.py
│ │ │ │ ├── cost_explorer_operations.py
│ │ │ │ ├── cost_explorer_tools.py
│ │ │ │ ├── cost_optimization_hub_helpers.py
│ │ │ │ ├── cost_optimization_hub_tools.py
│ │ │ │ ├── free_tier_usage_tools.py
│ │ │ │ ├── recommendation_details_tools.py
│ │ │ │ ├── ri_performance_tools.py
│ │ │ │ ├── sp_performance_tools.py
│ │ │ │ ├── storage_lens_tools.py
│ │ │ │ └── unified_sql_tools.py
│ │ │ └── utilities
│ │ │ ├── __init__.py
│ │ │ ├── aws_service_base.py
│ │ │ ├── constants.py
│ │ │ ├── logging_utils.py
│ │ │ └── sql_utils.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── requirements.txt
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── prompts
│ │ │ │ ├── __init__.py
│ │ │ │ └── test_prompts.py
│ │ │ ├── README.md
│ │ │ ├── test_models.py
│ │ │ ├── test_server.py
│ │ │ ├── tools
│ │ │ │ ├── __init__.py
│ │ │ │ ├── fixtures.py
│ │ │ │ ├── test_aws_pricing_tools.py
│ │ │ │ ├── test_budget_tools.py
│ │ │ │ ├── test_compute_optimizer_tools.py
│ │ │ │ ├── test_cost_anomaly_tools_enhanced.py
│ │ │ │ ├── test_cost_anomaly_tools.py
│ │ │ │ ├── test_cost_comparison_tools.py
│ │ │ │ ├── test_cost_explorer_operations.py
│ │ │ │ ├── test_cost_explorer_tools.py
│ │ │ │ ├── test_cost_optimization_hub_helpers.py
│ │ │ │ ├── test_cost_optimization_hub_tools.py
│ │ │ │ ├── test_free_tier_usage_tools_new.py
│ │ │ │ ├── test_recommendation_details_tools.py
│ │ │ │ ├── test_ri_performance_tools.py
│ │ │ │ ├── test_sp_performance_tools.py
│ │ │ │ ├── test_storage_lens_tools.py
│ │ │ │ └── test_unified_sql_tools.py
│ │ │ └── utilities
│ │ │ ├── test_aws_service_base.py
│ │ │ └── test_sql_utils.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── ccapi-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── ccapi_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── aws_client.py
│ │ │ ├── cloud_control_utils.py
│ │ │ ├── context.py
│ │ │ ├── errors.py
│ │ │ ├── iac_generator.py
│ │ │ ├── impl
│ │ │ │ ├── __init__.py
│ │ │ │ ├── tools
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── explanation.py
│ │ │ │ │ ├── infrastructure_generation.py
│ │ │ │ │ ├── resource_operations.py
│ │ │ │ │ ├── security_scanning.py
│ │ │ │ │ └── session_management.py
│ │ │ │ └── utils
│ │ │ │ ├── __init__.py
│ │ │ │ └── validation.py
│ │ │ ├── infrastructure_generator.py
│ │ │ ├── models
│ │ │ │ ├── __init__.py
│ │ │ │ └── models.py
│ │ │ ├── schema_manager.py
│ │ │ ├── server.py
│ │ │ └── static
│ │ │ └── __init__.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── run_tests.sh
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── test_aws_client.py
│ │ │ ├── test_checkov_install.py
│ │ │ ├── test_cloud_control_utils.py
│ │ │ ├── test_context.py
│ │ │ ├── test_errors.py
│ │ │ ├── test_explanation.py
│ │ │ ├── test_iac_generator.py
│ │ │ ├── test_infrastructure_generation.py
│ │ │ ├── test_infrastructure_generator.py
│ │ │ ├── test_models.py
│ │ │ ├── test_resource_operations.py
│ │ │ ├── test_schema_manager.py
│ │ │ ├── test_security_scanning.py
│ │ │ ├── test_server.py
│ │ │ ├── test_session_management.py
│ │ │ └── test_validation.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── cdk-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── cdk_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── core
│ │ │ │ ├── __init__.py
│ │ │ │ ├── resources.py
│ │ │ │ ├── search_utils.py
│ │ │ │ ├── server.py
│ │ │ │ └── tools.py
│ │ │ ├── data
│ │ │ │ ├── __init__.py
│ │ │ │ ├── cdk_nag_parser.py
│ │ │ │ ├── construct_descriptions.py
│ │ │ │ ├── genai_cdk_loader.py
│ │ │ │ ├── lambda_layer_parser.py
│ │ │ │ ├── lambda_powertools_loader.py
│ │ │ │ ├── schema_generator.py
│ │ │ │ └── solutions_constructs_parser.py
│ │ │ ├── server.py
│ │ │ └── static
│ │ │ ├── __init__.py
│ │ │ ├── CDK_GENERAL_GUIDANCE.md
│ │ │ ├── CDK_NAG_GUIDANCE.md
│ │ │ └── lambda_powertools
│ │ │ ├── bedrock.md
│ │ │ ├── cdk.md
│ │ │ ├── dependencies.md
│ │ │ ├── index.md
│ │ │ ├── insights.md
│ │ │ ├── logging.md
│ │ │ ├── metrics.md
│ │ │ └── tracing.md
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── core
│ │ │ │ ├── test_resources_enhanced.py
│ │ │ │ ├── test_resources.py
│ │ │ │ ├── test_search_utils.py
│ │ │ │ ├── test_server.py
│ │ │ │ └── test_tools.py
│ │ │ └── data
│ │ │ ├── test_cdk_nag_parser.py
│ │ │ ├── test_genai_cdk_loader.py
│ │ │ ├── test_lambda_powertools_loader.py
│ │ │ ├── test_schema_generator.py
│ │ │ └── test_solutions_constructs_parser.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── cfn-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── cfn_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── aws_client.py
│ │ │ ├── cloud_control_utils.py
│ │ │ ├── context.py
│ │ │ ├── errors.py
│ │ │ ├── iac_generator.py
│ │ │ ├── schema_manager.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── run_tests.sh
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── test_aws_client.py
│ │ │ ├── test_cloud_control_utils.py
│ │ │ ├── test_errors.py
│ │ │ ├── test_iac_generator.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_schema_manager.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── cloudtrail-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── cloudtrail_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── common.py
│ │ │ ├── models.py
│ │ │ ├── server.py
│ │ │ └── tools.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_models.py
│ │ │ ├── test_server.py
│ │ │ └── test_tools.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── cloudwatch-appsignals-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── cloudwatch_appsignals_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── audit_presentation_utils.py
│ │ │ ├── audit_utils.py
│ │ │ ├── aws_clients.py
│ │ │ ├── canary_utils.py
│ │ │ ├── server.py
│ │ │ ├── service_audit_utils.py
│ │ │ ├── service_tools.py
│ │ │ ├── sli_report_client.py
│ │ │ ├── slo_tools.py
│ │ │ ├── trace_tools.py
│ │ │ └── utils.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── conftest.py
│ │ │ ├── test_audit_presentation_utils.py
│ │ │ ├── test_audit_utils.py
│ │ │ ├── test_aws_profile.py
│ │ │ ├── test_canary_utils.py
│ │ │ ├── test_initialization.py
│ │ │ ├── test_server_audit_functions.py
│ │ │ ├── test_server_audit_tools.py
│ │ │ ├── test_server.py
│ │ │ ├── test_service_audit_utils.py
│ │ │ ├── test_service_tools_operations.py
│ │ │ ├── test_sli_report_client.py
│ │ │ ├── test_slo_tools.py
│ │ │ └── test_utils.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── cloudwatch-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── cloudwatch_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── cloudwatch_alarms
│ │ │ │ ├── models.py
│ │ │ │ └── tools.py
│ │ │ ├── cloudwatch_logs
│ │ │ │ ├── models.py
│ │ │ │ └── tools.py
│ │ │ ├── cloudwatch_metrics
│ │ │ │ ├── data
│ │ │ │ │ └── metric_metadata.json
│ │ │ │ ├── models.py
│ │ │ │ └── tools.py
│ │ │ ├── common.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── cloudwatch_alarms
│ │ │ │ ├── test_active_alarms.py
│ │ │ │ ├── test_alarm_history_integration.py
│ │ │ │ ├── test_alarm_history.py
│ │ │ │ └── test_alarms_error_handling.py
│ │ │ ├── cloudwatch_logs
│ │ │ │ ├── test_logs_error_handling.py
│ │ │ │ ├── test_logs_models.py
│ │ │ │ └── test_logs_server.py
│ │ │ ├── cloudwatch_metrics
│ │ │ │ ├── test_metrics_error_handling.py
│ │ │ │ ├── test_metrics_models.py
│ │ │ │ ├── test_metrics_server.py
│ │ │ │ └── test_validation_error.py
│ │ │ ├── test_common_and_server.py
│ │ │ ├── test_init.py
│ │ │ └── test_main.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── code-doc-gen-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── code_doc_gen_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── server.py
│ │ │ └── utils
│ │ │ ├── doc_generator.py
│ │ │ ├── models.py
│ │ │ ├── repomix_manager.py
│ │ │ └── templates.py
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── test_doc_generator_edge_cases.py
│ │ │ ├── test_doc_generator.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_repomix_manager_scenarios.py
│ │ │ ├── test_repomix_manager.py
│ │ │ ├── test_repomix_statistics.py
│ │ │ ├── test_server_extended.py
│ │ │ ├── test_server.py
│ │ │ └── test_templates.py
│ │ └── uv.lock
│ ├── core-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── core_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── server.py
│ │ │ └── static
│ │ │ ├── __init__.py
│ │ │ └── PROMPT_UNDERSTANDING.md
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── README.md
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_response_types.py
│ │ │ ├── test_server.py
│ │ │ └── test_static.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── cost-explorer-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── cost_explorer_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── comparison_handler.py
│ │ │ ├── constants.py
│ │ │ ├── cost_usage_handler.py
│ │ │ ├── forecasting_handler.py
│ │ │ ├── helpers.py
│ │ │ ├── metadata_handler.py
│ │ │ ├── models.py
│ │ │ ├── server.py
│ │ │ └── utility_handler.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── test_comparison_handler.py
│ │ │ ├── test_cost_usage_handler.py
│ │ │ ├── test_forecasting_handler.py
│ │ │ ├── test_helpers.py
│ │ │ ├── test_metadata_handler.py
│ │ │ ├── test_models.py
│ │ │ ├── test_server.py
│ │ │ └── test_utility_handler.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── documentdb-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ └── documentdb_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── analytic_tools.py
│ │ │ ├── config.py
│ │ │ ├── connection_tools.py
│ │ │ ├── db_management_tools.py
│ │ │ ├── query_tools.py
│ │ │ ├── server.py
│ │ │ └── write_tools.py
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── conftest.py
│ │ │ ├── test_analytic_tools.py
│ │ │ ├── test_connection_tools.py
│ │ │ ├── test_db_management_tools.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_query_tools.py
│ │ │ └── test_write_tools.py
│ │ └── uv.lock
│ ├── dynamodb-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── dynamodb_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── common.py
│ │ │ ├── database_analysis_queries.py
│ │ │ ├── database_analyzers.py
│ │ │ ├── prompts
│ │ │ │ └── dynamodb_architect.md
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── evals
│ │ │ │ ├── dynamic_evaluators.py
│ │ │ │ ├── evaluation_registry.py
│ │ │ │ ├── logging_config.py
│ │ │ │ ├── multiturn_evaluator.py
│ │ │ │ ├── README.md
│ │ │ │ ├── scenarios.py
│ │ │ │ └── test_dspy_evals.py
│ │ │ ├── test_dynamodb_server.py
│ │ │ └── test_source_db_integration.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── ecs-mcp-server
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── ecs_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── api
│ │ │ │ ├── __init__.py
│ │ │ │ ├── containerize.py
│ │ │ │ ├── delete.py
│ │ │ │ ├── ecs_troubleshooting.py
│ │ │ │ ├── infrastructure.py
│ │ │ │ ├── resource_management.py
│ │ │ │ ├── status.py
│ │ │ │ └── troubleshooting_tools
│ │ │ │ ├── __init__.py
│ │ │ │ ├── detect_image_pull_failures.py
│ │ │ │ ├── fetch_cloudformation_status.py
│ │ │ │ ├── fetch_network_configuration.py
│ │ │ │ ├── fetch_service_events.py
│ │ │ │ ├── fetch_task_failures.py
│ │ │ │ ├── fetch_task_logs.py
│ │ │ │ ├── get_ecs_troubleshooting_guidance.py
│ │ │ │ └── utils.py
│ │ │ ├── main.py
│ │ │ ├── modules
│ │ │ │ ├── __init__.py
│ │ │ │ ├── aws_knowledge_proxy.py
│ │ │ │ ├── containerize.py
│ │ │ │ ├── delete.py
│ │ │ │ ├── deployment_status.py
│ │ │ │ ├── infrastructure.py
│ │ │ │ ├── resource_management.py
│ │ │ │ └── troubleshooting.py
│ │ │ ├── templates
│ │ │ │ ├── ecr_infrastructure.json
│ │ │ │ └── ecs_infrastructure.json
│ │ │ └── utils
│ │ │ ├── arn_parser.py
│ │ │ ├── aws.py
│ │ │ ├── config.py
│ │ │ ├── docker.py
│ │ │ ├── security.py
│ │ │ ├── templates.py
│ │ │ └── time_utils.py
│ │ ├── DEVELOPMENT.md
│ │ ├── pyproject.toml
│ │ ├── pyrightconfig.json
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── integ
│ │ │ │ └── mcp-inspector
│ │ │ │ ├── .gitignore
│ │ │ │ ├── README.md
│ │ │ │ ├── run-tests.sh
│ │ │ │ └── scenarios
│ │ │ │ ├── 01_comprehensive_troubleshooting
│ │ │ │ │ ├── 01_create.sh
│ │ │ │ │ ├── 02_validate.sh
│ │ │ │ │ ├── 03_cleanup.sh
│ │ │ │ │ ├── description.txt
│ │ │ │ │ └── utils
│ │ │ │ │ ├── mcp_helpers.sh
│ │ │ │ │ └── validation_helpers.sh
│ │ │ │ └── 02_test_knowledge_proxy_tools
│ │ │ │ ├── 01_create.sh
│ │ │ │ ├── 02_validate.sh
│ │ │ │ ├── 03_cleanup.sh
│ │ │ │ ├── description.txt
│ │ │ │ └── utils
│ │ │ │ ├── knowledge_validation_helpers.sh
│ │ │ │ └── mcp_knowledge_helpers.sh
│ │ │ ├── llm_testing
│ │ │ │ ├── invalid_cfn_template.yaml
│ │ │ │ ├── README.md
│ │ │ │ ├── run_tests.sh
│ │ │ │ ├── scenarios
│ │ │ │ │ ├── 01_cloudformation_failure
│ │ │ │ │ │ ├── 01_create.sh
│ │ │ │ │ │ ├── 02_validate.sh
│ │ │ │ │ │ ├── 03_prompts.txt
│ │ │ │ │ │ ├── 04_evaluation.md
│ │ │ │ │ │ ├── 05_cleanup.sh
│ │ │ │ │ │ └── description.txt
│ │ │ │ │ ├── 02_service_failure
│ │ │ │ │ │ ├── 01_create.sh
│ │ │ │ │ │ ├── 02_validate.sh
│ │ │ │ │ │ ├── 03_prompts.txt
│ │ │ │ │ │ ├── 04_evaluation.md
│ │ │ │ │ │ ├── 05_cleanup.sh
│ │ │ │ │ │ └── description.txt
│ │ │ │ │ ├── 03_task_exit_failure
│ │ │ │ │ │ ├── 01_create.sh
│ │ │ │ │ │ ├── 02_validate.sh
│ │ │ │ │ │ ├── 03_prompts.txt
│ │ │ │ │ │ ├── 04_evaluation.md
│ │ │ │ │ │ ├── 05_cleanup.sh
│ │ │ │ │ │ └── description.txt
│ │ │ │ │ ├── 04_network_configuration_failure
│ │ │ │ │ │ ├── 01_create.sh
│ │ │ │ │ │ ├── 02_validate.sh
│ │ │ │ │ │ ├── 03_prompts.txt
│ │ │ │ │ │ ├── 05_cleanup.sh
│ │ │ │ │ │ └── description.txt
│ │ │ │ │ ├── 05_resource_constraint_failure
│ │ │ │ │ │ ├── 01_create.sh
│ │ │ │ │ │ ├── 02_validate.sh
│ │ │ │ │ │ ├── 03_prompts.txt
│ │ │ │ │ │ ├── 05_cleanup.sh
│ │ │ │ │ │ └── description.txt
│ │ │ │ │ └── 06_load_balancer_failure
│ │ │ │ │ ├── 01_create.sh
│ │ │ │ │ ├── 02_validate.sh
│ │ │ │ │ ├── 03_prompts.txt
│ │ │ │ │ ├── 05_cleanup.sh
│ │ │ │ │ └── description.txt
│ │ │ │ ├── SCRIPT_IMPROVEMENTS.md
│ │ │ │ └── utils
│ │ │ │ ├── aws_helpers.sh
│ │ │ │ └── evaluation_template.md
│ │ │ └── unit
│ │ │ ├── __init__.py
│ │ │ ├── api
│ │ │ │ ├── conftest.py
│ │ │ │ ├── test_delete_api.py
│ │ │ │ ├── test_ecs_troubleshooting.py
│ │ │ │ ├── test_resource_management_api.py
│ │ │ │ └── troubleshooting_tools
│ │ │ │ └── test_fetch_network_configuration.py
│ │ │ ├── conftest.py
│ │ │ ├── modules
│ │ │ │ ├── test_aws_knowledge_proxy.py
│ │ │ │ └── test_resource_management_module.py
│ │ │ ├── test_aws_role_utils.py
│ │ │ ├── test_aws_utils.py
│ │ │ ├── test_containerize.py
│ │ │ ├── test_delete.py
│ │ │ ├── test_docker_utils.py
│ │ │ ├── test_docker_with_role.py
│ │ │ ├── test_image_pull_failure_extended.py
│ │ │ ├── test_image_pull_failure.py
│ │ │ ├── test_infrastructure_role.py
│ │ │ ├── test_infrastructure.py
│ │ │ ├── test_integration.py
│ │ │ ├── test_main.py
│ │ │ ├── test_resource_management_api_operation.py
│ │ │ ├── test_resource_management_tool.py
│ │ │ ├── test_resource_management.py
│ │ │ ├── test_security_integration.py
│ │ │ ├── test_status_pytest.py
│ │ │ ├── test_status.py
│ │ │ ├── troubleshooting_tools
│ │ │ │ ├── __init__.py
│ │ │ │ ├── conftest.py
│ │ │ │ ├── test_detect_image_pull_failures.py
│ │ │ │ ├── test_fetch_cloudformation_status.py
│ │ │ │ ├── test_fetch_service_events.py
│ │ │ │ ├── test_fetch_task_failures.py
│ │ │ │ ├── test_fetch_task_logs.py
│ │ │ │ ├── test_get_ecs_troubleshooting_guidance.py
│ │ │ │ ├── test_is_ecr_image_security.py
│ │ │ │ └── test_utils.py
│ │ │ └── utils
│ │ │ ├── __init__.py
│ │ │ ├── async_test_utils.py
│ │ │ ├── test_arn_parser.py
│ │ │ ├── test_config.py
│ │ │ ├── test_docker.py
│ │ │ ├── test_response_sanitization.py
│ │ │ ├── test_security_extended.py
│ │ │ ├── test_security.py
│ │ │ ├── test_templates.py
│ │ │ └── test_time_utils.py
│ │ └── uv.lock
│ ├── eks-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── eks_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── aws_helper.py
│ │ │ ├── cloudwatch_handler.py
│ │ │ ├── cloudwatch_metrics_guidance_handler.py
│ │ │ ├── consts.py
│ │ │ ├── data
│ │ │ │ └── eks_cloudwatch_metrics_guidance.json
│ │ │ ├── eks_kb_handler.py
│ │ │ ├── eks_stack_handler.py
│ │ │ ├── iam_handler.py
│ │ │ ├── insights_handler.py
│ │ │ ├── k8s_apis.py
│ │ │ ├── k8s_client_cache.py
│ │ │ ├── k8s_handler.py
│ │ │ ├── logging_helper.py
│ │ │ ├── models.py
│ │ │ ├── scripts
│ │ │ │ └── update_eks_cloudwatch_metrics_guidance.py
│ │ │ ├── server.py
│ │ │ ├── templates
│ │ │ │ ├── eks-templates
│ │ │ │ │ └── eks-with-vpc.yaml
│ │ │ │ └── k8s-templates
│ │ │ │ ├── deployment.yaml
│ │ │ │ └── service.yaml
│ │ │ └── vpc_config_handler.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── test_aws_helper.py
│ │ │ ├── test_cloudwatch_handler.py
│ │ │ ├── test_cloudwatch_metrics_guidance_handler.py
│ │ │ ├── test_eks_kb_handler.py
│ │ │ ├── test_eks_stack_handler.py
│ │ │ ├── test_iam_handler.py
│ │ │ ├── test_init.py
│ │ │ ├── test_insights_handler.py
│ │ │ ├── test_k8s_apis.py
│ │ │ ├── test_k8s_client_cache.py
│ │ │ ├── test_k8s_handler.py
│ │ │ ├── test_logging_helper.py
│ │ │ ├── test_main.py
│ │ │ ├── test_models.py
│ │ │ ├── test_server.py
│ │ │ └── test_vpc_config_handler.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── elasticache-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── elasticache_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── common
│ │ │ │ ├── __init__.py
│ │ │ │ ├── connection.py
│ │ │ │ ├── decorators.py
│ │ │ │ └── server.py
│ │ │ ├── context.py
│ │ │ ├── main.py
│ │ │ └── tools
│ │ │ ├── __init__.py
│ │ │ ├── cc
│ │ │ │ ├── __init__.py
│ │ │ │ ├── connect.py
│ │ │ │ ├── create.py
│ │ │ │ ├── delete.py
│ │ │ │ ├── describe.py
│ │ │ │ ├── modify.py
│ │ │ │ ├── parsers.py
│ │ │ │ └── processors.py
│ │ │ ├── ce
│ │ │ │ ├── __init__.py
│ │ │ │ └── get_cost_and_usage.py
│ │ │ ├── cw
│ │ │ │ ├── __init__.py
│ │ │ │ └── get_metric_statistics.py
│ │ │ ├── cwlogs
│ │ │ │ ├── __init__.py
│ │ │ │ ├── create_log_group.py
│ │ │ │ ├── describe_log_groups.py
│ │ │ │ ├── describe_log_streams.py
│ │ │ │ ├── filter_log_events.py
│ │ │ │ └── get_log_events.py
│ │ │ ├── firehose
│ │ │ │ ├── __init__.py
│ │ │ │ └── list_delivery_streams.py
│ │ │ ├── misc
│ │ │ │ ├── __init__.py
│ │ │ │ ├── batch_apply_update_action.py
│ │ │ │ ├── batch_stop_update_action.py
│ │ │ │ ├── describe_cache_engine_versions.py
│ │ │ │ ├── describe_engine_default_parameters.py
│ │ │ │ ├── describe_events.py
│ │ │ │ └── describe_service_updates.py
│ │ │ ├── rg
│ │ │ │ ├── __init__.py
│ │ │ │ ├── complete_migration.py
│ │ │ │ ├── connect.py
│ │ │ │ ├── create.py
│ │ │ │ ├── delete.py
│ │ │ │ ├── describe.py
│ │ │ │ ├── modify.py
│ │ │ │ ├── parsers.py
│ │ │ │ ├── processors.py
│ │ │ │ ├── start_migration.py
│ │ │ │ └── test_migration.py
│ │ │ └── serverless
│ │ │ ├── __init__.py
│ │ │ ├── connect.py
│ │ │ ├── create.py
│ │ │ ├── delete.py
│ │ │ ├── describe.py
│ │ │ ├── models.py
│ │ │ └── modify.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── test_connection.py
│ │ │ ├── test_decorators.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ └── tools
│ │ │ ├── cc
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_connect_additional.py
│ │ │ │ ├── test_connect_coverage_additional.py
│ │ │ │ ├── test_connect_coverage.py
│ │ │ │ ├── test_connect.py
│ │ │ │ ├── test_create_additional.py
│ │ │ │ ├── test_create.py
│ │ │ │ ├── test_delete.py
│ │ │ │ ├── test_describe.py
│ │ │ │ ├── test_modify.py
│ │ │ │ ├── test_parsers.py
│ │ │ │ └── test_processors.py
│ │ │ ├── ce
│ │ │ │ ├── __init__.py
│ │ │ │ └── test_get_cost_and_usage.py
│ │ │ ├── cw
│ │ │ │ └── test_get_metric_statistics.py
│ │ │ ├── cwlogs
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_create_log_group.py
│ │ │ │ ├── test_describe_log_groups.py
│ │ │ │ ├── test_describe_log_streams.py
│ │ │ │ ├── test_filter_log_events.py
│ │ │ │ └── test_get_log_events.py
│ │ │ ├── firehose
│ │ │ │ └── test_list_delivery_streams.py
│ │ │ ├── misc
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_batch_apply_update_action.py
│ │ │ │ ├── test_batch_stop_update_action.py
│ │ │ │ ├── test_describe_cache_engine_versions.py
│ │ │ │ ├── test_describe_engine_default_parameters.py
│ │ │ │ ├── test_describe_events.py
│ │ │ │ └── test_describe_service_updates.py
│ │ │ ├── rg
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_complete_migration.py
│ │ │ │ ├── test_connect_additional.py
│ │ │ │ ├── test_connect_coverage_additional.py
│ │ │ │ ├── test_connect_optional_fields.py
│ │ │ │ ├── test_connect_partial_coverage.py
│ │ │ │ ├── test_connect.py
│ │ │ │ ├── test_create.py
│ │ │ │ ├── test_delete.py
│ │ │ │ ├── test_describe.py
│ │ │ │ ├── test_modify.py
│ │ │ │ ├── test_parsers.py
│ │ │ │ ├── test_processors.py
│ │ │ │ ├── test_start_migration.py
│ │ │ │ └── test_test_migration.py
│ │ │ └── serverless
│ │ │ ├── test_connect_additional.py
│ │ │ ├── test_connect_coverage_additional.py
│ │ │ ├── test_connect_optional_fields.py
│ │ │ ├── test_connect.py
│ │ │ ├── test_create.py
│ │ │ ├── test_delete.py
│ │ │ ├── test_describe.py
│ │ │ └── test_modify.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── finch-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── finch_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── consts.py
│ │ │ ├── models.py
│ │ │ ├── server.py
│ │ │ └── utils
│ │ │ ├── __init__.py
│ │ │ ├── build.py
│ │ │ ├── common.py
│ │ │ ├── ecr.py
│ │ │ ├── push.py
│ │ │ └── vm.py
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── test_cli_flags.py
│ │ │ ├── test_logging_configuration.py
│ │ │ ├── test_server.py
│ │ │ ├── test_utils_build.py
│ │ │ ├── test_utils_common.py
│ │ │ ├── test_utils_ecr.py
│ │ │ ├── test_utils_push.py
│ │ │ └── test_utils_vm.py
│ │ └── uv.lock
│ ├── frontend-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── frontend_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── server.py
│ │ │ ├── static
│ │ │ │ └── react
│ │ │ │ ├── essential-knowledge.md
│ │ │ │ └── troubleshooting.md
│ │ │ └── utils
│ │ │ ├── __init__.py
│ │ │ └── file_utils.py
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── test_file_utils.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ └── test_server.py
│ │ └── uv.lock
│ ├── git-repo-research-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── git_repo_research_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── defaults.py
│ │ │ ├── embeddings.py
│ │ │ ├── github_search.py
│ │ │ ├── indexer.py
│ │ │ ├── models.py
│ │ │ ├── repository.py
│ │ │ ├── search.py
│ │ │ ├── server.py
│ │ │ └── utils.py
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── run_tests.sh
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── test_errors_repository.py
│ │ │ ├── test_github_search_edge_cases.py
│ │ │ ├── test_graphql_github_search.py
│ │ │ ├── test_local_repository.py
│ │ │ ├── test_repository_utils.py
│ │ │ ├── test_rest_github_search.py
│ │ │ ├── test_search.py
│ │ │ ├── test_server.py
│ │ │ └── test_url_repository.py
│ │ └── uv.lock
│ ├── healthlake-mcp-server
│ │ ├── .dockerignore
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── healthlake_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── fhir_operations.py
│ │ │ ├── main.py
│ │ │ ├── models.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── CONTRIBUTING.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── examples
│ │ │ ├── mcp_config.json
│ │ │ └── README.md
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── conftest.py
│ │ │ ├── test_fhir_client_comprehensive.py
│ │ │ ├── test_fhir_error_scenarios.py
│ │ │ ├── test_fhir_operations.py
│ │ │ ├── test_integration_mock_based.py
│ │ │ ├── test_main_edge_cases.py
│ │ │ ├── test_main.py
│ │ │ ├── test_mcp_integration_coverage.py
│ │ │ ├── test_models_edge_cases.py
│ │ │ ├── test_models.py
│ │ │ ├── test_readonly_mode.py
│ │ │ ├── test_server_core.py
│ │ │ ├── test_server_error_handling.py
│ │ │ ├── test_server_mcp_handlers.py
│ │ │ ├── test_server_toolhandler.py
│ │ │ └── test_server_validation.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── iam-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── iam_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── aws_client.py
│ │ │ ├── context.py
│ │ │ ├── errors.py
│ │ │ ├── models.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── DESIGN_COMPLIANCE.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── examples
│ │ │ ├── get_policy_document_example.py
│ │ │ └── inline_policy_demo.py
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── run_tests.sh
│ │ ├── tests
│ │ │ ├── test_context.py
│ │ │ ├── test_errors.py
│ │ │ ├── test_inline_policies.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── lambda-tool-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── lambda_tool_mcp_server
│ │ │ ├── __init__.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── examples
│ │ │ ├── README.md
│ │ │ └── sample_functions
│ │ │ ├── customer-create
│ │ │ │ └── app.py
│ │ │ ├── customer-id-from-email
│ │ │ │ └── app.py
│ │ │ ├── customer-info-from-id
│ │ │ │ └── app.py
│ │ │ └── template.yml
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── .gitignore
│ │ │ ├── conftest.py
│ │ │ ├── README.md
│ │ │ ├── test_format_lambda_response.py
│ │ │ ├── test_integration_coverage.py
│ │ │ ├── test_integration.py
│ │ │ ├── test_register_lambda_functions.py
│ │ │ ├── test_schema_integration.py
│ │ │ ├── test_server_coverage_additional.py
│ │ │ ├── test_server_coverage.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── mcp-lambda-handler
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ └── mcp_lambda_handler
│ │ │ ├── __init__.py
│ │ │ ├── mcp_lambda_handler.py
│ │ │ ├── session.py
│ │ │ └── types.py
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ └── test_lambda_handler.py
│ │ └── uv.lock
│ ├── memcached-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── memcached_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── common
│ │ │ │ ├── config.py
│ │ │ │ ├── connection.py
│ │ │ │ └── server.py
│ │ │ ├── context.py
│ │ │ ├── main.py
│ │ │ └── tools
│ │ │ └── cache.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── ELASTICACHECONNECT.md
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── test_cache_readonly.py
│ │ │ ├── test_cache.py
│ │ │ ├── test_connection.py
│ │ │ ├── test_init.py
│ │ │ └── test_main.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── mysql-mcp-server
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── mysql_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── mutable_sql_detector.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── conftest.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── nova-canvas-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── nova_canvas_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── consts.py
│ │ │ ├── models.py
│ │ │ ├── novacanvas.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── .gitignore
│ │ │ ├── conftest.py
│ │ │ ├── README.md
│ │ │ ├── test_models.py
│ │ │ ├── test_novacanvas.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── openapi-mcp-server
│ │ ├── .coveragerc
│ │ ├── .dockerignore
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── AUTHENTICATION.md
│ │ ├── AWS_BEST_PRACTICES.md
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── openapi_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── api
│ │ │ │ ├── __init__.py
│ │ │ │ └── config.py
│ │ │ ├── auth
│ │ │ │ ├── __init__.py
│ │ │ │ ├── api_key_auth.py
│ │ │ │ ├── auth_cache.py
│ │ │ │ ├── auth_errors.py
│ │ │ │ ├── auth_factory.py
│ │ │ │ ├── auth_protocol.py
│ │ │ │ ├── auth_provider.py
│ │ │ │ ├── base_auth.py
│ │ │ │ ├── basic_auth.py
│ │ │ │ ├── bearer_auth.py
│ │ │ │ ├── cognito_auth.py
│ │ │ │ └── register.py
│ │ │ ├── patch
│ │ │ │ └── __init__.py
│ │ │ ├── prompts
│ │ │ │ ├── __init__.py
│ │ │ │ ├── generators
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── operation_prompts.py
│ │ │ │ │ └── workflow_prompts.py
│ │ │ │ ├── models.py
│ │ │ │ └── prompt_manager.py
│ │ │ ├── server.py
│ │ │ └── utils
│ │ │ ├── __init__.py
│ │ │ ├── cache_provider.py
│ │ │ ├── config.py
│ │ │ ├── error_handler.py
│ │ │ ├── http_client.py
│ │ │ ├── metrics_provider.py
│ │ │ ├── openapi_validator.py
│ │ │ └── openapi.py
│ │ ├── CHANGELOG.md
│ │ ├── DEPLOYMENT.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── OBSERVABILITY.md
│ │ ├── pyproject.toml
│ │ ├── pyrightconfig.json
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── api
│ │ │ │ └── test_config.py
│ │ │ ├── auth
│ │ │ │ ├── test_api_key_auth.py
│ │ │ │ ├── test_auth_cache.py
│ │ │ │ ├── test_auth_errors.py
│ │ │ │ ├── test_auth_factory_caching.py
│ │ │ │ ├── test_auth_factory_coverage.py
│ │ │ │ ├── test_auth_factory.py
│ │ │ │ ├── test_auth_protocol_additional.py
│ │ │ │ ├── test_auth_protocol_boost.py
│ │ │ │ ├── test_auth_protocol_coverage.py
│ │ │ │ ├── test_auth_protocol_extended.py
│ │ │ │ ├── test_auth_protocol_improved.py
│ │ │ │ ├── test_auth_protocol.py
│ │ │ │ ├── test_auth_provider_additional.py
│ │ │ │ ├── test_base_auth_coverage.py
│ │ │ │ ├── test_base_auth.py
│ │ │ │ ├── test_basic_auth.py
│ │ │ │ ├── test_bearer_auth.py
│ │ │ │ ├── test_cognito_auth_additional_coverage.py
│ │ │ │ ├── test_cognito_auth_boost_coverage.py
│ │ │ │ ├── test_cognito_auth_client_credentials.py
│ │ │ │ ├── test_cognito_auth_coverage_boost.py
│ │ │ │ ├── test_cognito_auth_exceptions.py
│ │ │ │ ├── test_cognito_auth.py
│ │ │ │ ├── test_register_coverage.py
│ │ │ │ └── test_register.py
│ │ │ ├── prompts
│ │ │ │ ├── standalone
│ │ │ │ │ ├── test_operation_prompt.py
│ │ │ │ │ ├── test_prompt_arguments.py
│ │ │ │ │ └── test_secure_operation_prompt.py
│ │ │ │ ├── test_mcp_prompt_manager_integration.py
│ │ │ │ ├── test_mcp_prompt_manager.py
│ │ │ │ ├── test_models_dict_method.py
│ │ │ │ ├── test_operation_prompts_extended.py
│ │ │ │ ├── test_prompt_manager_additional.py
│ │ │ │ ├── test_prompt_manager_comprehensive.py
│ │ │ │ ├── test_prompt_manager_coverage.py
│ │ │ │ └── test_prompt_registration.py
│ │ │ ├── README.md
│ │ │ ├── test_api_name.py
│ │ │ ├── test_cache_coverage_89.py
│ │ │ ├── test_client.py
│ │ │ ├── test_coverage_boost.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main_extended.py
│ │ │ ├── test_main.py
│ │ │ ├── test_openapi_coverage_89.py
│ │ │ ├── test_server_auth_errors.py
│ │ │ ├── test_server_coverage_boost_2.py
│ │ │ ├── test_server_coverage_boost.py
│ │ │ ├── test_server_exception_handling.py
│ │ │ ├── test_server_extended.py
│ │ │ ├── test_server_httpx_version.py
│ │ │ ├── test_server_part1.py
│ │ │ ├── test_server_route_logging.py
│ │ │ ├── test_server_signal_handlers.py
│ │ │ ├── test_server.py
│ │ │ └── utils
│ │ │ ├── test_cache_provider.py
│ │ │ ├── test_error_handler_boost.py
│ │ │ ├── test_error_handler_extended.py
│ │ │ ├── test_error_handler_fix.py
│ │ │ ├── test_error_handler.py
│ │ │ ├── test_http_client_comprehensive.py
│ │ │ ├── test_http_client_extended.py
│ │ │ ├── test_http_client_extended2.py
│ │ │ ├── test_http_client_import_error.py
│ │ │ ├── test_http_client.py
│ │ │ ├── test_metrics_provider_decorators.py
│ │ │ ├── test_metrics_provider_extended2.py
│ │ │ ├── test_metrics_provider_prometheus.py
│ │ │ ├── test_metrics_provider.py
│ │ │ ├── test_openapi_validator.py
│ │ │ └── test_openapi.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── postgres-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── postgres_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── connection
│ │ │ │ ├── __init__.py
│ │ │ │ ├── abstract_db_connection.py
│ │ │ │ ├── db_connection_singleton.py
│ │ │ │ ├── psycopg_pool_connection.py
│ │ │ │ └── rds_api_connection.py
│ │ │ ├── mutable_sql_detector.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── conftest.py
│ │ │ ├── test_psycopg_connector.py
│ │ │ ├── test_server.py
│ │ │ └── test_singleton.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── prometheus-mcp-server
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── prometheus_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── consts.py
│ │ │ ├── models.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── conftest.py
│ │ │ ├── test_aws_credentials.py
│ │ │ ├── test_config_manager.py
│ │ │ ├── test_consts.py
│ │ │ ├── test_coverage_gaps.py
│ │ │ ├── test_coverage_improvement.py
│ │ │ ├── test_final_coverage.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_models.py
│ │ │ ├── test_prometheus_client.py
│ │ │ ├── test_prometheus_connection.py
│ │ │ ├── test_security_validator.py
│ │ │ ├── test_server_coverage.py
│ │ │ ├── test_tools.py
│ │ │ └── test_workspace_config.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── redshift-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── redshift_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── consts.py
│ │ │ ├── models.py
│ │ │ ├── redshift.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_redshift.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── s3-tables-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── s3_tables_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── constants.py
│ │ │ ├── database.py
│ │ │ ├── engines
│ │ │ │ ├── __init__.py
│ │ │ │ └── pyiceberg.py
│ │ │ ├── file_processor
│ │ │ │ ├── __init__.py
│ │ │ │ ├── csv.py
│ │ │ │ ├── parquet.py
│ │ │ │ └── utils.py
│ │ │ ├── models.py
│ │ │ ├── namespaces.py
│ │ │ ├── resources.py
│ │ │ ├── s3_operations.py
│ │ │ ├── server.py
│ │ │ ├── table_buckets.py
│ │ │ ├── tables.py
│ │ │ └── utils.py
│ │ ├── CHANGELOG.md
│ │ ├── CONTEXT.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── test_csv.py
│ │ │ ├── test_database.py
│ │ │ ├── test_file_processor_utils.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_namespaces.py
│ │ │ ├── test_parquet.py
│ │ │ ├── test_pyiceberg.py
│ │ │ ├── test_resources.py
│ │ │ ├── test_s3_operations.py
│ │ │ ├── test_server.py
│ │ │ ├── test_table_buckets.py
│ │ │ ├── test_tables.py
│ │ │ └── test_utils.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── stepfunctions-tool-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── stepfunctions_tool_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── aws_helper.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── .gitignore
│ │ │ ├── README.md
│ │ │ ├── test_aws_helper.py
│ │ │ ├── test_create_state_machine_tool.py
│ │ │ ├── test_filter_state_machines_by_tag.py
│ │ │ ├── test_format_state_machine_response.py
│ │ │ ├── test_get_schema_arn_from_state_machine_arn.py
│ │ │ ├── test_get_schema_from_registry.py
│ │ │ ├── test_invoke_express_state_machine_impl.py
│ │ │ ├── test_invoke_standard_state_machine_impl.py
│ │ │ ├── test_main.py
│ │ │ ├── test_register_state_machines.py
│ │ │ ├── test_sanitize_tool_name.py
│ │ │ ├── test_server.py
│ │ │ └── test_validate_state_machine_name.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── syntheticdata-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── syntheticdata_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── pandas_interpreter.py
│ │ │ ├── server.py
│ │ │ └── storage
│ │ │ ├── __init__.py
│ │ │ ├── base.py
│ │ │ ├── loader.py
│ │ │ └── s3.py
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── test_constants.py
│ │ │ ├── test_pandas_interpreter.py
│ │ │ ├── test_server.py
│ │ │ └── test_storage
│ │ │ ├── __init__.py
│ │ │ ├── test_loader.py
│ │ │ └── test_s3.py
│ │ └── uv.lock
│ ├── terraform-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── terraform_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── impl
│ │ │ │ ├── resources
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── terraform_aws_provider_resources_listing.py
│ │ │ │ │ └── terraform_awscc_provider_resources_listing.py
│ │ │ │ └── tools
│ │ │ │ ├── __init__.py
│ │ │ │ ├── execute_terraform_command.py
│ │ │ │ ├── execute_terragrunt_command.py
│ │ │ │ ├── run_checkov_scan.py
│ │ │ │ ├── search_aws_provider_docs.py
│ │ │ │ ├── search_awscc_provider_docs.py
│ │ │ │ ├── search_specific_aws_ia_modules.py
│ │ │ │ ├── search_user_provided_module.py
│ │ │ │ └── utils.py
│ │ │ ├── models
│ │ │ │ ├── __init__.py
│ │ │ │ └── models.py
│ │ │ ├── scripts
│ │ │ │ ├── generate_aws_provider_resources.py
│ │ │ │ ├── generate_awscc_provider_resources.py
│ │ │ │ └── scrape_aws_terraform_best_practices.py
│ │ │ ├── server.py
│ │ │ └── static
│ │ │ ├── __init__.py
│ │ │ ├── AWS_PROVIDER_RESOURCES.md
│ │ │ ├── AWS_TERRAFORM_BEST_PRACTICES.md
│ │ │ ├── AWSCC_PROVIDER_RESOURCES.md
│ │ │ ├── MCP_INSTRUCTIONS.md
│ │ │ └── TERRAFORM_WORKFLOW_GUIDE.md
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── .gitignore
│ │ │ ├── conftest.py
│ │ │ ├── README.md
│ │ │ ├── test_command_impl.py
│ │ │ ├── test_execute_terraform_command.py
│ │ │ ├── test_execute_terragrunt_command.py
│ │ │ ├── test_models.py
│ │ │ ├── test_parameter_annotations.py
│ │ │ ├── test_resources.py
│ │ │ ├── test_run_checkov_scan.py
│ │ │ ├── test_search_user_provided_module.py
│ │ │ ├── test_server.py
│ │ │ ├── test_tool_implementations.py
│ │ │ ├── test_utils_additional.py
│ │ │ └── test_utils.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── timestream-for-influxdb-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── timestream_for_influxdb_mcp_server
│ │ │ ├── __init__.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── valkey-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── valkey_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── common
│ │ │ │ ├── __init__.py
│ │ │ │ ├── config.py
│ │ │ │ ├── connection.py
│ │ │ │ └── server.py
│ │ │ ├── context.py
│ │ │ ├── main.py
│ │ │ ├── tools
│ │ │ │ ├── __init__.py
│ │ │ │ ├── bitmap.py
│ │ │ │ ├── hash.py
│ │ │ │ ├── hyperloglog.py
│ │ │ │ ├── json.py
│ │ │ │ ├── list.py
│ │ │ │ ├── misc.py
│ │ │ │ ├── server_management.py
│ │ │ │ ├── set.py
│ │ │ │ ├── sorted_set.py
│ │ │ │ ├── stream.py
│ │ │ │ └── string.py
│ │ │ └── version.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── ELASTICACHECONNECT.md
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── test_bitmap.py
│ │ │ ├── test_config.py
│ │ │ ├── test_connection.py
│ │ │ ├── test_hash.py
│ │ │ ├── test_hyperloglog.py
│ │ │ ├── test_init.py
│ │ │ ├── test_json_additional.py
│ │ │ ├── test_json_readonly.py
│ │ │ ├── test_json.py
│ │ │ ├── test_list_additional.py
│ │ │ ├── test_list_readonly.py
│ │ │ ├── test_list.py
│ │ │ ├── test_main.py
│ │ │ ├── test_misc.py
│ │ │ ├── test_server_management.py
│ │ │ ├── test_set_readonly.py
│ │ │ ├── test_set.py
│ │ │ ├── test_sorted_set_additional.py
│ │ │ ├── test_sorted_set_readonly.py
│ │ │ ├── test_sorted_set.py
│ │ │ ├── test_stream_additional.py
│ │ │ ├── test_stream_readonly.py
│ │ │ ├── test_stream.py
│ │ │ └── test_string.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ └── well-architected-security-mcp-server
│ ├── .python-version
│ ├── awslabs
│ │ └── well_architected_security_mcp_server
│ │ ├── __init__.py
│ │ ├── consts.py
│ │ ├── server.py
│ │ └── util
│ │ ├── __init__.py
│ │ ├── network_security.py
│ │ ├── prompt_utils.py
│ │ ├── resource_utils.py
│ │ ├── security_services.py
│ │ └── storage_security.py
│ ├── PROMPT_TEMPLATE.md
│ ├── pyproject.toml
│ ├── README.md
│ ├── tests
│ │ ├── __init__.py
│ │ ├── conftest.py
│ │ ├── README.md
│ │ ├── test_access_analyzer_fix.py
│ │ ├── test_network_security_additional.py
│ │ ├── test_network_security.py
│ │ ├── test_prompt_utils_coverage.py
│ │ ├── test_prompt_utils.py
│ │ ├── test_resource_utils_fix.py
│ │ ├── test_resource_utils.py
│ │ ├── test_security_services_additional.py
│ │ ├── test_security_services_coverage.py
│ │ ├── test_security_services.py
│ │ ├── test_server_additional.py
│ │ ├── test_server_coverage.py
│ │ ├── test_server_prompts.py
│ │ ├── test_server_security_findings.py
│ │ ├── test_server.py
│ │ ├── test_storage_security_additional.py
│ │ ├── test_storage_security_comprehensive.py
│ │ ├── test_storage_security_edge_cases.py
│ │ ├── test_storage_security_recommendations.py
│ │ ├── test_storage_security.py
│ │ └── test_user_agent_config.py
│ └── uv.lock
└── VIBE_CODING_TIPS_TRICKS.md
```
# Files
--------------------------------------------------------------------------------
/src/cloudwatch-appsignals-mcp-server/tests/test_server.py:
--------------------------------------------------------------------------------
```python
"""Tests for CloudWatch Application Signals MCP Server."""
import json
import pytest
from awslabs.cloudwatch_appsignals_mcp_server.server import _filter_operation_targets, main
from awslabs.cloudwatch_appsignals_mcp_server.service_tools import (
get_service_detail,
list_monitored_services,
query_service_metrics,
)
from awslabs.cloudwatch_appsignals_mcp_server.slo_tools import get_slo
from awslabs.cloudwatch_appsignals_mcp_server.trace_tools import (
check_transaction_search_enabled,
get_trace_summaries_paginated,
list_slis,
query_sampled_traces,
search_transaction_spans,
)
from awslabs.cloudwatch_appsignals_mcp_server.utils import remove_null_values
from botocore.exceptions import ClientError
from datetime import datetime, timedelta, timezone
from unittest.mock import AsyncMock, MagicMock, patch
@pytest.fixture(autouse=True)
def mock_aws_clients():
"""Mock all AWS clients to prevent real API calls during tests."""
# Create mock clients
mock_logs_client = MagicMock()
mock_appsignals_client = MagicMock()
mock_cloudwatch_client = MagicMock()
mock_xray_client = MagicMock()
mock_synthetics_client = MagicMock()
mock_s3_client = MagicMock()
# Patch the clients in all modules where they're imported
patches = [
# Original aws_clients module
patch(
'awslabs.cloudwatch_appsignals_mcp_server.aws_clients.logs_client', mock_logs_client
),
patch(
'awslabs.cloudwatch_appsignals_mcp_server.aws_clients.appsignals_client',
mock_appsignals_client,
),
patch(
'awslabs.cloudwatch_appsignals_mcp_server.aws_clients.cloudwatch_client',
mock_cloudwatch_client,
),
patch(
'awslabs.cloudwatch_appsignals_mcp_server.aws_clients.xray_client', mock_xray_client
),
patch(
'awslabs.cloudwatch_appsignals_mcp_server.aws_clients.synthetics_client',
mock_synthetics_client,
),
patch('awslabs.cloudwatch_appsignals_mcp_server.aws_clients.s3_client', mock_s3_client),
# Service tools module (check what's actually imported)
patch(
'awslabs.cloudwatch_appsignals_mcp_server.service_tools.appsignals_client',
mock_appsignals_client,
),
patch(
'awslabs.cloudwatch_appsignals_mcp_server.service_tools.cloudwatch_client',
mock_cloudwatch_client,
),
# SLO tools module (check what's actually imported)
patch(
'awslabs.cloudwatch_appsignals_mcp_server.slo_tools.appsignals_client',
mock_appsignals_client,
),
# Trace tools module (logs_client, xray_client, and appsignals_client are imported)
patch(
'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.logs_client', mock_logs_client
),
patch(
'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.xray_client', mock_xray_client
),
patch(
'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.appsignals_client',
mock_appsignals_client,
),
# SLI report client module (appsignals_client and cloudwatch_client are imported)
patch(
'awslabs.cloudwatch_appsignals_mcp_server.sli_report_client.appsignals_client',
mock_appsignals_client,
),
patch(
'awslabs.cloudwatch_appsignals_mcp_server.sli_report_client.cloudwatch_client',
mock_cloudwatch_client,
),
patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.synthetics_client',
mock_synthetics_client,
),
patch('awslabs.cloudwatch_appsignals_mcp_server.server.s3_client', mock_s3_client),
patch('awslabs.cloudwatch_appsignals_mcp_server.server.iam_client', MagicMock()),
]
# Start all patches
for p in patches:
p.start()
try:
yield {
'logs_client': mock_logs_client,
'appsignals_client': mock_appsignals_client,
'cloudwatch_client': mock_cloudwatch_client,
'xray_client': mock_xray_client,
'synthetics_client': mock_synthetics_client,
's3_client': mock_s3_client,
}
finally:
# Stop all patches
for p in patches:
p.stop()
@pytest.fixture
def mock_mcp():
"""Mock the FastMCP instance."""
with patch('awslabs.cloudwatch_appsignals_mcp_server.server.mcp') as mock:
yield mock
@pytest.mark.asyncio
async def test_list_monitored_services_success(mock_aws_clients):
"""Test successful listing of monitored services."""
mock_response = {
'ServiceSummaries': [
{
'KeyAttributes': {
'Name': 'test-service',
'Type': 'AWS::ECS::Service',
'Environment': 'production',
}
}
]
}
mock_aws_clients['appsignals_client'].list_services.return_value = mock_response
result = await list_monitored_services()
assert 'Application Signals Services (1 total)' in result
assert 'test-service' in result
assert 'AWS::ECS::Service' in result
@pytest.mark.asyncio
async def test_list_monitored_services_empty(mock_aws_clients):
"""Test when no services are found."""
mock_response = {'ServiceSummaries': []}
mock_aws_clients['appsignals_client'].list_services.return_value = mock_response
result = await list_monitored_services()
assert result == 'No services found in Application Signals.'
@pytest.mark.asyncio
async def test_get_service_detail_success(mock_aws_clients):
"""Test successful retrieval of service details."""
mock_list_response = {
'ServiceSummaries': [
{'KeyAttributes': {'Name': 'test-service', 'Type': 'AWS::ECS::Service'}}
]
}
mock_get_response = {
'Service': {
'KeyAttributes': {'Name': 'test-service', 'Type': 'AWS::ECS::Service'},
'AttributeMaps': [{'Platform': 'ECS', 'Application': 'test-app'}],
'MetricReferences': [
{
'Namespace': 'AWS/ApplicationSignals',
'MetricName': 'Latency',
'MetricType': 'GAUGE',
'Dimensions': [{'Name': 'Service', 'Value': 'test-service'}],
}
],
'LogGroupReferences': [{'Identifier': '/aws/ecs/test-service'}],
}
}
mock_aws_clients['appsignals_client'].list_services.return_value = mock_list_response
mock_aws_clients['appsignals_client'].get_service.return_value = mock_get_response
result = await get_service_detail('test-service')
assert 'Service Details: test-service' in result
assert 'AWS::ECS::Service' in result
assert 'Platform: ECS' in result
assert 'AWS/ApplicationSignals/Latency' in result
assert '/aws/ecs/test-service' in result
@pytest.mark.asyncio
async def test_get_service_detail_not_found(mock_aws_clients):
"""Test when service is not found."""
mock_response = {'ServiceSummaries': []}
mock_aws_clients['appsignals_client'].list_services.return_value = mock_response
result = await get_service_detail('nonexistent-service')
assert "Service 'nonexistent-service' not found" in result
@pytest.mark.asyncio
async def test_query_service_metrics_success(mock_aws_clients):
"""Test successful query of service metrics."""
mock_list_response = {
'ServiceSummaries': [
{'KeyAttributes': {'Name': 'test-service', 'Type': 'AWS::ECS::Service'}}
]
}
mock_get_response = {
'Service': {
'MetricReferences': [
{
'Namespace': 'AWS/ApplicationSignals',
'MetricName': 'Latency',
'Dimensions': [{'Name': 'Service', 'Value': 'test-service'}],
}
]
}
}
mock_metric_response = {
'Datapoints': [
{
'Timestamp': datetime.now(timezone.utc),
'Average': 100.5,
'p99': 150.2,
'Unit': 'Milliseconds',
}
]
}
mock_aws_clients['appsignals_client'].list_services.return_value = mock_list_response
mock_aws_clients['appsignals_client'].get_service.return_value = mock_get_response
mock_aws_clients['cloudwatch_client'].get_metric_statistics.return_value = mock_metric_response
result = await query_service_metrics(
service_name='test-service',
metric_name='Latency',
statistic='Average',
extended_statistic='p99',
hours=1,
)
assert 'Metrics for test-service - Latency' in result
assert 'Average Statistics:' in result
assert 'p99 Statistics:' in result
@pytest.mark.asyncio
async def test_query_service_metrics_list_available(mock_aws_clients):
"""Test listing available metrics when no specific metric is requested."""
mock_list_response = {
'ServiceSummaries': [
{'KeyAttributes': {'Name': 'test-service', 'Type': 'AWS::ECS::Service'}}
]
}
mock_get_response = {
'Service': {
'MetricReferences': [
{
'MetricName': 'Latency',
'Namespace': 'AWS/ApplicationSignals',
'MetricType': 'GAUGE',
},
{
'MetricName': 'Error',
'Namespace': 'AWS/ApplicationSignals',
'MetricType': 'COUNT',
},
]
}
}
mock_aws_clients['appsignals_client'].list_services.return_value = mock_list_response
mock_aws_clients['appsignals_client'].get_service.return_value = mock_get_response
result = await query_service_metrics(
service_name='test-service',
metric_name='', # Empty to list available metrics
statistic='Average',
extended_statistic='p99',
hours=1,
)
assert "Available metrics for service 'test-service'" in result
assert 'Latency' in result
assert 'Error' in result
@pytest.mark.asyncio
async def test_get_slo_success(mock_aws_clients):
"""Test successful retrieval of SLO details."""
mock_slo_response = {
'Slo': {
'Name': 'test-slo',
'Arn': 'arn:aws:application-signals:us-east-1:123456789012:slo/test-slo',
'Description': 'Test SLO for latency',
'EvaluationType': 'REQUEST_BASED',
'CreatedTime': '2024-01-01T00:00:00Z',
'LastUpdatedTime': '2024-01-02T00:00:00Z',
'Goal': {
'AttainmentGoal': 99.9,
'WarningThreshold': 99.0,
'Interval': {'RollingInterval': {'Duration': 7, 'DurationUnit': 'DAYS'}},
},
'RequestBasedSli': {
'RequestBasedSliMetric': {
'KeyAttributes': {'Name': 'test-service', 'Type': 'AWS::ECS::Service'},
'OperationName': 'GET /api/test',
'MetricType': 'LATENCY',
'MetricDataQueries': [
{
'Id': 'query1',
'MetricStat': {
'Metric': {
'Namespace': 'AWS/ApplicationSignals',
'MetricName': 'Latency',
'Dimensions': [{'Name': 'Service', 'Value': 'test-service'}],
},
'Period': 60,
'Stat': 'Average',
},
}
],
},
'MetricThreshold': 1000,
'ComparisonOperator': 'GreaterThan',
},
}
}
mock_aws_clients[
'appsignals_client'
].get_service_level_objective.return_value = mock_slo_response
result = await get_slo('test-slo-id')
assert 'Service Level Objective Details' in result
assert 'test-slo' in result
assert 'REQUEST_BASED' in result
assert '99.9%' in result
assert 'GET /api/test' in result
assert 'annotation[aws.local.operation]="GET /api/test"' in result
@pytest.mark.asyncio
async def test_get_slo_not_found(mock_aws_clients):
"""Test when SLO is not found."""
mock_aws_clients['appsignals_client'].get_service_level_objective.return_value = {'Slo': None}
result = await get_slo('nonexistent-slo')
assert 'No SLO found with ID: nonexistent-slo' in result
@pytest.mark.asyncio
async def test_search_transaction_spans_success(mock_aws_clients):
"""Test successful transaction search."""
mock_query_response = {
'queryId': 'test-query-id',
'status': 'Complete',
'statistics': {'recordsMatched': 10},
'results': [
[
{'field': 'spanId', 'value': 'span1'},
{'field': 'timestamp', 'value': '2024-01-01T00:00:00Z'},
]
],
}
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.check_transaction_search_enabled'
) as mock_check:
mock_check.return_value = (True, 'CloudWatchLogs', 'ACTIVE')
mock_aws_clients['logs_client'].start_query.return_value = {'queryId': 'test-query-id'}
mock_aws_clients['logs_client'].get_query_results.return_value = mock_query_response
result = await search_transaction_spans(
log_group_name='aws/spans',
start_time='2024-01-01T00:00:00+00:00', # Fixed ISO format
end_time='2024-01-01T01:00:00+00:00',
query_string='fields @timestamp, spanId',
limit=100,
max_timeout=30,
)
assert result['status'] == 'Complete'
assert result['queryId'] == 'test-query-id'
assert len(result['results']) == 1
assert result['results'][0]['spanId'] == 'span1'
@pytest.mark.asyncio
async def test_search_transaction_spans_not_enabled(mock_aws_clients):
"""Test when transaction search is not enabled."""
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.check_transaction_search_enabled'
) as mock_check:
mock_check.return_value = (False, 'XRay', 'INACTIVE')
result = await search_transaction_spans(
log_group_name='',
start_time='2024-01-01T00:00:00+00:00',
end_time='2024-01-01T01:00:00+00:00',
query_string='fields @timestamp',
limit=None,
max_timeout=30,
)
assert result['status'] == 'Transaction Search Not Available'
assert not result['transaction_search_status']['enabled']
@pytest.mark.asyncio
async def test_list_slis_success(mock_aws_clients):
"""Test successful listing of SLI status."""
mock_services_response = {
'ServiceSummaries': [
{
'KeyAttributes': {
'Name': 'test-service',
'Type': 'AWS::ECS::Service',
'Environment': 'production',
}
}
]
}
# Mock boto3.client calls in SLIReportClient
with patch('boto3.client') as mock_boto3_client:
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.check_transaction_search_enabled'
) as mock_check:
# Configure boto3.client to return our mocked clients
def boto3_client_side_effect(service_name, **kwargs):
if service_name == 'application-signals':
return mock_aws_clients['appsignals_client']
elif service_name == 'cloudwatch':
return mock_aws_clients['cloudwatch_client']
else:
return MagicMock()
mock_boto3_client.side_effect = boto3_client_side_effect
mock_aws_clients[
'appsignals_client'
].list_services.return_value = mock_services_response
mock_check.return_value = (True, 'CloudWatchLogs', 'ACTIVE')
# Mock SLO summaries response for SLIReportClient
mock_slo_response = {
'SloSummaries': [
{
'Name': 'test-slo',
'Arn': 'arn:aws:application-signals:us-east-1:123456789012:slo/test-slo',
'KeyAttributes': {'Name': 'test-service'},
'OperationName': 'GET /api',
'CreatedTime': datetime.now(timezone.utc),
}
]
}
mock_aws_clients[
'appsignals_client'
].list_service_level_objectives.return_value = mock_slo_response
# Mock metric data response showing breach
mock_metric_response = {
'MetricDataResults': [
{
'Id': 'slo0',
'Timestamps': [datetime.now(timezone.utc)],
'Values': [1.0], # Breach count > 0 indicates breach
}
]
}
mock_aws_clients[
'cloudwatch_client'
].get_metric_data.return_value = mock_metric_response
result = await list_slis(hours=24)
assert 'SLI Status Report - Last 24 hours' in result
assert 'Transaction Search: ENABLED' in result
assert 'BREACHED SERVICES:' in result
assert 'test-service' in result
@pytest.mark.asyncio
async def test_query_sampled_traces_success(mock_aws_clients):
"""Test successful query of sampled traces."""
mock_traces = [
{
'Id': 'trace1',
'Duration': 0.5,
'ResponseTime': 500,
'HasError': False,
'HasFault': True,
'HasThrottle': False,
'Http': {'HttpStatus': 500},
'FaultRootCauses': [
{
'Services': [
{
'Name': 'test-service',
'Exceptions': [{'Message': 'Internal server error'}],
}
]
}
],
}
]
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.get_trace_summaries_paginated'
) as mock_get_traces:
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.check_transaction_search_enabled'
) as mock_check:
mock_get_traces.return_value = mock_traces
mock_check.return_value = (False, 'XRay', 'INACTIVE')
result_json = await query_sampled_traces(
start_time='2024-01-01T00:00:00Z',
end_time='2024-01-01T01:00:00Z',
filter_expression='service("test-service"){fault = true}',
)
result = json.loads(result_json)
assert result['TraceCount'] == 1
assert result['TraceSummaries'][0]['Id'] == 'trace1'
assert result['TraceSummaries'][0]['HasFault'] is True
@pytest.mark.asyncio
async def test_query_sampled_traces_time_window_too_large(mock_aws_clients):
"""Test when time window is too large."""
result_json = await query_sampled_traces(
start_time='2024-01-01T00:00:00Z',
end_time='2024-01-02T00:00:00Z', # 24 hours > 6 hours max
filter_expression='service("test-service")',
)
result = json.loads(result_json)
assert 'error' in result
assert 'Time window too large' in result['error']
def test_get_trace_summaries_paginated():
"""Test paginated trace retrieval."""
mock_client = MagicMock()
mock_responses = [
{'TraceSummaries': [{'Id': 'trace1'}, {'Id': 'trace2'}], 'NextToken': 'token1'},
{'TraceSummaries': [{'Id': 'trace3'}]},
]
mock_client.get_trace_summaries.side_effect = mock_responses
start_time = datetime.now(timezone.utc) - timedelta(hours=1)
end_time = datetime.now(timezone.utc)
traces = get_trace_summaries_paginated(
mock_client, start_time, end_time, 'service("test")', max_traces=10
)
assert len(traces) == 3
assert traces[0]['Id'] == 'trace1'
assert traces[2]['Id'] == 'trace3'
def test_get_trace_summaries_paginated_with_error():
"""Test paginated trace retrieval with error."""
mock_client = MagicMock()
mock_client.get_trace_summaries.side_effect = Exception('API Error')
start_time = datetime.now(timezone.utc) - timedelta(hours=1)
end_time = datetime.now(timezone.utc)
traces = get_trace_summaries_paginated(
mock_client, start_time, end_time, 'service("test")', max_traces=10
)
assert len(traces) == 0 # Should return empty list on error
def test_check_transaction_search_enabled(mock_aws_clients):
"""Test checking transaction search status."""
mock_aws_clients['xray_client'].get_trace_segment_destination.return_value = {
'Destination': 'CloudWatchLogs',
'Status': 'ACTIVE',
}
is_enabled, destination, status = check_transaction_search_enabled()
assert is_enabled is True
assert destination == 'CloudWatchLogs'
assert status == 'ACTIVE'
def test_check_transaction_search_enabled_not_active(mock_aws_clients):
"""Test checking transaction search when not active."""
mock_aws_clients['xray_client'].get_trace_segment_destination.return_value = {
'Destination': 'XRay',
'Status': 'INACTIVE',
}
is_enabled, destination, status = check_transaction_search_enabled()
assert is_enabled is False
assert destination == 'XRay'
assert status == 'INACTIVE'
def test_check_transaction_search_enabled_error(mock_aws_clients):
"""Test checking transaction search with error."""
mock_aws_clients['xray_client'].get_trace_segment_destination.side_effect = Exception(
'API Error'
)
is_enabled, destination, status = check_transaction_search_enabled()
assert is_enabled is False
assert destination == 'Unknown'
assert status == 'Error'
def test_remove_null_values():
"""Test remove_null_values function."""
# Test with mix of None and non-None values
input_dict = {
'key1': 'value1',
'key2': None,
'key3': 'value3',
'key4': None,
'key5': 0, # Should not be removed
'key6': '', # Should not be removed
'key7': False, # Should not be removed
}
result = remove_null_values(input_dict)
assert result == {
'key1': 'value1',
'key3': 'value3',
'key5': 0,
'key6': '',
'key7': False,
}
assert 'key2' not in result
assert 'key4' not in result
@pytest.mark.asyncio
async def test_list_monitored_services_client_error(mock_aws_clients):
"""Test ClientError handling in list_monitored_services."""
mock_aws_clients['appsignals_client'].list_services.side_effect = ClientError(
error_response={
'Error': {
'Code': 'AccessDeniedException',
'Message': 'User is not authorized to perform this action',
}
},
operation_name='ListServices',
)
result = await list_monitored_services()
assert 'AWS Error: User is not authorized to perform this action' in result
@pytest.mark.asyncio
async def test_list_monitored_services_general_exception(mock_aws_clients):
"""Test general exception handling in list_monitored_services."""
mock_aws_clients['appsignals_client'].list_services.side_effect = Exception(
'Unexpected error occurred'
)
result = await list_monitored_services()
assert 'Error: Unexpected error occurred' in result
@pytest.mark.asyncio
async def test_get_service_detail_client_error(mock_aws_clients):
"""Test ClientError handling in get_service_detail."""
mock_list_response = {
'ServiceSummaries': [
{'KeyAttributes': {'Name': 'test-service', 'Type': 'AWS::ECS::Service'}}
]
}
mock_aws_clients['appsignals_client'].list_services.return_value = mock_list_response
mock_aws_clients['appsignals_client'].get_service.side_effect = ClientError(
error_response={
'Error': {
'Code': 'ResourceNotFoundException',
'Message': 'Service not found in Application Signals',
}
},
operation_name='GetService',
)
result = await get_service_detail('test-service')
assert 'AWS Error: Service not found in Application Signals' in result
@pytest.mark.asyncio
async def test_get_service_detail_general_exception(mock_aws_clients):
"""Test general exception handling in get_service_detail."""
mock_list_response = {
'ServiceSummaries': [
{'KeyAttributes': {'Name': 'test-service', 'Type': 'AWS::ECS::Service'}}
]
}
mock_aws_clients['appsignals_client'].list_services.return_value = mock_list_response
mock_aws_clients['appsignals_client'].get_service.side_effect = Exception(
'Unexpected error in get_service'
)
result = await get_service_detail('test-service')
assert 'Error: Unexpected error in get_service' in result
@pytest.mark.asyncio
async def test_query_service_metrics_no_datapoints(mock_aws_clients):
"""Test query service metrics when no datapoints are returned."""
mock_list_response = {
'ServiceSummaries': [
{'KeyAttributes': {'Name': 'test-service', 'Type': 'AWS::ECS::Service'}}
]
}
mock_get_response = {
'Service': {
'MetricReferences': [
{
'Namespace': 'AWS/ApplicationSignals',
'MetricName': 'Latency',
'Dimensions': [{'Name': 'Service', 'Value': 'test-service'}],
}
]
}
}
mock_metric_response = {'Datapoints': []}
mock_aws_clients['appsignals_client'].list_services.return_value = mock_list_response
mock_aws_clients['appsignals_client'].get_service.return_value = mock_get_response
mock_aws_clients['cloudwatch_client'].get_metric_statistics.return_value = mock_metric_response
result = await query_service_metrics(
service_name='test-service',
metric_name='Latency',
statistic='Average',
extended_statistic='p99',
hours=1,
)
assert 'No data points found' in result
@pytest.mark.asyncio
async def test_search_transaction_spans_timeout(mock_aws_clients):
"""Test search transaction spans with timeout."""
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.check_transaction_search_enabled'
) as mock_check:
with patch('awslabs.cloudwatch_appsignals_mcp_server.trace_tools.timer') as mock_timer:
# Mock asyncio.sleep to prevent actual waiting
with patch('asyncio.sleep', new_callable=AsyncMock):
mock_check.return_value = (True, 'CloudWatchLogs', 'ACTIVE')
mock_aws_clients['logs_client'].start_query.return_value = {
'queryId': 'test-query-id'
}
mock_aws_clients['logs_client'].get_query_results.return_value = {
'status': 'Running'
}
# Simulate timeout by making timer exceed max_timeout
mock_timer.side_effect = [
0,
0,
0,
31,
31,
] # start_time_perf, poll_start, poll check 1, poll check 2
result = await search_transaction_spans(
log_group_name='',
start_time='2024-01-01T00:00:00+00:00',
end_time='2024-01-01T01:00:00+00:00',
query_string='fields @timestamp',
limit=None,
max_timeout=30,
)
assert result['status'] == 'Polling Timeout'
assert 'did not complete within 30 seconds' in result['message']
def test_main_normal_execution(mock_mcp):
"""Test normal execution of main function."""
main()
mock_mcp.run.assert_called_once_with(transport='stdio')
def test_main_keyboard_interrupt(mock_mcp):
"""Test KeyboardInterrupt handling in main function."""
mock_mcp.run.side_effect = KeyboardInterrupt()
# Should not raise an exception
main()
mock_mcp.run.assert_called_once_with(transport='stdio')
def test_main_general_exception(mock_mcp):
"""Test general exception handling in main function."""
mock_mcp.run.side_effect = Exception('Server error')
with pytest.raises(Exception, match='Server error'):
main()
mock_mcp.run.assert_called_once_with(transport='stdio')
@pytest.mark.asyncio
async def test_get_slo_period_based(mock_aws_clients):
"""Test get_slo with period-based SLI configuration."""
mock_slo_response = {
'Slo': {
'Name': 'test-slo-period',
'Arn': 'arn:aws:application-signals:us-east-1:123456789012:slo/test-slo',
'EvaluationType': 'PERIOD_BASED',
'Sli': {
'SliMetric': {
'KeyAttributes': {'Name': 'test-service', 'Type': 'AWS::Lambda::Function'},
'OperationName': 'ProcessOrder',
'MetricType': 'AVAILABILITY',
'DependencyConfig': {
'DependencyKeyAttributes': {'Name': 'payment-service'},
'DependencyOperationName': 'ProcessPayment',
},
},
'MetricThreshold': 0.99,
'ComparisonOperator': 'LessThan',
},
'BurnRateConfigurations': [
{'LookBackWindowMinutes': 5},
{'LookBackWindowMinutes': 60},
],
}
}
mock_aws_clients[
'appsignals_client'
].get_service_level_objective.return_value = mock_slo_response
result = await get_slo('test-slo-period')
assert 'PERIOD_BASED' in result
assert 'ProcessOrder' in result
assert 'annotation[aws.remote.operation]="ProcessPayment"' in result
assert 'Burn Rate Configurations' in result
@pytest.mark.asyncio
async def test_list_slis_with_error_in_sli_client(mock_aws_clients):
"""Test list_slis when SLIReportClient throws error for some services."""
mock_services_response = {
'ServiceSummaries': [
{
'KeyAttributes': {
'Name': 'test-service-1',
'Type': 'AWS::ECS::Service',
'Environment': 'production',
}
},
{
'KeyAttributes': {
'Name': 'test-service-2',
'Type': 'AWS::Lambda::Function',
'Environment': 'staging',
}
},
]
}
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.SLIReportClient'
) as mock_sli_client:
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.check_transaction_search_enabled'
) as mock_check:
mock_aws_clients[
'appsignals_client'
].list_services.return_value = mock_services_response
mock_check.return_value = (False, 'XRay', 'INACTIVE')
# First service succeeds, second fails
mock_report = MagicMock()
mock_report.breached_slo_count = 0
mock_report.ok_slo_count = 3
mock_report.total_slo_count = 3
mock_report.sli_status = 'OK'
mock_report.start_time = datetime.now(timezone.utc) - timedelta(hours=24)
mock_report.end_time = datetime.now(timezone.utc)
mock_sli_client.return_value.generate_sli_report.side_effect = [
mock_report,
Exception('Failed to get SLI report'),
]
result = await list_slis(hours=24)
assert 'Transaction Search: NOT ENABLED' in result
assert 'HEALTHY SERVICES:' in result
assert 'INSUFFICIENT DATA:' in result
assert 'test-service-1' in result
assert 'test-service-2' in result
@pytest.mark.asyncio
async def test_query_service_metrics_service_not_found(mock_aws_clients):
"""Test query service metrics when service is not found."""
mock_list_response = {'ServiceSummaries': []}
mock_aws_clients['appsignals_client'].list_services.return_value = mock_list_response
result = await query_service_metrics(
service_name='nonexistent-service',
metric_name='Latency',
statistic='Average',
extended_statistic='p99',
hours=1,
)
assert "Service 'nonexistent-service' not found" in result
@pytest.mark.asyncio
async def test_query_service_metrics_no_metrics(mock_aws_clients):
"""Test query service metrics when service has no metrics."""
mock_list_response = {
'ServiceSummaries': [
{'KeyAttributes': {'Name': 'test-service', 'Type': 'AWS::ECS::Service'}}
]
}
mock_get_response = {'Service': {'MetricReferences': []}}
mock_aws_clients['appsignals_client'].list_services.return_value = mock_list_response
mock_aws_clients['appsignals_client'].get_service.return_value = mock_get_response
result = await query_service_metrics(
service_name='test-service',
metric_name='Latency',
statistic='Average',
extended_statistic='p99',
hours=1,
)
assert "No metrics found for service 'test-service'" in result
@pytest.mark.asyncio
async def test_query_service_metrics_metric_not_found(mock_aws_clients):
"""Test query service metrics when specific metric is not found."""
mock_list_response = {
'ServiceSummaries': [
{'KeyAttributes': {'Name': 'test-service', 'Type': 'AWS::ECS::Service'}}
]
}
mock_get_response = {
'Service': {
'MetricReferences': [
{
'Namespace': 'AWS/ApplicationSignals',
'MetricName': 'Error',
'Dimensions': [{'Name': 'Service', 'Value': 'test-service'}],
}
]
}
}
mock_aws_clients['appsignals_client'].list_services.return_value = mock_list_response
mock_aws_clients['appsignals_client'].get_service.return_value = mock_get_response
result = await query_service_metrics(
service_name='test-service',
metric_name='Latency', # Looking for Latency but only Error exists
statistic='Average',
extended_statistic='p99',
hours=1,
)
assert "Metric 'Latency' not found" in result
assert 'Available: Error' in result
@pytest.mark.asyncio
async def test_query_service_metrics_client_error(mock_aws_clients):
"""Test query service metrics with client error."""
mock_aws_clients['appsignals_client'].list_services.side_effect = ClientError(
error_response={
'Error': {
'Code': 'AccessDeniedException',
'Message': 'User is not authorized',
}
},
operation_name='ListServices',
)
result = await query_service_metrics(
service_name='test-service',
metric_name='Latency',
statistic='Average',
extended_statistic='p99',
hours=1,
)
assert 'AWS Error: User is not authorized' in result
@pytest.mark.asyncio
async def test_search_transaction_spans_failed_query(mock_aws_clients):
"""Test search transaction spans when query fails."""
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.check_transaction_search_enabled'
) as mock_check:
mock_check.return_value = (True, 'CloudWatchLogs', 'ACTIVE')
mock_aws_clients['logs_client'].start_query.return_value = {'queryId': 'test-query-id'}
mock_aws_clients['logs_client'].get_query_results.return_value = {
'queryId': 'test-query-id',
'status': 'Failed',
'statistics': {'error': 'Query syntax error'},
}
result = await search_transaction_spans(
log_group_name='',
start_time='2024-01-01T00:00:00+00:00',
end_time='2024-01-01T01:00:00+00:00',
query_string='invalid query',
limit=None,
max_timeout=30,
)
assert result['status'] == 'Failed'
@pytest.mark.asyncio
async def test_get_slo_client_error(mock_aws_clients):
"""Test get_slo with client error."""
mock_aws_clients['appsignals_client'].get_service_level_objective.side_effect = ClientError(
error_response={
'Error': {
'Code': 'ResourceNotFoundException',
'Message': 'SLO not found',
}
},
operation_name='GetServiceLevelObjective',
)
result = await get_slo('test-slo-id')
assert 'AWS Error: SLO not found' in result
@pytest.mark.asyncio
async def test_search_transaction_spans_empty_log_group(mock_aws_clients):
"""Test search transaction spans with empty log group defaults to aws/spans."""
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.check_transaction_search_enabled'
) as mock_check:
mock_check.return_value = (True, 'CloudWatchLogs', 'ACTIVE')
mock_aws_clients['logs_client'].start_query.return_value = {'queryId': 'test-query-id'}
mock_aws_clients['logs_client'].get_query_results.return_value = {
'queryId': 'test-query-id',
'status': 'Complete',
'results': [],
}
await search_transaction_spans(
log_group_name='', # Empty string should default to 'aws/spans'
start_time='2024-01-01T00:00:00+00:00',
end_time='2024-01-01T01:00:00+00:00',
query_string='fields @timestamp',
limit=None,
max_timeout=30,
)
# Verify start_query was called with default 'aws/spans'
mock_aws_clients['logs_client'].start_query.assert_called()
call_args = mock_aws_clients['logs_client'].start_query.call_args[1]
assert 'aws/spans' in call_args['logGroupNames']
@pytest.mark.asyncio
async def test_list_slis_no_services(mock_aws_clients):
"""Test list_slis when no services exist."""
mock_aws_clients['appsignals_client'].list_services.return_value = {'ServiceSummaries': []}
result = await list_slis(hours=24)
assert 'No services found in Application Signals.' in result
@pytest.mark.asyncio
async def test_get_slo_with_calendar_interval(mock_aws_clients):
"""Test get_slo with calendar interval in goal."""
mock_slo_response = {
'Slo': {
'Name': 'test-slo-calendar',
'Goal': {
'AttainmentGoal': 99.5,
'Interval': {
'CalendarInterval': {
'Duration': 1,
'DurationUnit': 'MONTH',
'StartTime': '2024-01-01T00:00:00Z',
}
},
},
}
}
mock_aws_clients[
'appsignals_client'
].get_service_level_objective.return_value = mock_slo_response
result = await get_slo('test-slo-calendar')
assert 'Calendar 1 MONTH starting 2024-01-01T00:00:00Z' in result
@pytest.mark.asyncio
async def test_query_service_metrics_different_periods(mock_aws_clients):
"""Test query service metrics with different time periods."""
# Test data for different hour ranges
test_cases = [
(2, 60), # 2 hours -> 1 minute period
(12, 300), # 12 hours -> 5 minute period
(48, 3600), # 48 hours -> 1 hour period
]
for hours, expected_period in test_cases:
mock_list_response = {
'ServiceSummaries': [
{'KeyAttributes': {'Name': 'test-service', 'Type': 'AWS::ECS::Service'}}
]
}
mock_get_response = {
'Service': {
'MetricReferences': [
{
'Namespace': 'AWS/ApplicationSignals',
'MetricName': 'Latency',
'Dimensions': [],
}
]
}
}
mock_metric_response = {
'Datapoints': [{'Timestamp': datetime.now(timezone.utc), 'Average': 100.0}]
}
mock_aws_clients['appsignals_client'].list_services.return_value = mock_list_response
mock_aws_clients['appsignals_client'].get_service.return_value = mock_get_response
mock_aws_clients[
'cloudwatch_client'
].get_metric_statistics.return_value = mock_metric_response
await query_service_metrics(
service_name='test-service',
metric_name='Latency',
statistic='Average',
extended_statistic='p99',
hours=hours,
)
# Verify the period was set correctly
call_args = mock_aws_clients['cloudwatch_client'].get_metric_statistics.call_args[1]
assert call_args['Period'] == expected_period
@pytest.mark.asyncio
async def test_query_service_metrics_general_exception(mock_aws_clients):
"""Test query service metrics with unexpected exception."""
mock_aws_clients['appsignals_client'].list_services.side_effect = Exception('Unexpected error')
result = await query_service_metrics(
service_name='test-service',
metric_name='Latency',
statistic='Average',
extended_statistic='p99',
hours=1,
)
assert 'Error: Unexpected error' in result
@pytest.mark.asyncio
async def test_search_transaction_spans_general_exception(mock_aws_clients):
"""Test search transaction spans with general exception."""
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.check_transaction_search_enabled'
) as mock_check:
mock_check.return_value = (True, 'CloudWatchLogs', 'ACTIVE')
mock_aws_clients['logs_client'].start_query.side_effect = Exception('Query failed')
with pytest.raises(Exception) as exc_info:
await search_transaction_spans(
log_group_name='aws/spans',
start_time='2024-01-01T00:00:00+00:00',
end_time='2024-01-01T01:00:00+00:00',
query_string='fields @timestamp',
limit=100,
max_timeout=30,
)
assert 'Query failed' in str(exc_info.value)
@pytest.mark.asyncio
async def test_list_monitored_services_with_attributes_branch(mock_aws_clients):
"""Test list_monitored_services with key attributes that trigger the branch."""
mock_response = {
'ServiceSummaries': [
{
'KeyAttributes': {} # Empty attributes to test the branch
}
]
}
mock_aws_clients['appsignals_client'].list_services.return_value = mock_response
result = await list_monitored_services()
assert 'Application Signals Services (1 total)' in result
assert 'Key Attributes:' not in result # Should not show when empty
@pytest.mark.asyncio
async def test_get_trace_summaries_paginated_with_limit(mock_aws_clients):
"""Test get_trace_summaries_paginated when it hits the max_traces limit."""
# Mock responses with more traces than the limit
mock_response_1 = {
'TraceSummaries': [{'Id': f'trace-{i}', 'Duration': 100} for i in range(10)],
'NextToken': 'token1',
}
mock_response_2 = {
'TraceSummaries': [{'Id': f'trace-{i}', 'Duration': 100} for i in range(10, 15)]
}
mock_aws_clients['xray_client'].get_trace_summaries.side_effect = [
mock_response_1,
mock_response_2,
]
# Test with max_traces=12
traces = get_trace_summaries_paginated(
mock_aws_clients['xray_client'],
datetime.now(timezone.utc),
datetime.now(timezone.utc),
'service("test")',
max_traces=12,
)
# The function continues until it gets all traces from the current page
# before checking the limit, so we might get more than max_traces
assert len(traces) >= 12 # Should have at least the limit
@pytest.mark.asyncio
async def test_get_slo_with_period_based_sli_full_details(mock_aws_clients):
"""Test get_slo with comprehensive period-based SLI configuration."""
mock_response = {
'Slo': {
'Name': 'test-slo',
'Arn': 'arn:aws:slo:test',
'Description': 'Test SLO',
'EvaluationType': 'PERIOD_BASED',
'CreatedTime': datetime.now(timezone.utc),
'LastUpdatedTime': datetime.now(timezone.utc),
'Goal': {
'AttainmentGoal': 99.9,
'WarningThreshold': 95,
'Interval': {
'CalendarInterval': {
'Duration': 1,
'DurationUnit': 'MONTH',
'StartTime': datetime.now(timezone.utc),
}
},
},
'Sli': {
'SliMetric': {
'KeyAttributes': {'Service': 'test-service', 'Environment': 'prod'},
'OperationName': 'GetItem',
'MetricType': 'LATENCY',
'MetricDataQueries': [
{
'Id': 'query1',
'MetricStat': {
'Metric': {
'Namespace': 'AWS/ApplicationSignals',
'MetricName': 'Latency',
'Dimensions': [
{'Name': 'Service', 'Value': 'test-service'},
{'Name': 'Operation', 'Value': 'GetItem'},
],
},
'Period': 300,
'Stat': 'p99',
'Unit': 'Milliseconds',
},
'ReturnData': True,
},
{'Id': 'query2', 'Expression': 'query1 * 2', 'ReturnData': False},
],
'DependencyConfig': {
'DependencyKeyAttributes': {
'RemoteService': 'downstream-service',
'RemoteEnvironment': 'prod',
},
'DependencyOperationName': 'ProcessRequest',
},
},
'MetricThreshold': 1000,
'ComparisonOperator': 'LessThan',
},
'BurnRateConfigurations': [
{'LookBackWindowMinutes': 5},
{'LookBackWindowMinutes': 60},
],
}
}
mock_aws_clients['appsignals_client'].get_service_level_objective.return_value = mock_response
result = await get_slo('test-slo-id')
# Verify all sections are present
assert 'Service Level Objective Details' in result
assert 'Goal Configuration' in result
assert 'Calendar 1 MONTH' in result
assert 'Period-Based SLI Configuration' in result
assert 'Key Attributes:' in result
assert 'Service: test-service' in result
assert 'Operation Name: GetItem' in result
assert 'Metric Data Queries:' in result
assert 'Query ID: query1' in result
assert 'Namespace: AWS/ApplicationSignals' in result
assert 'Dimensions:' in result
assert 'Expression: query1 * 2' in result
assert 'ReturnData: False' in result
assert 'Dependency Configuration:' in result
assert 'RemoteService: downstream-service' in result
assert 'Dependency Operation: ProcessRequest' in result
assert 'Burn Rate Configurations:' in result
@pytest.mark.asyncio
async def test_get_slo_with_request_based_sli_full_details(mock_aws_clients):
"""Test get_slo with comprehensive request-based SLI configuration."""
mock_response = {
'Slo': {
'Name': 'test-slo-rbs',
'Arn': 'arn:aws:slo:test-rbs',
'Goal': {
'AttainmentGoal': 99.5,
'Interval': {'RollingInterval': {'Duration': 7, 'DurationUnit': 'DAY'}},
},
'RequestBasedSli': {
'RequestBasedSliMetric': {
'KeyAttributes': {'Service': 'api-service', 'Type': 'AWS::Lambda::Function'},
'OperationName': 'ProcessOrder',
'MetricType': 'AVAILABILITY',
'MetricDataQueries': [
{
'Id': 'success',
'MetricStat': {
'Metric': {
'Namespace': 'AWS/Lambda',
'MetricName': 'Success',
'Dimensions': [
{'Name': 'FunctionName', 'Value': 'process-order'}
],
},
'Period': 60,
'Stat': 'Sum',
},
},
{
'Id': 'errors',
'MetricStat': {
'Metric': {
'Namespace': 'AWS/Lambda',
'MetricName': 'Errors',
'Dimensions': [
{'Name': 'FunctionName', 'Value': 'process-order'}
],
},
'Period': 60,
'Stat': 'Sum',
'Unit': 'Count',
},
},
{'Id': 'availability', 'Expression': 'success / (success + errors) * 100'},
],
'DependencyConfig': {
'DependencyKeyAttributes': {'Database': 'orders-db'},
'DependencyOperationName': 'Query',
},
},
'MetricThreshold': 99.0,
'ComparisonOperator': 'GreaterThan',
},
}
}
mock_aws_clients['appsignals_client'].get_service_level_objective.return_value = mock_response
result = await get_slo('test-slo-rbs-id')
# Verify request-based sections
assert 'Request-Based SLI Configuration:' in result
assert 'api-service' in result
assert 'ProcessOrder' in result
assert 'AVAILABILITY' in result
assert 'Expression: success / (success + errors) * 100' in result
assert 'Dependency Configuration:' in result
assert 'Database: orders-db' in result
assert 'Unit: Count' in result
@pytest.mark.asyncio
async def test_get_slo_general_exception(mock_aws_clients):
"""Test get_slo with general exception."""
mock_aws_clients['appsignals_client'].get_service_level_objective.side_effect = Exception(
'Unexpected error'
)
result = await get_slo('test-slo-id')
assert 'Error: Unexpected error' in result
@pytest.mark.asyncio
async def test_search_transaction_spans_with_none_log_group(mock_aws_clients):
"""Test search_transaction_spans when log_group_name is None."""
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.check_transaction_search_enabled'
) as mock_check:
mock_check.return_value = (True, 'CloudWatchLogs', 'ACTIVE')
mock_aws_clients['logs_client'].start_query.return_value = {'queryId': 'test-query-id'}
mock_aws_clients['logs_client'].get_query_results.return_value = {
'queryId': 'test-query-id',
'status': 'Complete',
'results': [],
}
# Pass None for log_group_name to test the default handling
await search_transaction_spans(
log_group_name=None, # type: ignore
start_time='2024-01-01T00:00:00+00:00',
end_time='2024-01-01T01:00:00+00:00',
query_string='fields @timestamp',
limit=100,
max_timeout=30,
)
# Verify it used the default log group
call_args = mock_aws_clients['logs_client'].start_query.call_args[1]
assert 'aws/spans' in call_args['logGroupNames']
@pytest.mark.asyncio
async def test_search_transaction_spans_complete_with_statistics(mock_aws_clients):
"""Test search_transaction_spans when query completes with detailed statistics."""
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.check_transaction_search_enabled'
) as mock_check:
mock_check.return_value = (True, 'CloudWatchLogs', 'ACTIVE')
mock_aws_clients['logs_client'].start_query.return_value = {'queryId': 'test-query-id'}
# First return Running, then Complete
mock_aws_clients['logs_client'].get_query_results.side_effect = [
{'queryId': 'test-query-id', 'status': 'Running'},
{
'queryId': 'test-query-id',
'status': 'Complete',
'statistics': {
'recordsMatched': 100,
'recordsScanned': 1000,
'bytesScanned': 50000,
},
'results': [
[
{'field': 'spanId', 'value': 'span1'},
{'field': '@timestamp', 'value': '2024-01-01 00:00:00'},
]
],
},
]
result = await search_transaction_spans(
log_group_name='aws/spans',
start_time='2024-01-01T00:00:00+00:00',
end_time='2024-01-01T01:00:00+00:00',
query_string='fields @timestamp, spanId',
limit=100,
max_timeout=30,
)
assert result['status'] == 'Complete'
assert result['statistics']['recordsMatched'] == 100
assert len(result['results']) == 1
@pytest.mark.asyncio
async def test_list_slis_general_exception(mock_aws_clients):
"""Test list_slis with general exception."""
mock_aws_clients['appsignals_client'].list_services.side_effect = Exception(
'Service unavailable'
)
result = await list_slis(hours=24)
assert 'Error getting SLI status: Service unavailable' in result
@pytest.mark.asyncio
async def test_query_sampled_traces_with_defaults(mock_aws_clients):
"""Test query_sampled_traces with default start_time and end_time."""
mock_trace_response = {
'TraceSummaries': [
{
'Id': 'trace1',
'Duration': 100,
'HasError': True,
'ErrorRootCauses': [
{
'Services': [
{
'Name': 'test-service',
'Names': ['test-service'],
'Type': 'AWS::ECS::Service',
'AccountId': '123456789012',
'EntityPath': [
{'Name': 'test-service', 'Coverage': 1.0, 'Remote': False}
],
'Inferred': False,
}
],
'ClientImpacting': True,
}
],
}
]
}
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.get_trace_summaries_paginated'
) as mock_paginated:
mock_paginated.return_value = mock_trace_response['TraceSummaries']
# Call without start_time and end_time to test defaults
result_json = await query_sampled_traces(
filter_expression='service("test-service")',
start_time=None,
end_time=None,
region='us-east-1',
)
result = json.loads(result_json)
assert result['TraceCount'] == 1
assert result['TraceSummaries'][0]['HasError'] is True
# Verify the time window was set to 3 hours
call_args = mock_paginated.call_args[0]
time_diff = call_args[2] - call_args[1] # end_time - start_time
assert 2.9 < time_diff.total_seconds() / 3600 < 3.1 # Approximately 3 hours
@pytest.mark.asyncio
async def test_query_sampled_traces_with_annotations(mock_aws_clients):
"""Test query_sampled_traces with annotations filtering."""
mock_trace = {
'Id': 'trace1',
'Duration': 100,
'Annotations': {
'aws.local.operation': 'GetItem',
'aws.remote.operation': 'Query',
'custom.field': 'should-be-filtered',
'another.field': 'also-filtered',
},
'Users': [
{'UserName': 'user1', 'ServiceIds': []},
{'UserName': 'user2', 'ServiceIds': []},
{'UserName': 'user3', 'ServiceIds': []}, # Should be limited to 2
],
}
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.get_trace_summaries_paginated'
) as mock_paginated:
mock_paginated.return_value = [mock_trace]
result_json = await query_sampled_traces(
start_time='2024-01-01T00:00:00Z',
end_time='2024-01-01T01:00:00Z',
filter_expression='service("test")',
)
result = json.loads(result_json)
trace_summary = result['TraceSummaries'][0]
# Check annotations were filtered
assert 'Annotations' in trace_summary
assert 'aws.local.operation' in trace_summary['Annotations']
assert 'aws.remote.operation' in trace_summary['Annotations']
assert 'custom.field' not in trace_summary['Annotations']
# Check users were limited
assert len(trace_summary['Users']) == 2
@pytest.mark.asyncio
async def test_query_sampled_traces_with_fault_causes(mock_aws_clients):
"""Test query_sampled_traces with fault root causes."""
mock_trace = {
'Id': 'trace1',
'Duration': 100,
'HasFault': True,
'FaultRootCauses': [
{'Services': [{'Name': 'service1', 'Exceptions': [{'Message': 'Test fault error'}]}]},
{'Services': [{'Name': 'service2'}]},
{'Services': [{'Name': 'service3'}]},
{'Services': [{'Name': 'service4'}]}, # Should be limited to 3
],
'ResponseTimeRootCauses': [{'Services': [{'Name': 'slow-service'}]}],
}
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.get_trace_summaries_paginated'
) as mock_paginated:
mock_paginated.return_value = [mock_trace]
result_json = await query_sampled_traces(
start_time='2024-01-01T00:00:00Z', end_time='2024-01-01T01:00:00Z'
)
result = json.loads(result_json)
trace_summary = result['TraceSummaries'][0]
# Check root causes were limited to 3
assert len(trace_summary['FaultRootCauses']) == 3
assert 'ResponseTimeRootCauses' in trace_summary
@pytest.mark.asyncio
async def test_query_sampled_traces_general_exception(mock_aws_clients):
"""Test query_sampled_traces with general exception."""
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.get_trace_summaries_paginated'
) as mock_paginated:
mock_paginated.side_effect = Exception('Trace query failed')
result_json = await query_sampled_traces(
start_time='2024-01-01T00:00:00Z', end_time='2024-01-01T01:00:00Z'
)
result = json.loads(result_json)
assert 'error' in result
assert 'Trace query failed' in result['error']
@pytest.mark.asyncio
async def test_query_sampled_traces_datetime_conversion(mock_aws_clients):
"""Test query_sampled_traces with datetime objects that need conversion."""
# The convert_datetime function in server.py only processes top-level fields,
# not nested datetime objects. Let's test with a datetime at the top level.
mock_trace = {
'Id': 'trace1',
'Duration': 100,
'Http': {'HttpStatus': 200, 'HttpMethod': 'GET'},
'StartTime': datetime.now(timezone.utc), # This will be processed by convert_datetime
'EndTime': datetime.now(timezone.utc) + timedelta(minutes=1),
}
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.get_trace_summaries_paginated'
) as mock_paginated:
mock_paginated.return_value = [mock_trace]
result_json = await query_sampled_traces(
start_time='2024-01-01T00:00:00Z', end_time='2024-01-01T01:00:00Z'
)
# Should not raise JSON serialization error
result = json.loads(result_json)
assert result['TraceCount'] == 1
# The datetime fields should have been converted during processing
trace_summary = result['TraceSummaries'][0]
assert (
'StartTime' not in trace_summary
) # These fields are not included in the simplified output
assert 'EndTime' not in trace_summary
@pytest.mark.asyncio
async def test_query_sampled_traces_deduplication(mock_aws_clients):
"""Test query_sampled_traces deduplicates traces with same fault message.
Note: Only FaultRootCauses are deduplicated, not ErrorRootCauses.
This is because the primary use case is investigating server faults (5xx errors),
not client errors (4xx).
"""
# Create 5 traces with the same fault message
mock_traces = [
{
'Id': f'trace{i}',
'Duration': 100 + i * 10,
'ResponseTime': 95 + i * 10,
'HasFault': True,
'FaultRootCauses': [
{
'Services': [
{
'Name': 'test-service',
'Exceptions': [{'Message': 'Database connection timeout'}],
}
]
}
],
}
for i in range(1, 6)
]
# Add 2 traces with ErrorRootCauses (these should NOT be deduplicated)
mock_traces.extend(
[
{
'Id': 'trace6',
'Duration': 200,
'HasError': True,
'ErrorRootCauses': [
{
'Services': [
{
'Name': 'api-service',
'Exceptions': [{'Message': 'Invalid API key'}],
}
]
}
],
},
{
'Id': 'trace7',
'Duration': 210,
'HasError': True,
'ErrorRootCauses': [
{
'Services': [
{
'Name': 'api-service',
'Exceptions': [{'Message': 'Invalid API key'}],
}
]
}
],
},
]
)
# Add 2 healthy traces
mock_traces.extend(
[
{
'Id': 'trace8',
'Duration': 50,
'ResponseTime': 45,
'HasError': False,
'HasFault': False,
},
{
'Id': 'trace9',
'Duration': 55,
'ResponseTime': 50,
'HasError': False,
'HasFault': False,
},
]
)
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.get_trace_summaries_paginated'
) as mock_paginated:
mock_paginated.return_value = mock_traces
result_json = await query_sampled_traces(
start_time='2024-01-01T00:00:00Z', end_time='2024-01-01T01:00:00Z'
)
result = json.loads(result_json)
# Verify deduplication worked - should only have 5 traces
# 1 for database timeout fault (deduplicated from 5)
# 2 for API key errors (NOT deduplicated - only faults are deduped)
# 2 healthy traces (not deduplicated)
assert result['TraceCount'] == 5
assert len(result['TraceSummaries']) == 5
# Verify deduplication stats
assert 'DeduplicationStats' in result
assert result['DeduplicationStats']['OriginalTraceCount'] == 9
assert result['DeduplicationStats']['DuplicatesRemoved'] == 4 # 9 - 5 = 4
assert (
result['DeduplicationStats']['UniqueFaultMessages'] == 1
) # Only counting FaultRootCauses
# Find the trace with fault
db_trace = next(
(
t
for t in result['TraceSummaries']
if t.get('FaultRootCauses')
and any(
'Database connection timeout' in str(s.get('Exceptions', []))
for cause in t['FaultRootCauses']
for s in cause.get('Services', [])
)
),
None,
)
assert db_trace is not None
assert db_trace['HasFault'] is True
# Verify both error traces are present (not deduplicated)
error_traces = [
t
for t in result['TraceSummaries']
if t.get('ErrorRootCauses')
and any(
'Invalid API key' in str(s.get('Exceptions', []))
for cause in t['ErrorRootCauses']
for s in cause.get('Services', [])
)
]
assert len(error_traces) == 2 # Both error traces should be kept
assert all(t['HasError'] is True for t in error_traces)
# Verify healthy traces are included
healthy_count = sum(
1
for t in result['TraceSummaries']
if not t.get('HasError') and not t.get('HasFault') and not t.get('HasThrottle')
)
assert healthy_count == 2
def test_main_success(mock_aws_clients):
"""Test main function normal execution."""
with patch('awslabs.cloudwatch_appsignals_mcp_server.server.mcp') as mock_mcp:
main()
mock_mcp.run.assert_called_once_with(transport='stdio')
def test_main_exception(mock_aws_clients):
"""Test main function with general exception."""
with patch('awslabs.cloudwatch_appsignals_mcp_server.server.mcp') as mock_mcp:
mock_mcp.run.side_effect = Exception('Server error')
with pytest.raises(Exception) as exc_info:
main()
assert 'Server error' in str(exc_info.value)
def test_main_entry_point(mock_aws_clients):
"""Test the if __name__ == '__main__' entry point."""
# The __main__ block is simple and just calls main()
# We can't easily test it without executing the module
# So we'll just ensure the main() function works
# The actual line 1346 will be covered when the module is imported
# during normal test execution
# Instead, let's just verify the main function exists and is callable
from awslabs.cloudwatch_appsignals_mcp_server.server import main
assert callable(main)
# And verify that running main with mocked mcp doesn't raise
with patch('awslabs.cloudwatch_appsignals_mcp_server.server.mcp') as mock_mcp:
mock_mcp.run.side_effect = KeyboardInterrupt()
# Should handle KeyboardInterrupt gracefully
main()
@pytest.mark.asyncio
async def test_analyze_canary_failures_no_runs(mock_aws_clients):
"""Test analyze_canary_failures when no runs are found."""
from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': []}
mock_aws_clients['synthetics_client'].get_canary.return_value = {
'Canary': {'Name': 'test-canary'}
}
result = await analyze_canary_failures('test-canary', 'us-east-1')
assert 'No run history found for test-canary' in result
@pytest.mark.asyncio
async def test_analyze_canary_failures_healthy_canary(mock_aws_clients):
"""Test analyze_canary_failures with healthy canary."""
from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
mock_runs = [
{
'Id': 'run1',
'Status': {'State': 'PASSED'},
'Timeline': {'Started': '2024-01-01T00:00:00Z'},
}
]
mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
mock_aws_clients['synthetics_client'].get_canary.return_value = {
'Canary': {'Name': 'test-canary'}
}
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
) as mock_insights:
mock_insights.return_value = 'Telemetry insights available'
result = await analyze_canary_failures('test-canary', 'us-east-1')
assert 'Canary is healthy - no failures since last success' in result
assert '🔍 Comprehensive Failure Analysis for test-canary' in result
@pytest.mark.asyncio
async def test_analyze_canary_failures_telemetry_unavailable(mock_aws_clients):
"""Test analyze_canary_failures when telemetry is unavailable."""
from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
mock_runs = [
{
'Id': 'run1',
'Status': {'State': 'PASSED'},
'Timeline': {'Started': '2024-01-01T00:00:00Z'},
}
]
mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
mock_aws_clients['synthetics_client'].get_canary.return_value = {
'Canary': {'Name': 'test-canary'}
}
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
) as mock_insights:
mock_insights.side_effect = Exception('Telemetry API error')
result = await analyze_canary_failures('test-canary', 'us-east-1')
assert 'Telemetry API unavailable: Telemetry API error' in result
@pytest.mark.asyncio
async def test_analyze_canary_failures_with_failures(mock_aws_clients):
"""Test analyze_canary_failures with actual failures."""
from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
mock_runs = [
{
'Id': 'failed-run-1',
'Status': {'State': 'FAILED', 'StateReason': 'Navigation timeout'},
'Timeline': {'Started': '2024-01-01T01:00:00Z'},
},
{
'Id': 'failed-run-2',
'Status': {'State': 'FAILED', 'StateReason': 'Navigation timeout'},
'Timeline': {'Started': '2024-01-01T00:30:00Z'},
},
{
'Id': 'success-run',
'Status': {'State': 'PASSED'},
'Timeline': {'Started': '2024-01-01T00:00:00Z'},
},
]
mock_canary = {
'Name': 'test-canary',
'ArtifactS3Location': 's3://test-bucket/canary/us-east-1/test-canary',
}
mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
# Mock S3 artifacts
mock_aws_clients['s3_client'].list_objects_v2.return_value = {
'Contents': [
{'Key': 'canary/us-east-1/test-canary/2024/01/01/test.har'},
{'Key': 'canary/us-east-1/test-canary/2024/01/01/screenshot.png'},
{'Key': 'canary/us-east-1/test-canary/2024/01/01/logs.txt'},
]
}
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
) as mock_insights:
with patch('awslabs.cloudwatch_appsignals_mcp_server.server.analyze_har_file') as mock_har:
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.analyze_screenshots'
) as mock_screenshots:
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.analyze_log_files'
) as mock_logs:
mock_insights.return_value = 'Telemetry insights'
mock_har.return_value = {
'failed_requests': 2,
'total_requests': 10,
'request_details': [
{'url': 'https://example.com', 'status': 500, 'time': 1000}
],
}
mock_screenshots.return_value = {'insights': ['Screenshot analysis']}
mock_logs.return_value = {'insights': ['Log analysis']}
result = await analyze_canary_failures('test-canary', 'us-east-1')
assert 'Found 2 consecutive failures since last success' in result
assert 'All failures have same cause: Navigation timeout' in result
@pytest.mark.asyncio
async def test_analyze_canary_failures_iam_analysis(mock_aws_clients):
"""Test analyze_canary_failures with IAM-related failures."""
from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
mock_runs = [
{
'Id': 'failed-run',
'Status': {'State': 'FAILED', 'StateReason': 'Access denied'},
'Timeline': {'Started': '2024-01-01T00:00:00Z'},
}
]
mock_canary = {'Name': 'test-canary', 'ArtifactS3Location': ''}
mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
) as mock_insights:
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.analyze_iam_role_and_policies'
) as mock_iam:
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.check_resource_arns_correct'
) as mock_arn:
mock_insights.return_value = 'Telemetry insights'
mock_iam.return_value = {
'status': 'issues_found',
'checks': {'role_exists': 'PASS', 'policies_attached': 'FAIL'},
'issues_found': ['Missing S3 permissions'],
'recommendations': ['Add S3 read permissions'],
}
mock_arn.return_value = {
'correct': False,
'error': 'Invalid bucket ARN',
'issues': ['Bucket name mismatch'],
}
result = await analyze_canary_failures('test-canary', 'us-east-1')
assert 'RUNNING COMPREHENSIVE IAM ANALYSIS' in result
assert 'IAM Role Analysis Status: issues_found' in result
assert 'ALL IAM ISSUES FOUND (2 total):' in result
assert 'IAM Policy: Missing S3 permissions' in result
assert 'Resource ARN: Bucket name mismatch' in result
@pytest.mark.asyncio
async def test_analyze_canary_failures_enospc_error(mock_aws_clients):
"""Test analyze_canary_failures with ENOSPC error."""
from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
mock_runs = [
{
'Id': 'failed-run',
'Status': {'State': 'FAILED', 'StateReason': 'ENOSPC: no space left on device'},
'Timeline': {'Started': '2024-01-01T00:00:00Z'},
}
]
mock_canary = {'Name': 'test-canary', 'ArtifactS3Location': ''}
mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
) as mock_insights:
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.extract_disk_memory_usage_metrics'
) as mock_metrics:
mock_insights.return_value = 'Telemetry insights'
mock_metrics.return_value = {
'maxEphemeralStorageUsageInMb': 512.5,
'maxEphemeralStorageUsagePercent': 95.2,
}
result = await analyze_canary_failures('test-canary', 'us-east-1')
assert 'DISK USAGE ROOT CAUSE ANALYSIS:' in result
assert 'Storage: 512.5 MB peak' in result
assert 'Usage: 95.2% peak' in result
@pytest.mark.asyncio
async def test_analyze_canary_failures_protocol_error(mock_aws_clients):
"""Test analyze_canary_failures with protocol error."""
from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
mock_runs = [
{
'Id': 'failed-run',
'Status': {
'State': 'FAILED',
'StateReason': 'Protocol error (Target.activateTarget): Session closed',
},
'Timeline': {'Started': '2024-01-01T00:00:00Z'},
}
]
mock_canary = {'Name': 'test-canary', 'ArtifactS3Location': ''}
mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
) as mock_insights:
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.extract_disk_memory_usage_metrics'
) as mock_metrics:
mock_insights.return_value = 'Telemetry insights'
mock_metrics.return_value = {'maxSyntheticsMemoryUsageInMB': 256.8}
result = await analyze_canary_failures('test-canary', 'us-east-1')
assert 'MEMORY USAGE ROOT CAUSE ANALYSIS:' in result
assert 'Memory: 256.8 MB peak' in result
@pytest.mark.asyncio
async def test_analyze_canary_failures_navigation_timeout_with_har(mock_aws_clients):
"""Test analyze_canary_failures with navigation timeout and HAR analysis."""
from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
mock_runs = [
{
'Id': 'failed-run',
'Status': {'State': 'FAILED', 'StateReason': 'Navigation timed out after 30000ms'},
'Timeline': {'Started': '2024-01-01T00:00:00Z'},
}
]
mock_canary = {
'Name': 'test-canary',
'ArtifactS3Location': 's3://test-bucket/canary/us-east-1/test-canary',
}
mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
# Mock S3 to return HAR files
mock_aws_clients['s3_client'].list_objects_v2.return_value = {
'Contents': [
{'Key': 'canary/us-east-1/test-canary/2024/01/01/test.har'},
]
}
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
) as mock_insights:
with patch('awslabs.cloudwatch_appsignals_mcp_server.server.analyze_har_file') as mock_har:
mock_insights.return_value = 'Telemetry insights'
mock_har.return_value = {
'failed_requests': 5,
'total_requests': 10,
'request_details': [
{'url': 'https://example.com/slow', 'status': 200, 'time': 5000}
],
'insights': [
'Slow DNS resolution detected',
'High server response time',
'Network connectivity issues',
'Resource loading delays',
'JavaScript execution timeout',
],
}
result = await analyze_canary_failures('test-canary', 'us-east-1')
assert '🔍 Comprehensive Failure Analysis for test-canary' in result
assert 'Slow DNS resolution detected' in result
@pytest.mark.asyncio
async def test_analyze_canary_failures_s3_exception(mock_aws_clients):
"""Test analyze_canary_failures when S3 operations fail."""
from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
mock_runs = [
{
'Id': 'failed-run',
'Status': {'State': 'FAILED', 'StateReason': 'Test failure'},
'Timeline': {'Started': '2024-01-01T00:00:00Z'},
}
]
mock_canary = {
'Name': 'test-canary',
'ArtifactS3Location': 's3://test-bucket/canary/us-east-1/test-canary',
}
mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
mock_aws_clients['s3_client'].list_objects_v2.side_effect = Exception('S3 access denied')
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
) as mock_insights:
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.analyze_canary_logs_with_time_window'
) as mock_logs:
mock_insights.return_value = 'Telemetry insights'
mock_logs.return_value = {
'status': 'success',
'time_window': '2024-01-01 00:00:00 - 2024-01-01 00:05:00',
'total_events': 5,
'error_events': [
{'timestamp': datetime.now(timezone.utc), 'message': 'Test error message'}
],
}
result = await analyze_canary_failures('test-canary', 'us-east-1')
# Should fall back to CloudWatch Logs analysis when S3 fails
assert '⚠️ Artifacts not available - Checking CloudWatch Logs for root cause' in result
assert 'CLOUDWATCH LOGS ANALYSIS' in result
@pytest.mark.asyncio
async def test_analyze_canary_failures_visual_variation(mock_aws_clients):
"""Test analyze_canary_failures with visual variation error."""
from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
mock_runs = [
{
'Id': 'failed-run',
'Status': {'State': 'FAILED', 'StateReason': 'Visual variation detected'},
'Timeline': {'Started': '2024-01-01T00:00:00Z'},
}
]
mock_canary = {'Name': 'test-canary', 'ArtifactS3Location': ''}
mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
) as mock_insights:
with patch('awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_code') as mock_code:
mock_insights.return_value = 'Telemetry insights'
mock_code.return_value = {'code_content': 'const synthetics = require("Synthetics");'}
result = await analyze_canary_failures('test-canary', 'us-east-1')
assert 'VISUAL MONITORING ISSUE DETECTED' in result
assert 'Website UI changed - not a technical failure' in result
assert 'canary code:' in result
@pytest.mark.asyncio
async def test_analyze_canary_failures_get_canary_code_exception(mock_aws_clients):
"""Test analyze_canary_failures when get_canary_code fails."""
from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
mock_runs = [
{
'Id': 'failed-run',
'Status': {'State': 'FAILED', 'StateReason': 'Test failure'},
'Timeline': {'Started': '2024-01-01T00:00:00Z'},
}
]
mock_canary = {'Name': 'test-canary', 'ArtifactS3Location': ''}
mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
) as mock_insights:
with patch('awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_code') as mock_code:
mock_insights.return_value = 'Telemetry insights'
# Make get_canary_code raise an exception
mock_code.side_effect = Exception('Code retrieval failed')
result = await analyze_canary_failures('test-canary', 'us-east-1')
assert 'Note: Could not retrieve canary code: Code retrieval failed' in result
@pytest.mark.asyncio
async def test_analyze_canary_failures_iam_analysis_exception(mock_aws_clients):
"""Test analyze_canary_failures when IAM analysis fails."""
from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
mock_runs = [
{
'Id': 'failed-run',
'Status': {'State': 'FAILED', 'StateReason': 'Access denied'},
'Timeline': {'Started': '2024-01-01T00:00:00Z'},
}
]
mock_canary = {'Name': 'test-canary', 'ArtifactS3Location': ''}
mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
) as mock_insights:
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.analyze_iam_role_and_policies'
) as mock_iam:
mock_insights.return_value = 'Telemetry insights'
# Make IAM analysis raise an exception
mock_iam.side_effect = Exception('IAM analysis failed')
result = await analyze_canary_failures('test-canary', 'us-east-1')
assert '⚠️ IAM analysis failed: IAM analysis failed' in result
@pytest.mark.asyncio
async def test_analyze_canary_failures_disk_usage_exception(mock_aws_clients):
"""Test analyze_canary_failures when disk usage analysis fails."""
from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
mock_runs = [
{
'Id': 'failed-run',
'Status': {'State': 'FAILED', 'StateReason': 'ENOSPC: no space left on device'},
'Timeline': {'Started': '2024-01-01T00:00:00Z'},
}
]
mock_canary = {'Name': 'test-canary', 'ArtifactS3Location': ''}
mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
) as mock_insights:
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.extract_disk_memory_usage_metrics'
) as mock_metrics:
mock_insights.return_value = 'Telemetry insights'
# Make disk usage analysis raise an exception
mock_metrics.side_effect = Exception('Disk usage analysis failed')
result = await analyze_canary_failures('test-canary', 'us-east-1')
assert (
'⚠️ Could not generate disk usage debugging code: Disk usage analysis failed'
in result
)
@pytest.mark.asyncio
async def test_analyze_canary_failures_memory_usage_exception(mock_aws_clients):
"""Test analyze_canary_failures when memory usage analysis fails."""
from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
mock_runs = [
{
'Id': 'failed-run',
'Status': {
'State': 'FAILED',
'StateReason': 'Protocol error (Target.activateTarget): Session closed',
},
'Timeline': {'Started': '2024-01-01T00:00:00Z'},
}
]
mock_canary = {'Name': 'test-canary', 'ArtifactS3Location': ''}
mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
) as mock_insights:
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.extract_disk_memory_usage_metrics'
) as mock_metrics:
mock_insights.return_value = 'Telemetry insights'
# Make memory usage analysis raise an exception
mock_metrics.side_effect = Exception('Memory usage analysis failed')
result = await analyze_canary_failures('test-canary', 'us-east-1')
assert (
'⚠️ Could not collect memory usage metrics: Memory usage analysis failed' in result
)
@pytest.mark.asyncio
async def test_analyze_canary_failures_har_timeout_exception(mock_aws_clients):
"""Test analyze_canary_failures when HAR timeout analysis fails."""
from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
mock_runs = [
{
'Id': 'failed-run',
'Status': {'State': 'FAILED', 'StateReason': 'Navigation timed out after 30000ms'},
'Timeline': {'Started': '2024-01-01T00:00:00Z'},
}
]
mock_canary = {
'Name': 'test-canary',
'ArtifactS3Location': 's3://test-bucket/canary/us-east-1/test-canary',
}
mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
# Mock S3 to return HAR files
mock_aws_clients['s3_client'].list_objects_v2.return_value = {
'Contents': [
{'Key': 'canary/us-east-1/test-canary/2024/01/01/test.har'},
]
}
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
) as mock_insights:
with patch('awslabs.cloudwatch_appsignals_mcp_server.server.analyze_har_file') as mock_har:
mock_insights.return_value = 'Telemetry insights'
# Make HAR analysis raise an exception
mock_har.side_effect = Exception('HAR analysis failed')
result = await analyze_canary_failures('test-canary', 'us-east-1')
assert '⚠️ HAR analysis failed: HAR analysis failed' in result
@pytest.mark.asyncio
async def test_analyze_canary_failures_success_artifacts_exception(mock_aws_clients):
"""Test analyze_canary_failures when success artifacts retrieval fails."""
from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
mock_runs = [
{
'Id': 'failed-run',
'Status': {'State': 'FAILED', 'StateReason': 'Test failure'},
'Timeline': {'Started': '2024-01-01T00:00:00Z'},
},
{
'Id': 'success-run',
'Status': {'State': 'PASSED'},
'Timeline': {'Started': '2024-01-01T00:00:00Z'},
},
]
mock_canary = {
'Name': 'test-canary',
'ArtifactS3Location': 's3://test-bucket/canary/us-east-1/test-canary',
}
mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
# Mock S3 to return failure artifacts but fail on success artifacts
def s3_side_effect(*args, **kwargs):
prefix = kwargs.get('Prefix', '')
if 'success' in prefix or len(prefix.split('/')) > 5: # Simulate success path failure
raise Exception('Success artifacts access failed')
return {
'Contents': [
{'Key': 'canary/us-east-1/test-canary/2024/01/01/test.har'},
]
}
mock_aws_clients['s3_client'].list_objects_v2.side_effect = s3_side_effect
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
) as mock_insights:
with patch('awslabs.cloudwatch_appsignals_mcp_server.server.analyze_har_file') as mock_har:
mock_insights.return_value = 'Telemetry insights'
mock_har.return_value = {
'failed_requests': 2,
'total_requests': 10,
'request_details': [],
}
result = await analyze_canary_failures('test-canary', 'us-east-1')
# Should still process failure artifacts even if success artifacts fail
assert '🔍 Comprehensive Failure Analysis for test-canary' in result
@pytest.mark.asyncio
async def test_analyze_canary_failures_no_failure_timestamp(mock_aws_clients):
"""Test analyze_canary_failures when failure has no timestamp."""
from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
mock_runs = [
{
'Id': 'failed-run',
'Status': {'State': 'FAILED', 'StateReason': 'Test failure'},
'Timeline': {}, # No Started timestamp
}
]
mock_canary = {'Name': 'test-canary', 'ArtifactS3Location': ''}
mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
) as mock_insights:
mock_insights.return_value = 'Telemetry insights'
result = await analyze_canary_failures('test-canary', 'us-east-1')
assert '📋 No failure timestamp available for targeted log analysis' in result
@pytest.mark.asyncio
async def test_analyze_canary_failures_log_analysis_failure(mock_aws_clients):
"""Test analyze_canary_failures when log analysis fails."""
from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
mock_runs = [
{
'Id': 'failed-run',
'Status': {'State': 'FAILED', 'StateReason': 'Test failure'},
'Timeline': {'Started': '2024-01-01T00:00:00Z'},
}
]
mock_canary = {'Name': 'test-canary', 'ArtifactS3Location': ''}
mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
) as mock_insights:
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.analyze_canary_logs_with_time_window'
) as mock_logs:
mock_insights.return_value = 'Telemetry insights'
mock_logs.return_value = {
'status': 'failed',
'insights': ['Log analysis failed due to missing log group'],
}
result = await analyze_canary_failures('test-canary', 'us-east-1')
assert '📋 Log analysis failed due to missing log group' in result
@pytest.mark.asyncio
async def test_analyze_canary_failures_main_exception(mock_aws_clients):
"""Test analyze_canary_failures when main function fails."""
from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
# Make get_canary_runs raise an exception
mock_aws_clients['synthetics_client'].get_canary_runs.side_effect = Exception('API error')
result = await analyze_canary_failures('test-canary', 'us-east-1')
assert '❌ Error in comprehensive failure analysis: API error' in result
@pytest.mark.asyncio
async def test_analyze_canary_failures_no_har_files_navigation_timeout(mock_aws_clients):
"""Test analyze_canary_failures navigation timeout without HAR files."""
from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
mock_runs = [
{
'Id': 'failed-run',
'Status': {'State': 'FAILED', 'StateReason': 'Navigation timed out after 30000ms'},
'Timeline': {'Started': '2024-01-01T00:00:00Z'},
}
]
mock_canary = {'Name': 'test-canary', 'ArtifactS3Location': ''}
mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
) as mock_insights:
mock_insights.return_value = 'Telemetry insights'
result = await analyze_canary_failures('test-canary', 'us-east-1')
assert 'NAVIGATION TIMEOUT DETECTED:' in result
assert 'No HAR files available for detailed analysis' in result
@pytest.mark.asyncio
async def test_analyze_canary_failures_artifact_location_without_s3_prefix(mock_aws_clients):
"""Test analyze_canary_failures with artifact location without s3:// prefix."""
from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
mock_runs = [
{
'Id': 'failed-run',
'Status': {'State': 'FAILED', 'StateReason': 'Test failure'},
'Timeline': {'Started': '2024-01-01T00:00:00Z'},
}
]
mock_canary = {
'Name': 'test-canary',
'ArtifactS3Location': 'test-bucket/canary/us-east-1/test-canary', # No s3:// prefix
}
mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
# Mock S3 to return artifacts
mock_aws_clients['s3_client'].list_objects_v2.return_value = {
'Contents': [
{'Key': 'canary/us-east-1/test-canary/2024/01/01/test.har'},
]
}
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
) as mock_insights:
with patch('awslabs.cloudwatch_appsignals_mcp_server.server.analyze_har_file') as mock_har:
mock_insights.return_value = 'Telemetry insights'
mock_har.return_value = {
'failed_requests': 1,
'total_requests': 5,
'request_details': [],
}
result = await analyze_canary_failures('test-canary', 'us-east-1')
# Should still process artifacts even without s3:// prefix
assert 'FAILURE ANALYSIS' in result
@pytest.mark.asyncio
async def test_analyze_canary_failures_empty_base_path(mock_aws_clients):
"""Test analyze_canary_failures with empty base path."""
from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
mock_runs = [
{
'Id': 'failed-run',
'Status': {'State': 'FAILED', 'StateReason': 'Test failure'},
'Timeline': {'Started': '2024-01-01T00:00:00Z'},
}
]
mock_canary = {
'Name': 'test-canary',
'ArtifactS3Location': 's3://test-bucket', # Only bucket, no path
}
mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
# Mock S3 to return artifacts
mock_aws_clients['s3_client'].list_objects_v2.return_value = {
'Contents': [
{'Key': 'canary/us-east-1/test-canary/2024/01/01/test.har'},
]
}
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
) as mock_insights:
with patch('awslabs.cloudwatch_appsignals_mcp_server.server.analyze_har_file') as mock_har:
mock_insights.return_value = 'Telemetry insights'
mock_har.return_value = {
'failed_requests': 1,
'total_requests': 5,
'request_details': [],
}
result = await analyze_canary_failures('test-canary', 'us-east-1')
# Should construct default canary path when base_path is empty
assert 'FAILURE ANALYSIS' in result
@pytest.mark.asyncio
async def test_analyze_canary_failures_multiple_failure_causes(mock_aws_clients):
"""Test analyze_canary_failures with multiple different failure causes."""
from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
mock_runs = [
{
'Id': 'failed-run-1',
'Status': {'State': 'FAILED', 'StateReason': 'Navigation timeout'},
'Timeline': {'Started': '2024-01-01T00:00:00Z'},
},
{
'Id': 'failed-run-2',
'Status': {'State': 'FAILED', 'StateReason': 'Access denied'},
'Timeline': {'Started': '2024-01-01T00:01:00Z'},
},
{
'Id': 'success-run',
'Status': {'State': 'PASSED'},
'Timeline': {'Started': '2023-12-31T23:59:00Z'},
},
]
mock_canary = {'Name': 'test-canary', 'ArtifactS3Location': ''}
mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
) as mock_insights:
mock_insights.return_value = 'Telemetry insights'
result = await analyze_canary_failures('test-canary', 'us-east-1')
assert 'Multiple failure causes (2 different issues):' in result
assert '1. **Navigation timeout** (1 occurrences)' in result
assert '2. **Access denied** (1 occurrences)' in result
@pytest.mark.asyncio
async def test_analyze_canary_failures_no_failure_time_fallback(mock_aws_clients):
"""Test analyze_canary_failures fallback when no failure time."""
from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
mock_runs = [
{
'Id': 'failed-run',
'Status': {'State': 'FAILED', 'StateReason': 'Test failure'},
'Timeline': {}, # No Started time
}
]
mock_canary = {
'Name': 'test-canary',
'ArtifactS3Location': 's3://test-bucket/canary/us-east-1/test-canary',
}
mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
# Mock S3 to return artifacts
mock_aws_clients['s3_client'].list_objects_v2.return_value = {
'Contents': [
{'Key': 'canary/us-east-1/test-canary/2024/01/01/test.har'},
]
}
with patch(
'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
) as mock_insights:
with patch('awslabs.cloudwatch_appsignals_mcp_server.server.analyze_har_file') as mock_har:
mock_insights.return_value = 'Telemetry insights'
mock_har.return_value = {
'failed_requests': 1,
'total_requests': 5,
'request_details': [],
}
result = await analyze_canary_failures('test-canary', 'us-east-1')
# Should use current time when no failure time available
assert 'FAILURE ANALYSIS' in result
def test_filter_operation_targets_fault_to_availability():
"""Test _filter_operation_targets converts Fault to Availability."""
provided = [
{
'Type': 'service_operation',
'Data': {
'ServiceOperation': {
'Service': {'Type': 'Service', 'Name': 'test-service'},
'Operation': 'GET /api',
'MetricType': 'Fault',
}
},
}
]
operation_targets, has_wildcards = _filter_operation_targets(provided)
# Verify the MetricType was changed from Fault to Availability
assert len(operation_targets) == 1
assert operation_targets[0]['Data']['ServiceOperation']['MetricType'] == 'Availability'
assert has_wildcards is False
def test_filter_operation_targets_non_fault_unchanged():
"""Test _filter_operation_targets leaves non-Fault MetricTypes unchanged."""
provided = [
{
'Type': 'service_operation',
'Data': {
'ServiceOperation': {
'Service': {'Type': 'Service', 'Name': 'test-service'},
'Operation': 'GET /api',
'MetricType': 'Latency',
}
},
},
{
'Type': 'service_operation',
'Data': {
'ServiceOperation': {
'Service': {'Type': 'Service', 'Name': 'test-service-2'},
'Operation': 'POST /api',
'MetricType': 'Error',
}
},
},
]
operation_targets, has_wildcards = _filter_operation_targets(provided)
# Verify non-Fault MetricTypes are unchanged
assert len(operation_targets) == 2
assert operation_targets[0]['Data']['ServiceOperation']['MetricType'] == 'Latency'
assert operation_targets[1]['Data']['ServiceOperation']['MetricType'] == 'Error'
assert has_wildcards is False
def test_filter_operation_targets_multiple_fault_conversions():
"""Test _filter_operation_targets converts multiple Fault entries to Availability."""
provided = [
{
'Type': 'service_operation',
'Data': {
'ServiceOperation': {
'Service': {'Type': 'Service', 'Name': 'service-1'},
'Operation': 'GET /api',
'MetricType': 'Fault',
}
},
},
{
'Type': 'service_operation',
'Data': {
'ServiceOperation': {
'Service': {'Type': 'Service', 'Name': 'service-2'},
'Operation': 'POST /api',
'MetricType': 'Latency',
}
},
},
{
'Type': 'service_operation',
'Data': {
'ServiceOperation': {
'Service': {'Type': 'Service', 'Name': 'service-3'},
'Operation': 'PUT /api',
'MetricType': 'Fault',
}
},
},
]
operation_targets, has_wildcards = _filter_operation_targets(provided)
# Verify multiple Fault entries are converted
assert len(operation_targets) == 3
assert operation_targets[0]['Data']['ServiceOperation']['MetricType'] == 'Availability'
assert operation_targets[1]['Data']['ServiceOperation']['MetricType'] == 'Latency'
assert operation_targets[2]['Data']['ServiceOperation']['MetricType'] == 'Availability'
assert has_wildcards is False
def test_filter_operation_targets_with_wildcards():
"""Test _filter_operation_targets detects wildcards and converts Fault to Availability."""
provided = [
{
'Type': 'service_operation',
'Data': {
'ServiceOperation': {
'Service': {'Type': 'Service', 'Name': '*payment*'},
'Operation': '*GET*',
'MetricType': 'Fault',
}
},
}
]
operation_targets, has_wildcards = _filter_operation_targets(provided)
# Verify wildcard detection and Fault conversion
assert len(operation_targets) == 1
assert operation_targets[0]['Data']['ServiceOperation']['MetricType'] == 'Availability'
assert has_wildcards is True
def test_filter_operation_targets_ignores_non_service_operation():
"""Test _filter_operation_targets ignores non-service_operation targets."""
provided = [
{
'Type': 'service',
'Data': {'Service': {'Type': 'Service', 'Name': 'test-service'}},
},
{
'Type': 'service_operation',
'Data': {
'ServiceOperation': {
'Service': {'Type': 'Service', 'Name': 'test-service'},
'Operation': 'GET /api',
'MetricType': 'Fault',
}
},
},
]
operation_targets, has_wildcards = _filter_operation_targets(provided)
# Verify only service_operation targets are included
assert len(operation_targets) == 1
assert operation_targets[0]['Type'] == 'service_operation'
assert operation_targets[0]['Data']['ServiceOperation']['MetricType'] == 'Availability'
assert has_wildcards is False
def test_filter_operation_targets_empty_metric_type():
"""Test _filter_operation_targets handles empty MetricType gracefully."""
provided = [
{
'Type': 'service_operation',
'Data': {
'ServiceOperation': {
'Service': {'Type': 'Service', 'Name': 'test-service'},
'Operation': 'GET /api',
'MetricType': '',
}
},
}
]
operation_targets, has_wildcards = _filter_operation_targets(provided)
# Verify empty MetricType is unchanged
assert len(operation_targets) == 1
assert operation_targets[0]['Data']['ServiceOperation']['MetricType'] == ''
assert has_wildcards is False
def test_filter_operation_targets_missing_metric_type():
"""Test _filter_operation_targets handles missing MetricType gracefully."""
provided = [
{
'Type': 'service_operation',
'Data': {
'ServiceOperation': {
'Service': {'Type': 'Service', 'Name': 'test-service'},
'Operation': 'GET /api',
# MetricType is missing
}
},
}
]
operation_targets, has_wildcards = _filter_operation_targets(provided)
# Verify missing MetricType doesn't cause errors
assert len(operation_targets) == 1
# MetricType should remain missing (empty string from .get())
assert operation_targets[0]['Data']['ServiceOperation'].get('MetricType', '') == ''
assert has_wildcards is False
def test_filter_operation_targets_case_sensitive():
"""Test _filter_operation_targets is case-sensitive for Fault conversion."""
provided = [
{
'Type': 'service_operation',
'Data': {
'ServiceOperation': {
'Service': {'Type': 'Service', 'Name': 'test-service'},
'Operation': 'GET /api',
'MetricType': 'fault', # lowercase
}
},
},
{
'Type': 'service_operation',
'Data': {
'ServiceOperation': {
'Service': {'Type': 'Service', 'Name': 'test-service-2'},
'Operation': 'POST /api',
'MetricType': 'FAULT', # uppercase
}
},
},
]
operation_targets, has_wildcards = _filter_operation_targets(provided)
# Verify only exact case "Fault" is converted
assert len(operation_targets) == 2
assert operation_targets[0]['Data']['ServiceOperation']['MetricType'] == 'fault' # unchanged
assert operation_targets[1]['Data']['ServiceOperation']['MetricType'] == 'FAULT' # unchanged
assert has_wildcards is False
```