This is page 507 of 522. 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
├── scripts
│ ├── README.md
│ └── verify_package_name.py
├── 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
│ │ │ ├── models.py
│ │ │ ├── prompts
│ │ │ │ ├── __init__.py
│ │ │ │ ├── asset_hierarchy.py
│ │ │ │ ├── bulk_import_workflow.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
│ │ │ │ └── sitewise_metadata_transfer.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_client.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_models.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_sitewise_metadata_transfer.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
│ │ │ │ ├── poller
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── esm_diagnosis.py
│ │ │ │ │ ├── esm_guidance.py
│ │ │ │ │ └── esm_recommend.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_esm_diagnosis.py
│ │ │ ├── test_esm_guidance.py
│ │ │ ├── test_esm_recommend.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
│ │ │ │ ├── bcm_pricing_calculator_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_bcm_pricing_calculator_tools.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/aws-dataprocessing-mcp-server/tests/handlers/commons/test_common_resource_handler.py:
--------------------------------------------------------------------------------
```python
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | import json
16 | import pytest
17 | from awslabs.aws_dataprocessing_mcp_server.handlers.commons.common_resource_handler import (
18 | CommonResourceHandler,
19 | )
20 | from botocore.exceptions import ClientError
21 | from datetime import datetime, timedelta
22 | from mcp.server.fastmcp import Context
23 | from typing import Type
24 | from unittest.mock import Mock, patch
25 |
26 |
27 | class Exceptions:
28 | """Mock exceptions class for IAM client testing."""
29 |
30 | class NoSuchEntityException(ClientError):
31 | """Mock NoSuchEntityException for testing IAM client exceptions."""
32 |
33 | def __init__(self):
34 | """Initialize the NoSuchEntityException with appropriate error response."""
35 | operation_name = 'GetRolePolicy'
36 | error_response = {
37 | 'Error': {'Code': 'NoSuchEntity', 'Message': 'Role policy not found'}
38 | }
39 | super().__init__(error_response, operation_name)
40 |
41 |
42 | class MockIAMClient(Mock):
43 | """Mock IAM client for testing with exception handling capabilities."""
44 |
45 | exceptions: Type[Exceptions]
46 |
47 | def __init__(self, *args, **kwargs):
48 | """Initialize the MockIAMClient with exceptions property."""
49 | super().__init__(*args, **kwargs)
50 | # Set up exceptions as a property
51 | self.exceptions = Exceptions
52 |
53 |
54 | @pytest.fixture
55 | def mock_iam_client():
56 | """Create a mock IAM client instance for testing."""
57 | return Mock()
58 |
59 |
60 | @pytest.fixture
61 | def mock_s3_client():
62 | """Create a mock S3 client instance for testing."""
63 | return Mock()
64 |
65 |
66 | @pytest.fixture
67 | def mock_aws_helper():
68 | """Create a mock AwsHelper instance for testing."""
69 | with patch(
70 | 'awslabs.aws_dataprocessing_mcp_server.handlers.commons.common_resource_handler.AwsHelper'
71 | ) as mock:
72 | mock.create_boto3_client.return_value = Mock()
73 | yield mock
74 |
75 |
76 | @pytest.fixture
77 | def handler(mock_aws_helper):
78 | """Create a mock CommonResourceHandler instance for testing."""
79 | mcp = Mock()
80 | return CommonResourceHandler(mcp, allow_write=True)
81 |
82 |
83 | @pytest.fixture
84 | def read_only_handler(mock_aws_helper):
85 | """Create a mock CommonResourceHandler instance with read-only access for testing."""
86 | mcp = Mock()
87 | return CommonResourceHandler(mcp, allow_write=False)
88 |
89 |
90 | @pytest.fixture
91 | def mock_context():
92 | """Create a mock context instance for testing."""
93 | return Mock(spec=Context)
94 |
95 |
96 | # ============================================================================
97 | # IAM Operations Tests
98 | # ============================================================================
99 |
100 |
101 | @pytest.mark.asyncio
102 | async def test_get_policies_for_role_success(handler, mock_iam_client):
103 | """Test successful retrieval of policies for a role."""
104 | handler.iam_client = mock_iam_client
105 |
106 | # Mock role response
107 | mock_iam_client.get_role.return_value = {
108 | 'Role': {
109 | 'Arn': 'arn:aws:iam::123456789012:role/test-role',
110 | 'AssumeRolePolicyDocument': {
111 | 'Version': '2012-10-17',
112 | 'Statement': [
113 | {
114 | 'Effect': 'Allow',
115 | 'Principal': {'Service': 'glue.amazonaws.com'},
116 | 'Action': 'sts:AssumeRole',
117 | }
118 | ],
119 | },
120 | 'Description': 'Test role description',
121 | }
122 | }
123 |
124 | # Mock managed policies
125 | mock_iam_client.list_attached_role_policies.return_value = {
126 | 'AttachedPolicies': [
127 | {
128 | 'PolicyName': 'TestManagedPolicy',
129 | 'PolicyArn': 'arn:aws:iam::aws:policy/TestManagedPolicy',
130 | }
131 | ]
132 | }
133 |
134 | mock_iam_client.get_policy.return_value = {
135 | 'Policy': {'DefaultVersionId': 'v1', 'Description': 'Test managed policy'}
136 | }
137 |
138 | mock_iam_client.get_policy_version.return_value = {
139 | 'PolicyVersion': {
140 | 'Document': {
141 | 'Version': '2012-10-17',
142 | 'Statement': [{'Effect': 'Allow', 'Action': 's3:GetObject', 'Resource': '*'}],
143 | }
144 | }
145 | }
146 |
147 | # Mock inline policies
148 | mock_iam_client.list_role_policies.return_value = {'PolicyNames': ['TestInlinePolicy']}
149 |
150 | mock_iam_client.get_role_policy.return_value = {
151 | 'PolicyDocument': {
152 | 'Version': '2012-10-17',
153 | 'Statement': [{'Effect': 'Allow', 'Action': 's3:PutObject', 'Resource': '*'}],
154 | }
155 | }
156 |
157 | ctx = Mock()
158 | response = await handler.get_policies_for_role(ctx, role_name='test-role')
159 |
160 | assert not response.isError
161 | assert response.role_arn == 'arn:aws:iam::123456789012:role/test-role'
162 | assert response.description == 'Test role description'
163 | assert len(response.managed_policies) == 1
164 | assert len(response.inline_policies) == 1
165 | assert response.managed_policies[0].policy_type == 'Managed'
166 | assert response.inline_policies[0].policy_type == 'Inline'
167 |
168 |
169 | @pytest.mark.asyncio
170 | async def test_get_policies_for_role_with_string_assume_role_policy(handler, mock_iam_client):
171 | """Test retrieval of policies for a role with string assume role policy document."""
172 | handler.iam_client = mock_iam_client
173 |
174 | # Mock role response with string assume role policy document
175 | assume_role_policy_str = json.dumps(
176 | {
177 | 'Version': '2012-10-17',
178 | 'Statement': [
179 | {
180 | 'Effect': 'Allow',
181 | 'Principal': {'Service': 'glue.amazonaws.com'},
182 | 'Action': 'sts:AssumeRole',
183 | }
184 | ],
185 | }
186 | )
187 |
188 | mock_iam_client.get_role.return_value = {
189 | 'Role': {
190 | 'Arn': 'arn:aws:iam::123456789012:role/test-role',
191 | 'AssumeRolePolicyDocument': assume_role_policy_str,
192 | 'Description': 'Test role description',
193 | }
194 | }
195 |
196 | # Mock empty policies
197 | mock_iam_client.list_attached_role_policies.return_value = {'AttachedPolicies': []}
198 | mock_iam_client.list_role_policies.return_value = {'PolicyNames': []}
199 |
200 | ctx = Mock()
201 | response = await handler.get_policies_for_role(ctx, role_name='test-role')
202 |
203 | assert not response.isError
204 | assert response.assume_role_policy_document['Version'] == '2012-10-17'
205 | assert len(response.assume_role_policy_document['Statement']) == 1
206 |
207 |
208 | @pytest.mark.asyncio
209 | async def test_get_policies_for_role_error_handling(handler, mock_iam_client):
210 | """Test error handling when getting policies for a role fails."""
211 | handler.iam_client = mock_iam_client
212 | mock_iam_client.get_role.side_effect = ClientError(
213 | {'Error': {'Code': 'NoSuchEntity', 'Message': 'Role not found'}}, 'GetRole'
214 | )
215 |
216 | ctx = Mock()
217 | response = await handler.get_policies_for_role(ctx, role_name='nonexistent-role')
218 |
219 | assert response.isError
220 | assert 'Failed to describe IAM role' in response.content[0].text
221 | assert response.role_arn == ''
222 |
223 |
224 | @pytest.mark.asyncio
225 | async def test_add_inline_policy_success(handler):
226 | """Test successful addition of an inline policy."""
227 | mock_iam_local_client = MockIAMClient()
228 | mock_iam_local_client.get_role_policy.side_effect = (
229 | mock_iam_local_client.exceptions.NoSuchEntityException()
230 | )
231 | handler.iam_client = mock_iam_local_client
232 |
233 | permissions = {
234 | 'Effect': 'Allow',
235 | 'Action': ['s3:GetObject', 's3:PutObject'],
236 | 'Resource': 'arn:aws:s3:::test-bucket/*',
237 | }
238 |
239 | ctx = Mock()
240 | response = await handler.add_inline_policy(
241 | ctx, policy_name='test-policy', role_name='test-role', permissions=permissions
242 | )
243 |
244 | assert not response.isError
245 | assert response.policy_name == 'test-policy'
246 | assert response.role_name == 'test-role'
247 | assert response.permissions_added == permissions
248 | mock_iam_local_client.put_role_policy.assert_called_once()
249 |
250 |
251 | @pytest.mark.asyncio
252 | async def test_add_inline_policy_with_list_permissions(handler):
253 | """Test successful addition of an inline policy with list of permissions."""
254 | mock_iam_local_client = MockIAMClient()
255 | mock_iam_local_client.get_role_policy.side_effect = (
256 | mock_iam_local_client.exceptions.NoSuchEntityException()
257 | )
258 | mock_iam_local_client.put_role_policy.return_value = {
259 | 'ResponseMetadata': {
260 | 'test': 'dummy',
261 | },
262 | }
263 | handler.iam_client = mock_iam_local_client
264 |
265 | permissions = [
266 | {'Effect': 'Allow', 'Action': ['s3:GetObject'], 'Resource': 'arn:aws:s3:::test-bucket/*'},
267 | {'Effect': 'Allow', 'Action': ['s3:PutObject'], 'Resource': 'arn:aws:s3:::test-bucket/*'},
268 | ]
269 |
270 | ctx = Mock()
271 | response = await handler.add_inline_policy(
272 | ctx, policy_name='test-policy', role_name='test-role', permissions=permissions
273 | )
274 |
275 | assert not response.isError
276 | assert response.policy_name == 'test-policy'
277 | assert response.role_name == 'test-role'
278 | assert response.permissions_added == permissions
279 |
280 |
281 | @pytest.mark.asyncio
282 | async def test_add_inline_policy_without_write_permission(read_only_handler):
283 | """Test that adding inline policy fails when write access is disabled."""
284 | ctx = Mock()
285 | response = await read_only_handler.add_inline_policy(
286 | ctx,
287 | policy_name='test-policy',
288 | role_name='test-role',
289 | permissions={'Effect': 'Allow', 'Action': 's3:GetObject', 'Resource': '*'},
290 | )
291 |
292 | assert response.isError
293 | assert 'requires --allow-write flag' in response.content[0].text
294 |
295 |
296 | @pytest.mark.asyncio
297 | async def test_add_inline_policy_already_exists(handler, mock_iam_client):
298 | """Test that adding inline policy fails when policy already exists."""
299 | handler.iam_client = mock_iam_client
300 |
301 | # Mock that policy already exists
302 | mock_iam_client.get_role_policy.return_value = {
303 | 'PolicyDocument': {'Version': '2012-10-17', 'Statement': []}
304 | }
305 |
306 | ctx = Mock()
307 | response = await handler.add_inline_policy(
308 | ctx,
309 | policy_name='existing-policy',
310 | role_name='test-role',
311 | permissions={'Effect': 'Allow', 'Action': 's3:GetObject', 'Resource': '*'},
312 | )
313 |
314 | assert response.isError
315 | assert 'already exists' in response.content[0].text
316 |
317 |
318 | @pytest.mark.asyncio
319 | async def test_create_data_processing_role_glue_success(handler, mock_iam_client):
320 | """Test successful creation of a Glue data processing role."""
321 | handler.iam_client = mock_iam_client
322 |
323 | mock_iam_client.create_role.return_value = {
324 | 'Role': {'Arn': 'arn:aws:iam::123456789012:role/test-glue-role'}
325 | }
326 |
327 | ctx = Mock()
328 | response = await handler.create_data_processing_role(
329 | ctx,
330 | role_name='test-glue-role',
331 | service_type='glue',
332 | description='Test Glue role',
333 | managed_policy_arns=['arn:aws:iam::aws:policy/service-role/AWSGlueServiceRole'],
334 | )
335 |
336 | assert not response.isError
337 | assert response.role_name == 'test-glue-role'
338 | assert response.role_arn == 'arn:aws:iam::123456789012:role/test-glue-role'
339 |
340 | # Verify create_role was called with correct trust relationship
341 | create_role_call = mock_iam_client.create_role.call_args
342 | assume_role_policy = json.loads(create_role_call[1]['AssumeRolePolicyDocument'])
343 | assert assume_role_policy['Statement'][0]['Principal']['Service'] == 'glue.amazonaws.com'
344 |
345 | # Verify managed policy was attached
346 | mock_iam_client.attach_role_policy.assert_called_once_with(
347 | RoleName='test-glue-role',
348 | PolicyArn='arn:aws:iam::aws:policy/service-role/AWSGlueServiceRole',
349 | )
350 |
351 |
352 | @pytest.mark.asyncio
353 | async def test_create_data_processing_role_emr_success(handler, mock_iam_client):
354 | """Test successful creation of an EMR data processing role."""
355 | handler.iam_client = mock_iam_client
356 |
357 | mock_iam_client.create_role.return_value = {
358 | 'Role': {'Arn': 'arn:aws:iam::123456789012:role/test-emr-role'}
359 | }
360 |
361 | ctx = Mock()
362 | response = await handler.create_data_processing_role(
363 | ctx, role_name='test-emr-role', service_type='emr'
364 | )
365 |
366 | assert not response.isError
367 | assert response.role_name == 'test-emr-role'
368 |
369 | # Verify create_role was called with correct trust relationship
370 | create_role_call = mock_iam_client.create_role.call_args
371 | assume_role_policy = json.loads(create_role_call[1]['AssumeRolePolicyDocument'])
372 | assert (
373 | assume_role_policy['Statement'][0]['Principal']['Service']
374 | == 'elasticmapreduce.amazonaws.com'
375 | )
376 |
377 |
378 | @pytest.mark.asyncio
379 | async def test_create_data_processing_role_athena_success(handler, mock_iam_client):
380 | """Test successful creation of an Athena data processing role."""
381 | handler.iam_client = mock_iam_client
382 |
383 | mock_iam_client.create_role.return_value = {
384 | 'Role': {'Arn': 'arn:aws:iam::123456789012:role/test-athena-role'}
385 | }
386 |
387 | ctx = Mock()
388 | response = await handler.create_data_processing_role(
389 | ctx, role_name='test-athena-role', service_type='athena'
390 | )
391 |
392 | assert not response.isError
393 | assert response.role_name == 'test-athena-role'
394 |
395 | # Verify create_role was called with correct trust relationship
396 | create_role_call = mock_iam_client.create_role.call_args
397 | assume_role_policy = json.loads(create_role_call[1]['AssumeRolePolicyDocument'])
398 | assert assume_role_policy['Statement'][0]['Principal']['Service'] == 'athena.amazonaws.com'
399 |
400 |
401 | @pytest.mark.asyncio
402 | async def test_create_data_processing_role_with_inline_policy(handler, mock_iam_client):
403 | """Test successful creation of a role with inline policy."""
404 | handler.iam_client = mock_iam_client
405 |
406 | mock_iam_client.create_role.return_value = {
407 | 'Role': {'Arn': 'arn:aws:iam::123456789012:role/test-role'}
408 | }
409 |
410 | inline_policy = {
411 | 'Effect': 'Allow',
412 | 'Action': ['s3:GetObject'],
413 | 'Resource': 'arn:aws:s3:::test-bucket/*',
414 | }
415 |
416 | ctx = Mock()
417 | response = await handler.create_data_processing_role(
418 | ctx, role_name='test-role', service_type='glue', inline_policy=inline_policy
419 | )
420 |
421 | assert not response.isError
422 |
423 | # Verify inline policy was added
424 | mock_iam_client.put_role_policy.assert_called_once()
425 | put_policy_call = mock_iam_client.put_role_policy.call_args
426 | policy_document = json.loads(put_policy_call[1]['PolicyDocument'])
427 | assert policy_document['Statement'][0]['Action'] == ['s3:GetObject']
428 |
429 |
430 | @pytest.mark.asyncio
431 | async def test_create_data_processing_role_invalid_service_type(handler):
432 | """Test that creating role fails with invalid service type."""
433 | ctx = Mock()
434 | response = await handler.create_data_processing_role(
435 | ctx, role_name='test-role', service_type='invalid-service'
436 | )
437 |
438 | assert response.isError
439 | assert 'Invalid service type' in response.content[0].text
440 |
441 |
442 | @pytest.mark.asyncio
443 | async def test_create_data_processing_role_without_write_permission(read_only_handler):
444 | """Test that creating role fails when write access is disabled."""
445 | ctx = Mock()
446 | response = await read_only_handler.create_data_processing_role(
447 | ctx, role_name='test-role', service_type='glue'
448 | )
449 |
450 | assert response.isError
451 | assert 'requires --allow-write flag' in response.content[0].text
452 |
453 |
454 | @pytest.mark.asyncio
455 | async def test_get_roles_for_service_success(handler, mock_iam_client):
456 | """Test successful retrieval of roles for a service."""
457 | handler.iam_client = mock_iam_client
458 |
459 | # Mock paginator
460 | mock_paginator = Mock()
461 | mock_iam_client.get_paginator.return_value = mock_paginator
462 |
463 | # Mock role data
464 | mock_paginator.paginate.return_value = [
465 | {
466 | 'Roles': [
467 | {
468 | 'RoleName': 'glue-role-1',
469 | 'Arn': 'arn:aws:iam::123456789012:role/glue-role-1',
470 | 'Description': 'Glue role 1',
471 | 'CreateDate': datetime(2023, 1, 1),
472 | 'AssumeRolePolicyDocument': {
473 | 'Version': '2012-10-17',
474 | 'Statement': [
475 | {
476 | 'Effect': 'Allow',
477 | 'Principal': {'Service': 'glue.amazonaws.com'},
478 | 'Action': 'sts:AssumeRole',
479 | }
480 | ],
481 | },
482 | },
483 | {
484 | 'RoleName': 'emr-role-1',
485 | 'Arn': 'arn:aws:iam::123456789012:role/emr-role-1',
486 | 'CreateDate': datetime(2023, 1, 2),
487 | 'AssumeRolePolicyDocument': {
488 | 'Version': '2012-10-17',
489 | 'Statement': [
490 | {
491 | 'Effect': 'Allow',
492 | 'Principal': {'Service': 'elasticmapreduce.amazonaws.com'},
493 | 'Action': 'sts:AssumeRole',
494 | }
495 | ],
496 | },
497 | },
498 | ]
499 | }
500 | ]
501 |
502 | ctx = Mock()
503 | response = await handler.get_roles_for_service(ctx, service_type='glue')
504 |
505 | assert not response.isError
506 | assert response.service_type == 'glue'
507 | assert len(response.roles) == 1 # Only the Glue role should be returned
508 | assert response.roles[0].role_name == 'glue-role-1'
509 |
510 |
511 | @pytest.mark.asyncio
512 | async def test_get_roles_for_service_with_string_assume_role_policy(handler, mock_iam_client):
513 | """Test retrieval of roles for a service with string assume role policy document."""
514 | handler.iam_client = mock_iam_client
515 |
516 | # Mock paginator
517 | mock_paginator = Mock()
518 | mock_iam_client.get_paginator.return_value = mock_paginator
519 |
520 | # Mock role data with string assume role policy document
521 | assume_role_policy_str = json.dumps(
522 | {
523 | 'Version': '2012-10-17',
524 | 'Statement': [
525 | {
526 | 'Effect': 'Allow',
527 | 'Principal': {'Service': 'glue.amazonaws.com'},
528 | 'Action': 'sts:AssumeRole',
529 | }
530 | ],
531 | }
532 | )
533 |
534 | mock_paginator.paginate.return_value = [
535 | {
536 | 'Roles': [
537 | {
538 | 'RoleName': 'glue-role-1',
539 | 'Arn': 'arn:aws:iam::123456789012:role/glue-role-1',
540 | 'CreateDate': datetime(2023, 1, 1),
541 | 'AssumeRolePolicyDocument': assume_role_policy_str,
542 | }
543 | ]
544 | }
545 | ]
546 |
547 | ctx = Mock()
548 | response = await handler.get_roles_for_service(ctx, service_type='glue')
549 |
550 | assert not response.isError
551 | assert len(response.roles) == 1
552 | assert response.roles[0].role_name == 'glue-role-1'
553 |
554 |
555 | @pytest.mark.asyncio
556 | async def test_get_roles_for_service_error_handling(handler, mock_iam_client):
557 | """Test error handling when getting roles for a service fails."""
558 | handler.iam_client = mock_iam_client
559 | mock_iam_client.get_paginator.side_effect = ClientError(
560 | {'Error': {'Code': 'AccessDenied', 'Message': 'Access denied'}}, 'ListRoles'
561 | )
562 |
563 | ctx = Mock()
564 | response = await handler.get_roles_for_service(ctx, service_type='glue')
565 |
566 | assert response.isError
567 | assert 'Failed to list IAM roles' in response.content[0].text
568 |
569 |
570 | # ============================================================================
571 | # S3 Operations Tests
572 | # ============================================================================
573 |
574 |
575 | @pytest.mark.asyncio
576 | async def test_list_s3_buckets_success(handler, mock_s3_client):
577 | """Test successful listing of S3 buckets."""
578 | handler.s3_client = mock_s3_client
579 |
580 | # Mock list_buckets response
581 | mock_s3_client.list_buckets.return_value = {
582 | 'Buckets': [
583 | {'Name': 'test-glue-bucket', 'CreationDate': datetime(2023, 1, 1)},
584 | {'Name': 'other-bucket', 'CreationDate': datetime(2023, 1, 2)},
585 | ]
586 | }
587 |
588 | # Mock bucket location
589 | mock_s3_client.get_bucket_location.return_value = {'LocationConstraint': 'us-east-1'}
590 |
591 | # Mock list_objects_v2 response
592 | mock_s3_client.list_objects_v2.return_value = {
593 | 'KeyCount': 5,
594 | 'Contents': [{'LastModified': datetime(2023, 6, 1)}],
595 | }
596 |
597 | ctx = Mock()
598 | response = await handler.list_s3_buckets(ctx, region='us-east-1')
599 |
600 | assert not response.isError
601 | assert response.region == 'us-east-1'
602 | assert response.bucket_count == 1 # Only the bucket with 'glue' in name
603 | assert len(response.buckets) == 1
604 | assert response.buckets[0].name == 'test-glue-bucket'
605 |
606 |
607 | @pytest.mark.asyncio
608 | async def test_list_s3_buckets_with_environment_region(handler, mock_s3_client):
609 | """Test listing S3 buckets using environment region."""
610 | handler.s3_client = mock_s3_client
611 |
612 | with patch('os.getenv', return_value='us-west-2'):
613 | mock_s3_client.list_buckets.return_value = {'Buckets': []}
614 |
615 | ctx = Mock()
616 | response = await handler.list_s3_buckets(ctx)
617 |
618 | assert not response.isError
619 | assert response.region == 'us-west-2'
620 |
621 |
622 | @pytest.mark.asyncio
623 | async def test_list_s3_buckets_error_handling(handler, mock_s3_client):
624 | """Test error handling when listing S3 buckets fails."""
625 | handler.s3_client = mock_s3_client
626 | mock_s3_client.list_buckets.side_effect = ClientError(
627 | {'Error': {'Code': 'AccessDenied', 'Message': 'Access denied'}}, 'ListBuckets'
628 | )
629 |
630 | ctx = Mock()
631 | response = await handler.list_s3_buckets(ctx)
632 |
633 | assert response.isError
634 | assert 'AWS Error' in response.content[0].text
635 |
636 |
637 | @pytest.mark.asyncio
638 | async def test_analyze_s3_usage_glue_connections_error(handler, mock_s3_client):
639 | """Test error handling when Glue connections check fails in analyze_s3_usage_for_data_processing."""
640 | handler.s3_client = mock_s3_client
641 |
642 | # Mock list_buckets response
643 | mock_s3_client.list_buckets.return_value = {'Buckets': [{'Name': 'test-bucket'}]}
644 |
645 | # Mock list_objects_v2 response
646 | mock_s3_client.list_objects_v2.return_value = {'KeyCount': 0}
647 |
648 | # Mock AWS service clients
649 | with patch(
650 | 'awslabs.aws_dataprocessing_mcp_server.handlers.commons.common_resource_handler.AwsHelper'
651 | ) as mock_aws_helper:
652 | mock_glue_client = Mock()
653 | mock_athena_client = Mock()
654 | mock_emr_client = Mock()
655 |
656 | mock_aws_helper.create_boto3_client.side_effect = lambda service: {
657 | 'glue': mock_glue_client,
658 | 'athena': mock_athena_client,
659 | 'emr': mock_emr_client,
660 | }[service]
661 |
662 | # Mock Glue connections to raise an exception
663 | mock_glue_client.get_connections.side_effect = ClientError(
664 | {'Error': {'Code': 'AccessDenied', 'Message': 'Access denied'}}, 'GetConnections'
665 | )
666 |
667 | # Mock other service responses
668 | mock_athena_client.list_work_groups.return_value = {'WorkGroups': []}
669 | mock_emr_client.list_clusters.return_value = {'Clusters': []}
670 |
671 | ctx = Mock()
672 | response = await handler.analyze_s3_usage_for_data_processing(ctx)
673 |
674 | assert not response.isError
675 | # Should still return results but with error details in the text
676 | assert 'Error checking Glue usage' in response.analysis_summary
677 |
678 |
679 | @pytest.mark.asyncio
680 | async def test_analyze_s3_usage_athena_workgroups_error(handler, mock_s3_client):
681 | """Test error handling when Athena workgroups check fails in analyze_s3_usage_for_data_processing."""
682 | handler.s3_client = mock_s3_client
683 |
684 | # Mock list_buckets response
685 | mock_s3_client.list_buckets.return_value = {'Buckets': [{'Name': 'test-bucket'}]}
686 |
687 | # Mock list_objects_v2 response
688 | mock_s3_client.list_objects_v2.return_value = {'KeyCount': 0}
689 |
690 | # Mock AWS service clients
691 | with patch(
692 | 'awslabs.aws_dataprocessing_mcp_server.handlers.commons.common_resource_handler.AwsHelper'
693 | ) as mock_aws_helper:
694 | mock_glue_client = Mock()
695 | mock_athena_client = Mock()
696 | mock_emr_client = Mock()
697 |
698 | mock_aws_helper.create_boto3_client.side_effect = lambda service: {
699 | 'glue': mock_glue_client,
700 | 'athena': mock_athena_client,
701 | 'emr': mock_emr_client,
702 | }[service]
703 |
704 | # Mock service responses
705 | mock_glue_client.get_connections.return_value = {'ConnectionList': []}
706 | mock_glue_client.get_crawlers.return_value = {'Crawlers': []}
707 | mock_glue_client.get_jobs.return_value = {'Jobs': []}
708 |
709 | # Mock Athena list_work_groups to raise an exception
710 | mock_athena_client.list_work_groups.side_effect = ClientError(
711 | {'Error': {'Code': 'AccessDenied', 'Message': 'Access denied'}}, 'ListWorkGroups'
712 | )
713 |
714 | mock_emr_client.list_clusters.return_value = {'Clusters': []}
715 |
716 | ctx = Mock()
717 | response = await handler.analyze_s3_usage_for_data_processing(ctx)
718 |
719 | assert not response.isError
720 | # Should still return results but with error details in the text
721 | assert 'Error checking Athena usage' in response.analysis_summary
722 |
723 |
724 | @pytest.mark.asyncio
725 | async def test_analyze_s3_usage_athena_workgroup_details_error(handler, mock_s3_client):
726 | """Test error handling when getting Athena workgroup details fails in analyze_s3_usage_for_data_processing."""
727 | handler.s3_client = mock_s3_client
728 |
729 | # Mock list_buckets response
730 | mock_s3_client.list_buckets.return_value = {'Buckets': [{'Name': 'test-bucket'}]}
731 |
732 | # Mock list_objects_v2 response
733 | mock_s3_client.list_objects_v2.return_value = {'KeyCount': 0}
734 |
735 | # Mock AWS service clients
736 | with patch(
737 | 'awslabs.aws_dataprocessing_mcp_server.handlers.commons.common_resource_handler.AwsHelper'
738 | ) as mock_aws_helper:
739 | mock_glue_client = Mock()
740 | mock_athena_client = Mock()
741 | mock_emr_client = Mock()
742 |
743 | mock_aws_helper.create_boto3_client.side_effect = lambda service: {
744 | 'glue': mock_glue_client,
745 | 'athena': mock_athena_client,
746 | 'emr': mock_emr_client,
747 | }[service]
748 |
749 | # Mock service responses
750 | mock_glue_client.get_connections.return_value = {'ConnectionList': []}
751 | mock_glue_client.get_crawlers.return_value = {'Crawlers': []}
752 | mock_glue_client.get_jobs.return_value = {'Jobs': []}
753 |
754 | # Mock Athena workgroups
755 | mock_athena_client.list_work_groups.return_value = {
756 | 'WorkGroups': [{'Name': 'test-workgroup'}]
757 | }
758 |
759 | # Mock get_work_group to raise an exception
760 | mock_athena_client.get_work_group.side_effect = ClientError(
761 | {'Error': {'Code': 'AccessDenied', 'Message': 'Access denied'}}, 'GetWorkGroup'
762 | )
763 |
764 | mock_emr_client.list_clusters.return_value = {'Clusters': []}
765 |
766 | ctx = Mock()
767 | response = await handler.analyze_s3_usage_for_data_processing(ctx)
768 |
769 | assert not response.isError
770 | # Should still return results but with warning about workgroup check
771 | assert 'Warning: Could not check workgroup test-workgroup' in response.analysis_summary
772 |
773 |
774 | @pytest.mark.asyncio
775 | async def test_analyze_s3_usage_emr_clusters_error(handler, mock_s3_client):
776 | """Test error handling when EMR clusters check fails in analyze_s3_usage_for_data_processing."""
777 | handler.s3_client = mock_s3_client
778 |
779 | # Mock list_buckets response
780 | mock_s3_client.list_buckets.return_value = {'Buckets': [{'Name': 'test-bucket'}]}
781 |
782 | # Mock list_objects_v2 response
783 | mock_s3_client.list_objects_v2.return_value = {'KeyCount': 0}
784 |
785 | # Mock AWS service clients
786 | with patch(
787 | 'awslabs.aws_dataprocessing_mcp_server.handlers.commons.common_resource_handler.AwsHelper'
788 | ) as mock_aws_helper:
789 | mock_glue_client = Mock()
790 | mock_athena_client = Mock()
791 | mock_emr_client = Mock()
792 |
793 | mock_aws_helper.create_boto3_client.side_effect = lambda service: {
794 | 'glue': mock_glue_client,
795 | 'athena': mock_athena_client,
796 | 'emr': mock_emr_client,
797 | }[service]
798 |
799 | # Mock service responses
800 | mock_glue_client.get_connections.return_value = {'ConnectionList': []}
801 | mock_glue_client.get_crawlers.return_value = {'Crawlers': []}
802 | mock_glue_client.get_jobs.return_value = {'Jobs': []}
803 | mock_athena_client.list_work_groups.return_value = {'WorkGroups': []}
804 |
805 | # Mock EMR list_clusters to raise an exception
806 | mock_emr_client.list_clusters.side_effect = ClientError(
807 | {'Error': {'Code': 'AccessDenied', 'Message': 'Access denied'}}, 'ListClusters'
808 | )
809 |
810 | ctx = Mock()
811 | response = await handler.analyze_s3_usage_for_data_processing(ctx)
812 |
813 | assert not response.isError
814 | # Should still return results but with error details in the text
815 | assert 'Error checking EMR usage' in response.analysis_summary
816 |
817 |
818 | @pytest.mark.asyncio
819 | async def test_analyze_s3_usage_emr_cluster_details_error(handler, mock_s3_client):
820 | """Test error handling when getting EMR cluster details fails in analyze_s3_usage_for_data_processing."""
821 | handler.s3_client = mock_s3_client
822 |
823 | # Mock list_buckets response
824 | mock_s3_client.list_buckets.return_value = {'Buckets': [{'Name': 'test-bucket'}]}
825 |
826 | # Mock list_objects_v2 response
827 | mock_s3_client.list_objects_v2.return_value = {'KeyCount': 0}
828 |
829 | # Mock AWS service clients
830 | with patch(
831 | 'awslabs.aws_dataprocessing_mcp_server.handlers.commons.common_resource_handler.AwsHelper'
832 | ) as mock_aws_helper:
833 | mock_glue_client = Mock()
834 | mock_athena_client = Mock()
835 | mock_emr_client = Mock()
836 |
837 | mock_aws_helper.create_boto3_client.side_effect = lambda service: {
838 | 'glue': mock_glue_client,
839 | 'athena': mock_athena_client,
840 | 'emr': mock_emr_client,
841 | }[service]
842 |
843 | # Mock service responses
844 | mock_glue_client.get_connections.return_value = {'ConnectionList': []}
845 | mock_glue_client.get_crawlers.return_value = {'Crawlers': []}
846 | mock_glue_client.get_jobs.return_value = {'Jobs': []}
847 | mock_athena_client.list_work_groups.return_value = {'WorkGroups': []}
848 |
849 | # Mock EMR clusters
850 | mock_emr_client.list_clusters.return_value = {
851 | 'Clusters': [{'Id': 'j-1234567890123', 'Name': 'test-cluster'}]
852 | }
853 |
854 | # Mock describe_cluster to raise an exception
855 | mock_emr_client.describe_cluster.side_effect = ClientError(
856 | {'Error': {'Code': 'AccessDenied', 'Message': 'Access denied'}}, 'DescribeCluster'
857 | )
858 |
859 | ctx = Mock()
860 | response = await handler.analyze_s3_usage_for_data_processing(ctx)
861 |
862 | assert not response.isError
863 | # Should still return results but with warning about cluster check
864 | assert 'Warning: Could not check cluster j-1234567890123' in response.analysis_summary
865 |
866 |
867 | @pytest.mark.asyncio
868 | async def test_analyze_s3_usage_last_activity_error(handler, mock_s3_client):
869 | """Test error handling when checking last activity fails in analyze_s3_usage_for_data_processing."""
870 | handler.s3_client = mock_s3_client
871 |
872 | # Mock list_buckets response
873 | mock_s3_client.list_buckets.return_value = {'Buckets': [{'Name': 'test-bucket'}]}
874 |
875 | # Mock list_objects_v2 to raise an exception
876 | mock_s3_client.list_objects_v2.side_effect = ClientError(
877 | {'Error': {'Code': 'AccessDenied', 'Message': 'Access denied'}}, 'ListObjectsV2'
878 | )
879 |
880 | # Mock AWS service clients
881 | with patch(
882 | 'awslabs.aws_dataprocessing_mcp_server.handlers.commons.common_resource_handler.AwsHelper'
883 | ) as mock_aws_helper:
884 | mock_glue_client = Mock()
885 | mock_athena_client = Mock()
886 | mock_emr_client = Mock()
887 |
888 | mock_aws_helper.create_boto3_client.side_effect = lambda service: {
889 | 'glue': mock_glue_client,
890 | 'athena': mock_athena_client,
891 | 'emr': mock_emr_client,
892 | }[service]
893 |
894 | # Mock service responses
895 | mock_glue_client.get_connections.return_value = {'ConnectionList': []}
896 | mock_glue_client.get_crawlers.return_value = {'Crawlers': []}
897 | mock_glue_client.get_jobs.return_value = {'Jobs': []}
898 | mock_athena_client.list_work_groups.return_value = {'WorkGroups': []}
899 | mock_emr_client.list_clusters.return_value = {'Clusters': []}
900 |
901 | ctx = Mock()
902 | response = await handler.analyze_s3_usage_for_data_processing(ctx)
903 |
904 | assert not response.isError
905 | # Should still return results but with error details in the text
906 | assert 'Error checking last activity' in response.analysis_summary
907 |
908 |
909 | @pytest.mark.asyncio
910 | async def test_analyze_s3_usage_bucket_name_hints(handler, mock_s3_client):
911 | """Test bucket name hint detection in analyze_s3_usage_for_data_processing."""
912 | handler.s3_client = mock_s3_client
913 |
914 | # Mock list_buckets response with buckets that have hints in their names
915 | mock_s3_client.list_buckets.return_value = {
916 | 'Buckets': [
917 | {'Name': 'my-glue-etl-bucket'},
918 | {'Name': 'athena-query-results'},
919 | {'Name': 'emr-hadoop-logs'},
920 | {'Name': 'random-bucket-name'},
921 | ]
922 | }
923 |
924 | # Mock list_objects_v2 response
925 | mock_s3_client.list_objects_v2.return_value = {'KeyCount': 0}
926 |
927 | # Mock AWS service clients
928 | with patch(
929 | 'awslabs.aws_dataprocessing_mcp_server.handlers.commons.common_resource_handler.AwsHelper'
930 | ) as mock_aws_helper:
931 | mock_glue_client = Mock()
932 | mock_athena_client = Mock()
933 | mock_emr_client = Mock()
934 |
935 | mock_aws_helper.create_boto3_client.side_effect = lambda service: {
936 | 'glue': mock_glue_client,
937 | 'athena': mock_athena_client,
938 | 'emr': mock_emr_client,
939 | }[service]
940 |
941 | # Mock service responses - no active usage detected
942 | mock_glue_client.get_connections.return_value = {'ConnectionList': []}
943 | mock_glue_client.get_crawlers.return_value = {'Crawlers': []}
944 | mock_glue_client.get_jobs.return_value = {'Jobs': []}
945 | mock_athena_client.list_work_groups.return_value = {'WorkGroups': []}
946 | mock_emr_client.list_clusters.return_value = {'Clusters': []}
947 |
948 | ctx = Mock()
949 | response = await handler.analyze_s3_usage_for_data_processing(ctx)
950 |
951 | assert not response.isError
952 | # Should detect bucket name hints
953 | assert (
954 | 'Likely Glue bucket (based on name) but no active usage detected'
955 | in response.analysis_summary
956 | )
957 | assert (
958 | 'Likely Athena bucket (based on name) but no active usage detected'
959 | in response.analysis_summary
960 | )
961 | assert (
962 | 'Likely EMR bucket (based on name) but no active usage detected'
963 | in response.analysis_summary
964 | )
965 | assert 'No data processing service usage detected' in response.analysis_summary
966 |
967 |
968 | @pytest.mark.asyncio
969 | async def test_upload_to_s3_success(handler, mock_s3_client):
970 | """Test successful upload to S3."""
971 | handler.s3_client = mock_s3_client
972 |
973 | # Mock bucket location
974 | mock_s3_client.get_bucket_location.return_value = {'LocationConstraint': 'us-east-1'}
975 |
976 | code_content = "print('Hello, World!')"
977 |
978 | ctx = Mock()
979 | response = await handler.upload_to_s3(
980 | ctx, code_content=code_content, bucket_name='test-bucket', s3_key='scripts/test.py'
981 | )
982 |
983 | assert not response.isError
984 | assert response.s3_uri == 's3://test-bucket/scripts/test.py'
985 | assert response.bucket_name == 'test-bucket'
986 | assert response.s3_key == 'scripts/test.py'
987 |
988 | # Verify put_object was called
989 | mock_s3_client.put_object.assert_called_once_with(
990 | Body=code_content, Bucket='test-bucket', Key='scripts/test.py', ContentType='text/x-python'
991 | )
992 |
993 |
994 | @pytest.mark.asyncio
995 | async def test_upload_to_s3_make_public(handler, mock_s3_client):
996 | """Test successful upload to S3 with public access."""
997 | handler.s3_client = mock_s3_client
998 |
999 | # Mock bucket location
1000 | mock_s3_client.get_bucket_location.return_value = {'LocationConstraint': 'us-east-1'}
1001 |
1002 | code_content = "print('Hello, World!')"
1003 |
1004 | ctx = Mock()
1005 | response = await handler.upload_to_s3(
1006 | ctx,
1007 | code_content=code_content,
1008 | bucket_name='test-bucket',
1009 | s3_key='scripts/test.py',
1010 | make_public=True,
1011 | )
1012 |
1013 | assert not response.isError
1014 |
1015 | # Verify put_object_acl was called
1016 | mock_s3_client.put_object_acl.assert_called_once_with(
1017 | Bucket='test-bucket', Key='scripts/test.py', ACL='public-read'
1018 | )
1019 |
1020 |
1021 | @pytest.mark.asyncio
1022 | async def test_upload_to_s3_without_write_permission(read_only_handler):
1023 | """Test that uploading to S3 fails when write access is disabled."""
1024 | ctx = Mock()
1025 | response = await read_only_handler.upload_to_s3(
1026 | ctx, code_content="print('test')", bucket_name='test-bucket', s3_key='test.py'
1027 | )
1028 |
1029 | assert response.isError
1030 | assert 'requires --allow-write flag' in response.content[0].text
1031 |
1032 |
1033 | @pytest.mark.asyncio
1034 | async def test_upload_to_s3_bucket_not_found(handler, mock_s3_client):
1035 | """Test upload to S3 when bucket doesn't exist."""
1036 | handler.s3_client = mock_s3_client
1037 |
1038 | # Mock bucket not found
1039 | mock_s3_client.head_bucket.side_effect = ClientError(
1040 | {'Error': {'Code': '404', 'Message': 'Not Found'}}, 'HeadBucket'
1041 | )
1042 |
1043 | ctx = Mock()
1044 | response = await handler.upload_to_s3(
1045 | ctx, code_content="print('test')", bucket_name='nonexistent-bucket', s3_key='test.py'
1046 | )
1047 |
1048 | assert response.isError
1049 | assert 'does not exist' in response.content[0].text
1050 |
1051 |
1052 | @pytest.mark.asyncio
1053 | async def test_analyze_s3_usage_for_data_processing_success(handler, mock_s3_client):
1054 | """Test successful S3 usage analysis."""
1055 | handler.s3_client = mock_s3_client
1056 |
1057 | # Mock list_buckets response
1058 | mock_s3_client.list_buckets.return_value = {'Buckets': [{'Name': 'test-glue-bucket'}]}
1059 |
1060 | # Mock list_objects_v2 response
1061 | mock_s3_client.list_objects_v2.return_value = {
1062 | 'KeyCount': 1,
1063 | 'Contents': [{'LastModified': datetime(2023, 6, 1)}],
1064 | }
1065 |
1066 | # Mock AWS service clients
1067 | with patch(
1068 | 'awslabs.aws_dataprocessing_mcp_server.handlers.commons.common_resource_handler.AwsHelper'
1069 | ) as mock_aws_helper:
1070 | mock_glue_client = Mock()
1071 | mock_athena_client = Mock()
1072 | mock_emr_client = Mock()
1073 |
1074 | mock_aws_helper.create_boto3_client.side_effect = lambda service: {
1075 | 'glue': mock_glue_client,
1076 | 'athena': mock_athena_client,
1077 | 'emr': mock_emr_client,
1078 | }[service]
1079 |
1080 | # Mock service responses
1081 | mock_glue_client.get_connections.return_value = {'ConnectionList': []}
1082 | mock_glue_client.get_crawlers.return_value = {'Crawlers': []}
1083 | mock_glue_client.get_jobs.return_value = {'Jobs': []}
1084 | mock_athena_client.list_work_groups.return_value = {'WorkGroups': []}
1085 | mock_emr_client.list_clusters.return_value = {'Clusters': []}
1086 |
1087 | ctx = Mock()
1088 | response = await handler.analyze_s3_usage_for_data_processing(ctx)
1089 |
1090 | assert not response.isError
1091 | assert 'S3 Usage Analysis' in response.analysis_summary
1092 | assert response.service_usage is not None
1093 | assert 'glue' in response.service_usage
1094 | assert 'athena' in response.service_usage
1095 | assert 'emr' in response.service_usage
1096 | assert 'idle' in response.service_usage
1097 | assert 'unknown' in response.service_usage
1098 |
1099 |
1100 | @pytest.mark.asyncio
1101 | async def test_analyze_s3_usage_specific_bucket(handler, mock_s3_client):
1102 | """Test S3 usage analysis for a specific bucket."""
1103 | handler.s3_client = mock_s3_client
1104 |
1105 | # Mock list_objects_v2 response
1106 | mock_s3_client.list_objects_v2.return_value = {
1107 | 'KeyCount': 1,
1108 | 'Contents': [{'LastModified': datetime(2023, 6, 1)}],
1109 | }
1110 |
1111 | # Mock AWS service clients
1112 | with patch(
1113 | 'awslabs.aws_dataprocessing_mcp_server.handlers.commons.common_resource_handler.AwsHelper'
1114 | ) as mock_aws_helper:
1115 | mock_glue_client = Mock()
1116 | mock_athena_client = Mock()
1117 | mock_emr_client = Mock()
1118 |
1119 | mock_aws_helper.create_boto3_client.side_effect = lambda service: {
1120 | 'glue': mock_glue_client,
1121 | 'athena': mock_athena_client,
1122 | 'emr': mock_emr_client,
1123 | }[service]
1124 |
1125 | # Mock service responses
1126 | mock_glue_client.get_connections.return_value = {'ConnectionList': []}
1127 | mock_glue_client.get_crawlers.return_value = {'Crawlers': []}
1128 | mock_glue_client.get_jobs.return_value = {'Jobs': []}
1129 | mock_athena_client.list_work_groups.return_value = {'WorkGroups': []}
1130 | mock_emr_client.list_clusters.return_value = {'Clusters': []}
1131 |
1132 | ctx = Mock()
1133 | response = await handler.analyze_s3_usage_for_data_processing(
1134 | ctx, bucket_name='test-bucket'
1135 | )
1136 |
1137 | assert not response.isError
1138 | assert 'S3 Usage Analysis' in response.analysis_summary
1139 |
1140 |
1141 | @pytest.mark.asyncio
1142 | async def test_analyze_s3_usage_bucket_not_found(handler, mock_s3_client):
1143 | """Test S3 usage analysis when specific bucket doesn't exist."""
1144 | handler.s3_client = mock_s3_client
1145 |
1146 | # Mock bucket not found
1147 | mock_s3_client.head_bucket.side_effect = ClientError(
1148 | {'Error': {'Code': '404', 'Message': 'Not Found'}}, 'HeadBucket'
1149 | )
1150 |
1151 | ctx = Mock()
1152 | response = await handler.analyze_s3_usage_for_data_processing(
1153 | ctx, bucket_name='nonexistent-bucket'
1154 | )
1155 |
1156 | assert response.isError
1157 | assert 'does not exist or is not accessible' in response.content[0].text
1158 |
1159 |
1160 | @pytest.mark.asyncio
1161 | async def test_analyze_s3_usage_error_handling(handler, mock_s3_client):
1162 | """Test error handling when S3 usage analysis fails."""
1163 | handler.s3_client = mock_s3_client
1164 | mock_s3_client.list_buckets.side_effect = ClientError(
1165 | {'Error': {'Code': 'AccessDenied', 'Message': 'Access denied'}}, 'ListBuckets'
1166 | )
1167 |
1168 | ctx = Mock()
1169 | response = await handler.analyze_s3_usage_for_data_processing(ctx)
1170 |
1171 | assert response.isError
1172 | assert 'AWS Error' in response.content[0].text
1173 |
1174 |
1175 | # ============================================================================
1176 | # Helper Methods Tests
1177 | # ============================================================================
1178 |
1179 |
1180 | def test_get_trust_relationship_for_service_glue(handler):
1181 | """Test trust relationship generation for Glue service."""
1182 | trust_relationship = handler._get_trust_relationship_for_service('glue')
1183 |
1184 | assert trust_relationship['Version'] == '2012-10-17'
1185 | assert len(trust_relationship['Statement']) == 1
1186 | assert trust_relationship['Statement'][0]['Effect'] == 'Allow'
1187 | assert trust_relationship['Statement'][0]['Principal']['Service'] == 'glue.amazonaws.com'
1188 | assert trust_relationship['Statement'][0]['Action'] == 'sts:AssumeRole'
1189 |
1190 |
1191 | def test_get_trust_relationship_for_service_emr(handler):
1192 | """Test trust relationship generation for EMR service."""
1193 | trust_relationship = handler._get_trust_relationship_for_service('emr')
1194 |
1195 | assert (
1196 | trust_relationship['Statement'][0]['Principal']['Service']
1197 | == 'elasticmapreduce.amazonaws.com'
1198 | )
1199 |
1200 |
1201 | def test_get_trust_relationship_for_service_athena(handler):
1202 | """Test trust relationship generation for Athena service."""
1203 | trust_relationship = handler._get_trust_relationship_for_service('athena')
1204 |
1205 | assert trust_relationship['Statement'][0]['Principal']['Service'] == 'athena.amazonaws.com'
1206 |
1207 |
1208 | def test_get_service_principal_known_services(handler):
1209 | """Test service principal mapping for known services."""
1210 | assert handler._get_service_principal('glue') == 'glue.amazonaws.com'
1211 | assert handler._get_service_principal('emr') == 'elasticmapreduce.amazonaws.com'
1212 | assert handler._get_service_principal('athena') == 'athena.amazonaws.com'
1213 | assert handler._get_service_principal('lambda') == 'lambda.amazonaws.com'
1214 | assert handler._get_service_principal('ec2') == 'ec2.amazonaws.com'
1215 |
1216 |
1217 | def test_get_service_principal_unknown_service(handler):
1218 | """Test service principal mapping for unknown services."""
1219 | assert handler._get_service_principal('unknown-service') == 'unknown-service.amazonaws.com'
1220 |
1221 |
1222 | def test_can_be_assumed_by_service_single_service(handler):
1223 | """Test checking if role can be assumed by service with single service principal."""
1224 | assume_role_policy = {
1225 | 'Version': '2012-10-17',
1226 | 'Statement': [
1227 | {
1228 | 'Effect': 'Allow',
1229 | 'Principal': {'Service': 'glue.amazonaws.com'},
1230 | 'Action': 'sts:AssumeRole',
1231 | }
1232 | ],
1233 | }
1234 |
1235 | assert handler._can_be_assumed_by_service(assume_role_policy, 'glue.amazonaws.com')
1236 | assert not handler._can_be_assumed_by_service(assume_role_policy, 'emr.amazonaws.com')
1237 |
1238 |
1239 | def test_can_be_assumed_by_service_multiple_services(handler):
1240 | """Test checking if role can be assumed by service with multiple service principals."""
1241 | assume_role_policy = {
1242 | 'Version': '2012-10-17',
1243 | 'Statement': [
1244 | {
1245 | 'Effect': 'Allow',
1246 | 'Principal': {'Service': ['glue.amazonaws.com', 'emr.amazonaws.com']},
1247 | 'Action': 'sts:AssumeRole',
1248 | }
1249 | ],
1250 | }
1251 |
1252 | assert handler._can_be_assumed_by_service(assume_role_policy, 'glue.amazonaws.com')
1253 | assert handler._can_be_assumed_by_service(assume_role_policy, 'emr.amazonaws.com')
1254 | assert not handler._can_be_assumed_by_service(assume_role_policy, 'athena.amazonaws.com')
1255 |
1256 |
1257 | def test_can_be_assumed_by_service_string_action(handler):
1258 | """Test checking if role can be assumed by service with string action."""
1259 | assume_role_policy = {
1260 | 'Version': '2012-10-17',
1261 | 'Statement': [
1262 | {
1263 | 'Effect': 'Allow',
1264 | 'Principal': {'Service': 'glue.amazonaws.com'},
1265 | 'Action': 'sts:AssumeRole',
1266 | }
1267 | ],
1268 | }
1269 |
1270 | assert handler._can_be_assumed_by_service(assume_role_policy, 'glue.amazonaws.com')
1271 |
1272 |
1273 | def test_can_be_assumed_by_service_deny_effect(handler):
1274 | """Test checking if role can be assumed by service with Deny effect."""
1275 | assume_role_policy = {
1276 | 'Version': '2012-10-17',
1277 | 'Statement': [
1278 | {
1279 | 'Effect': 'Deny',
1280 | 'Principal': {'Service': 'glue.amazonaws.com'},
1281 | 'Action': 'sts:AssumeRole',
1282 | }
1283 | ],
1284 | }
1285 |
1286 | # The implementation correctly ignores Deny statements and only processes Allow statements
1287 | assert not handler._can_be_assumed_by_service(assume_role_policy, 'glue.amazonaws.com')
1288 |
1289 |
1290 | def test_can_be_assumed_by_service_wrong_action(handler):
1291 | """Test checking if role can be assumed by service with wrong action."""
1292 | assume_role_policy = {
1293 | 'Version': '2012-10-17',
1294 | 'Statement': [
1295 | {
1296 | 'Effect': 'Allow',
1297 | 'Principal': {'Service': 'glue.amazonaws.com'},
1298 | 'Action': 'sts:GetCallerIdentity',
1299 | }
1300 | ],
1301 | }
1302 |
1303 | assert not handler._can_be_assumed_by_service(assume_role_policy, 'glue.amazonaws.com')
1304 |
1305 |
1306 | def test_can_be_assumed_by_service_empty_policy(handler):
1307 | """Test checking if role can be assumed by service with empty policy."""
1308 | assert not handler._can_be_assumed_by_service({}, 'glue.amazonaws.com')
1309 | assert not handler._can_be_assumed_by_service({'Statement': []}, 'glue.amazonaws.com')
1310 |
1311 |
1312 | def test_add_permissions_to_document_single_statement(handler):
1313 | """Test adding single permission statement to policy document."""
1314 | policy_document = {'Version': '2012-10-17', 'Statement': []}
1315 | permissions = {
1316 | 'Effect': 'Allow',
1317 | 'Action': ['s3:GetObject'],
1318 | 'Resource': 'arn:aws:s3:::test-bucket/*',
1319 | }
1320 |
1321 | handler._add_permissions_to_document(policy_document, permissions)
1322 |
1323 | assert len(policy_document['Statement']) == 1
1324 | assert policy_document['Statement'][0] == permissions
1325 |
1326 |
1327 | def test_add_permissions_to_document_multiple_statements(handler):
1328 | """Test adding multiple permission statements to policy document."""
1329 | policy_document = {'Version': '2012-10-17', 'Statement': []}
1330 | permissions = [
1331 | {'Effect': 'Allow', 'Action': ['s3:GetObject'], 'Resource': 'arn:aws:s3:::test-bucket/*'},
1332 | {'Effect': 'Allow', 'Action': ['s3:PutObject'], 'Resource': 'arn:aws:s3:::test-bucket/*'},
1333 | ]
1334 |
1335 | handler._add_permissions_to_document(policy_document, permissions)
1336 |
1337 | assert len(policy_document['Statement']) == 2
1338 | assert policy_document['Statement'][0] == permissions[0]
1339 | assert policy_document['Statement'][1] == permissions[1]
1340 |
1341 |
1342 | # ============================================================================
1343 | # Initialization Tests
1344 | # ============================================================================
1345 |
1346 |
1347 | @pytest.mark.asyncio
1348 | async def test_initialization_parameters(mock_aws_helper):
1349 | """Test initialization of parameters for CommonResourceHandler object."""
1350 | mcp = Mock()
1351 | handler = CommonResourceHandler(mcp, allow_write=True)
1352 |
1353 | assert handler.allow_write
1354 | assert handler.mcp == mcp
1355 |
1356 |
1357 | @pytest.mark.asyncio
1358 | async def test_initialization_registers_tools(mock_aws_helper):
1359 | """Test that initialization registers the tools with the MCP server."""
1360 | mcp = Mock()
1361 | CommonResourceHandler(mcp)
1362 |
1363 | # Verify IAM tools are registered
1364 | mcp.tool.assert_any_call(name='add_inline_policy')
1365 | mcp.tool.assert_any_call(name='get_policies_for_role')
1366 | mcp.tool.assert_any_call(name='create_data_processing_role')
1367 | mcp.tool.assert_any_call(name='get_roles_for_service')
1368 |
1369 | # Verify S3 tools are registered
1370 | mcp.tool.assert_any_call(name='list_s3_buckets')
1371 | mcp.tool.assert_any_call(name='upload_to_s3')
1372 | mcp.tool.assert_any_call(name='analyze_s3_usage_for_data_processing')
1373 |
1374 |
1375 | @pytest.mark.asyncio
1376 | async def test_initialization_default_parameters(mock_aws_helper):
1377 | """Test initialization with default parameters."""
1378 | mcp = Mock()
1379 | handler = CommonResourceHandler(mcp)
1380 |
1381 | assert not handler.allow_write # Default should be False
1382 | assert handler.mcp == mcp
1383 |
1384 |
1385 | @pytest.mark.asyncio
1386 | async def test_get_managed_policies_error_handling(handler, mock_iam_client):
1387 | """Test error handling in _get_managed_policies method."""
1388 | handler.iam_client = mock_iam_client
1389 |
1390 | # Mock successful list_attached_role_policies
1391 | mock_iam_client.list_attached_role_policies.return_value = {
1392 | 'AttachedPolicies': [
1393 | {
1394 | 'PolicyName': 'TestManagedPolicy',
1395 | 'PolicyArn': 'arn:aws:iam::aws:policy/TestManagedPolicy',
1396 | }
1397 | ]
1398 | }
1399 |
1400 | # Mock successful get_policy
1401 | mock_iam_client.get_policy.return_value = {
1402 | 'Policy': {'DefaultVersionId': 'v1', 'Description': 'Test managed policy'}
1403 | }
1404 |
1405 | # Mock get_policy_version to raise an exception
1406 | mock_iam_client.get_policy_version.side_effect = ClientError(
1407 | {'Error': {'Code': 'AccessDenied', 'Message': 'Access denied'}}, 'GetPolicyVersion'
1408 | )
1409 |
1410 | ctx = Mock()
1411 | managed_policies = handler._get_managed_policies(ctx, 'test-role')
1412 |
1413 | # Should still return policy summary even if policy version fails
1414 | assert len(managed_policies) == 1
1415 | assert managed_policies[0].policy_type == 'Managed'
1416 | assert managed_policies[0].policy_document is None
1417 |
1418 |
1419 | @pytest.mark.asyncio
1420 | async def test_create_data_processing_role_create_role_error(handler, mock_iam_client):
1421 | """Test error handling when create_role fails in create_data_processing_role."""
1422 | handler.iam_client = mock_iam_client
1423 |
1424 | # Mock create_role to raise an exception
1425 | mock_iam_client.create_role.side_effect = ClientError(
1426 | {'Error': {'Code': 'EntityAlreadyExists', 'Message': 'Role already exists'}}, 'CreateRole'
1427 | )
1428 |
1429 | ctx = Mock()
1430 | response = await handler.create_data_processing_role(
1431 | ctx, role_name='existing-role', service_type='glue'
1432 | )
1433 |
1434 | assert response.isError
1435 | assert 'Failed to create IAM role' in response.content[0].text
1436 |
1437 |
1438 | @pytest.mark.asyncio
1439 | async def test_analyze_s3_usage_idle_bucket_detection(handler, mock_s3_client):
1440 | """Test idle bucket detection in analyze_s3_usage_for_data_processing."""
1441 | handler.s3_client = mock_s3_client
1442 |
1443 | # Mock list_buckets response with a bucket that has old activity
1444 | mock_s3_client.list_buckets.return_value = {'Buckets': [{'Name': 'old-bucket'}]}
1445 |
1446 | # Mock list_objects_v2 response with old last modified date (>90 days ago)
1447 | old_date = datetime.now() - timedelta(days=100)
1448 | mock_s3_client.list_objects_v2.return_value = {
1449 | 'KeyCount': 1,
1450 | 'Contents': [{'LastModified': old_date}],
1451 | }
1452 |
1453 | # Mock AWS service clients
1454 | with patch(
1455 | 'awslabs.aws_dataprocessing_mcp_server.handlers.commons.common_resource_handler.AwsHelper'
1456 | ) as mock_aws_helper:
1457 | mock_glue_client = Mock()
1458 | mock_athena_client = Mock()
1459 | mock_emr_client = Mock()
1460 |
1461 | mock_aws_helper.create_boto3_client.side_effect = lambda service: {
1462 | 'glue': mock_glue_client,
1463 | 'athena': mock_athena_client,
1464 | 'emr': mock_emr_client,
1465 | }[service]
1466 |
1467 | # Mock service responses - no active usage detected
1468 | mock_glue_client.get_connections.return_value = {'ConnectionList': []}
1469 | mock_glue_client.get_crawlers.return_value = {'Crawlers': []}
1470 | mock_glue_client.get_jobs.return_value = {'Jobs': []}
1471 | mock_athena_client.list_work_groups.return_value = {'WorkGroups': []}
1472 | mock_emr_client.list_clusters.return_value = {'Clusters': []}
1473 |
1474 | ctx = Mock()
1475 | response = await handler.analyze_s3_usage_for_data_processing(ctx)
1476 |
1477 | assert not response.isError
1478 | # Should detect idle bucket
1479 | assert (
1480 | 'IDLE: No data processing service usage detected and no activity for 90+ days'
1481 | in response.analysis_summary
1482 | )
1483 |
1484 |
1485 | @pytest.mark.asyncio
1486 | async def test_analyze_s3_usage_glue_job_detection(handler, mock_s3_client):
1487 | """Test Glue job bucket detection in analyze_s3_usage_for_data_processing."""
1488 | handler.s3_client = mock_s3_client
1489 |
1490 | # Mock list_buckets response
1491 | mock_s3_client.list_buckets.return_value = {'Buckets': [{'Name': 'test-bucket'}]}
1492 |
1493 | # Mock list_objects_v2 response
1494 | mock_s3_client.list_objects_v2.return_value = {'KeyCount': 0}
1495 |
1496 | # Mock AWS service clients
1497 | with patch(
1498 | 'awslabs.aws_dataprocessing_mcp_server.handlers.commons.common_resource_handler.AwsHelper'
1499 | ) as mock_aws_helper:
1500 | mock_glue_client = Mock()
1501 | mock_athena_client = Mock()
1502 | mock_emr_client = Mock()
1503 |
1504 | mock_aws_helper.create_boto3_client.side_effect = lambda service: {
1505 | 'glue': mock_glue_client,
1506 | 'athena': mock_athena_client,
1507 | 'emr': mock_emr_client,
1508 | }[service]
1509 |
1510 | # Mock Glue job with bucket reference in DefaultArguments
1511 | mock_glue_client.get_connections.return_value = {'ConnectionList': []}
1512 | mock_glue_client.get_crawlers.return_value = {'Crawlers': []}
1513 | mock_glue_client.get_jobs.return_value = {
1514 | 'Jobs': [
1515 | {
1516 | 'Name': 'test-job',
1517 | 'DefaultArguments': {
1518 | '--TempDir': 's3://test-bucket/temp/',
1519 | '--job-bookmark-option': 'job-bookmark-enable',
1520 | },
1521 | }
1522 | ]
1523 | }
1524 | mock_athena_client.list_work_groups.return_value = {'WorkGroups': []}
1525 | mock_emr_client.list_clusters.return_value = {'Clusters': []}
1526 |
1527 | ctx = Mock()
1528 | response = await handler.analyze_s3_usage_for_data_processing(ctx)
1529 |
1530 | assert not response.isError
1531 | # Should detect Glue usage
1532 | assert '✅ Used by AWS Glue' in response.analysis_summary
1533 |
1534 |
1535 | @pytest.mark.asyncio
1536 | async def test_analyze_s3_usage_glue_crawler_detection(handler, mock_s3_client):
1537 | """Test Glue crawler bucket detection in analyze_s3_usage_for_data_processing."""
1538 | handler.s3_client = mock_s3_client
1539 |
1540 | # Mock list_buckets response
1541 | mock_s3_client.list_buckets.return_value = {'Buckets': [{'Name': 'test-bucket'}]}
1542 |
1543 | # Mock list_objects_v2 response
1544 | mock_s3_client.list_objects_v2.return_value = {'KeyCount': 0}
1545 |
1546 | # Mock AWS service clients
1547 | with patch(
1548 | 'awslabs.aws_dataprocessing_mcp_server.handlers.commons.common_resource_handler.AwsHelper'
1549 | ) as mock_aws_helper:
1550 | mock_glue_client = Mock()
1551 | mock_athena_client = Mock()
1552 | mock_emr_client = Mock()
1553 |
1554 | mock_aws_helper.create_boto3_client.side_effect = lambda service: {
1555 | 'glue': mock_glue_client,
1556 | 'athena': mock_athena_client,
1557 | 'emr': mock_emr_client,
1558 | }[service]
1559 |
1560 | # Mock Glue crawler with S3 target
1561 | mock_glue_client.get_connections.return_value = {'ConnectionList': []}
1562 | mock_glue_client.get_crawlers.return_value = {
1563 | 'Crawlers': [
1564 | {
1565 | 'Name': 'test-crawler',
1566 | 'Targets': {'S3Targets': [{'Path': 's3://test-bucket/data/'}]},
1567 | }
1568 | ]
1569 | }
1570 | mock_glue_client.get_jobs.return_value = {'Jobs': []}
1571 | mock_athena_client.list_work_groups.return_value = {'WorkGroups': []}
1572 | mock_emr_client.list_clusters.return_value = {'Clusters': []}
1573 |
1574 | ctx = Mock()
1575 | response = await handler.analyze_s3_usage_for_data_processing(ctx)
1576 |
1577 | assert not response.isError
1578 | # Should detect Glue usage
1579 | assert '✅ Used by AWS Glue' in response.analysis_summary
1580 |
1581 |
1582 | @pytest.mark.asyncio
1583 | async def test_analyze_s3_usage_athena_workgroup_detection(handler, mock_s3_client):
1584 | """Test Athena workgroup bucket detection in analyze_s3_usage_for_data_processing."""
1585 | handler.s3_client = mock_s3_client
1586 |
1587 | # Mock list_buckets response
1588 | mock_s3_client.list_buckets.return_value = {'Buckets': [{'Name': 'test-bucket'}]}
1589 |
1590 | # Mock list_objects_v2 response
1591 | mock_s3_client.list_objects_v2.return_value = {'KeyCount': 0}
1592 |
1593 | # Mock AWS service clients
1594 | with patch(
1595 | 'awslabs.aws_dataprocessing_mcp_server.handlers.commons.common_resource_handler.AwsHelper'
1596 | ) as mock_aws_helper:
1597 | mock_glue_client = Mock()
1598 | mock_athena_client = Mock()
1599 | mock_emr_client = Mock()
1600 |
1601 | mock_aws_helper.create_boto3_client.side_effect = lambda service: {
1602 | 'glue': mock_glue_client,
1603 | 'athena': mock_athena_client,
1604 | 'emr': mock_emr_client,
1605 | }[service]
1606 |
1607 | # Mock service responses
1608 | mock_glue_client.get_connections.return_value = {'ConnectionList': []}
1609 | mock_glue_client.get_crawlers.return_value = {'Crawlers': []}
1610 | mock_glue_client.get_jobs.return_value = {'Jobs': []}
1611 |
1612 | # Mock Athena workgroup with output location
1613 | mock_athena_client.list_work_groups.return_value = {
1614 | 'WorkGroups': [{'Name': 'test-workgroup'}]
1615 | }
1616 | mock_athena_client.get_work_group.return_value = {
1617 | 'WorkGroup': {
1618 | 'Configuration': {
1619 | 'ResultConfiguration': {'OutputLocation': 's3://test-bucket/athena-results/'}
1620 | }
1621 | }
1622 | }
1623 |
1624 | mock_emr_client.list_clusters.return_value = {'Clusters': []}
1625 |
1626 | ctx = Mock()
1627 | response = await handler.analyze_s3_usage_for_data_processing(ctx)
1628 |
1629 | assert not response.isError
1630 | # Should detect Athena usage
1631 | assert '✅ Used by Amazon Athena' in response.analysis_summary
1632 |
1633 |
1634 | @pytest.mark.asyncio
1635 | async def test_analyze_s3_usage_emr_cluster_detection(handler, mock_s3_client):
1636 | """Test EMR cluster bucket detection in analyze_s3_usage_for_data_processing."""
1637 | handler.s3_client = mock_s3_client
1638 |
1639 | # Mock list_buckets response
1640 | mock_s3_client.list_buckets.return_value = {'Buckets': [{'Name': 'test-bucket'}]}
1641 |
1642 | # Mock list_objects_v2 response
1643 | mock_s3_client.list_objects_v2.return_value = {'KeyCount': 0}
1644 |
1645 | # Mock AWS service clients
1646 | with patch(
1647 | 'awslabs.aws_dataprocessing_mcp_server.handlers.commons.common_resource_handler.AwsHelper'
1648 | ) as mock_aws_helper:
1649 | mock_glue_client = Mock()
1650 | mock_athena_client = Mock()
1651 | mock_emr_client = Mock()
1652 |
1653 | mock_aws_helper.create_boto3_client.side_effect = lambda service: {
1654 | 'glue': mock_glue_client,
1655 | 'athena': mock_athena_client,
1656 | 'emr': mock_emr_client,
1657 | }[service]
1658 |
1659 | # Mock service responses
1660 | mock_glue_client.get_connections.return_value = {'ConnectionList': []}
1661 | mock_glue_client.get_crawlers.return_value = {'Crawlers': []}
1662 | mock_glue_client.get_jobs.return_value = {'Jobs': []}
1663 | mock_athena_client.list_work_groups.return_value = {'WorkGroups': []}
1664 |
1665 | # Mock EMR cluster with log URI
1666 | mock_emr_client.list_clusters.return_value = {
1667 | 'Clusters': [{'Id': 'j-1234567890123', 'Name': 'test-cluster'}]
1668 | }
1669 | mock_emr_client.describe_cluster.return_value = {
1670 | 'Cluster': {'LogUri': 's3://test-bucket/emr-logs/'}
1671 | }
1672 |
1673 | ctx = Mock()
1674 | response = await handler.analyze_s3_usage_for_data_processing(ctx)
1675 |
1676 | assert not response.isError
1677 | # Should detect EMR usage
1678 | assert '✅ Used by Amazon EMR' in response.analysis_summary
1679 |
1680 |
1681 | @pytest.mark.asyncio
1682 | async def test_list_s3_buckets_us_east_1_location_constraint(handler, mock_s3_client):
1683 | """Test list_s3_buckets with us-east-1 location constraint (None)."""
1684 | handler.s3_client = mock_s3_client
1685 |
1686 | # Mock list_buckets response
1687 | mock_s3_client.list_buckets.return_value = {
1688 | 'Buckets': [{'Name': 'test-glue-bucket', 'CreationDate': datetime(2023, 1, 1)}]
1689 | }
1690 |
1691 | # Mock bucket location returning None (us-east-1 case)
1692 | mock_s3_client.get_bucket_location.return_value = {'LocationConstraint': None}
1693 |
1694 | # Mock list_objects_v2 response
1695 | mock_s3_client.list_objects_v2.return_value = {
1696 | 'KeyCount': 5,
1697 | 'Contents': [{'LastModified': datetime(2023, 6, 1)}],
1698 | }
1699 |
1700 | ctx = Mock()
1701 | response = await handler.list_s3_buckets(ctx, region='us-east-1')
1702 |
1703 | assert not response.isError
1704 | assert response.region == 'us-east-1'
1705 | assert response.bucket_count == 1
1706 | assert len(response.buckets) == 1
1707 | assert response.buckets[0].name == 'test-glue-bucket'
1708 |
1709 |
1710 | @pytest.mark.asyncio
1711 | async def test_list_s3_buckets_truncated_objects(handler, mock_s3_client):
1712 | """Test list_s3_buckets with truncated object list."""
1713 | handler.s3_client = mock_s3_client
1714 |
1715 | # Mock list_buckets response
1716 | mock_s3_client.list_buckets.return_value = {
1717 | 'Buckets': [{'Name': 'test-glue-bucket', 'CreationDate': datetime(2023, 1, 1)}]
1718 | }
1719 |
1720 | # Mock bucket location
1721 | mock_s3_client.get_bucket_location.return_value = {'LocationConstraint': 'us-east-1'}
1722 |
1723 | # Mock list_objects_v2 response with truncated results
1724 | mock_s3_client.list_objects_v2.return_value = {
1725 | 'KeyCount': 1000,
1726 | 'IsTruncated': True,
1727 | 'Contents': [{'LastModified': datetime(2023, 6, 1)}],
1728 | }
1729 |
1730 | ctx = Mock()
1731 | response = await handler.list_s3_buckets(ctx, region='us-east-1')
1732 |
1733 | assert not response.isError
1734 | assert response.region == 'us-east-1'
1735 | assert response.bucket_count == 1
1736 | assert len(response.buckets) == 1
1737 | assert response.buckets[0].name == 'test-glue-bucket'
1738 | # Should show truncated count
1739 | assert '1000+ (truncated)' in response.content[0].text
1740 |
1741 |
1742 | @pytest.mark.asyncio
1743 | async def test_upload_to_s3_us_east_1_location_constraint(handler, mock_s3_client):
1744 | """Test upload_to_s3 with us-east-1 location constraint (None)."""
1745 | handler.s3_client = mock_s3_client
1746 |
1747 | # Mock successful head_bucket
1748 | mock_s3_client.head_bucket.return_value = {}
1749 |
1750 | # Mock bucket location returning None (us-east-1 case)
1751 | mock_s3_client.get_bucket_location.return_value = {'LocationConstraint': None}
1752 |
1753 | code_content = "print('Hello, World!')"
1754 |
1755 | ctx = Mock()
1756 | response = await handler.upload_to_s3(
1757 | ctx, code_content=code_content, bucket_name='test-bucket', s3_key='scripts/test.py'
1758 | )
1759 |
1760 | assert not response.isError
1761 | assert response.s3_uri == 's3://test-bucket/scripts/test.py'
1762 | assert response.bucket_name == 'test-bucket'
1763 | assert response.s3_key == 'scripts/test.py'
1764 |
1765 | # Verify put_object was called
1766 | mock_s3_client.put_object.assert_called_once_with(
1767 | Body=code_content, Bucket='test-bucket', Key='scripts/test.py', ContentType='text/x-python'
1768 | )
1769 |
1770 |
1771 | def test_can_be_assumed_by_service_list_actions(handler):
1772 | """Test checking if role can be assumed by service with list of actions."""
1773 | assume_role_policy = {
1774 | 'Version': '2012-10-17',
1775 | 'Statement': [
1776 | {
1777 | 'Effect': 'Allow',
1778 | 'Principal': {'Service': 'glue.amazonaws.com'},
1779 | 'Action': ['sts:AssumeRole', 'sts:GetCallerIdentity'],
1780 | }
1781 | ],
1782 | }
1783 |
1784 | assert handler._can_be_assumed_by_service(assume_role_policy, 'glue.amazonaws.com')
1785 |
1786 |
1787 | def test_can_be_assumed_by_service_no_principal_service(handler):
1788 | """Test checking if role can be assumed by service with no Principal.Service."""
1789 | assume_role_policy = {
1790 | 'Version': '2012-10-17',
1791 | 'Statement': [
1792 | {
1793 | 'Effect': 'Allow',
1794 | 'Principal': {'AWS': 'arn:aws:iam::123456789012:root'},
1795 | 'Action': 'sts:AssumeRole',
1796 | }
1797 | ],
1798 | }
1799 |
1800 | assert not handler._can_be_assumed_by_service(assume_role_policy, 'glue.amazonaws.com')
1801 |
1802 |
1803 | @pytest.mark.asyncio
1804 | async def test_list_s3_buckets_bucket_location_error(handler, mock_s3_client):
1805 | """Test error handling when get_bucket_location fails in list_s3_buckets."""
1806 | handler.s3_client = mock_s3_client
1807 |
1808 | # Mock list_buckets response
1809 | mock_s3_client.list_buckets.return_value = {
1810 | 'Buckets': [{'Name': 'test-glue-bucket', 'CreationDate': datetime(2023, 1, 1)}]
1811 | }
1812 |
1813 | # Mock get_bucket_location to raise an exception
1814 | mock_s3_client.get_bucket_location.side_effect = ClientError(
1815 | {'Error': {'Code': 'AccessDenied', 'Message': 'Access denied'}}, 'GetBucketLocation'
1816 | )
1817 |
1818 | ctx = Mock()
1819 | response = await handler.list_s3_buckets(ctx, region='us-east-1')
1820 |
1821 | assert not response.isError
1822 | # Should still return results but with error details in the text
1823 | assert 'Error getting details' in response.content[0].text
1824 |
1825 |
1826 | @pytest.mark.asyncio
1827 | async def test_list_s3_buckets_list_objects_error(handler, mock_s3_client):
1828 | """Test error handling when list_objects_v2 fails in list_s3_buckets."""
1829 | handler.s3_client = mock_s3_client
1830 |
1831 | # Mock list_buckets response
1832 | mock_s3_client.list_buckets.return_value = {
1833 | 'Buckets': [{'Name': 'test-glue-bucket', 'CreationDate': datetime(2023, 1, 1)}]
1834 | }
1835 |
1836 | # Mock successful get_bucket_location
1837 | mock_s3_client.get_bucket_location.return_value = {'LocationConstraint': 'us-east-1'}
1838 |
1839 | # Mock list_objects_v2 to raise an exception
1840 | mock_s3_client.list_objects_v2.side_effect = ClientError(
1841 | {'Error': {'Code': 'AccessDenied', 'Message': 'Access denied'}}, 'ListObjectsV2'
1842 | )
1843 |
1844 | ctx = Mock()
1845 | response = await handler.list_s3_buckets(ctx, region='us-east-1')
1846 |
1847 | assert not response.isError
1848 | # Should still return results but with error details in the text
1849 | assert 'Error getting details' in response.content[0].text
1850 |
1851 |
1852 | @pytest.mark.asyncio
1853 | async def test_upload_to_s3_bucket_access_denied(handler, mock_s3_client):
1854 | """Test upload to S3 when access is denied to bucket."""
1855 | handler.s3_client = mock_s3_client
1856 |
1857 | # Mock bucket access denied
1858 | mock_s3_client.head_bucket.side_effect = ClientError(
1859 | {'Error': {'Code': '403', 'Message': 'Forbidden'}}, 'HeadBucket'
1860 | )
1861 |
1862 | ctx = Mock()
1863 | response = await handler.upload_to_s3(
1864 | ctx, code_content="print('test')", bucket_name='forbidden-bucket', s3_key='test.py'
1865 | )
1866 |
1867 | assert response.isError
1868 | assert 'Access denied to bucket' in response.content[0].text
1869 |
1870 |
1871 | @pytest.mark.asyncio
1872 | async def test_create_data_processing_role_attach_policy_error(handler, mock_iam_client):
1873 | """Test error handling when attach_role_policy fails in create_data_processing_role."""
1874 | handler.iam_client = mock_iam_client
1875 |
1876 | # Mock successful create_role
1877 | mock_iam_client.create_role.return_value = {
1878 | 'Role': {'Arn': 'arn:aws:iam::123456789012:role/test-role'}
1879 | }
1880 |
1881 | # Mock attach_role_policy to raise an exception
1882 | mock_iam_client.attach_role_policy.side_effect = ClientError(
1883 | {'Error': {'Code': 'NoSuchEntity', 'Message': 'Policy not found'}}, 'AttachRolePolicy'
1884 | )
1885 |
1886 | ctx = Mock()
1887 | response = await handler.create_data_processing_role(
1888 | ctx,
1889 | role_name='test-role',
1890 | service_type='glue',
1891 | managed_policy_arns=['arn:aws:iam::aws:policy/NonExistentPolicy'],
1892 | )
1893 |
1894 | assert response.isError
1895 | assert 'Failed to create IAM role' in response.content[0].text
1896 |
1897 |
1898 | @pytest.mark.asyncio
1899 | async def test_create_data_processing_role_inline_policy_error(handler, mock_iam_client):
1900 | """Test error handling when put_role_policy fails in create_data_processing_role."""
1901 | handler.iam_client = mock_iam_client
1902 |
1903 | # Mock successful create_role
1904 | mock_iam_client.create_role.return_value = {
1905 | 'Role': {'Arn': 'arn:aws:iam::123456789012:role/test-role'}
1906 | }
1907 |
1908 | # Mock put_role_policy to raise an exception
1909 | mock_iam_client.put_role_policy.side_effect = ClientError(
1910 | {'Error': {'Code': 'MalformedPolicyDocument', 'Message': 'Invalid policy'}},
1911 | 'PutRolePolicy',
1912 | )
1913 |
1914 | inline_policy = {
1915 | 'Effect': 'Allow',
1916 | 'Action': ['s3:GetObject'],
1917 | 'Resource': 'arn:aws:s3:::test-bucket/*',
1918 | }
1919 |
1920 | ctx = Mock()
1921 | response = await handler.create_data_processing_role(
1922 | ctx, role_name='test-role', service_type='glue', inline_policy=inline_policy
1923 | )
1924 |
1925 | assert response.isError
1926 | assert 'Failed to create IAM role' in response.content[0].text
1927 |
```