#
tokens: 71627/50000 1/2431 files (page 500/503)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 500 of 503. Use http://codebase.md/awslabs/mcp?lines=true&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
   1 | """Tests for CloudWatch Application Signals MCP Server."""
   2 | 
   3 | import json
   4 | import pytest
   5 | from awslabs.cloudwatch_appsignals_mcp_server.server import _filter_operation_targets, main
   6 | from awslabs.cloudwatch_appsignals_mcp_server.service_tools import (
   7 |     get_service_detail,
   8 |     list_monitored_services,
   9 |     query_service_metrics,
  10 | )
  11 | from awslabs.cloudwatch_appsignals_mcp_server.slo_tools import get_slo
  12 | from awslabs.cloudwatch_appsignals_mcp_server.trace_tools import (
  13 |     check_transaction_search_enabled,
  14 |     get_trace_summaries_paginated,
  15 |     list_slis,
  16 |     query_sampled_traces,
  17 |     search_transaction_spans,
  18 | )
  19 | from awslabs.cloudwatch_appsignals_mcp_server.utils import remove_null_values
  20 | from botocore.exceptions import ClientError
  21 | from datetime import datetime, timedelta, timezone
  22 | from unittest.mock import AsyncMock, MagicMock, patch
  23 | 
  24 | 
  25 | @pytest.fixture(autouse=True)
  26 | def mock_aws_clients():
  27 |     """Mock all AWS clients to prevent real API calls during tests."""
  28 |     # Create mock clients
  29 |     mock_logs_client = MagicMock()
  30 |     mock_appsignals_client = MagicMock()
  31 |     mock_cloudwatch_client = MagicMock()
  32 |     mock_xray_client = MagicMock()
  33 |     mock_synthetics_client = MagicMock()
  34 |     mock_s3_client = MagicMock()
  35 | 
  36 |     # Patch the clients in all modules where they're imported
  37 |     patches = [
  38 |         # Original aws_clients module
  39 |         patch(
  40 |             'awslabs.cloudwatch_appsignals_mcp_server.aws_clients.logs_client', mock_logs_client
  41 |         ),
  42 |         patch(
  43 |             'awslabs.cloudwatch_appsignals_mcp_server.aws_clients.appsignals_client',
  44 |             mock_appsignals_client,
  45 |         ),
  46 |         patch(
  47 |             'awslabs.cloudwatch_appsignals_mcp_server.aws_clients.cloudwatch_client',
  48 |             mock_cloudwatch_client,
  49 |         ),
  50 |         patch(
  51 |             'awslabs.cloudwatch_appsignals_mcp_server.aws_clients.xray_client', mock_xray_client
  52 |         ),
  53 |         patch(
  54 |             'awslabs.cloudwatch_appsignals_mcp_server.aws_clients.synthetics_client',
  55 |             mock_synthetics_client,
  56 |         ),
  57 |         patch('awslabs.cloudwatch_appsignals_mcp_server.aws_clients.s3_client', mock_s3_client),
  58 |         # Service tools module (check what's actually imported)
  59 |         patch(
  60 |             'awslabs.cloudwatch_appsignals_mcp_server.service_tools.appsignals_client',
  61 |             mock_appsignals_client,
  62 |         ),
  63 |         patch(
  64 |             'awslabs.cloudwatch_appsignals_mcp_server.service_tools.cloudwatch_client',
  65 |             mock_cloudwatch_client,
  66 |         ),
  67 |         # SLO tools module (check what's actually imported)
  68 |         patch(
  69 |             'awslabs.cloudwatch_appsignals_mcp_server.slo_tools.appsignals_client',
  70 |             mock_appsignals_client,
  71 |         ),
  72 |         # Trace tools module (logs_client, xray_client, and appsignals_client are imported)
  73 |         patch(
  74 |             'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.logs_client', mock_logs_client
  75 |         ),
  76 |         patch(
  77 |             'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.xray_client', mock_xray_client
  78 |         ),
  79 |         patch(
  80 |             'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.appsignals_client',
  81 |             mock_appsignals_client,
  82 |         ),
  83 |         # SLI report client module (appsignals_client and cloudwatch_client are imported)
  84 |         patch(
  85 |             'awslabs.cloudwatch_appsignals_mcp_server.sli_report_client.appsignals_client',
  86 |             mock_appsignals_client,
  87 |         ),
  88 |         patch(
  89 |             'awslabs.cloudwatch_appsignals_mcp_server.sli_report_client.cloudwatch_client',
  90 |             mock_cloudwatch_client,
  91 |         ),
  92 |         patch(
  93 |             'awslabs.cloudwatch_appsignals_mcp_server.server.synthetics_client',
  94 |             mock_synthetics_client,
  95 |         ),
  96 |         patch('awslabs.cloudwatch_appsignals_mcp_server.server.s3_client', mock_s3_client),
  97 |         patch('awslabs.cloudwatch_appsignals_mcp_server.server.iam_client', MagicMock()),
  98 |     ]
  99 | 
 100 |     # Start all patches
 101 |     for p in patches:
 102 |         p.start()
 103 | 
 104 |     try:
 105 |         yield {
 106 |             'logs_client': mock_logs_client,
 107 |             'appsignals_client': mock_appsignals_client,
 108 |             'cloudwatch_client': mock_cloudwatch_client,
 109 |             'xray_client': mock_xray_client,
 110 |             'synthetics_client': mock_synthetics_client,
 111 |             's3_client': mock_s3_client,
 112 |         }
 113 |     finally:
 114 |         # Stop all patches
 115 |         for p in patches:
 116 |             p.stop()
 117 | 
 118 | 
 119 | @pytest.fixture
 120 | def mock_mcp():
 121 |     """Mock the FastMCP instance."""
 122 |     with patch('awslabs.cloudwatch_appsignals_mcp_server.server.mcp') as mock:
 123 |         yield mock
 124 | 
 125 | 
 126 | @pytest.mark.asyncio
 127 | async def test_list_monitored_services_success(mock_aws_clients):
 128 |     """Test successful listing of monitored services."""
 129 |     mock_response = {
 130 |         'ServiceSummaries': [
 131 |             {
 132 |                 'KeyAttributes': {
 133 |                     'Name': 'test-service',
 134 |                     'Type': 'AWS::ECS::Service',
 135 |                     'Environment': 'production',
 136 |                 }
 137 |             }
 138 |         ]
 139 |     }
 140 | 
 141 |     mock_aws_clients['appsignals_client'].list_services.return_value = mock_response
 142 | 
 143 |     result = await list_monitored_services()
 144 | 
 145 |     assert 'Application Signals Services (1 total)' in result
 146 |     assert 'test-service' in result
 147 |     assert 'AWS::ECS::Service' in result
 148 | 
 149 | 
 150 | @pytest.mark.asyncio
 151 | async def test_list_monitored_services_empty(mock_aws_clients):
 152 |     """Test when no services are found."""
 153 |     mock_response = {'ServiceSummaries': []}
 154 | 
 155 |     mock_aws_clients['appsignals_client'].list_services.return_value = mock_response
 156 | 
 157 |     result = await list_monitored_services()
 158 | 
 159 |     assert result == 'No services found in Application Signals.'
 160 | 
 161 | 
 162 | @pytest.mark.asyncio
 163 | async def test_get_service_detail_success(mock_aws_clients):
 164 |     """Test successful retrieval of service details."""
 165 |     mock_list_response = {
 166 |         'ServiceSummaries': [
 167 |             {'KeyAttributes': {'Name': 'test-service', 'Type': 'AWS::ECS::Service'}}
 168 |         ]
 169 |     }
 170 | 
 171 |     mock_get_response = {
 172 |         'Service': {
 173 |             'KeyAttributes': {'Name': 'test-service', 'Type': 'AWS::ECS::Service'},
 174 |             'AttributeMaps': [{'Platform': 'ECS', 'Application': 'test-app'}],
 175 |             'MetricReferences': [
 176 |                 {
 177 |                     'Namespace': 'AWS/ApplicationSignals',
 178 |                     'MetricName': 'Latency',
 179 |                     'MetricType': 'GAUGE',
 180 |                     'Dimensions': [{'Name': 'Service', 'Value': 'test-service'}],
 181 |                 }
 182 |             ],
 183 |             'LogGroupReferences': [{'Identifier': '/aws/ecs/test-service'}],
 184 |         }
 185 |     }
 186 | 
 187 |     mock_aws_clients['appsignals_client'].list_services.return_value = mock_list_response
 188 |     mock_aws_clients['appsignals_client'].get_service.return_value = mock_get_response
 189 | 
 190 |     result = await get_service_detail('test-service')
 191 | 
 192 |     assert 'Service Details: test-service' in result
 193 |     assert 'AWS::ECS::Service' in result
 194 |     assert 'Platform: ECS' in result
 195 |     assert 'AWS/ApplicationSignals/Latency' in result
 196 |     assert '/aws/ecs/test-service' in result
 197 | 
 198 | 
 199 | @pytest.mark.asyncio
 200 | async def test_get_service_detail_not_found(mock_aws_clients):
 201 |     """Test when service is not found."""
 202 |     mock_response = {'ServiceSummaries': []}
 203 | 
 204 |     mock_aws_clients['appsignals_client'].list_services.return_value = mock_response
 205 | 
 206 |     result = await get_service_detail('nonexistent-service')
 207 | 
 208 |     assert "Service 'nonexistent-service' not found" in result
 209 | 
 210 | 
 211 | @pytest.mark.asyncio
 212 | async def test_query_service_metrics_success(mock_aws_clients):
 213 |     """Test successful query of service metrics."""
 214 |     mock_list_response = {
 215 |         'ServiceSummaries': [
 216 |             {'KeyAttributes': {'Name': 'test-service', 'Type': 'AWS::ECS::Service'}}
 217 |         ]
 218 |     }
 219 | 
 220 |     mock_get_response = {
 221 |         'Service': {
 222 |             'MetricReferences': [
 223 |                 {
 224 |                     'Namespace': 'AWS/ApplicationSignals',
 225 |                     'MetricName': 'Latency',
 226 |                     'Dimensions': [{'Name': 'Service', 'Value': 'test-service'}],
 227 |                 }
 228 |             ]
 229 |         }
 230 |     }
 231 | 
 232 |     mock_metric_response = {
 233 |         'Datapoints': [
 234 |             {
 235 |                 'Timestamp': datetime.now(timezone.utc),
 236 |                 'Average': 100.5,
 237 |                 'p99': 150.2,
 238 |                 'Unit': 'Milliseconds',
 239 |             }
 240 |         ]
 241 |     }
 242 | 
 243 |     mock_aws_clients['appsignals_client'].list_services.return_value = mock_list_response
 244 |     mock_aws_clients['appsignals_client'].get_service.return_value = mock_get_response
 245 |     mock_aws_clients['cloudwatch_client'].get_metric_statistics.return_value = mock_metric_response
 246 | 
 247 |     result = await query_service_metrics(
 248 |         service_name='test-service',
 249 |         metric_name='Latency',
 250 |         statistic='Average',
 251 |         extended_statistic='p99',
 252 |         hours=1,
 253 |     )
 254 | 
 255 |     assert 'Metrics for test-service - Latency' in result
 256 |     assert 'Average Statistics:' in result
 257 |     assert 'p99 Statistics:' in result
 258 | 
 259 | 
 260 | @pytest.mark.asyncio
 261 | async def test_query_service_metrics_list_available(mock_aws_clients):
 262 |     """Test listing available metrics when no specific metric is requested."""
 263 |     mock_list_response = {
 264 |         'ServiceSummaries': [
 265 |             {'KeyAttributes': {'Name': 'test-service', 'Type': 'AWS::ECS::Service'}}
 266 |         ]
 267 |     }
 268 | 
 269 |     mock_get_response = {
 270 |         'Service': {
 271 |             'MetricReferences': [
 272 |                 {
 273 |                     'MetricName': 'Latency',
 274 |                     'Namespace': 'AWS/ApplicationSignals',
 275 |                     'MetricType': 'GAUGE',
 276 |                 },
 277 |                 {
 278 |                     'MetricName': 'Error',
 279 |                     'Namespace': 'AWS/ApplicationSignals',
 280 |                     'MetricType': 'COUNT',
 281 |                 },
 282 |             ]
 283 |         }
 284 |     }
 285 | 
 286 |     mock_aws_clients['appsignals_client'].list_services.return_value = mock_list_response
 287 |     mock_aws_clients['appsignals_client'].get_service.return_value = mock_get_response
 288 | 
 289 |     result = await query_service_metrics(
 290 |         service_name='test-service',
 291 |         metric_name='',  # Empty to list available metrics
 292 |         statistic='Average',
 293 |         extended_statistic='p99',
 294 |         hours=1,
 295 |     )
 296 | 
 297 |     assert "Available metrics for service 'test-service'" in result
 298 |     assert 'Latency' in result
 299 |     assert 'Error' in result
 300 | 
 301 | 
 302 | @pytest.mark.asyncio
 303 | async def test_get_slo_success(mock_aws_clients):
 304 |     """Test successful retrieval of SLO details."""
 305 |     mock_slo_response = {
 306 |         'Slo': {
 307 |             'Name': 'test-slo',
 308 |             'Arn': 'arn:aws:application-signals:us-east-1:123456789012:slo/test-slo',
 309 |             'Description': 'Test SLO for latency',
 310 |             'EvaluationType': 'REQUEST_BASED',
 311 |             'CreatedTime': '2024-01-01T00:00:00Z',
 312 |             'LastUpdatedTime': '2024-01-02T00:00:00Z',
 313 |             'Goal': {
 314 |                 'AttainmentGoal': 99.9,
 315 |                 'WarningThreshold': 99.0,
 316 |                 'Interval': {'RollingInterval': {'Duration': 7, 'DurationUnit': 'DAYS'}},
 317 |             },
 318 |             'RequestBasedSli': {
 319 |                 'RequestBasedSliMetric': {
 320 |                     'KeyAttributes': {'Name': 'test-service', 'Type': 'AWS::ECS::Service'},
 321 |                     'OperationName': 'GET /api/test',
 322 |                     'MetricType': 'LATENCY',
 323 |                     'MetricDataQueries': [
 324 |                         {
 325 |                             'Id': 'query1',
 326 |                             'MetricStat': {
 327 |                                 'Metric': {
 328 |                                     'Namespace': 'AWS/ApplicationSignals',
 329 |                                     'MetricName': 'Latency',
 330 |                                     'Dimensions': [{'Name': 'Service', 'Value': 'test-service'}],
 331 |                                 },
 332 |                                 'Period': 60,
 333 |                                 'Stat': 'Average',
 334 |                             },
 335 |                         }
 336 |                     ],
 337 |                 },
 338 |                 'MetricThreshold': 1000,
 339 |                 'ComparisonOperator': 'GreaterThan',
 340 |             },
 341 |         }
 342 |     }
 343 | 
 344 |     mock_aws_clients[
 345 |         'appsignals_client'
 346 |     ].get_service_level_objective.return_value = mock_slo_response
 347 | 
 348 |     result = await get_slo('test-slo-id')
 349 | 
 350 |     assert 'Service Level Objective Details' in result
 351 |     assert 'test-slo' in result
 352 |     assert 'REQUEST_BASED' in result
 353 |     assert '99.9%' in result
 354 |     assert 'GET /api/test' in result
 355 |     assert 'annotation[aws.local.operation]="GET /api/test"' in result
 356 | 
 357 | 
 358 | @pytest.mark.asyncio
 359 | async def test_get_slo_not_found(mock_aws_clients):
 360 |     """Test when SLO is not found."""
 361 |     mock_aws_clients['appsignals_client'].get_service_level_objective.return_value = {'Slo': None}
 362 | 
 363 |     result = await get_slo('nonexistent-slo')
 364 | 
 365 |     assert 'No SLO found with ID: nonexistent-slo' in result
 366 | 
 367 | 
 368 | @pytest.mark.asyncio
 369 | async def test_search_transaction_spans_success(mock_aws_clients):
 370 |     """Test successful transaction search."""
 371 |     mock_query_response = {
 372 |         'queryId': 'test-query-id',
 373 |         'status': 'Complete',
 374 |         'statistics': {'recordsMatched': 10},
 375 |         'results': [
 376 |             [
 377 |                 {'field': 'spanId', 'value': 'span1'},
 378 |                 {'field': 'timestamp', 'value': '2024-01-01T00:00:00Z'},
 379 |             ]
 380 |         ],
 381 |     }
 382 | 
 383 |     with patch(
 384 |         'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.check_transaction_search_enabled'
 385 |     ) as mock_check:
 386 |         mock_check.return_value = (True, 'CloudWatchLogs', 'ACTIVE')
 387 |         mock_aws_clients['logs_client'].start_query.return_value = {'queryId': 'test-query-id'}
 388 |         mock_aws_clients['logs_client'].get_query_results.return_value = mock_query_response
 389 | 
 390 |         result = await search_transaction_spans(
 391 |             log_group_name='aws/spans',
 392 |             start_time='2024-01-01T00:00:00+00:00',  # Fixed ISO format
 393 |             end_time='2024-01-01T01:00:00+00:00',
 394 |             query_string='fields @timestamp, spanId',
 395 |             limit=100,
 396 |             max_timeout=30,
 397 |         )
 398 | 
 399 |         assert result['status'] == 'Complete'
 400 |         assert result['queryId'] == 'test-query-id'
 401 |         assert len(result['results']) == 1
 402 |         assert result['results'][0]['spanId'] == 'span1'
 403 | 
 404 | 
 405 | @pytest.mark.asyncio
 406 | async def test_search_transaction_spans_not_enabled(mock_aws_clients):
 407 |     """Test when transaction search is not enabled."""
 408 |     with patch(
 409 |         'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.check_transaction_search_enabled'
 410 |     ) as mock_check:
 411 |         mock_check.return_value = (False, 'XRay', 'INACTIVE')
 412 | 
 413 |         result = await search_transaction_spans(
 414 |             log_group_name='',
 415 |             start_time='2024-01-01T00:00:00+00:00',
 416 |             end_time='2024-01-01T01:00:00+00:00',
 417 |             query_string='fields @timestamp',
 418 |             limit=None,
 419 |             max_timeout=30,
 420 |         )
 421 | 
 422 |         assert result['status'] == 'Transaction Search Not Available'
 423 |         assert not result['transaction_search_status']['enabled']
 424 | 
 425 | 
 426 | @pytest.mark.asyncio
 427 | async def test_list_slis_success(mock_aws_clients):
 428 |     """Test successful listing of SLI status."""
 429 |     mock_services_response = {
 430 |         'ServiceSummaries': [
 431 |             {
 432 |                 'KeyAttributes': {
 433 |                     'Name': 'test-service',
 434 |                     'Type': 'AWS::ECS::Service',
 435 |                     'Environment': 'production',
 436 |                 }
 437 |             }
 438 |         ]
 439 |     }
 440 | 
 441 |     # Mock boto3.client calls in SLIReportClient
 442 |     with patch('boto3.client') as mock_boto3_client:
 443 |         with patch(
 444 |             'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.check_transaction_search_enabled'
 445 |         ) as mock_check:
 446 |             # Configure boto3.client to return our mocked clients
 447 |             def boto3_client_side_effect(service_name, **kwargs):
 448 |                 if service_name == 'application-signals':
 449 |                     return mock_aws_clients['appsignals_client']
 450 |                 elif service_name == 'cloudwatch':
 451 |                     return mock_aws_clients['cloudwatch_client']
 452 |                 else:
 453 |                     return MagicMock()
 454 | 
 455 |             mock_boto3_client.side_effect = boto3_client_side_effect
 456 | 
 457 |             mock_aws_clients[
 458 |                 'appsignals_client'
 459 |             ].list_services.return_value = mock_services_response
 460 |             mock_check.return_value = (True, 'CloudWatchLogs', 'ACTIVE')
 461 | 
 462 |             # Mock SLO summaries response for SLIReportClient
 463 |             mock_slo_response = {
 464 |                 'SloSummaries': [
 465 |                     {
 466 |                         'Name': 'test-slo',
 467 |                         'Arn': 'arn:aws:application-signals:us-east-1:123456789012:slo/test-slo',
 468 |                         'KeyAttributes': {'Name': 'test-service'},
 469 |                         'OperationName': 'GET /api',
 470 |                         'CreatedTime': datetime.now(timezone.utc),
 471 |                     }
 472 |                 ]
 473 |             }
 474 |             mock_aws_clients[
 475 |                 'appsignals_client'
 476 |             ].list_service_level_objectives.return_value = mock_slo_response
 477 | 
 478 |             # Mock metric data response showing breach
 479 |             mock_metric_response = {
 480 |                 'MetricDataResults': [
 481 |                     {
 482 |                         'Id': 'slo0',
 483 |                         'Timestamps': [datetime.now(timezone.utc)],
 484 |                         'Values': [1.0],  # Breach count > 0 indicates breach
 485 |                     }
 486 |                 ]
 487 |             }
 488 |             mock_aws_clients[
 489 |                 'cloudwatch_client'
 490 |             ].get_metric_data.return_value = mock_metric_response
 491 | 
 492 |             result = await list_slis(hours=24)
 493 | 
 494 |             assert 'SLI Status Report - Last 24 hours' in result
 495 |             assert 'Transaction Search: ENABLED' in result
 496 |             assert 'BREACHED SERVICES:' in result
 497 |             assert 'test-service' in result
 498 | 
 499 | 
 500 | @pytest.mark.asyncio
 501 | async def test_query_sampled_traces_success(mock_aws_clients):
 502 |     """Test successful query of sampled traces."""
 503 |     mock_traces = [
 504 |         {
 505 |             'Id': 'trace1',
 506 |             'Duration': 0.5,
 507 |             'ResponseTime': 500,
 508 |             'HasError': False,
 509 |             'HasFault': True,
 510 |             'HasThrottle': False,
 511 |             'Http': {'HttpStatus': 500},
 512 |             'FaultRootCauses': [
 513 |                 {
 514 |                     'Services': [
 515 |                         {
 516 |                             'Name': 'test-service',
 517 |                             'Exceptions': [{'Message': 'Internal server error'}],
 518 |                         }
 519 |                     ]
 520 |                 }
 521 |             ],
 522 |         }
 523 |     ]
 524 | 
 525 |     with patch(
 526 |         'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.get_trace_summaries_paginated'
 527 |     ) as mock_get_traces:
 528 |         with patch(
 529 |             'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.check_transaction_search_enabled'
 530 |         ) as mock_check:
 531 |             mock_get_traces.return_value = mock_traces
 532 |             mock_check.return_value = (False, 'XRay', 'INACTIVE')
 533 | 
 534 |             result_json = await query_sampled_traces(
 535 |                 start_time='2024-01-01T00:00:00Z',
 536 |                 end_time='2024-01-01T01:00:00Z',
 537 |                 filter_expression='service("test-service"){fault = true}',
 538 |             )
 539 | 
 540 |             result = json.loads(result_json)
 541 |             assert result['TraceCount'] == 1
 542 |             assert result['TraceSummaries'][0]['Id'] == 'trace1'
 543 |             assert result['TraceSummaries'][0]['HasFault'] is True
 544 | 
 545 | 
 546 | @pytest.mark.asyncio
 547 | async def test_query_sampled_traces_time_window_too_large(mock_aws_clients):
 548 |     """Test when time window is too large."""
 549 |     result_json = await query_sampled_traces(
 550 |         start_time='2024-01-01T00:00:00Z',
 551 |         end_time='2024-01-02T00:00:00Z',  # 24 hours > 6 hours max
 552 |         filter_expression='service("test-service")',
 553 |     )
 554 | 
 555 |     result = json.loads(result_json)
 556 |     assert 'error' in result
 557 |     assert 'Time window too large' in result['error']
 558 | 
 559 | 
 560 | def test_get_trace_summaries_paginated():
 561 |     """Test paginated trace retrieval."""
 562 |     mock_client = MagicMock()
 563 |     mock_responses = [
 564 |         {'TraceSummaries': [{'Id': 'trace1'}, {'Id': 'trace2'}], 'NextToken': 'token1'},
 565 |         {'TraceSummaries': [{'Id': 'trace3'}]},
 566 |     ]
 567 |     mock_client.get_trace_summaries.side_effect = mock_responses
 568 | 
 569 |     start_time = datetime.now(timezone.utc) - timedelta(hours=1)
 570 |     end_time = datetime.now(timezone.utc)
 571 | 
 572 |     traces = get_trace_summaries_paginated(
 573 |         mock_client, start_time, end_time, 'service("test")', max_traces=10
 574 |     )
 575 | 
 576 |     assert len(traces) == 3
 577 |     assert traces[0]['Id'] == 'trace1'
 578 |     assert traces[2]['Id'] == 'trace3'
 579 | 
 580 | 
 581 | def test_get_trace_summaries_paginated_with_error():
 582 |     """Test paginated trace retrieval with error."""
 583 |     mock_client = MagicMock()
 584 |     mock_client.get_trace_summaries.side_effect = Exception('API Error')
 585 | 
 586 |     start_time = datetime.now(timezone.utc) - timedelta(hours=1)
 587 |     end_time = datetime.now(timezone.utc)
 588 | 
 589 |     traces = get_trace_summaries_paginated(
 590 |         mock_client, start_time, end_time, 'service("test")', max_traces=10
 591 |     )
 592 | 
 593 |     assert len(traces) == 0  # Should return empty list on error
 594 | 
 595 | 
 596 | def test_check_transaction_search_enabled(mock_aws_clients):
 597 |     """Test checking transaction search status."""
 598 |     mock_aws_clients['xray_client'].get_trace_segment_destination.return_value = {
 599 |         'Destination': 'CloudWatchLogs',
 600 |         'Status': 'ACTIVE',
 601 |     }
 602 | 
 603 |     is_enabled, destination, status = check_transaction_search_enabled()
 604 | 
 605 |     assert is_enabled is True
 606 |     assert destination == 'CloudWatchLogs'
 607 |     assert status == 'ACTIVE'
 608 | 
 609 | 
 610 | def test_check_transaction_search_enabled_not_active(mock_aws_clients):
 611 |     """Test checking transaction search when not active."""
 612 |     mock_aws_clients['xray_client'].get_trace_segment_destination.return_value = {
 613 |         'Destination': 'XRay',
 614 |         'Status': 'INACTIVE',
 615 |     }
 616 | 
 617 |     is_enabled, destination, status = check_transaction_search_enabled()
 618 | 
 619 |     assert is_enabled is False
 620 |     assert destination == 'XRay'
 621 |     assert status == 'INACTIVE'
 622 | 
 623 | 
 624 | def test_check_transaction_search_enabled_error(mock_aws_clients):
 625 |     """Test checking transaction search with error."""
 626 |     mock_aws_clients['xray_client'].get_trace_segment_destination.side_effect = Exception(
 627 |         'API Error'
 628 |     )
 629 | 
 630 |     is_enabled, destination, status = check_transaction_search_enabled()
 631 | 
 632 |     assert is_enabled is False
 633 |     assert destination == 'Unknown'
 634 |     assert status == 'Error'
 635 | 
 636 | 
 637 | def test_remove_null_values():
 638 |     """Test remove_null_values function."""
 639 |     # Test with mix of None and non-None values
 640 |     input_dict = {
 641 |         'key1': 'value1',
 642 |         'key2': None,
 643 |         'key3': 'value3',
 644 |         'key4': None,
 645 |         'key5': 0,  # Should not be removed
 646 |         'key6': '',  # Should not be removed
 647 |         'key7': False,  # Should not be removed
 648 |     }
 649 | 
 650 |     result = remove_null_values(input_dict)
 651 | 
 652 |     assert result == {
 653 |         'key1': 'value1',
 654 |         'key3': 'value3',
 655 |         'key5': 0,
 656 |         'key6': '',
 657 |         'key7': False,
 658 |     }
 659 |     assert 'key2' not in result
 660 |     assert 'key4' not in result
 661 | 
 662 | 
 663 | @pytest.mark.asyncio
 664 | async def test_list_monitored_services_client_error(mock_aws_clients):
 665 |     """Test ClientError handling in list_monitored_services."""
 666 |     mock_aws_clients['appsignals_client'].list_services.side_effect = ClientError(
 667 |         error_response={
 668 |             'Error': {
 669 |                 'Code': 'AccessDeniedException',
 670 |                 'Message': 'User is not authorized to perform this action',
 671 |             }
 672 |         },
 673 |         operation_name='ListServices',
 674 |     )
 675 | 
 676 |     result = await list_monitored_services()
 677 | 
 678 |     assert 'AWS Error: User is not authorized to perform this action' in result
 679 | 
 680 | 
 681 | @pytest.mark.asyncio
 682 | async def test_list_monitored_services_general_exception(mock_aws_clients):
 683 |     """Test general exception handling in list_monitored_services."""
 684 |     mock_aws_clients['appsignals_client'].list_services.side_effect = Exception(
 685 |         'Unexpected error occurred'
 686 |     )
 687 | 
 688 |     result = await list_monitored_services()
 689 | 
 690 |     assert 'Error: Unexpected error occurred' in result
 691 | 
 692 | 
 693 | @pytest.mark.asyncio
 694 | async def test_get_service_detail_client_error(mock_aws_clients):
 695 |     """Test ClientError handling in get_service_detail."""
 696 |     mock_list_response = {
 697 |         'ServiceSummaries': [
 698 |             {'KeyAttributes': {'Name': 'test-service', 'Type': 'AWS::ECS::Service'}}
 699 |         ]
 700 |     }
 701 | 
 702 |     mock_aws_clients['appsignals_client'].list_services.return_value = mock_list_response
 703 |     mock_aws_clients['appsignals_client'].get_service.side_effect = ClientError(
 704 |         error_response={
 705 |             'Error': {
 706 |                 'Code': 'ResourceNotFoundException',
 707 |                 'Message': 'Service not found in Application Signals',
 708 |             }
 709 |         },
 710 |         operation_name='GetService',
 711 |     )
 712 | 
 713 |     result = await get_service_detail('test-service')
 714 | 
 715 |     assert 'AWS Error: Service not found in Application Signals' in result
 716 | 
 717 | 
 718 | @pytest.mark.asyncio
 719 | async def test_get_service_detail_general_exception(mock_aws_clients):
 720 |     """Test general exception handling in get_service_detail."""
 721 |     mock_list_response = {
 722 |         'ServiceSummaries': [
 723 |             {'KeyAttributes': {'Name': 'test-service', 'Type': 'AWS::ECS::Service'}}
 724 |         ]
 725 |     }
 726 | 
 727 |     mock_aws_clients['appsignals_client'].list_services.return_value = mock_list_response
 728 |     mock_aws_clients['appsignals_client'].get_service.side_effect = Exception(
 729 |         'Unexpected error in get_service'
 730 |     )
 731 | 
 732 |     result = await get_service_detail('test-service')
 733 | 
 734 |     assert 'Error: Unexpected error in get_service' in result
 735 | 
 736 | 
 737 | @pytest.mark.asyncio
 738 | async def test_query_service_metrics_no_datapoints(mock_aws_clients):
 739 |     """Test query service metrics when no datapoints are returned."""
 740 |     mock_list_response = {
 741 |         'ServiceSummaries': [
 742 |             {'KeyAttributes': {'Name': 'test-service', 'Type': 'AWS::ECS::Service'}}
 743 |         ]
 744 |     }
 745 | 
 746 |     mock_get_response = {
 747 |         'Service': {
 748 |             'MetricReferences': [
 749 |                 {
 750 |                     'Namespace': 'AWS/ApplicationSignals',
 751 |                     'MetricName': 'Latency',
 752 |                     'Dimensions': [{'Name': 'Service', 'Value': 'test-service'}],
 753 |                 }
 754 |             ]
 755 |         }
 756 |     }
 757 | 
 758 |     mock_metric_response = {'Datapoints': []}
 759 | 
 760 |     mock_aws_clients['appsignals_client'].list_services.return_value = mock_list_response
 761 |     mock_aws_clients['appsignals_client'].get_service.return_value = mock_get_response
 762 |     mock_aws_clients['cloudwatch_client'].get_metric_statistics.return_value = mock_metric_response
 763 | 
 764 |     result = await query_service_metrics(
 765 |         service_name='test-service',
 766 |         metric_name='Latency',
 767 |         statistic='Average',
 768 |         extended_statistic='p99',
 769 |         hours=1,
 770 |     )
 771 | 
 772 |     assert 'No data points found' in result
 773 | 
 774 | 
 775 | @pytest.mark.asyncio
 776 | async def test_search_transaction_spans_timeout(mock_aws_clients):
 777 |     """Test search transaction spans with timeout."""
 778 |     with patch(
 779 |         'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.check_transaction_search_enabled'
 780 |     ) as mock_check:
 781 |         with patch('awslabs.cloudwatch_appsignals_mcp_server.trace_tools.timer') as mock_timer:
 782 |             # Mock asyncio.sleep to prevent actual waiting
 783 |             with patch('asyncio.sleep', new_callable=AsyncMock):
 784 |                 mock_check.return_value = (True, 'CloudWatchLogs', 'ACTIVE')
 785 |                 mock_aws_clients['logs_client'].start_query.return_value = {
 786 |                     'queryId': 'test-query-id'
 787 |                 }
 788 |                 mock_aws_clients['logs_client'].get_query_results.return_value = {
 789 |                     'status': 'Running'
 790 |                 }
 791 | 
 792 |                 # Simulate timeout by making timer exceed max_timeout
 793 |                 mock_timer.side_effect = [
 794 |                     0,
 795 |                     0,
 796 |                     0,
 797 |                     31,
 798 |                     31,
 799 |                 ]  # start_time_perf, poll_start, poll check 1, poll check 2
 800 | 
 801 |                 result = await search_transaction_spans(
 802 |                     log_group_name='',
 803 |                     start_time='2024-01-01T00:00:00+00:00',
 804 |                     end_time='2024-01-01T01:00:00+00:00',
 805 |                     query_string='fields @timestamp',
 806 |                     limit=None,
 807 |                     max_timeout=30,
 808 |                 )
 809 | 
 810 |                 assert result['status'] == 'Polling Timeout'
 811 |                 assert 'did not complete within 30 seconds' in result['message']
 812 | 
 813 | 
 814 | def test_main_normal_execution(mock_mcp):
 815 |     """Test normal execution of main function."""
 816 |     main()
 817 |     mock_mcp.run.assert_called_once_with(transport='stdio')
 818 | 
 819 | 
 820 | def test_main_keyboard_interrupt(mock_mcp):
 821 |     """Test KeyboardInterrupt handling in main function."""
 822 |     mock_mcp.run.side_effect = KeyboardInterrupt()
 823 |     # Should not raise an exception
 824 |     main()
 825 |     mock_mcp.run.assert_called_once_with(transport='stdio')
 826 | 
 827 | 
 828 | def test_main_general_exception(mock_mcp):
 829 |     """Test general exception handling in main function."""
 830 |     mock_mcp.run.side_effect = Exception('Server error')
 831 |     with pytest.raises(Exception, match='Server error'):
 832 |         main()
 833 |     mock_mcp.run.assert_called_once_with(transport='stdio')
 834 | 
 835 | 
 836 | @pytest.mark.asyncio
 837 | async def test_get_slo_period_based(mock_aws_clients):
 838 |     """Test get_slo with period-based SLI configuration."""
 839 |     mock_slo_response = {
 840 |         'Slo': {
 841 |             'Name': 'test-slo-period',
 842 |             'Arn': 'arn:aws:application-signals:us-east-1:123456789012:slo/test-slo',
 843 |             'EvaluationType': 'PERIOD_BASED',
 844 |             'Sli': {
 845 |                 'SliMetric': {
 846 |                     'KeyAttributes': {'Name': 'test-service', 'Type': 'AWS::Lambda::Function'},
 847 |                     'OperationName': 'ProcessOrder',
 848 |                     'MetricType': 'AVAILABILITY',
 849 |                     'DependencyConfig': {
 850 |                         'DependencyKeyAttributes': {'Name': 'payment-service'},
 851 |                         'DependencyOperationName': 'ProcessPayment',
 852 |                     },
 853 |                 },
 854 |                 'MetricThreshold': 0.99,
 855 |                 'ComparisonOperator': 'LessThan',
 856 |             },
 857 |             'BurnRateConfigurations': [
 858 |                 {'LookBackWindowMinutes': 5},
 859 |                 {'LookBackWindowMinutes': 60},
 860 |             ],
 861 |         }
 862 |     }
 863 | 
 864 |     mock_aws_clients[
 865 |         'appsignals_client'
 866 |     ].get_service_level_objective.return_value = mock_slo_response
 867 | 
 868 |     result = await get_slo('test-slo-period')
 869 | 
 870 |     assert 'PERIOD_BASED' in result
 871 |     assert 'ProcessOrder' in result
 872 |     assert 'annotation[aws.remote.operation]="ProcessPayment"' in result
 873 |     assert 'Burn Rate Configurations' in result
 874 | 
 875 | 
 876 | @pytest.mark.asyncio
 877 | async def test_list_slis_with_error_in_sli_client(mock_aws_clients):
 878 |     """Test list_slis when SLIReportClient throws error for some services."""
 879 |     mock_services_response = {
 880 |         'ServiceSummaries': [
 881 |             {
 882 |                 'KeyAttributes': {
 883 |                     'Name': 'test-service-1',
 884 |                     'Type': 'AWS::ECS::Service',
 885 |                     'Environment': 'production',
 886 |                 }
 887 |             },
 888 |             {
 889 |                 'KeyAttributes': {
 890 |                     'Name': 'test-service-2',
 891 |                     'Type': 'AWS::Lambda::Function',
 892 |                     'Environment': 'staging',
 893 |                 }
 894 |             },
 895 |         ]
 896 |     }
 897 | 
 898 |     with patch(
 899 |         'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.SLIReportClient'
 900 |     ) as mock_sli_client:
 901 |         with patch(
 902 |             'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.check_transaction_search_enabled'
 903 |         ) as mock_check:
 904 |             mock_aws_clients[
 905 |                 'appsignals_client'
 906 |             ].list_services.return_value = mock_services_response
 907 |             mock_check.return_value = (False, 'XRay', 'INACTIVE')
 908 | 
 909 |             # First service succeeds, second fails
 910 |             mock_report = MagicMock()
 911 |             mock_report.breached_slo_count = 0
 912 |             mock_report.ok_slo_count = 3
 913 |             mock_report.total_slo_count = 3
 914 |             mock_report.sli_status = 'OK'
 915 |             mock_report.start_time = datetime.now(timezone.utc) - timedelta(hours=24)
 916 |             mock_report.end_time = datetime.now(timezone.utc)
 917 | 
 918 |             mock_sli_client.return_value.generate_sli_report.side_effect = [
 919 |                 mock_report,
 920 |                 Exception('Failed to get SLI report'),
 921 |             ]
 922 | 
 923 |             result = await list_slis(hours=24)
 924 | 
 925 |             assert 'Transaction Search: NOT ENABLED' in result
 926 |             assert 'HEALTHY SERVICES:' in result
 927 |             assert 'INSUFFICIENT DATA:' in result
 928 |             assert 'test-service-1' in result
 929 |             assert 'test-service-2' in result
 930 | 
 931 | 
 932 | @pytest.mark.asyncio
 933 | async def test_query_service_metrics_service_not_found(mock_aws_clients):
 934 |     """Test query service metrics when service is not found."""
 935 |     mock_list_response = {'ServiceSummaries': []}
 936 | 
 937 |     mock_aws_clients['appsignals_client'].list_services.return_value = mock_list_response
 938 | 
 939 |     result = await query_service_metrics(
 940 |         service_name='nonexistent-service',
 941 |         metric_name='Latency',
 942 |         statistic='Average',
 943 |         extended_statistic='p99',
 944 |         hours=1,
 945 |     )
 946 | 
 947 |     assert "Service 'nonexistent-service' not found" in result
 948 | 
 949 | 
 950 | @pytest.mark.asyncio
 951 | async def test_query_service_metrics_no_metrics(mock_aws_clients):
 952 |     """Test query service metrics when service has no metrics."""
 953 |     mock_list_response = {
 954 |         'ServiceSummaries': [
 955 |             {'KeyAttributes': {'Name': 'test-service', 'Type': 'AWS::ECS::Service'}}
 956 |         ]
 957 |     }
 958 | 
 959 |     mock_get_response = {'Service': {'MetricReferences': []}}
 960 | 
 961 |     mock_aws_clients['appsignals_client'].list_services.return_value = mock_list_response
 962 |     mock_aws_clients['appsignals_client'].get_service.return_value = mock_get_response
 963 | 
 964 |     result = await query_service_metrics(
 965 |         service_name='test-service',
 966 |         metric_name='Latency',
 967 |         statistic='Average',
 968 |         extended_statistic='p99',
 969 |         hours=1,
 970 |     )
 971 | 
 972 |     assert "No metrics found for service 'test-service'" in result
 973 | 
 974 | 
 975 | @pytest.mark.asyncio
 976 | async def test_query_service_metrics_metric_not_found(mock_aws_clients):
 977 |     """Test query service metrics when specific metric is not found."""
 978 |     mock_list_response = {
 979 |         'ServiceSummaries': [
 980 |             {'KeyAttributes': {'Name': 'test-service', 'Type': 'AWS::ECS::Service'}}
 981 |         ]
 982 |     }
 983 | 
 984 |     mock_get_response = {
 985 |         'Service': {
 986 |             'MetricReferences': [
 987 |                 {
 988 |                     'Namespace': 'AWS/ApplicationSignals',
 989 |                     'MetricName': 'Error',
 990 |                     'Dimensions': [{'Name': 'Service', 'Value': 'test-service'}],
 991 |                 }
 992 |             ]
 993 |         }
 994 |     }
 995 | 
 996 |     mock_aws_clients['appsignals_client'].list_services.return_value = mock_list_response
 997 |     mock_aws_clients['appsignals_client'].get_service.return_value = mock_get_response
 998 | 
 999 |     result = await query_service_metrics(
1000 |         service_name='test-service',
1001 |         metric_name='Latency',  # Looking for Latency but only Error exists
1002 |         statistic='Average',
1003 |         extended_statistic='p99',
1004 |         hours=1,
1005 |     )
1006 | 
1007 |     assert "Metric 'Latency' not found" in result
1008 |     assert 'Available: Error' in result
1009 | 
1010 | 
1011 | @pytest.mark.asyncio
1012 | async def test_query_service_metrics_client_error(mock_aws_clients):
1013 |     """Test query service metrics with client error."""
1014 |     mock_aws_clients['appsignals_client'].list_services.side_effect = ClientError(
1015 |         error_response={
1016 |             'Error': {
1017 |                 'Code': 'AccessDeniedException',
1018 |                 'Message': 'User is not authorized',
1019 |             }
1020 |         },
1021 |         operation_name='ListServices',
1022 |     )
1023 | 
1024 |     result = await query_service_metrics(
1025 |         service_name='test-service',
1026 |         metric_name='Latency',
1027 |         statistic='Average',
1028 |         extended_statistic='p99',
1029 |         hours=1,
1030 |     )
1031 | 
1032 |     assert 'AWS Error: User is not authorized' in result
1033 | 
1034 | 
1035 | @pytest.mark.asyncio
1036 | async def test_search_transaction_spans_failed_query(mock_aws_clients):
1037 |     """Test search transaction spans when query fails."""
1038 |     with patch(
1039 |         'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.check_transaction_search_enabled'
1040 |     ) as mock_check:
1041 |         mock_check.return_value = (True, 'CloudWatchLogs', 'ACTIVE')
1042 |         mock_aws_clients['logs_client'].start_query.return_value = {'queryId': 'test-query-id'}
1043 |         mock_aws_clients['logs_client'].get_query_results.return_value = {
1044 |             'queryId': 'test-query-id',
1045 |             'status': 'Failed',
1046 |             'statistics': {'error': 'Query syntax error'},
1047 |         }
1048 | 
1049 |         result = await search_transaction_spans(
1050 |             log_group_name='',
1051 |             start_time='2024-01-01T00:00:00+00:00',
1052 |             end_time='2024-01-01T01:00:00+00:00',
1053 |             query_string='invalid query',
1054 |             limit=None,
1055 |             max_timeout=30,
1056 |         )
1057 | 
1058 |         assert result['status'] == 'Failed'
1059 | 
1060 | 
1061 | @pytest.mark.asyncio
1062 | async def test_get_slo_client_error(mock_aws_clients):
1063 |     """Test get_slo with client error."""
1064 |     mock_aws_clients['appsignals_client'].get_service_level_objective.side_effect = ClientError(
1065 |         error_response={
1066 |             'Error': {
1067 |                 'Code': 'ResourceNotFoundException',
1068 |                 'Message': 'SLO not found',
1069 |             }
1070 |         },
1071 |         operation_name='GetServiceLevelObjective',
1072 |     )
1073 | 
1074 |     result = await get_slo('test-slo-id')
1075 | 
1076 |     assert 'AWS Error: SLO not found' in result
1077 | 
1078 | 
1079 | @pytest.mark.asyncio
1080 | async def test_search_transaction_spans_empty_log_group(mock_aws_clients):
1081 |     """Test search transaction spans with empty log group defaults to aws/spans."""
1082 |     with patch(
1083 |         'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.check_transaction_search_enabled'
1084 |     ) as mock_check:
1085 |         mock_check.return_value = (True, 'CloudWatchLogs', 'ACTIVE')
1086 |         mock_aws_clients['logs_client'].start_query.return_value = {'queryId': 'test-query-id'}
1087 |         mock_aws_clients['logs_client'].get_query_results.return_value = {
1088 |             'queryId': 'test-query-id',
1089 |             'status': 'Complete',
1090 |             'results': [],
1091 |         }
1092 | 
1093 |         await search_transaction_spans(
1094 |             log_group_name='',  # Empty string should default to 'aws/spans'
1095 |             start_time='2024-01-01T00:00:00+00:00',
1096 |             end_time='2024-01-01T01:00:00+00:00',
1097 |             query_string='fields @timestamp',
1098 |             limit=None,
1099 |             max_timeout=30,
1100 |         )
1101 | 
1102 |         # Verify start_query was called with default 'aws/spans'
1103 |         mock_aws_clients['logs_client'].start_query.assert_called()
1104 |         call_args = mock_aws_clients['logs_client'].start_query.call_args[1]
1105 |         assert 'aws/spans' in call_args['logGroupNames']
1106 | 
1107 | 
1108 | @pytest.mark.asyncio
1109 | async def test_list_slis_no_services(mock_aws_clients):
1110 |     """Test list_slis when no services exist."""
1111 |     mock_aws_clients['appsignals_client'].list_services.return_value = {'ServiceSummaries': []}
1112 | 
1113 |     result = await list_slis(hours=24)
1114 | 
1115 |     assert 'No services found in Application Signals.' in result
1116 | 
1117 | 
1118 | @pytest.mark.asyncio
1119 | async def test_get_slo_with_calendar_interval(mock_aws_clients):
1120 |     """Test get_slo with calendar interval in goal."""
1121 |     mock_slo_response = {
1122 |         'Slo': {
1123 |             'Name': 'test-slo-calendar',
1124 |             'Goal': {
1125 |                 'AttainmentGoal': 99.5,
1126 |                 'Interval': {
1127 |                     'CalendarInterval': {
1128 |                         'Duration': 1,
1129 |                         'DurationUnit': 'MONTH',
1130 |                         'StartTime': '2024-01-01T00:00:00Z',
1131 |                     }
1132 |                 },
1133 |             },
1134 |         }
1135 |     }
1136 | 
1137 |     mock_aws_clients[
1138 |         'appsignals_client'
1139 |     ].get_service_level_objective.return_value = mock_slo_response
1140 | 
1141 |     result = await get_slo('test-slo-calendar')
1142 | 
1143 |     assert 'Calendar 1 MONTH starting 2024-01-01T00:00:00Z' in result
1144 | 
1145 | 
1146 | @pytest.mark.asyncio
1147 | async def test_query_service_metrics_different_periods(mock_aws_clients):
1148 |     """Test query service metrics with different time periods."""
1149 |     # Test data for different hour ranges
1150 |     test_cases = [
1151 |         (2, 60),  # 2 hours -> 1 minute period
1152 |         (12, 300),  # 12 hours -> 5 minute period
1153 |         (48, 3600),  # 48 hours -> 1 hour period
1154 |     ]
1155 | 
1156 |     for hours, expected_period in test_cases:
1157 |         mock_list_response = {
1158 |             'ServiceSummaries': [
1159 |                 {'KeyAttributes': {'Name': 'test-service', 'Type': 'AWS::ECS::Service'}}
1160 |             ]
1161 |         }
1162 | 
1163 |         mock_get_response = {
1164 |             'Service': {
1165 |                 'MetricReferences': [
1166 |                     {
1167 |                         'Namespace': 'AWS/ApplicationSignals',
1168 |                         'MetricName': 'Latency',
1169 |                         'Dimensions': [],
1170 |                     }
1171 |                 ]
1172 |             }
1173 |         }
1174 | 
1175 |         mock_metric_response = {
1176 |             'Datapoints': [{'Timestamp': datetime.now(timezone.utc), 'Average': 100.0}]
1177 |         }
1178 | 
1179 |         mock_aws_clients['appsignals_client'].list_services.return_value = mock_list_response
1180 |         mock_aws_clients['appsignals_client'].get_service.return_value = mock_get_response
1181 |         mock_aws_clients[
1182 |             'cloudwatch_client'
1183 |         ].get_metric_statistics.return_value = mock_metric_response
1184 | 
1185 |         await query_service_metrics(
1186 |             service_name='test-service',
1187 |             metric_name='Latency',
1188 |             statistic='Average',
1189 |             extended_statistic='p99',
1190 |             hours=hours,
1191 |         )
1192 | 
1193 |         # Verify the period was set correctly
1194 |         call_args = mock_aws_clients['cloudwatch_client'].get_metric_statistics.call_args[1]
1195 |         assert call_args['Period'] == expected_period
1196 | 
1197 | 
1198 | @pytest.mark.asyncio
1199 | async def test_query_service_metrics_general_exception(mock_aws_clients):
1200 |     """Test query service metrics with unexpected exception."""
1201 |     mock_aws_clients['appsignals_client'].list_services.side_effect = Exception('Unexpected error')
1202 | 
1203 |     result = await query_service_metrics(
1204 |         service_name='test-service',
1205 |         metric_name='Latency',
1206 |         statistic='Average',
1207 |         extended_statistic='p99',
1208 |         hours=1,
1209 |     )
1210 | 
1211 |     assert 'Error: Unexpected error' in result
1212 | 
1213 | 
1214 | @pytest.mark.asyncio
1215 | async def test_search_transaction_spans_general_exception(mock_aws_clients):
1216 |     """Test search transaction spans with general exception."""
1217 |     with patch(
1218 |         'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.check_transaction_search_enabled'
1219 |     ) as mock_check:
1220 |         mock_check.return_value = (True, 'CloudWatchLogs', 'ACTIVE')
1221 |         mock_aws_clients['logs_client'].start_query.side_effect = Exception('Query failed')
1222 | 
1223 |         with pytest.raises(Exception) as exc_info:
1224 |             await search_transaction_spans(
1225 |                 log_group_name='aws/spans',
1226 |                 start_time='2024-01-01T00:00:00+00:00',
1227 |                 end_time='2024-01-01T01:00:00+00:00',
1228 |                 query_string='fields @timestamp',
1229 |                 limit=100,
1230 |                 max_timeout=30,
1231 |             )
1232 | 
1233 |         assert 'Query failed' in str(exc_info.value)
1234 | 
1235 | 
1236 | @pytest.mark.asyncio
1237 | async def test_list_monitored_services_with_attributes_branch(mock_aws_clients):
1238 |     """Test list_monitored_services with key attributes that trigger the branch."""
1239 |     mock_response = {
1240 |         'ServiceSummaries': [
1241 |             {
1242 |                 'KeyAttributes': {}  # Empty attributes to test the branch
1243 |             }
1244 |         ]
1245 |     }
1246 | 
1247 |     mock_aws_clients['appsignals_client'].list_services.return_value = mock_response
1248 | 
1249 |     result = await list_monitored_services()
1250 | 
1251 |     assert 'Application Signals Services (1 total)' in result
1252 |     assert 'Key Attributes:' not in result  # Should not show when empty
1253 | 
1254 | 
1255 | @pytest.mark.asyncio
1256 | async def test_get_trace_summaries_paginated_with_limit(mock_aws_clients):
1257 |     """Test get_trace_summaries_paginated when it hits the max_traces limit."""
1258 |     # Mock responses with more traces than the limit
1259 |     mock_response_1 = {
1260 |         'TraceSummaries': [{'Id': f'trace-{i}', 'Duration': 100} for i in range(10)],
1261 |         'NextToken': 'token1',
1262 |     }
1263 |     mock_response_2 = {
1264 |         'TraceSummaries': [{'Id': f'trace-{i}', 'Duration': 100} for i in range(10, 15)]
1265 |     }
1266 | 
1267 |     mock_aws_clients['xray_client'].get_trace_summaries.side_effect = [
1268 |         mock_response_1,
1269 |         mock_response_2,
1270 |     ]
1271 | 
1272 |     # Test with max_traces=12
1273 |     traces = get_trace_summaries_paginated(
1274 |         mock_aws_clients['xray_client'],
1275 |         datetime.now(timezone.utc),
1276 |         datetime.now(timezone.utc),
1277 |         'service("test")',
1278 |         max_traces=12,
1279 |     )
1280 | 
1281 |     # The function continues until it gets all traces from the current page
1282 |     # before checking the limit, so we might get more than max_traces
1283 |     assert len(traces) >= 12  # Should have at least the limit
1284 | 
1285 | 
1286 | @pytest.mark.asyncio
1287 | async def test_get_slo_with_period_based_sli_full_details(mock_aws_clients):
1288 |     """Test get_slo with comprehensive period-based SLI configuration."""
1289 |     mock_response = {
1290 |         'Slo': {
1291 |             'Name': 'test-slo',
1292 |             'Arn': 'arn:aws:slo:test',
1293 |             'Description': 'Test SLO',
1294 |             'EvaluationType': 'PERIOD_BASED',
1295 |             'CreatedTime': datetime.now(timezone.utc),
1296 |             'LastUpdatedTime': datetime.now(timezone.utc),
1297 |             'Goal': {
1298 |                 'AttainmentGoal': 99.9,
1299 |                 'WarningThreshold': 95,
1300 |                 'Interval': {
1301 |                     'CalendarInterval': {
1302 |                         'Duration': 1,
1303 |                         'DurationUnit': 'MONTH',
1304 |                         'StartTime': datetime.now(timezone.utc),
1305 |                     }
1306 |                 },
1307 |             },
1308 |             'Sli': {
1309 |                 'SliMetric': {
1310 |                     'KeyAttributes': {'Service': 'test-service', 'Environment': 'prod'},
1311 |                     'OperationName': 'GetItem',
1312 |                     'MetricType': 'LATENCY',
1313 |                     'MetricDataQueries': [
1314 |                         {
1315 |                             'Id': 'query1',
1316 |                             'MetricStat': {
1317 |                                 'Metric': {
1318 |                                     'Namespace': 'AWS/ApplicationSignals',
1319 |                                     'MetricName': 'Latency',
1320 |                                     'Dimensions': [
1321 |                                         {'Name': 'Service', 'Value': 'test-service'},
1322 |                                         {'Name': 'Operation', 'Value': 'GetItem'},
1323 |                                     ],
1324 |                                 },
1325 |                                 'Period': 300,
1326 |                                 'Stat': 'p99',
1327 |                                 'Unit': 'Milliseconds',
1328 |                             },
1329 |                             'ReturnData': True,
1330 |                         },
1331 |                         {'Id': 'query2', 'Expression': 'query1 * 2', 'ReturnData': False},
1332 |                     ],
1333 |                     'DependencyConfig': {
1334 |                         'DependencyKeyAttributes': {
1335 |                             'RemoteService': 'downstream-service',
1336 |                             'RemoteEnvironment': 'prod',
1337 |                         },
1338 |                         'DependencyOperationName': 'ProcessRequest',
1339 |                     },
1340 |                 },
1341 |                 'MetricThreshold': 1000,
1342 |                 'ComparisonOperator': 'LessThan',
1343 |             },
1344 |             'BurnRateConfigurations': [
1345 |                 {'LookBackWindowMinutes': 5},
1346 |                 {'LookBackWindowMinutes': 60},
1347 |             ],
1348 |         }
1349 |     }
1350 | 
1351 |     mock_aws_clients['appsignals_client'].get_service_level_objective.return_value = mock_response
1352 | 
1353 |     result = await get_slo('test-slo-id')
1354 | 
1355 |     # Verify all sections are present
1356 |     assert 'Service Level Objective Details' in result
1357 |     assert 'Goal Configuration' in result
1358 |     assert 'Calendar 1 MONTH' in result
1359 |     assert 'Period-Based SLI Configuration' in result
1360 |     assert 'Key Attributes:' in result
1361 |     assert 'Service: test-service' in result
1362 |     assert 'Operation Name: GetItem' in result
1363 |     assert 'Metric Data Queries:' in result
1364 |     assert 'Query ID: query1' in result
1365 |     assert 'Namespace: AWS/ApplicationSignals' in result
1366 |     assert 'Dimensions:' in result
1367 |     assert 'Expression: query1 * 2' in result
1368 |     assert 'ReturnData: False' in result
1369 |     assert 'Dependency Configuration:' in result
1370 |     assert 'RemoteService: downstream-service' in result
1371 |     assert 'Dependency Operation: ProcessRequest' in result
1372 |     assert 'Burn Rate Configurations:' in result
1373 | 
1374 | 
1375 | @pytest.mark.asyncio
1376 | async def test_get_slo_with_request_based_sli_full_details(mock_aws_clients):
1377 |     """Test get_slo with comprehensive request-based SLI configuration."""
1378 |     mock_response = {
1379 |         'Slo': {
1380 |             'Name': 'test-slo-rbs',
1381 |             'Arn': 'arn:aws:slo:test-rbs',
1382 |             'Goal': {
1383 |                 'AttainmentGoal': 99.5,
1384 |                 'Interval': {'RollingInterval': {'Duration': 7, 'DurationUnit': 'DAY'}},
1385 |             },
1386 |             'RequestBasedSli': {
1387 |                 'RequestBasedSliMetric': {
1388 |                     'KeyAttributes': {'Service': 'api-service', 'Type': 'AWS::Lambda::Function'},
1389 |                     'OperationName': 'ProcessOrder',
1390 |                     'MetricType': 'AVAILABILITY',
1391 |                     'MetricDataQueries': [
1392 |                         {
1393 |                             'Id': 'success',
1394 |                             'MetricStat': {
1395 |                                 'Metric': {
1396 |                                     'Namespace': 'AWS/Lambda',
1397 |                                     'MetricName': 'Success',
1398 |                                     'Dimensions': [
1399 |                                         {'Name': 'FunctionName', 'Value': 'process-order'}
1400 |                                     ],
1401 |                                 },
1402 |                                 'Period': 60,
1403 |                                 'Stat': 'Sum',
1404 |                             },
1405 |                         },
1406 |                         {
1407 |                             'Id': 'errors',
1408 |                             'MetricStat': {
1409 |                                 'Metric': {
1410 |                                     'Namespace': 'AWS/Lambda',
1411 |                                     'MetricName': 'Errors',
1412 |                                     'Dimensions': [
1413 |                                         {'Name': 'FunctionName', 'Value': 'process-order'}
1414 |                                     ],
1415 |                                 },
1416 |                                 'Period': 60,
1417 |                                 'Stat': 'Sum',
1418 |                                 'Unit': 'Count',
1419 |                             },
1420 |                         },
1421 |                         {'Id': 'availability', 'Expression': 'success / (success + errors) * 100'},
1422 |                     ],
1423 |                     'DependencyConfig': {
1424 |                         'DependencyKeyAttributes': {'Database': 'orders-db'},
1425 |                         'DependencyOperationName': 'Query',
1426 |                     },
1427 |                 },
1428 |                 'MetricThreshold': 99.0,
1429 |                 'ComparisonOperator': 'GreaterThan',
1430 |             },
1431 |         }
1432 |     }
1433 | 
1434 |     mock_aws_clients['appsignals_client'].get_service_level_objective.return_value = mock_response
1435 | 
1436 |     result = await get_slo('test-slo-rbs-id')
1437 | 
1438 |     # Verify request-based sections
1439 |     assert 'Request-Based SLI Configuration:' in result
1440 |     assert 'api-service' in result
1441 |     assert 'ProcessOrder' in result
1442 |     assert 'AVAILABILITY' in result
1443 |     assert 'Expression: success / (success + errors) * 100' in result
1444 |     assert 'Dependency Configuration:' in result
1445 |     assert 'Database: orders-db' in result
1446 |     assert 'Unit: Count' in result
1447 | 
1448 | 
1449 | @pytest.mark.asyncio
1450 | async def test_get_slo_general_exception(mock_aws_clients):
1451 |     """Test get_slo with general exception."""
1452 |     mock_aws_clients['appsignals_client'].get_service_level_objective.side_effect = Exception(
1453 |         'Unexpected error'
1454 |     )
1455 | 
1456 |     result = await get_slo('test-slo-id')
1457 | 
1458 |     assert 'Error: Unexpected error' in result
1459 | 
1460 | 
1461 | @pytest.mark.asyncio
1462 | async def test_search_transaction_spans_with_none_log_group(mock_aws_clients):
1463 |     """Test search_transaction_spans when log_group_name is None."""
1464 |     with patch(
1465 |         'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.check_transaction_search_enabled'
1466 |     ) as mock_check:
1467 |         mock_check.return_value = (True, 'CloudWatchLogs', 'ACTIVE')
1468 |         mock_aws_clients['logs_client'].start_query.return_value = {'queryId': 'test-query-id'}
1469 |         mock_aws_clients['logs_client'].get_query_results.return_value = {
1470 |             'queryId': 'test-query-id',
1471 |             'status': 'Complete',
1472 |             'results': [],
1473 |         }
1474 | 
1475 |         # Pass None for log_group_name to test the default handling
1476 |         await search_transaction_spans(
1477 |             log_group_name=None,  # type: ignore
1478 |             start_time='2024-01-01T00:00:00+00:00',
1479 |             end_time='2024-01-01T01:00:00+00:00',
1480 |             query_string='fields @timestamp',
1481 |             limit=100,
1482 |             max_timeout=30,
1483 |         )
1484 | 
1485 |         # Verify it used the default log group
1486 |         call_args = mock_aws_clients['logs_client'].start_query.call_args[1]
1487 |         assert 'aws/spans' in call_args['logGroupNames']
1488 | 
1489 | 
1490 | @pytest.mark.asyncio
1491 | async def test_search_transaction_spans_complete_with_statistics(mock_aws_clients):
1492 |     """Test search_transaction_spans when query completes with detailed statistics."""
1493 |     with patch(
1494 |         'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.check_transaction_search_enabled'
1495 |     ) as mock_check:
1496 |         mock_check.return_value = (True, 'CloudWatchLogs', 'ACTIVE')
1497 |         mock_aws_clients['logs_client'].start_query.return_value = {'queryId': 'test-query-id'}
1498 | 
1499 |         # First return Running, then Complete
1500 |         mock_aws_clients['logs_client'].get_query_results.side_effect = [
1501 |             {'queryId': 'test-query-id', 'status': 'Running'},
1502 |             {
1503 |                 'queryId': 'test-query-id',
1504 |                 'status': 'Complete',
1505 |                 'statistics': {
1506 |                     'recordsMatched': 100,
1507 |                     'recordsScanned': 1000,
1508 |                     'bytesScanned': 50000,
1509 |                 },
1510 |                 'results': [
1511 |                     [
1512 |                         {'field': 'spanId', 'value': 'span1'},
1513 |                         {'field': '@timestamp', 'value': '2024-01-01 00:00:00'},
1514 |                     ]
1515 |                 ],
1516 |             },
1517 |         ]
1518 | 
1519 |         result = await search_transaction_spans(
1520 |             log_group_name='aws/spans',
1521 |             start_time='2024-01-01T00:00:00+00:00',
1522 |             end_time='2024-01-01T01:00:00+00:00',
1523 |             query_string='fields @timestamp, spanId',
1524 |             limit=100,
1525 |             max_timeout=30,
1526 |         )
1527 | 
1528 |         assert result['status'] == 'Complete'
1529 |         assert result['statistics']['recordsMatched'] == 100
1530 |         assert len(result['results']) == 1
1531 | 
1532 | 
1533 | @pytest.mark.asyncio
1534 | async def test_list_slis_general_exception(mock_aws_clients):
1535 |     """Test list_slis with general exception."""
1536 |     mock_aws_clients['appsignals_client'].list_services.side_effect = Exception(
1537 |         'Service unavailable'
1538 |     )
1539 | 
1540 |     result = await list_slis(hours=24)
1541 | 
1542 |     assert 'Error getting SLI status: Service unavailable' in result
1543 | 
1544 | 
1545 | @pytest.mark.asyncio
1546 | async def test_query_sampled_traces_with_defaults(mock_aws_clients):
1547 |     """Test query_sampled_traces with default start_time and end_time."""
1548 |     mock_trace_response = {
1549 |         'TraceSummaries': [
1550 |             {
1551 |                 'Id': 'trace1',
1552 |                 'Duration': 100,
1553 |                 'HasError': True,
1554 |                 'ErrorRootCauses': [
1555 |                     {
1556 |                         'Services': [
1557 |                             {
1558 |                                 'Name': 'test-service',
1559 |                                 'Names': ['test-service'],
1560 |                                 'Type': 'AWS::ECS::Service',
1561 |                                 'AccountId': '123456789012',
1562 |                                 'EntityPath': [
1563 |                                     {'Name': 'test-service', 'Coverage': 1.0, 'Remote': False}
1564 |                                 ],
1565 |                                 'Inferred': False,
1566 |                             }
1567 |                         ],
1568 |                         'ClientImpacting': True,
1569 |                     }
1570 |                 ],
1571 |             }
1572 |         ]
1573 |     }
1574 | 
1575 |     with patch(
1576 |         'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.get_trace_summaries_paginated'
1577 |     ) as mock_paginated:
1578 |         mock_paginated.return_value = mock_trace_response['TraceSummaries']
1579 | 
1580 |         # Call without start_time and end_time to test defaults
1581 |         result_json = await query_sampled_traces(
1582 |             filter_expression='service("test-service")',
1583 |             start_time=None,
1584 |             end_time=None,
1585 |             region='us-east-1',
1586 |         )
1587 | 
1588 |         result = json.loads(result_json)
1589 |         assert result['TraceCount'] == 1
1590 |         assert result['TraceSummaries'][0]['HasError'] is True
1591 | 
1592 |         # Verify the time window was set to 3 hours
1593 |         call_args = mock_paginated.call_args[0]
1594 |         time_diff = call_args[2] - call_args[1]  # end_time - start_time
1595 |         assert 2.9 < time_diff.total_seconds() / 3600 < 3.1  # Approximately 3 hours
1596 | 
1597 | 
1598 | @pytest.mark.asyncio
1599 | async def test_query_sampled_traces_with_annotations(mock_aws_clients):
1600 |     """Test query_sampled_traces with annotations filtering."""
1601 |     mock_trace = {
1602 |         'Id': 'trace1',
1603 |         'Duration': 100,
1604 |         'Annotations': {
1605 |             'aws.local.operation': 'GetItem',
1606 |             'aws.remote.operation': 'Query',
1607 |             'custom.field': 'should-be-filtered',
1608 |             'another.field': 'also-filtered',
1609 |         },
1610 |         'Users': [
1611 |             {'UserName': 'user1', 'ServiceIds': []},
1612 |             {'UserName': 'user2', 'ServiceIds': []},
1613 |             {'UserName': 'user3', 'ServiceIds': []},  # Should be limited to 2
1614 |         ],
1615 |     }
1616 | 
1617 |     with patch(
1618 |         'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.get_trace_summaries_paginated'
1619 |     ) as mock_paginated:
1620 |         mock_paginated.return_value = [mock_trace]
1621 | 
1622 |         result_json = await query_sampled_traces(
1623 |             start_time='2024-01-01T00:00:00Z',
1624 |             end_time='2024-01-01T01:00:00Z',
1625 |             filter_expression='service("test")',
1626 |         )
1627 | 
1628 |         result = json.loads(result_json)
1629 |         trace_summary = result['TraceSummaries'][0]
1630 | 
1631 |         # Check annotations were filtered
1632 |         assert 'Annotations' in trace_summary
1633 |         assert 'aws.local.operation' in trace_summary['Annotations']
1634 |         assert 'aws.remote.operation' in trace_summary['Annotations']
1635 |         assert 'custom.field' not in trace_summary['Annotations']
1636 | 
1637 |         # Check users were limited
1638 |         assert len(trace_summary['Users']) == 2
1639 | 
1640 | 
1641 | @pytest.mark.asyncio
1642 | async def test_query_sampled_traces_with_fault_causes(mock_aws_clients):
1643 |     """Test query_sampled_traces with fault root causes."""
1644 |     mock_trace = {
1645 |         'Id': 'trace1',
1646 |         'Duration': 100,
1647 |         'HasFault': True,
1648 |         'FaultRootCauses': [
1649 |             {'Services': [{'Name': 'service1', 'Exceptions': [{'Message': 'Test fault error'}]}]},
1650 |             {'Services': [{'Name': 'service2'}]},
1651 |             {'Services': [{'Name': 'service3'}]},
1652 |             {'Services': [{'Name': 'service4'}]},  # Should be limited to 3
1653 |         ],
1654 |         'ResponseTimeRootCauses': [{'Services': [{'Name': 'slow-service'}]}],
1655 |     }
1656 | 
1657 |     with patch(
1658 |         'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.get_trace_summaries_paginated'
1659 |     ) as mock_paginated:
1660 |         mock_paginated.return_value = [mock_trace]
1661 | 
1662 |         result_json = await query_sampled_traces(
1663 |             start_time='2024-01-01T00:00:00Z', end_time='2024-01-01T01:00:00Z'
1664 |         )
1665 | 
1666 |         result = json.loads(result_json)
1667 |         trace_summary = result['TraceSummaries'][0]
1668 | 
1669 |         # Check root causes were limited to 3
1670 |         assert len(trace_summary['FaultRootCauses']) == 3
1671 |         assert 'ResponseTimeRootCauses' in trace_summary
1672 | 
1673 | 
1674 | @pytest.mark.asyncio
1675 | async def test_query_sampled_traces_general_exception(mock_aws_clients):
1676 |     """Test query_sampled_traces with general exception."""
1677 |     with patch(
1678 |         'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.get_trace_summaries_paginated'
1679 |     ) as mock_paginated:
1680 |         mock_paginated.side_effect = Exception('Trace query failed')
1681 | 
1682 |         result_json = await query_sampled_traces(
1683 |             start_time='2024-01-01T00:00:00Z', end_time='2024-01-01T01:00:00Z'
1684 |         )
1685 | 
1686 |         result = json.loads(result_json)
1687 |         assert 'error' in result
1688 |         assert 'Trace query failed' in result['error']
1689 | 
1690 | 
1691 | @pytest.mark.asyncio
1692 | async def test_query_sampled_traces_datetime_conversion(mock_aws_clients):
1693 |     """Test query_sampled_traces with datetime objects that need conversion."""
1694 |     # The convert_datetime function in server.py only processes top-level fields,
1695 |     # not nested datetime objects. Let's test with a datetime at the top level.
1696 |     mock_trace = {
1697 |         'Id': 'trace1',
1698 |         'Duration': 100,
1699 |         'Http': {'HttpStatus': 200, 'HttpMethod': 'GET'},
1700 |         'StartTime': datetime.now(timezone.utc),  # This will be processed by convert_datetime
1701 |         'EndTime': datetime.now(timezone.utc) + timedelta(minutes=1),
1702 |     }
1703 | 
1704 |     with patch(
1705 |         'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.get_trace_summaries_paginated'
1706 |     ) as mock_paginated:
1707 |         mock_paginated.return_value = [mock_trace]
1708 | 
1709 |         result_json = await query_sampled_traces(
1710 |             start_time='2024-01-01T00:00:00Z', end_time='2024-01-01T01:00:00Z'
1711 |         )
1712 | 
1713 |         # Should not raise JSON serialization error
1714 |         result = json.loads(result_json)
1715 |         assert result['TraceCount'] == 1
1716 |         # The datetime fields should have been converted during processing
1717 |         trace_summary = result['TraceSummaries'][0]
1718 |         assert (
1719 |             'StartTime' not in trace_summary
1720 |         )  # These fields are not included in the simplified output
1721 |         assert 'EndTime' not in trace_summary
1722 | 
1723 | 
1724 | @pytest.mark.asyncio
1725 | async def test_query_sampled_traces_deduplication(mock_aws_clients):
1726 |     """Test query_sampled_traces deduplicates traces with same fault message.
1727 | 
1728 |     Note: Only FaultRootCauses are deduplicated, not ErrorRootCauses.
1729 |     This is because the primary use case is investigating server faults (5xx errors),
1730 |     not client errors (4xx).
1731 |     """
1732 |     # Create 5 traces with the same fault message
1733 |     mock_traces = [
1734 |         {
1735 |             'Id': f'trace{i}',
1736 |             'Duration': 100 + i * 10,
1737 |             'ResponseTime': 95 + i * 10,
1738 |             'HasFault': True,
1739 |             'FaultRootCauses': [
1740 |                 {
1741 |                     'Services': [
1742 |                         {
1743 |                             'Name': 'test-service',
1744 |                             'Exceptions': [{'Message': 'Database connection timeout'}],
1745 |                         }
1746 |                     ]
1747 |                 }
1748 |             ],
1749 |         }
1750 |         for i in range(1, 6)
1751 |     ]
1752 | 
1753 |     # Add 2 traces with ErrorRootCauses (these should NOT be deduplicated)
1754 |     mock_traces.extend(
1755 |         [
1756 |             {
1757 |                 'Id': 'trace6',
1758 |                 'Duration': 200,
1759 |                 'HasError': True,
1760 |                 'ErrorRootCauses': [
1761 |                     {
1762 |                         'Services': [
1763 |                             {
1764 |                                 'Name': 'api-service',
1765 |                                 'Exceptions': [{'Message': 'Invalid API key'}],
1766 |                             }
1767 |                         ]
1768 |                     }
1769 |                 ],
1770 |             },
1771 |             {
1772 |                 'Id': 'trace7',
1773 |                 'Duration': 210,
1774 |                 'HasError': True,
1775 |                 'ErrorRootCauses': [
1776 |                     {
1777 |                         'Services': [
1778 |                             {
1779 |                                 'Name': 'api-service',
1780 |                                 'Exceptions': [{'Message': 'Invalid API key'}],
1781 |                             }
1782 |                         ]
1783 |                     }
1784 |                 ],
1785 |             },
1786 |         ]
1787 |     )
1788 | 
1789 |     # Add 2 healthy traces
1790 |     mock_traces.extend(
1791 |         [
1792 |             {
1793 |                 'Id': 'trace8',
1794 |                 'Duration': 50,
1795 |                 'ResponseTime': 45,
1796 |                 'HasError': False,
1797 |                 'HasFault': False,
1798 |             },
1799 |             {
1800 |                 'Id': 'trace9',
1801 |                 'Duration': 55,
1802 |                 'ResponseTime': 50,
1803 |                 'HasError': False,
1804 |                 'HasFault': False,
1805 |             },
1806 |         ]
1807 |     )
1808 | 
1809 |     with patch(
1810 |         'awslabs.cloudwatch_appsignals_mcp_server.trace_tools.get_trace_summaries_paginated'
1811 |     ) as mock_paginated:
1812 |         mock_paginated.return_value = mock_traces
1813 | 
1814 |         result_json = await query_sampled_traces(
1815 |             start_time='2024-01-01T00:00:00Z', end_time='2024-01-01T01:00:00Z'
1816 |         )
1817 | 
1818 |         result = json.loads(result_json)
1819 | 
1820 |         # Verify deduplication worked - should only have 5 traces
1821 |         # 1 for database timeout fault (deduplicated from 5)
1822 |         # 2 for API key errors (NOT deduplicated - only faults are deduped)
1823 |         # 2 healthy traces (not deduplicated)
1824 |         assert result['TraceCount'] == 5
1825 |         assert len(result['TraceSummaries']) == 5
1826 | 
1827 |         # Verify deduplication stats
1828 |         assert 'DeduplicationStats' in result
1829 |         assert result['DeduplicationStats']['OriginalTraceCount'] == 9
1830 |         assert result['DeduplicationStats']['DuplicatesRemoved'] == 4  # 9 - 5 = 4
1831 |         assert (
1832 |             result['DeduplicationStats']['UniqueFaultMessages'] == 1
1833 |         )  # Only counting FaultRootCauses
1834 | 
1835 |         # Find the trace with fault
1836 |         db_trace = next(
1837 |             (
1838 |                 t
1839 |                 for t in result['TraceSummaries']
1840 |                 if t.get('FaultRootCauses')
1841 |                 and any(
1842 |                     'Database connection timeout' in str(s.get('Exceptions', []))
1843 |                     for cause in t['FaultRootCauses']
1844 |                     for s in cause.get('Services', [])
1845 |                 )
1846 |             ),
1847 |             None,
1848 |         )
1849 |         assert db_trace is not None
1850 |         assert db_trace['HasFault'] is True
1851 | 
1852 |         # Verify both error traces are present (not deduplicated)
1853 |         error_traces = [
1854 |             t
1855 |             for t in result['TraceSummaries']
1856 |             if t.get('ErrorRootCauses')
1857 |             and any(
1858 |                 'Invalid API key' in str(s.get('Exceptions', []))
1859 |                 for cause in t['ErrorRootCauses']
1860 |                 for s in cause.get('Services', [])
1861 |             )
1862 |         ]
1863 |         assert len(error_traces) == 2  # Both error traces should be kept
1864 |         assert all(t['HasError'] is True for t in error_traces)
1865 | 
1866 |         # Verify healthy traces are included
1867 |         healthy_count = sum(
1868 |             1
1869 |             for t in result['TraceSummaries']
1870 |             if not t.get('HasError') and not t.get('HasFault') and not t.get('HasThrottle')
1871 |         )
1872 |         assert healthy_count == 2
1873 | 
1874 | 
1875 | def test_main_success(mock_aws_clients):
1876 |     """Test main function normal execution."""
1877 |     with patch('awslabs.cloudwatch_appsignals_mcp_server.server.mcp') as mock_mcp:
1878 |         main()
1879 |         mock_mcp.run.assert_called_once_with(transport='stdio')
1880 | 
1881 | 
1882 | def test_main_exception(mock_aws_clients):
1883 |     """Test main function with general exception."""
1884 |     with patch('awslabs.cloudwatch_appsignals_mcp_server.server.mcp') as mock_mcp:
1885 |         mock_mcp.run.side_effect = Exception('Server error')
1886 | 
1887 |         with pytest.raises(Exception) as exc_info:
1888 |             main()
1889 | 
1890 |         assert 'Server error' in str(exc_info.value)
1891 | 
1892 | 
1893 | def test_main_entry_point(mock_aws_clients):
1894 |     """Test the if __name__ == '__main__' entry point."""
1895 |     # The __main__ block is simple and just calls main()
1896 |     # We can't easily test it without executing the module
1897 |     # So we'll just ensure the main() function works
1898 |     # The actual line 1346 will be covered when the module is imported
1899 |     # during normal test execution
1900 | 
1901 |     # Instead, let's just verify the main function exists and is callable
1902 |     from awslabs.cloudwatch_appsignals_mcp_server.server import main
1903 | 
1904 |     assert callable(main)
1905 | 
1906 |     # And verify that running main with mocked mcp doesn't raise
1907 |     with patch('awslabs.cloudwatch_appsignals_mcp_server.server.mcp') as mock_mcp:
1908 |         mock_mcp.run.side_effect = KeyboardInterrupt()
1909 |         # Should handle KeyboardInterrupt gracefully
1910 |         main()
1911 | 
1912 | 
1913 | @pytest.mark.asyncio
1914 | async def test_analyze_canary_failures_no_runs(mock_aws_clients):
1915 |     """Test analyze_canary_failures when no runs are found."""
1916 |     from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
1917 | 
1918 |     mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': []}
1919 |     mock_aws_clients['synthetics_client'].get_canary.return_value = {
1920 |         'Canary': {'Name': 'test-canary'}
1921 |     }
1922 | 
1923 |     result = await analyze_canary_failures('test-canary', 'us-east-1')
1924 | 
1925 |     assert 'No run history found for test-canary' in result
1926 | 
1927 | 
1928 | @pytest.mark.asyncio
1929 | async def test_analyze_canary_failures_healthy_canary(mock_aws_clients):
1930 |     """Test analyze_canary_failures with healthy canary."""
1931 |     from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
1932 | 
1933 |     mock_runs = [
1934 |         {
1935 |             'Id': 'run1',
1936 |             'Status': {'State': 'PASSED'},
1937 |             'Timeline': {'Started': '2024-01-01T00:00:00Z'},
1938 |         }
1939 |     ]
1940 | 
1941 |     mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
1942 |     mock_aws_clients['synthetics_client'].get_canary.return_value = {
1943 |         'Canary': {'Name': 'test-canary'}
1944 |     }
1945 | 
1946 |     with patch(
1947 |         'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
1948 |     ) as mock_insights:
1949 |         mock_insights.return_value = 'Telemetry insights available'
1950 | 
1951 |         result = await analyze_canary_failures('test-canary', 'us-east-1')
1952 | 
1953 |         assert 'Canary is healthy - no failures since last success' in result
1954 |         assert '🔍 Comprehensive Failure Analysis for test-canary' in result
1955 | 
1956 | 
1957 | @pytest.mark.asyncio
1958 | async def test_analyze_canary_failures_telemetry_unavailable(mock_aws_clients):
1959 |     """Test analyze_canary_failures when telemetry is unavailable."""
1960 |     from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
1961 | 
1962 |     mock_runs = [
1963 |         {
1964 |             'Id': 'run1',
1965 |             'Status': {'State': 'PASSED'},
1966 |             'Timeline': {'Started': '2024-01-01T00:00:00Z'},
1967 |         }
1968 |     ]
1969 | 
1970 |     mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
1971 |     mock_aws_clients['synthetics_client'].get_canary.return_value = {
1972 |         'Canary': {'Name': 'test-canary'}
1973 |     }
1974 | 
1975 |     with patch(
1976 |         'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
1977 |     ) as mock_insights:
1978 |         mock_insights.side_effect = Exception('Telemetry API error')
1979 | 
1980 |         result = await analyze_canary_failures('test-canary', 'us-east-1')
1981 | 
1982 |         assert 'Telemetry API unavailable: Telemetry API error' in result
1983 | 
1984 | 
1985 | @pytest.mark.asyncio
1986 | async def test_analyze_canary_failures_with_failures(mock_aws_clients):
1987 |     """Test analyze_canary_failures with actual failures."""
1988 |     from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
1989 | 
1990 |     mock_runs = [
1991 |         {
1992 |             'Id': 'failed-run-1',
1993 |             'Status': {'State': 'FAILED', 'StateReason': 'Navigation timeout'},
1994 |             'Timeline': {'Started': '2024-01-01T01:00:00Z'},
1995 |         },
1996 |         {
1997 |             'Id': 'failed-run-2',
1998 |             'Status': {'State': 'FAILED', 'StateReason': 'Navigation timeout'},
1999 |             'Timeline': {'Started': '2024-01-01T00:30:00Z'},
2000 |         },
2001 |         {
2002 |             'Id': 'success-run',
2003 |             'Status': {'State': 'PASSED'},
2004 |             'Timeline': {'Started': '2024-01-01T00:00:00Z'},
2005 |         },
2006 |     ]
2007 | 
2008 |     mock_canary = {
2009 |         'Name': 'test-canary',
2010 |         'ArtifactS3Location': 's3://test-bucket/canary/us-east-1/test-canary',
2011 |     }
2012 | 
2013 |     mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
2014 |     mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
2015 | 
2016 |     # Mock S3 artifacts
2017 |     mock_aws_clients['s3_client'].list_objects_v2.return_value = {
2018 |         'Contents': [
2019 |             {'Key': 'canary/us-east-1/test-canary/2024/01/01/test.har'},
2020 |             {'Key': 'canary/us-east-1/test-canary/2024/01/01/screenshot.png'},
2021 |             {'Key': 'canary/us-east-1/test-canary/2024/01/01/logs.txt'},
2022 |         ]
2023 |     }
2024 | 
2025 |     with patch(
2026 |         'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
2027 |     ) as mock_insights:
2028 |         with patch('awslabs.cloudwatch_appsignals_mcp_server.server.analyze_har_file') as mock_har:
2029 |             with patch(
2030 |                 'awslabs.cloudwatch_appsignals_mcp_server.server.analyze_screenshots'
2031 |             ) as mock_screenshots:
2032 |                 with patch(
2033 |                     'awslabs.cloudwatch_appsignals_mcp_server.server.analyze_log_files'
2034 |                 ) as mock_logs:
2035 |                     mock_insights.return_value = 'Telemetry insights'
2036 |                     mock_har.return_value = {
2037 |                         'failed_requests': 2,
2038 |                         'total_requests': 10,
2039 |                         'request_details': [
2040 |                             {'url': 'https://example.com', 'status': 500, 'time': 1000}
2041 |                         ],
2042 |                     }
2043 |                     mock_screenshots.return_value = {'insights': ['Screenshot analysis']}
2044 |                     mock_logs.return_value = {'insights': ['Log analysis']}
2045 | 
2046 |                     result = await analyze_canary_failures('test-canary', 'us-east-1')
2047 | 
2048 |                     assert 'Found 2 consecutive failures since last success' in result
2049 |                     assert 'All failures have same cause: Navigation timeout' in result
2050 | 
2051 | 
2052 | @pytest.mark.asyncio
2053 | async def test_analyze_canary_failures_iam_analysis(mock_aws_clients):
2054 |     """Test analyze_canary_failures with IAM-related failures."""
2055 |     from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
2056 | 
2057 |     mock_runs = [
2058 |         {
2059 |             'Id': 'failed-run',
2060 |             'Status': {'State': 'FAILED', 'StateReason': 'Access denied'},
2061 |             'Timeline': {'Started': '2024-01-01T00:00:00Z'},
2062 |         }
2063 |     ]
2064 | 
2065 |     mock_canary = {'Name': 'test-canary', 'ArtifactS3Location': ''}
2066 | 
2067 |     mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
2068 |     mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
2069 | 
2070 |     with patch(
2071 |         'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
2072 |     ) as mock_insights:
2073 |         with patch(
2074 |             'awslabs.cloudwatch_appsignals_mcp_server.server.analyze_iam_role_and_policies'
2075 |         ) as mock_iam:
2076 |             with patch(
2077 |                 'awslabs.cloudwatch_appsignals_mcp_server.server.check_resource_arns_correct'
2078 |             ) as mock_arn:
2079 |                 mock_insights.return_value = 'Telemetry insights'
2080 |                 mock_iam.return_value = {
2081 |                     'status': 'issues_found',
2082 |                     'checks': {'role_exists': 'PASS', 'policies_attached': 'FAIL'},
2083 |                     'issues_found': ['Missing S3 permissions'],
2084 |                     'recommendations': ['Add S3 read permissions'],
2085 |                 }
2086 |                 mock_arn.return_value = {
2087 |                     'correct': False,
2088 |                     'error': 'Invalid bucket ARN',
2089 |                     'issues': ['Bucket name mismatch'],
2090 |                 }
2091 | 
2092 |                 result = await analyze_canary_failures('test-canary', 'us-east-1')
2093 | 
2094 |                 assert 'RUNNING COMPREHENSIVE IAM ANALYSIS' in result
2095 |                 assert 'IAM Role Analysis Status: issues_found' in result
2096 |                 assert 'ALL IAM ISSUES FOUND (2 total):' in result
2097 |                 assert 'IAM Policy: Missing S3 permissions' in result
2098 |                 assert 'Resource ARN: Bucket name mismatch' in result
2099 | 
2100 | 
2101 | @pytest.mark.asyncio
2102 | async def test_analyze_canary_failures_enospc_error(mock_aws_clients):
2103 |     """Test analyze_canary_failures with ENOSPC error."""
2104 |     from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
2105 | 
2106 |     mock_runs = [
2107 |         {
2108 |             'Id': 'failed-run',
2109 |             'Status': {'State': 'FAILED', 'StateReason': 'ENOSPC: no space left on device'},
2110 |             'Timeline': {'Started': '2024-01-01T00:00:00Z'},
2111 |         }
2112 |     ]
2113 | 
2114 |     mock_canary = {'Name': 'test-canary', 'ArtifactS3Location': ''}
2115 | 
2116 |     mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
2117 |     mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
2118 | 
2119 |     with patch(
2120 |         'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
2121 |     ) as mock_insights:
2122 |         with patch(
2123 |             'awslabs.cloudwatch_appsignals_mcp_server.server.extract_disk_memory_usage_metrics'
2124 |         ) as mock_metrics:
2125 |             mock_insights.return_value = 'Telemetry insights'
2126 |             mock_metrics.return_value = {
2127 |                 'maxEphemeralStorageUsageInMb': 512.5,
2128 |                 'maxEphemeralStorageUsagePercent': 95.2,
2129 |             }
2130 | 
2131 |             result = await analyze_canary_failures('test-canary', 'us-east-1')
2132 | 
2133 |             assert 'DISK USAGE ROOT CAUSE ANALYSIS:' in result
2134 |             assert 'Storage: 512.5 MB peak' in result
2135 |             assert 'Usage: 95.2% peak' in result
2136 | 
2137 | 
2138 | @pytest.mark.asyncio
2139 | async def test_analyze_canary_failures_protocol_error(mock_aws_clients):
2140 |     """Test analyze_canary_failures with protocol error."""
2141 |     from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
2142 | 
2143 |     mock_runs = [
2144 |         {
2145 |             'Id': 'failed-run',
2146 |             'Status': {
2147 |                 'State': 'FAILED',
2148 |                 'StateReason': 'Protocol error (Target.activateTarget): Session closed',
2149 |             },
2150 |             'Timeline': {'Started': '2024-01-01T00:00:00Z'},
2151 |         }
2152 |     ]
2153 | 
2154 |     mock_canary = {'Name': 'test-canary', 'ArtifactS3Location': ''}
2155 | 
2156 |     mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
2157 |     mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
2158 | 
2159 |     with patch(
2160 |         'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
2161 |     ) as mock_insights:
2162 |         with patch(
2163 |             'awslabs.cloudwatch_appsignals_mcp_server.server.extract_disk_memory_usage_metrics'
2164 |         ) as mock_metrics:
2165 |             mock_insights.return_value = 'Telemetry insights'
2166 |             mock_metrics.return_value = {'maxSyntheticsMemoryUsageInMB': 256.8}
2167 | 
2168 |             result = await analyze_canary_failures('test-canary', 'us-east-1')
2169 | 
2170 |             assert 'MEMORY USAGE ROOT CAUSE ANALYSIS:' in result
2171 |             assert 'Memory: 256.8 MB peak' in result
2172 | 
2173 | 
2174 | @pytest.mark.asyncio
2175 | async def test_analyze_canary_failures_navigation_timeout_with_har(mock_aws_clients):
2176 |     """Test analyze_canary_failures with navigation timeout and HAR analysis."""
2177 |     from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
2178 | 
2179 |     mock_runs = [
2180 |         {
2181 |             'Id': 'failed-run',
2182 |             'Status': {'State': 'FAILED', 'StateReason': 'Navigation timed out after 30000ms'},
2183 |             'Timeline': {'Started': '2024-01-01T00:00:00Z'},
2184 |         }
2185 |     ]
2186 | 
2187 |     mock_canary = {
2188 |         'Name': 'test-canary',
2189 |         'ArtifactS3Location': 's3://test-bucket/canary/us-east-1/test-canary',
2190 |     }
2191 | 
2192 |     mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
2193 |     mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
2194 | 
2195 |     # Mock S3 to return HAR files
2196 |     mock_aws_clients['s3_client'].list_objects_v2.return_value = {
2197 |         'Contents': [
2198 |             {'Key': 'canary/us-east-1/test-canary/2024/01/01/test.har'},
2199 |         ]
2200 |     }
2201 | 
2202 |     with patch(
2203 |         'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
2204 |     ) as mock_insights:
2205 |         with patch('awslabs.cloudwatch_appsignals_mcp_server.server.analyze_har_file') as mock_har:
2206 |             mock_insights.return_value = 'Telemetry insights'
2207 |             mock_har.return_value = {
2208 |                 'failed_requests': 5,
2209 |                 'total_requests': 10,
2210 |                 'request_details': [
2211 |                     {'url': 'https://example.com/slow', 'status': 200, 'time': 5000}
2212 |                 ],
2213 |                 'insights': [
2214 |                     'Slow DNS resolution detected',
2215 |                     'High server response time',
2216 |                     'Network connectivity issues',
2217 |                     'Resource loading delays',
2218 |                     'JavaScript execution timeout',
2219 |                 ],
2220 |             }
2221 | 
2222 |             result = await analyze_canary_failures('test-canary', 'us-east-1')
2223 | 
2224 |             assert '🔍 Comprehensive Failure Analysis for test-canary' in result
2225 |             assert 'Slow DNS resolution detected' in result
2226 | 
2227 | 
2228 | @pytest.mark.asyncio
2229 | async def test_analyze_canary_failures_s3_exception(mock_aws_clients):
2230 |     """Test analyze_canary_failures when S3 operations fail."""
2231 |     from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
2232 | 
2233 |     mock_runs = [
2234 |         {
2235 |             'Id': 'failed-run',
2236 |             'Status': {'State': 'FAILED', 'StateReason': 'Test failure'},
2237 |             'Timeline': {'Started': '2024-01-01T00:00:00Z'},
2238 |         }
2239 |     ]
2240 | 
2241 |     mock_canary = {
2242 |         'Name': 'test-canary',
2243 |         'ArtifactS3Location': 's3://test-bucket/canary/us-east-1/test-canary',
2244 |     }
2245 | 
2246 |     mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
2247 |     mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
2248 | 
2249 |     mock_aws_clients['s3_client'].list_objects_v2.side_effect = Exception('S3 access denied')
2250 | 
2251 |     with patch(
2252 |         'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
2253 |     ) as mock_insights:
2254 |         with patch(
2255 |             'awslabs.cloudwatch_appsignals_mcp_server.server.analyze_canary_logs_with_time_window'
2256 |         ) as mock_logs:
2257 |             mock_insights.return_value = 'Telemetry insights'
2258 |             mock_logs.return_value = {
2259 |                 'status': 'success',
2260 |                 'time_window': '2024-01-01 00:00:00 - 2024-01-01 00:05:00',
2261 |                 'total_events': 5,
2262 |                 'error_events': [
2263 |                     {'timestamp': datetime.now(timezone.utc), 'message': 'Test error message'}
2264 |                 ],
2265 |             }
2266 | 
2267 |             result = await analyze_canary_failures('test-canary', 'us-east-1')
2268 | 
2269 |             # Should fall back to CloudWatch Logs analysis when S3 fails
2270 |             assert '⚠️ Artifacts not available - Checking CloudWatch Logs for root cause' in result
2271 |             assert 'CLOUDWATCH LOGS ANALYSIS' in result
2272 | 
2273 | 
2274 | @pytest.mark.asyncio
2275 | async def test_analyze_canary_failures_visual_variation(mock_aws_clients):
2276 |     """Test analyze_canary_failures with visual variation error."""
2277 |     from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
2278 | 
2279 |     mock_runs = [
2280 |         {
2281 |             'Id': 'failed-run',
2282 |             'Status': {'State': 'FAILED', 'StateReason': 'Visual variation detected'},
2283 |             'Timeline': {'Started': '2024-01-01T00:00:00Z'},
2284 |         }
2285 |     ]
2286 | 
2287 |     mock_canary = {'Name': 'test-canary', 'ArtifactS3Location': ''}
2288 | 
2289 |     mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
2290 |     mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
2291 | 
2292 |     with patch(
2293 |         'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
2294 |     ) as mock_insights:
2295 |         with patch('awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_code') as mock_code:
2296 |             mock_insights.return_value = 'Telemetry insights'
2297 |             mock_code.return_value = {'code_content': 'const synthetics = require("Synthetics");'}
2298 | 
2299 |             result = await analyze_canary_failures('test-canary', 'us-east-1')
2300 | 
2301 |             assert 'VISUAL MONITORING ISSUE DETECTED' in result
2302 |             assert 'Website UI changed - not a technical failure' in result
2303 |             assert 'canary code:' in result
2304 | 
2305 | 
2306 | @pytest.mark.asyncio
2307 | async def test_analyze_canary_failures_get_canary_code_exception(mock_aws_clients):
2308 |     """Test analyze_canary_failures when get_canary_code fails."""
2309 |     from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
2310 | 
2311 |     mock_runs = [
2312 |         {
2313 |             'Id': 'failed-run',
2314 |             'Status': {'State': 'FAILED', 'StateReason': 'Test failure'},
2315 |             'Timeline': {'Started': '2024-01-01T00:00:00Z'},
2316 |         }
2317 |     ]
2318 | 
2319 |     mock_canary = {'Name': 'test-canary', 'ArtifactS3Location': ''}
2320 | 
2321 |     mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
2322 |     mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
2323 | 
2324 |     with patch(
2325 |         'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
2326 |     ) as mock_insights:
2327 |         with patch('awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_code') as mock_code:
2328 |             mock_insights.return_value = 'Telemetry insights'
2329 |             # Make get_canary_code raise an exception
2330 |             mock_code.side_effect = Exception('Code retrieval failed')
2331 | 
2332 |             result = await analyze_canary_failures('test-canary', 'us-east-1')
2333 | 
2334 |             assert 'Note: Could not retrieve canary code: Code retrieval failed' in result
2335 | 
2336 | 
2337 | @pytest.mark.asyncio
2338 | async def test_analyze_canary_failures_iam_analysis_exception(mock_aws_clients):
2339 |     """Test analyze_canary_failures when IAM analysis fails."""
2340 |     from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
2341 | 
2342 |     mock_runs = [
2343 |         {
2344 |             'Id': 'failed-run',
2345 |             'Status': {'State': 'FAILED', 'StateReason': 'Access denied'},
2346 |             'Timeline': {'Started': '2024-01-01T00:00:00Z'},
2347 |         }
2348 |     ]
2349 | 
2350 |     mock_canary = {'Name': 'test-canary', 'ArtifactS3Location': ''}
2351 | 
2352 |     mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
2353 |     mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
2354 | 
2355 |     with patch(
2356 |         'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
2357 |     ) as mock_insights:
2358 |         with patch(
2359 |             'awslabs.cloudwatch_appsignals_mcp_server.server.analyze_iam_role_and_policies'
2360 |         ) as mock_iam:
2361 |             mock_insights.return_value = 'Telemetry insights'
2362 |             # Make IAM analysis raise an exception
2363 |             mock_iam.side_effect = Exception('IAM analysis failed')
2364 | 
2365 |             result = await analyze_canary_failures('test-canary', 'us-east-1')
2366 | 
2367 |             assert '⚠️ IAM analysis failed: IAM analysis failed' in result
2368 | 
2369 | 
2370 | @pytest.mark.asyncio
2371 | async def test_analyze_canary_failures_disk_usage_exception(mock_aws_clients):
2372 |     """Test analyze_canary_failures when disk usage analysis fails."""
2373 |     from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
2374 | 
2375 |     mock_runs = [
2376 |         {
2377 |             'Id': 'failed-run',
2378 |             'Status': {'State': 'FAILED', 'StateReason': 'ENOSPC: no space left on device'},
2379 |             'Timeline': {'Started': '2024-01-01T00:00:00Z'},
2380 |         }
2381 |     ]
2382 | 
2383 |     mock_canary = {'Name': 'test-canary', 'ArtifactS3Location': ''}
2384 | 
2385 |     mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
2386 |     mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
2387 | 
2388 |     with patch(
2389 |         'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
2390 |     ) as mock_insights:
2391 |         with patch(
2392 |             'awslabs.cloudwatch_appsignals_mcp_server.server.extract_disk_memory_usage_metrics'
2393 |         ) as mock_metrics:
2394 |             mock_insights.return_value = 'Telemetry insights'
2395 |             # Make disk usage analysis raise an exception
2396 |             mock_metrics.side_effect = Exception('Disk usage analysis failed')
2397 | 
2398 |             result = await analyze_canary_failures('test-canary', 'us-east-1')
2399 | 
2400 |             assert (
2401 |                 '⚠️ Could not generate disk usage debugging code: Disk usage analysis failed'
2402 |                 in result
2403 |             )
2404 | 
2405 | 
2406 | @pytest.mark.asyncio
2407 | async def test_analyze_canary_failures_memory_usage_exception(mock_aws_clients):
2408 |     """Test analyze_canary_failures when memory usage analysis fails."""
2409 |     from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
2410 | 
2411 |     mock_runs = [
2412 |         {
2413 |             'Id': 'failed-run',
2414 |             'Status': {
2415 |                 'State': 'FAILED',
2416 |                 'StateReason': 'Protocol error (Target.activateTarget): Session closed',
2417 |             },
2418 |             'Timeline': {'Started': '2024-01-01T00:00:00Z'},
2419 |         }
2420 |     ]
2421 | 
2422 |     mock_canary = {'Name': 'test-canary', 'ArtifactS3Location': ''}
2423 | 
2424 |     mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
2425 |     mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
2426 | 
2427 |     with patch(
2428 |         'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
2429 |     ) as mock_insights:
2430 |         with patch(
2431 |             'awslabs.cloudwatch_appsignals_mcp_server.server.extract_disk_memory_usage_metrics'
2432 |         ) as mock_metrics:
2433 |             mock_insights.return_value = 'Telemetry insights'
2434 |             # Make memory usage analysis raise an exception
2435 |             mock_metrics.side_effect = Exception('Memory usage analysis failed')
2436 | 
2437 |             result = await analyze_canary_failures('test-canary', 'us-east-1')
2438 | 
2439 |             assert (
2440 |                 '⚠️ Could not collect memory usage metrics: Memory usage analysis failed' in result
2441 |             )
2442 | 
2443 | 
2444 | @pytest.mark.asyncio
2445 | async def test_analyze_canary_failures_har_timeout_exception(mock_aws_clients):
2446 |     """Test analyze_canary_failures when HAR timeout analysis fails."""
2447 |     from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
2448 | 
2449 |     mock_runs = [
2450 |         {
2451 |             'Id': 'failed-run',
2452 |             'Status': {'State': 'FAILED', 'StateReason': 'Navigation timed out after 30000ms'},
2453 |             'Timeline': {'Started': '2024-01-01T00:00:00Z'},
2454 |         }
2455 |     ]
2456 | 
2457 |     mock_canary = {
2458 |         'Name': 'test-canary',
2459 |         'ArtifactS3Location': 's3://test-bucket/canary/us-east-1/test-canary',
2460 |     }
2461 | 
2462 |     mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
2463 |     mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
2464 | 
2465 |     # Mock S3 to return HAR files
2466 |     mock_aws_clients['s3_client'].list_objects_v2.return_value = {
2467 |         'Contents': [
2468 |             {'Key': 'canary/us-east-1/test-canary/2024/01/01/test.har'},
2469 |         ]
2470 |     }
2471 | 
2472 |     with patch(
2473 |         'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
2474 |     ) as mock_insights:
2475 |         with patch('awslabs.cloudwatch_appsignals_mcp_server.server.analyze_har_file') as mock_har:
2476 |             mock_insights.return_value = 'Telemetry insights'
2477 |             # Make HAR analysis raise an exception
2478 |             mock_har.side_effect = Exception('HAR analysis failed')
2479 | 
2480 |             result = await analyze_canary_failures('test-canary', 'us-east-1')
2481 | 
2482 |             assert '⚠️ HAR analysis failed: HAR analysis failed' in result
2483 | 
2484 | 
2485 | @pytest.mark.asyncio
2486 | async def test_analyze_canary_failures_success_artifacts_exception(mock_aws_clients):
2487 |     """Test analyze_canary_failures when success artifacts retrieval fails."""
2488 |     from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
2489 | 
2490 |     mock_runs = [
2491 |         {
2492 |             'Id': 'failed-run',
2493 |             'Status': {'State': 'FAILED', 'StateReason': 'Test failure'},
2494 |             'Timeline': {'Started': '2024-01-01T00:00:00Z'},
2495 |         },
2496 |         {
2497 |             'Id': 'success-run',
2498 |             'Status': {'State': 'PASSED'},
2499 |             'Timeline': {'Started': '2024-01-01T00:00:00Z'},
2500 |         },
2501 |     ]
2502 | 
2503 |     mock_canary = {
2504 |         'Name': 'test-canary',
2505 |         'ArtifactS3Location': 's3://test-bucket/canary/us-east-1/test-canary',
2506 |     }
2507 | 
2508 |     mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
2509 |     mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
2510 | 
2511 |     # Mock S3 to return failure artifacts but fail on success artifacts
2512 |     def s3_side_effect(*args, **kwargs):
2513 |         prefix = kwargs.get('Prefix', '')
2514 |         if 'success' in prefix or len(prefix.split('/')) > 5:  # Simulate success path failure
2515 |             raise Exception('Success artifacts access failed')
2516 |         return {
2517 |             'Contents': [
2518 |                 {'Key': 'canary/us-east-1/test-canary/2024/01/01/test.har'},
2519 |             ]
2520 |         }
2521 | 
2522 |     mock_aws_clients['s3_client'].list_objects_v2.side_effect = s3_side_effect
2523 | 
2524 |     with patch(
2525 |         'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
2526 |     ) as mock_insights:
2527 |         with patch('awslabs.cloudwatch_appsignals_mcp_server.server.analyze_har_file') as mock_har:
2528 |             mock_insights.return_value = 'Telemetry insights'
2529 |             mock_har.return_value = {
2530 |                 'failed_requests': 2,
2531 |                 'total_requests': 10,
2532 |                 'request_details': [],
2533 |             }
2534 | 
2535 |             result = await analyze_canary_failures('test-canary', 'us-east-1')
2536 | 
2537 |             # Should still process failure artifacts even if success artifacts fail
2538 |             assert '🔍 Comprehensive Failure Analysis for test-canary' in result
2539 | 
2540 | 
2541 | @pytest.mark.asyncio
2542 | async def test_analyze_canary_failures_no_failure_timestamp(mock_aws_clients):
2543 |     """Test analyze_canary_failures when failure has no timestamp."""
2544 |     from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
2545 | 
2546 |     mock_runs = [
2547 |         {
2548 |             'Id': 'failed-run',
2549 |             'Status': {'State': 'FAILED', 'StateReason': 'Test failure'},
2550 |             'Timeline': {},  # No Started timestamp
2551 |         }
2552 |     ]
2553 | 
2554 |     mock_canary = {'Name': 'test-canary', 'ArtifactS3Location': ''}
2555 | 
2556 |     mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
2557 |     mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
2558 | 
2559 |     with patch(
2560 |         'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
2561 |     ) as mock_insights:
2562 |         mock_insights.return_value = 'Telemetry insights'
2563 | 
2564 |         result = await analyze_canary_failures('test-canary', 'us-east-1')
2565 | 
2566 |         assert '📋 No failure timestamp available for targeted log analysis' in result
2567 | 
2568 | 
2569 | @pytest.mark.asyncio
2570 | async def test_analyze_canary_failures_log_analysis_failure(mock_aws_clients):
2571 |     """Test analyze_canary_failures when log analysis fails."""
2572 |     from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
2573 | 
2574 |     mock_runs = [
2575 |         {
2576 |             'Id': 'failed-run',
2577 |             'Status': {'State': 'FAILED', 'StateReason': 'Test failure'},
2578 |             'Timeline': {'Started': '2024-01-01T00:00:00Z'},
2579 |         }
2580 |     ]
2581 | 
2582 |     mock_canary = {'Name': 'test-canary', 'ArtifactS3Location': ''}
2583 | 
2584 |     mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
2585 |     mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
2586 | 
2587 |     with patch(
2588 |         'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
2589 |     ) as mock_insights:
2590 |         with patch(
2591 |             'awslabs.cloudwatch_appsignals_mcp_server.server.analyze_canary_logs_with_time_window'
2592 |         ) as mock_logs:
2593 |             mock_insights.return_value = 'Telemetry insights'
2594 |             mock_logs.return_value = {
2595 |                 'status': 'failed',
2596 |                 'insights': ['Log analysis failed due to missing log group'],
2597 |             }
2598 | 
2599 |             result = await analyze_canary_failures('test-canary', 'us-east-1')
2600 | 
2601 |             assert '📋 Log analysis failed due to missing log group' in result
2602 | 
2603 | 
2604 | @pytest.mark.asyncio
2605 | async def test_analyze_canary_failures_main_exception(mock_aws_clients):
2606 |     """Test analyze_canary_failures when main function fails."""
2607 |     from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
2608 | 
2609 |     # Make get_canary_runs raise an exception
2610 |     mock_aws_clients['synthetics_client'].get_canary_runs.side_effect = Exception('API error')
2611 | 
2612 |     result = await analyze_canary_failures('test-canary', 'us-east-1')
2613 | 
2614 |     assert '❌ Error in comprehensive failure analysis: API error' in result
2615 | 
2616 | 
2617 | @pytest.mark.asyncio
2618 | async def test_analyze_canary_failures_no_har_files_navigation_timeout(mock_aws_clients):
2619 |     """Test analyze_canary_failures navigation timeout without HAR files."""
2620 |     from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
2621 | 
2622 |     mock_runs = [
2623 |         {
2624 |             'Id': 'failed-run',
2625 |             'Status': {'State': 'FAILED', 'StateReason': 'Navigation timed out after 30000ms'},
2626 |             'Timeline': {'Started': '2024-01-01T00:00:00Z'},
2627 |         }
2628 |     ]
2629 | 
2630 |     mock_canary = {'Name': 'test-canary', 'ArtifactS3Location': ''}
2631 | 
2632 |     mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
2633 |     mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
2634 | 
2635 |     with patch(
2636 |         'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
2637 |     ) as mock_insights:
2638 |         mock_insights.return_value = 'Telemetry insights'
2639 | 
2640 |         result = await analyze_canary_failures('test-canary', 'us-east-1')
2641 | 
2642 |         assert 'NAVIGATION TIMEOUT DETECTED:' in result
2643 |         assert 'No HAR files available for detailed analysis' in result
2644 | 
2645 | 
2646 | @pytest.mark.asyncio
2647 | async def test_analyze_canary_failures_artifact_location_without_s3_prefix(mock_aws_clients):
2648 |     """Test analyze_canary_failures with artifact location without s3:// prefix."""
2649 |     from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
2650 | 
2651 |     mock_runs = [
2652 |         {
2653 |             'Id': 'failed-run',
2654 |             'Status': {'State': 'FAILED', 'StateReason': 'Test failure'},
2655 |             'Timeline': {'Started': '2024-01-01T00:00:00Z'},
2656 |         }
2657 |     ]
2658 | 
2659 |     mock_canary = {
2660 |         'Name': 'test-canary',
2661 |         'ArtifactS3Location': 'test-bucket/canary/us-east-1/test-canary',  # No s3:// prefix
2662 |     }
2663 | 
2664 |     mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
2665 |     mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
2666 | 
2667 |     # Mock S3 to return artifacts
2668 |     mock_aws_clients['s3_client'].list_objects_v2.return_value = {
2669 |         'Contents': [
2670 |             {'Key': 'canary/us-east-1/test-canary/2024/01/01/test.har'},
2671 |         ]
2672 |     }
2673 | 
2674 |     with patch(
2675 |         'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
2676 |     ) as mock_insights:
2677 |         with patch('awslabs.cloudwatch_appsignals_mcp_server.server.analyze_har_file') as mock_har:
2678 |             mock_insights.return_value = 'Telemetry insights'
2679 |             mock_har.return_value = {
2680 |                 'failed_requests': 1,
2681 |                 'total_requests': 5,
2682 |                 'request_details': [],
2683 |             }
2684 | 
2685 |             result = await analyze_canary_failures('test-canary', 'us-east-1')
2686 | 
2687 |             # Should still process artifacts even without s3:// prefix
2688 |             assert 'FAILURE ANALYSIS' in result
2689 | 
2690 | 
2691 | @pytest.mark.asyncio
2692 | async def test_analyze_canary_failures_empty_base_path(mock_aws_clients):
2693 |     """Test analyze_canary_failures with empty base path."""
2694 |     from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
2695 | 
2696 |     mock_runs = [
2697 |         {
2698 |             'Id': 'failed-run',
2699 |             'Status': {'State': 'FAILED', 'StateReason': 'Test failure'},
2700 |             'Timeline': {'Started': '2024-01-01T00:00:00Z'},
2701 |         }
2702 |     ]
2703 | 
2704 |     mock_canary = {
2705 |         'Name': 'test-canary',
2706 |         'ArtifactS3Location': 's3://test-bucket',  # Only bucket, no path
2707 |     }
2708 | 
2709 |     mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
2710 |     mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
2711 | 
2712 |     # Mock S3 to return artifacts
2713 |     mock_aws_clients['s3_client'].list_objects_v2.return_value = {
2714 |         'Contents': [
2715 |             {'Key': 'canary/us-east-1/test-canary/2024/01/01/test.har'},
2716 |         ]
2717 |     }
2718 | 
2719 |     with patch(
2720 |         'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
2721 |     ) as mock_insights:
2722 |         with patch('awslabs.cloudwatch_appsignals_mcp_server.server.analyze_har_file') as mock_har:
2723 |             mock_insights.return_value = 'Telemetry insights'
2724 |             mock_har.return_value = {
2725 |                 'failed_requests': 1,
2726 |                 'total_requests': 5,
2727 |                 'request_details': [],
2728 |             }
2729 | 
2730 |             result = await analyze_canary_failures('test-canary', 'us-east-1')
2731 | 
2732 |             # Should construct default canary path when base_path is empty
2733 |             assert 'FAILURE ANALYSIS' in result
2734 | 
2735 | 
2736 | @pytest.mark.asyncio
2737 | async def test_analyze_canary_failures_multiple_failure_causes(mock_aws_clients):
2738 |     """Test analyze_canary_failures with multiple different failure causes."""
2739 |     from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
2740 | 
2741 |     mock_runs = [
2742 |         {
2743 |             'Id': 'failed-run-1',
2744 |             'Status': {'State': 'FAILED', 'StateReason': 'Navigation timeout'},
2745 |             'Timeline': {'Started': '2024-01-01T00:00:00Z'},
2746 |         },
2747 |         {
2748 |             'Id': 'failed-run-2',
2749 |             'Status': {'State': 'FAILED', 'StateReason': 'Access denied'},
2750 |             'Timeline': {'Started': '2024-01-01T00:01:00Z'},
2751 |         },
2752 |         {
2753 |             'Id': 'success-run',
2754 |             'Status': {'State': 'PASSED'},
2755 |             'Timeline': {'Started': '2023-12-31T23:59:00Z'},
2756 |         },
2757 |     ]
2758 | 
2759 |     mock_canary = {'Name': 'test-canary', 'ArtifactS3Location': ''}
2760 | 
2761 |     mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
2762 |     mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
2763 | 
2764 |     with patch(
2765 |         'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
2766 |     ) as mock_insights:
2767 |         mock_insights.return_value = 'Telemetry insights'
2768 | 
2769 |         result = await analyze_canary_failures('test-canary', 'us-east-1')
2770 | 
2771 |         assert 'Multiple failure causes (2 different issues):' in result
2772 |         assert '1. **Navigation timeout** (1 occurrences)' in result
2773 |         assert '2. **Access denied** (1 occurrences)' in result
2774 | 
2775 | 
2776 | @pytest.mark.asyncio
2777 | async def test_analyze_canary_failures_no_failure_time_fallback(mock_aws_clients):
2778 |     """Test analyze_canary_failures fallback when no failure time."""
2779 |     from awslabs.cloudwatch_appsignals_mcp_server.server import analyze_canary_failures
2780 | 
2781 |     mock_runs = [
2782 |         {
2783 |             'Id': 'failed-run',
2784 |             'Status': {'State': 'FAILED', 'StateReason': 'Test failure'},
2785 |             'Timeline': {},  # No Started time
2786 |         }
2787 |     ]
2788 | 
2789 |     mock_canary = {
2790 |         'Name': 'test-canary',
2791 |         'ArtifactS3Location': 's3://test-bucket/canary/us-east-1/test-canary',
2792 |     }
2793 | 
2794 |     mock_aws_clients['synthetics_client'].get_canary_runs.return_value = {'CanaryRuns': mock_runs}
2795 |     mock_aws_clients['synthetics_client'].get_canary.return_value = {'Canary': mock_canary}
2796 | 
2797 |     # Mock S3 to return artifacts
2798 |     mock_aws_clients['s3_client'].list_objects_v2.return_value = {
2799 |         'Contents': [
2800 |             {'Key': 'canary/us-east-1/test-canary/2024/01/01/test.har'},
2801 |         ]
2802 |     }
2803 | 
2804 |     with patch(
2805 |         'awslabs.cloudwatch_appsignals_mcp_server.server.get_canary_metrics_and_service_insights'
2806 |     ) as mock_insights:
2807 |         with patch('awslabs.cloudwatch_appsignals_mcp_server.server.analyze_har_file') as mock_har:
2808 |             mock_insights.return_value = 'Telemetry insights'
2809 |             mock_har.return_value = {
2810 |                 'failed_requests': 1,
2811 |                 'total_requests': 5,
2812 |                 'request_details': [],
2813 |             }
2814 | 
2815 |             result = await analyze_canary_failures('test-canary', 'us-east-1')
2816 | 
2817 |             # Should use current time when no failure time available
2818 |             assert 'FAILURE ANALYSIS' in result
2819 | 
2820 | 
2821 | def test_filter_operation_targets_fault_to_availability():
2822 |     """Test _filter_operation_targets converts Fault to Availability."""
2823 |     provided = [
2824 |         {
2825 |             'Type': 'service_operation',
2826 |             'Data': {
2827 |                 'ServiceOperation': {
2828 |                     'Service': {'Type': 'Service', 'Name': 'test-service'},
2829 |                     'Operation': 'GET /api',
2830 |                     'MetricType': 'Fault',
2831 |                 }
2832 |             },
2833 |         }
2834 |     ]
2835 | 
2836 |     operation_targets, has_wildcards = _filter_operation_targets(provided)
2837 | 
2838 |     # Verify the MetricType was changed from Fault to Availability
2839 |     assert len(operation_targets) == 1
2840 |     assert operation_targets[0]['Data']['ServiceOperation']['MetricType'] == 'Availability'
2841 |     assert has_wildcards is False
2842 | 
2843 | 
2844 | def test_filter_operation_targets_non_fault_unchanged():
2845 |     """Test _filter_operation_targets leaves non-Fault MetricTypes unchanged."""
2846 |     provided = [
2847 |         {
2848 |             'Type': 'service_operation',
2849 |             'Data': {
2850 |                 'ServiceOperation': {
2851 |                     'Service': {'Type': 'Service', 'Name': 'test-service'},
2852 |                     'Operation': 'GET /api',
2853 |                     'MetricType': 'Latency',
2854 |                 }
2855 |             },
2856 |         },
2857 |         {
2858 |             'Type': 'service_operation',
2859 |             'Data': {
2860 |                 'ServiceOperation': {
2861 |                     'Service': {'Type': 'Service', 'Name': 'test-service-2'},
2862 |                     'Operation': 'POST /api',
2863 |                     'MetricType': 'Error',
2864 |                 }
2865 |             },
2866 |         },
2867 |     ]
2868 | 
2869 |     operation_targets, has_wildcards = _filter_operation_targets(provided)
2870 | 
2871 |     # Verify non-Fault MetricTypes are unchanged
2872 |     assert len(operation_targets) == 2
2873 |     assert operation_targets[0]['Data']['ServiceOperation']['MetricType'] == 'Latency'
2874 |     assert operation_targets[1]['Data']['ServiceOperation']['MetricType'] == 'Error'
2875 |     assert has_wildcards is False
2876 | 
2877 | 
2878 | def test_filter_operation_targets_multiple_fault_conversions():
2879 |     """Test _filter_operation_targets converts multiple Fault entries to Availability."""
2880 |     provided = [
2881 |         {
2882 |             'Type': 'service_operation',
2883 |             'Data': {
2884 |                 'ServiceOperation': {
2885 |                     'Service': {'Type': 'Service', 'Name': 'service-1'},
2886 |                     'Operation': 'GET /api',
2887 |                     'MetricType': 'Fault',
2888 |                 }
2889 |             },
2890 |         },
2891 |         {
2892 |             'Type': 'service_operation',
2893 |             'Data': {
2894 |                 'ServiceOperation': {
2895 |                     'Service': {'Type': 'Service', 'Name': 'service-2'},
2896 |                     'Operation': 'POST /api',
2897 |                     'MetricType': 'Latency',
2898 |                 }
2899 |             },
2900 |         },
2901 |         {
2902 |             'Type': 'service_operation',
2903 |             'Data': {
2904 |                 'ServiceOperation': {
2905 |                     'Service': {'Type': 'Service', 'Name': 'service-3'},
2906 |                     'Operation': 'PUT /api',
2907 |                     'MetricType': 'Fault',
2908 |                 }
2909 |             },
2910 |         },
2911 |     ]
2912 | 
2913 |     operation_targets, has_wildcards = _filter_operation_targets(provided)
2914 | 
2915 |     # Verify multiple Fault entries are converted
2916 |     assert len(operation_targets) == 3
2917 |     assert operation_targets[0]['Data']['ServiceOperation']['MetricType'] == 'Availability'
2918 |     assert operation_targets[1]['Data']['ServiceOperation']['MetricType'] == 'Latency'
2919 |     assert operation_targets[2]['Data']['ServiceOperation']['MetricType'] == 'Availability'
2920 |     assert has_wildcards is False
2921 | 
2922 | 
2923 | def test_filter_operation_targets_with_wildcards():
2924 |     """Test _filter_operation_targets detects wildcards and converts Fault to Availability."""
2925 |     provided = [
2926 |         {
2927 |             'Type': 'service_operation',
2928 |             'Data': {
2929 |                 'ServiceOperation': {
2930 |                     'Service': {'Type': 'Service', 'Name': '*payment*'},
2931 |                     'Operation': '*GET*',
2932 |                     'MetricType': 'Fault',
2933 |                 }
2934 |             },
2935 |         }
2936 |     ]
2937 | 
2938 |     operation_targets, has_wildcards = _filter_operation_targets(provided)
2939 | 
2940 |     # Verify wildcard detection and Fault conversion
2941 |     assert len(operation_targets) == 1
2942 |     assert operation_targets[0]['Data']['ServiceOperation']['MetricType'] == 'Availability'
2943 |     assert has_wildcards is True
2944 | 
2945 | 
2946 | def test_filter_operation_targets_ignores_non_service_operation():
2947 |     """Test _filter_operation_targets ignores non-service_operation targets."""
2948 |     provided = [
2949 |         {
2950 |             'Type': 'service',
2951 |             'Data': {'Service': {'Type': 'Service', 'Name': 'test-service'}},
2952 |         },
2953 |         {
2954 |             'Type': 'service_operation',
2955 |             'Data': {
2956 |                 'ServiceOperation': {
2957 |                     'Service': {'Type': 'Service', 'Name': 'test-service'},
2958 |                     'Operation': 'GET /api',
2959 |                     'MetricType': 'Fault',
2960 |                 }
2961 |             },
2962 |         },
2963 |     ]
2964 | 
2965 |     operation_targets, has_wildcards = _filter_operation_targets(provided)
2966 | 
2967 |     # Verify only service_operation targets are included
2968 |     assert len(operation_targets) == 1
2969 |     assert operation_targets[0]['Type'] == 'service_operation'
2970 |     assert operation_targets[0]['Data']['ServiceOperation']['MetricType'] == 'Availability'
2971 |     assert has_wildcards is False
2972 | 
2973 | 
2974 | def test_filter_operation_targets_empty_metric_type():
2975 |     """Test _filter_operation_targets handles empty MetricType gracefully."""
2976 |     provided = [
2977 |         {
2978 |             'Type': 'service_operation',
2979 |             'Data': {
2980 |                 'ServiceOperation': {
2981 |                     'Service': {'Type': 'Service', 'Name': 'test-service'},
2982 |                     'Operation': 'GET /api',
2983 |                     'MetricType': '',
2984 |                 }
2985 |             },
2986 |         }
2987 |     ]
2988 | 
2989 |     operation_targets, has_wildcards = _filter_operation_targets(provided)
2990 | 
2991 |     # Verify empty MetricType is unchanged
2992 |     assert len(operation_targets) == 1
2993 |     assert operation_targets[0]['Data']['ServiceOperation']['MetricType'] == ''
2994 |     assert has_wildcards is False
2995 | 
2996 | 
2997 | def test_filter_operation_targets_missing_metric_type():
2998 |     """Test _filter_operation_targets handles missing MetricType gracefully."""
2999 |     provided = [
3000 |         {
3001 |             'Type': 'service_operation',
3002 |             'Data': {
3003 |                 'ServiceOperation': {
3004 |                     'Service': {'Type': 'Service', 'Name': 'test-service'},
3005 |                     'Operation': 'GET /api',
3006 |                     # MetricType is missing
3007 |                 }
3008 |             },
3009 |         }
3010 |     ]
3011 | 
3012 |     operation_targets, has_wildcards = _filter_operation_targets(provided)
3013 | 
3014 |     # Verify missing MetricType doesn't cause errors
3015 |     assert len(operation_targets) == 1
3016 |     # MetricType should remain missing (empty string from .get())
3017 |     assert operation_targets[0]['Data']['ServiceOperation'].get('MetricType', '') == ''
3018 |     assert has_wildcards is False
3019 | 
3020 | 
3021 | def test_filter_operation_targets_case_sensitive():
3022 |     """Test _filter_operation_targets is case-sensitive for Fault conversion."""
3023 |     provided = [
3024 |         {
3025 |             'Type': 'service_operation',
3026 |             'Data': {
3027 |                 'ServiceOperation': {
3028 |                     'Service': {'Type': 'Service', 'Name': 'test-service'},
3029 |                     'Operation': 'GET /api',
3030 |                     'MetricType': 'fault',  # lowercase
3031 |                 }
3032 |             },
3033 |         },
3034 |         {
3035 |             'Type': 'service_operation',
3036 |             'Data': {
3037 |                 'ServiceOperation': {
3038 |                     'Service': {'Type': 'Service', 'Name': 'test-service-2'},
3039 |                     'Operation': 'POST /api',
3040 |                     'MetricType': 'FAULT',  # uppercase
3041 |                 }
3042 |             },
3043 |         },
3044 |     ]
3045 | 
3046 |     operation_targets, has_wildcards = _filter_operation_targets(provided)
3047 | 
3048 |     # Verify only exact case "Fault" is converted
3049 |     assert len(operation_targets) == 2
3050 |     assert operation_targets[0]['Data']['ServiceOperation']['MetricType'] == 'fault'  # unchanged
3051 |     assert operation_targets[1]['Data']['ServiceOperation']['MetricType'] == 'FAULT'  # unchanged
3052 |     assert has_wildcards is False
3053 | 
```
Page 500/503FirstPrevNextLast