This is page 544 of 553. 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
│ │ │ └── sql_analyzer.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
│ │ │ └── test_sql_analyzer.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
│ │ ├── .secrets.baseline
│ │ ├── 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
│ │ │ ├── templates
│ │ │ │ ├── __init__.py
│ │ │ │ └── iam_policies.py
│ │ │ ├── tools
│ │ │ │ ├── common
│ │ │ │ │ └── base_tool.py
│ │ │ │ ├── esm
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── esm_diagnosis.py
│ │ │ │ │ ├── esm_guidance.py
│ │ │ │ │ ├── esm_recommend.py
│ │ │ │ │ └── secure_esm_guidance.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
│ │ │ ├── data_scrubber.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_data_scrubber.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_iam_policies.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_secure_esm_guidance.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
│ │ │ │ ├── cloudformation_template_generator.py
│ │ │ │ ├── constants.py
│ │ │ │ ├── data
│ │ │ │ │ └── metric_metadata.json
│ │ │ │ ├── metric_analyzer.py
│ │ │ │ ├── metric_data_decomposer.py
│ │ │ │ ├── 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_analyze_metric.py
│ │ │ │ ├── test_cloudformation_template_generator.py
│ │ │ │ ├── test_decomposer_trend.py
│ │ │ │ ├── test_metric_analyzer.py
│ │ │ │ ├── test_metrics_error_handling.py
│ │ │ │ ├── test_metrics_models.py
│ │ │ │ ├── test_metrics_server.py
│ │ │ │ ├── test_seasonal_detector.py
│ │ │ │ ├── test_seasonality_enum.py
│ │ │ │ ├── test_utils.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
│ │ │ ├── markdown_formatter.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_markdown_formatter.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
│ │ │ ├── connection
│ │ │ │ ├── __init__.py
│ │ │ │ ├── abstract_db_connection.py
│ │ │ │ ├── asyncmy_pool_connection.py
│ │ │ │ ├── db_connection_singleton.py
│ │ │ │ └── rds_data_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_abstract_db_connection.py
│ │ │ ├── test_asyncmy_pool_connection.py
│ │ │ ├── test_db_connection_singleton.py
│ │ │ ├── test_rds_data_api_connection.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/eks-mcp-server/tests/test_k8s_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 | # ruff: noqa: D101, D102, D103
15 | """Tests for the K8sHandler class."""
16 |
17 | import os
18 | import pytest
19 | from awslabs.eks_mcp_server.k8s_apis import K8sApis
20 | from awslabs.eks_mcp_server.k8s_handler import K8sHandler
21 | from mcp.server.fastmcp import Context
22 | from mcp.types import TextContent
23 | from unittest.mock import MagicMock, mock_open, patch
24 |
25 |
26 | @pytest.fixture
27 | def mock_context():
28 | """Create a mock MCP context."""
29 | ctx = MagicMock(spec=Context)
30 | ctx.request_id = 'test-request-id'
31 | return ctx
32 |
33 |
34 | @pytest.fixture
35 | def mock_mcp():
36 | """Create a mock MCP server."""
37 | return MagicMock()
38 |
39 |
40 | @pytest.fixture
41 | def mock_client_cache():
42 | """Create a mock K8sClientCache."""
43 | cache = MagicMock()
44 | mock_k8s_apis = MagicMock(spec=K8sApis)
45 | cache.get_client.return_value = mock_k8s_apis
46 | return cache
47 |
48 |
49 | @pytest.fixture
50 | def mock_k8s_apis():
51 | """Create a mock K8sApis instance."""
52 | return MagicMock(spec=K8sApis)
53 |
54 |
55 | class TestK8sHandler:
56 | """Tests for the K8sHandler class."""
57 |
58 | def test_init(self, mock_mcp, mock_client_cache):
59 | """Test initialization of K8sHandler."""
60 | # Initialize the K8s handler with allow_write=True
61 | with patch(
62 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
63 | ):
64 | handler = K8sHandler(mock_mcp, allow_write=True)
65 |
66 | # Verify that the handler has the correct attributes
67 | assert handler.mcp == mock_mcp
68 | assert handler.client_cache == mock_client_cache
69 | assert handler.allow_write is True
70 | assert handler.allow_sensitive_data_access is False
71 |
72 | # Verify that the tools were registered
73 | assert mock_mcp.tool.call_count == 7
74 |
75 | # Get all call args
76 | call_args_list = mock_mcp.tool.call_args_list
77 |
78 | # Get all tool names that were registered
79 | tool_names = [call_args[1]['name'] for call_args in call_args_list]
80 |
81 | # Verify that expected tools were registered
82 | assert 'list_k8s_resources' in tool_names
83 | assert 'generate_app_manifest' in tool_names
84 | assert 'apply_yaml' in tool_names
85 | assert 'manage_k8s_resource' in tool_names
86 | assert 'get_pod_logs' in tool_names
87 | assert 'get_k8s_events' in tool_names
88 |
89 | def test_init_with_sensitive_data_access(self, mock_mcp, mock_client_cache):
90 | """Test initialization of K8sHandler with sensitive data access enabled."""
91 | # Initialize the K8s handler with sensitive data access enabled
92 | with patch(
93 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
94 | ):
95 | handler = K8sHandler(mock_mcp, allow_write=False, allow_sensitive_data_access=True)
96 |
97 | # Verify that the handler has the correct attributes
98 | assert handler.mcp == mock_mcp
99 | assert handler.client_cache == mock_client_cache
100 | assert handler.allow_write is False
101 | assert handler.allow_sensitive_data_access is True
102 |
103 | # Verify that the tools were registered
104 | assert mock_mcp.tool.call_count == 7
105 |
106 | # Get all call args
107 | call_args_list = mock_mcp.tool.call_args_list
108 |
109 | # Get all tool names that were registered
110 | tool_names = [call_args[1]['name'] for call_args in call_args_list]
111 |
112 | # Verify that expected tools were registered
113 | assert 'list_k8s_resources' in tool_names
114 | assert 'get_pod_logs' in tool_names
115 | assert 'get_k8s_events' in tool_names
116 | assert 'list_api_versions' in tool_names
117 | assert 'manage_k8s_resource' in tool_names
118 | assert 'apply_yaml' in tool_names
119 | assert 'generate_app_manifest' in tool_names
120 |
121 | def test_get_client(self, mock_mcp, mock_client_cache):
122 | """Test get_client method delegates to the client cache."""
123 | # Initialize the K8s handler
124 | with patch(
125 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
126 | ):
127 | handler = K8sHandler(mock_mcp)
128 |
129 | # Get a client
130 | client = handler.get_client('test-cluster')
131 |
132 | # Verify that get_client was called on the cache
133 | mock_client_cache.get_client.assert_called_once_with('test-cluster')
134 |
135 | # Verify that the client was returned
136 | assert client == mock_client_cache.get_client.return_value
137 |
138 | def test_load_yaml_template_removes_checkov_annotations(self, mock_mcp, mock_client_cache):
139 | """Test _load_yaml_template method removes checkov skip annotations from deployment template."""
140 | # Initialize the K8s handler
141 | with patch(
142 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
143 | ):
144 | handler = K8sHandler(mock_mcp)
145 |
146 | # Create mock file content for templates with checkov skip annotations
147 | deployment_template = """apiVersion: apps/v1
148 | kind: Deployment
149 | metadata:
150 | name: APP_NAME
151 | namespace: NAMESPACE
152 | annotations:
153 | checkov.io/skip1: "CKV_K8S_14=We're using a specific image version"
154 | checkov.io/skip2: "CKV_K8S_43=Resource limits are set appropriately"
155 | other-annotation: "This should be preserved"
156 | spec:
157 | replicas: REPLICAS"""
158 |
159 | service_template = """apiVersion: v1
160 | kind: Service
161 | metadata:
162 | name: APP_NAME
163 | namespace: NAMESPACE
164 | annotations:
165 | service.beta.kubernetes.io/aws-load-balancer-scheme: LOAD_BALANCER_SCHEME"""
166 |
167 | # Mock open to return our test templates
168 | mock_open_func = mock_open()
169 | mock_file = MagicMock()
170 | mock_file.__enter__.return_value.read.side_effect = [deployment_template, service_template]
171 | mock_open_func.return_value = mock_file
172 |
173 | # Mock yaml.safe_load and yaml.dump to use real functions
174 | with patch('builtins.open', mock_open_func):
175 | # Test loading and processing templates
176 | template_files = ['deployment.yaml', 'service.yaml']
177 | values = {
178 | 'APP_NAME': 'test-app',
179 | 'NAMESPACE': 'test-namespace',
180 | 'REPLICAS': '3',
181 | 'LOAD_BALANCER_SCHEME': 'internal',
182 | }
183 |
184 | result = handler._load_yaml_template(template_files, values)
185 |
186 | # Verify open was called for each template
187 | assert mock_open_func.call_count == 2
188 |
189 | # Verify template content was properly processed
190 | assert 'kind: Deployment' in result
191 | assert 'kind: Service' in result
192 | assert 'name: test-app' in result
193 | assert 'namespace: test-namespace' in result
194 | assert 'replicas: 3' in result
195 | assert 'service.beta.kubernetes.io/aws-load-balancer-scheme: internal' in result
196 |
197 | # Verify checkov annotations were removed
198 | assert 'checkov.io/skip1' not in result
199 | assert 'checkov.io/skip2' not in result
200 |
201 | # Verify other annotations were preserved
202 | assert 'other-annotation: This should be preserved' in result
203 |
204 | @pytest.mark.asyncio
205 | async def test_apply_yaml_relative_path(self, mock_context, mock_mcp, mock_client_cache):
206 | """Test apply_yaml method with a relative path."""
207 | # Initialize the K8s handler
208 | with patch(
209 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
210 | ):
211 | handler = K8sHandler(mock_mcp)
212 |
213 | # Mock os.path.isabs to return False for relative paths
214 | with patch('os.path.isabs', return_value=False):
215 | # Apply YAML from a relative path
216 | result = await handler.apply_yaml(
217 | mock_context,
218 | yaml_path='relative/path/to/manifest.yaml',
219 | cluster_name='test-cluster',
220 | namespace='default',
221 | force=True,
222 | )
223 |
224 | # Verify the result
225 | assert result.isError
226 | assert isinstance(result.content[0], TextContent)
227 | assert 'Path must be absolute' in result.content[0].text
228 | assert 'relative/path/to/manifest.yaml' in result.content[0].text
229 |
230 | @pytest.mark.asyncio
231 | async def test_apply_yaml_success(self, mock_context, mock_mcp, mock_client_cache):
232 | """Test apply_yaml method with successful application."""
233 | # Initialize the K8s handler
234 | with patch(
235 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
236 | ):
237 | handler = K8sHandler(mock_mcp)
238 |
239 | # Mock get_client
240 | mock_k8s_apis = MagicMock()
241 | with patch.object(handler, 'get_client', return_value=mock_k8s_apis) as mock_client:
242 | # Mock os.path.isabs to return True for absolute paths
243 | with patch('os.path.isabs', return_value=True):
244 | # Mock open to read the YAML file
245 | yaml_content = """apiVersion: v1
246 | kind: Namespace
247 | metadata:
248 | name: test-namespace
249 | """
250 | with patch('builtins.open', mock_open(read_data=yaml_content)) as mocked_open:
251 | # Mock apply_from_yaml
252 | mock_k8s_apis.apply_from_yaml.return_value = ([], 1, 0)
253 |
254 | # Apply YAML from file
255 | result = await handler.apply_yaml(
256 | mock_context,
257 | yaml_path='/path/to/manifest.yaml',
258 | cluster_name='test-cluster',
259 | namespace='default',
260 | force=True,
261 | )
262 |
263 | # Verify that get_client was called
264 | mock_client.assert_called_once_with('test-cluster')
265 |
266 | # Verify that open was called with the correct path
267 | mocked_open.assert_called_once_with('/path/to/manifest.yaml', 'r')
268 |
269 | # Verify that apply_from_yaml was called with the correct parameters
270 | mock_k8s_apis.apply_from_yaml.assert_called_once()
271 | args, kwargs = mock_k8s_apis.apply_from_yaml.call_args
272 | assert len(kwargs['yaml_objects']) == 1
273 | assert kwargs['yaml_objects'][0]['kind'] == 'Namespace'
274 | assert kwargs['namespace'] == 'default'
275 | assert kwargs['force'] is True
276 |
277 | # Verify the result
278 | assert not result.isError
279 | assert isinstance(result.content[0], TextContent)
280 | assert (
281 | 'Successfully applied all resources from YAML file'
282 | in result.content[0].text
283 | )
284 |
285 | @pytest.mark.asyncio
286 | async def test_apply_yaml_file_not_found(self, mock_context, mock_mcp, mock_client_cache):
287 | """Test apply_yaml method with file not found error."""
288 | # Initialize the K8s handler
289 | with patch(
290 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
291 | ):
292 | handler = K8sHandler(mock_mcp)
293 |
294 | # Mock get_client
295 | mock_k8s_apis = MagicMock()
296 | with patch.object(handler, 'get_client', return_value=mock_k8s_apis) as mock_client:
297 | # Mock os.path.isabs to return True for absolute paths
298 | with patch('os.path.isabs', return_value=True):
299 | # Mock open to raise FileNotFoundError
300 | with patch('builtins.open', side_effect=FileNotFoundError()):
301 | # Apply YAML from file
302 | result = await handler.apply_yaml(
303 | mock_context,
304 | yaml_path='/path/to/nonexistent.yaml',
305 | cluster_name='test-cluster',
306 | namespace='default',
307 | force=True,
308 | )
309 |
310 | # Verify that get_client was called
311 | mock_client.assert_called_once_with('test-cluster')
312 |
313 | # Verify the result
314 | assert result.isError
315 | assert isinstance(result.content[0], TextContent)
316 | assert 'YAML file not found' in result.content[0].text
317 |
318 | @pytest.mark.asyncio
319 | async def test_apply_yaml_io_error(self, mock_context, mock_mcp, mock_client_cache):
320 | """Test apply_yaml method with IO error."""
321 | # Initialize the K8s handler
322 | with patch(
323 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
324 | ):
325 | handler = K8sHandler(mock_mcp)
326 |
327 | # Mock get_client
328 | mock_k8s_apis = MagicMock()
329 | with patch.object(handler, 'get_client', return_value=mock_k8s_apis) as mock_client:
330 | # Mock os.path.isabs to return True for absolute paths
331 | with patch('os.path.isabs', return_value=True):
332 | # Mock open to raise IOError
333 | with patch('builtins.open', side_effect=IOError('Permission denied')):
334 | # Apply YAML from file
335 | result = await handler.apply_yaml(
336 | mock_context,
337 | yaml_path='/path/to/protected.yaml',
338 | cluster_name='test-cluster',
339 | namespace='default',
340 | force=True,
341 | )
342 |
343 | # Verify that get_client was called
344 | mock_client.assert_called_once_with('test-cluster')
345 |
346 | # Verify the result
347 | assert result.isError
348 | assert isinstance(result.content[0], TextContent)
349 | assert 'Error reading YAML file' in result.content[0].text
350 | assert 'Permission denied' in result.content[0].text
351 |
352 | @pytest.mark.asyncio
353 | async def test_apply_yaml_create_error(self, mock_context, mock_mcp, mock_client_cache):
354 | """Test apply_yaml method with error from create_from_yaml."""
355 | # Initialize the K8s handler
356 | with patch(
357 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
358 | ):
359 | handler = K8sHandler(mock_mcp)
360 |
361 | # Mock get_client
362 | mock_k8s_apis = MagicMock()
363 | with patch.object(handler, 'get_client', return_value=mock_k8s_apis):
364 | # Mock os.path.isabs to return True for absolute paths
365 | with patch('os.path.isabs', return_value=True):
366 | # Mock open to read the YAML file
367 | yaml_content = """apiVersion: v1
368 | kind: Namespace
369 | metadata:
370 | name: test-namespace
371 | """
372 | with patch('builtins.open', mock_open(read_data=yaml_content)):
373 | # Mock apply_from_yaml to raise an exception
374 | mock_k8s_apis.apply_from_yaml.side_effect = Exception(
375 | 'Failed to create resource'
376 | )
377 |
378 | # Apply YAML from file
379 | result = await handler.apply_yaml(
380 | mock_context,
381 | yaml_path='/path/to/manifest.yaml',
382 | cluster_name='test-cluster',
383 | namespace='default',
384 | force=True,
385 | )
386 |
387 | # Verify the result
388 | assert result.isError
389 | assert isinstance(result.content[0], TextContent)
390 | assert 'Failed to apply YAML from file' in result.content[0].text
391 | assert 'Failed to create resource' in result.content[0].text
392 |
393 | # Note: TTL cache expiration tests have been moved to test_k8s_client_cache.py
394 |
395 | @pytest.mark.asyncio
396 | async def test_manage_k8s_resource_create(self, mock_context, mock_mcp, mock_client_cache):
397 | """Test manage_k8s_resource method with create operation."""
398 | # Initialize the K8s handler
399 | with patch(
400 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
401 | ):
402 | handler = K8sHandler(mock_mcp, allow_write=True)
403 |
404 | # Mock the get_client method and k8s_apis.manage_resource
405 | mock_k8s_apis = MagicMock()
406 | mock_response = MagicMock()
407 | mock_response.to_dict.return_value = {
408 | 'kind': 'Pod',
409 | 'apiVersion': 'v1',
410 | 'metadata': {'name': 'test-pod', 'namespace': 'test-namespace'},
411 | }
412 | mock_k8s_apis.manage_resource.return_value = mock_response
413 |
414 | with patch.object(handler, 'get_client', return_value=mock_k8s_apis) as mock_client:
415 | # Create a test resource
416 | body = {
417 | 'metadata': {'name': 'test-pod'},
418 | 'spec': {'containers': [{'name': 'test-container', 'image': 'nginx'}]},
419 | }
420 |
421 | result = await handler.manage_k8s_resource(
422 | mock_context,
423 | operation='create',
424 | cluster_name='test-cluster',
425 | kind='Pod',
426 | api_version='v1',
427 | name='test-pod',
428 | namespace='test-namespace',
429 | body=body,
430 | )
431 |
432 | # Verify that get_client was called
433 | mock_client.assert_called_once_with('test-cluster')
434 |
435 | # Verify that manage_resource was called with the correct parameters
436 | mock_k8s_apis.manage_resource.assert_called_once()
437 |
438 | # Verify the result
439 | assert not result.isError
440 | assert result.kind == 'Pod'
441 | assert result.name == 'test-pod'
442 | assert result.namespace == 'test-namespace'
443 | assert result.api_version == 'v1'
444 | assert result.operation == 'create'
445 | assert isinstance(result.content[0], TextContent)
446 | assert 'Successfully created Pod test-namespace/test-pod' in result.content[0].text
447 |
448 | @pytest.mark.asyncio
449 | async def test_read_k8s_resource(self, mock_context, mock_mcp, mock_client_cache):
450 | """Test read_k8s_resource method."""
451 | # Initialize the K8s handler
452 | with patch(
453 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
454 | ):
455 | handler = K8sHandler(mock_mcp)
456 |
457 | # Mock the get_client method and k8s_apis.manage_resource
458 | mock_k8s_apis = MagicMock()
459 | mock_response = MagicMock()
460 | mock_response.to_dict.return_value = {
461 | 'kind': 'Pod',
462 | 'apiVersion': 'v1',
463 | 'metadata': {'name': 'test-pod', 'namespace': 'test-namespace'},
464 | }
465 | mock_k8s_apis.manage_resource.return_value = mock_response
466 |
467 | with patch.object(handler, 'get_client', return_value=mock_k8s_apis):
468 | result = await handler.manage_k8s_resource(
469 | mock_context,
470 | operation='read',
471 | cluster_name='test-cluster',
472 | kind='Pod',
473 | api_version='v1',
474 | name='test-pod',
475 | namespace='test-namespace',
476 | )
477 |
478 | # Verify the result
479 | assert not result.isError
480 | assert result.kind == 'Pod'
481 | assert result.name == 'test-pod'
482 | assert result.namespace == 'test-namespace'
483 | assert result.api_version == 'v1'
484 | assert result.operation == 'read'
485 | assert result.resource is not None
486 | assert isinstance(result.content[0], TextContent)
487 | assert 'Successfully retrieved Pod test-namespace/test-pod' in result.content[0].text
488 |
489 | @pytest.mark.asyncio
490 | async def test_manage_k8s_resource_invalid_operation(
491 | self, mock_context, mock_mcp, mock_client_cache
492 | ):
493 | """Test manage_k8s_resource method with an invalid operation."""
494 | # Initialize the K8s handler
495 | with patch(
496 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
497 | ):
498 | handler = K8sHandler(mock_mcp)
499 |
500 | # Call manage_k8s_resource with an invalid operation
501 | result = await handler.manage_k8s_resource(
502 | mock_context,
503 | operation='invalid',
504 | cluster_name='test-cluster',
505 | kind='Pod',
506 | api_version='v1',
507 | name='test-pod',
508 | namespace='test-namespace',
509 | )
510 |
511 | # Verify the result
512 | assert result.isError
513 | assert result.kind == 'Pod'
514 | assert result.name == 'test-pod'
515 | assert result.namespace == 'test-namespace'
516 | assert result.api_version == 'v1'
517 | assert result.operation == 'invalid'
518 | assert isinstance(result.content[0], TextContent)
519 | assert 'Invalid operation: invalid' in result.content[0].text
520 |
521 | @pytest.mark.asyncio
522 | async def test_manage_k8s_resource_error(self, mock_context, mock_mcp, mock_client_cache):
523 | """Test manage_k8s_resource method with an error."""
524 | # Initialize the K8s handler
525 | with patch(
526 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
527 | ):
528 | handler = K8sHandler(mock_mcp)
529 |
530 | # Mock get_client
531 | mock_k8s_apis = MagicMock()
532 | mock_k8s_apis.manage_resource.side_effect = Exception('Resource not found')
533 |
534 | with patch.object(handler, 'get_client', return_value=mock_k8s_apis) as mock_client:
535 | result = await handler.manage_k8s_resource(
536 | mock_context,
537 | operation='read',
538 | cluster_name='test-cluster',
539 | kind='Pod',
540 | api_version='v1',
541 | name='test-pod',
542 | namespace='test-namespace',
543 | )
544 |
545 | # Verify that get_client was called
546 | mock_client.assert_called_once_with('test-cluster')
547 |
548 | # Verify that manage_resource was called with the correct parameters
549 | mock_k8s_apis.manage_resource.assert_called_once()
550 |
551 | # Verify the result
552 | assert result.isError
553 | assert result.kind == 'Pod'
554 | assert result.name == 'test-pod'
555 | assert result.namespace == 'test-namespace'
556 | assert result.api_version == 'v1'
557 | assert result.operation == 'read'
558 | assert isinstance(result.content[0], TextContent)
559 | assert (
560 | 'Failed to read Pod test-namespace/test-pod: Resource not found'
561 | in result.content[0].text
562 | )
563 |
564 | @pytest.mark.asyncio
565 | async def test_manage_k8s_resource_secret_sensitive_data_access_disabled(
566 | self, mock_context, mock_mcp, mock_client_cache
567 | ):
568 | """Test manage_k8s_resource method with Secret kind and sensitive data access disabled."""
569 | # Initialize the K8s handler with sensitive data access disabled but write access enabled
570 | with patch(
571 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
572 | ):
573 | handler = K8sHandler(mock_mcp, allow_write=True, allow_sensitive_data_access=False)
574 |
575 | # Test with read operation on Secret (should be rejected)
576 | result = await handler.manage_k8s_resource(
577 | mock_context,
578 | operation='read',
579 | cluster_name='test-cluster',
580 | kind='Secret',
581 | api_version='v1',
582 | name='test-secret',
583 | namespace='test-namespace',
584 | )
585 |
586 | # Verify the result
587 | assert result.isError
588 | assert result.kind == 'Secret'
589 | assert result.name == 'test-secret'
590 | assert result.namespace == 'test-namespace'
591 | assert result.api_version == 'v1'
592 | assert result.operation == 'read'
593 | assert isinstance(result.content[0], TextContent)
594 | assert (
595 | 'Access to Kubernetes Secrets requires --allow-sensitive-data-access flag'
596 | in result.content[0].text
597 | )
598 |
599 | # Test with create operation on Secret (should be rejected for sensitive data access, not write access)
600 | result = await handler.manage_k8s_resource(
601 | mock_context,
602 | operation='create',
603 | cluster_name='test-cluster',
604 | kind='Secret',
605 | api_version='v1',
606 | name='test-secret',
607 | namespace='test-namespace',
608 | body={'metadata': {'name': 'test-secret'}, 'data': {'key': 'dmFsdWU='}},
609 | )
610 |
611 | # Verify the result
612 | assert not result.isError
613 | assert result.kind == 'Secret'
614 | assert result.name == 'test-secret'
615 | assert result.namespace == 'test-namespace'
616 | assert result.api_version == 'v1'
617 | assert result.operation == 'create'
618 | assert isinstance(result.content[0], TextContent)
619 |
620 | @pytest.mark.asyncio
621 | async def test_manage_k8s_resource_write_access_disabled(
622 | self, mock_context, mock_mcp, mock_client_cache
623 | ):
624 | """Test manage_k8s_resource method with write access disabled for mutable operations."""
625 | # Initialize the K8s handler with write access disabled
626 | with patch(
627 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
628 | ):
629 | handler = K8sHandler(mock_mcp, allow_write=False)
630 |
631 | # Test with create operation (should be rejected)
632 | result = await handler.manage_k8s_resource(
633 | mock_context,
634 | operation='create',
635 | cluster_name='test-cluster',
636 | kind='Pod',
637 | api_version='v1',
638 | name='test-pod',
639 | namespace='test-namespace',
640 | body={'metadata': {'name': 'test-pod'}},
641 | )
642 |
643 | # Verify the result
644 | assert result.isError
645 | assert result.kind == 'Pod'
646 | assert result.name == 'test-pod'
647 | assert result.namespace == 'test-namespace'
648 | assert result.api_version == 'v1'
649 | assert result.operation == 'create'
650 | assert isinstance(result.content[0], TextContent)
651 | assert 'Operation create is not allowed without write access' in result.content[0].text
652 |
653 | # Test with replace operation (should be rejected when write access is disabled)
654 | result = await handler.manage_k8s_resource(
655 | mock_context,
656 | operation='replace',
657 | cluster_name='test-cluster',
658 | kind='Pod',
659 | api_version='v1',
660 | name='test-pod',
661 | namespace='test-namespace',
662 | body={'metadata': {'name': 'test-pod'}},
663 | )
664 |
665 | # Verify the result
666 | assert result.isError
667 | assert result.operation == 'replace'
668 | assert isinstance(result.content[0], TextContent)
669 | assert 'Operation replace is not allowed without write access' in result.content[0].text
670 |
671 | # Test with patch operation (should be rejected when write access is disabled)
672 | result = await handler.manage_k8s_resource(
673 | mock_context,
674 | operation='patch',
675 | cluster_name='test-cluster',
676 | kind='Pod',
677 | api_version='v1',
678 | name='test-pod',
679 | namespace='test-namespace',
680 | body={'metadata': {'labels': {'app': 'test'}}},
681 | )
682 |
683 | # Verify the result
684 | assert result.isError
685 | assert result.operation == 'patch'
686 | assert isinstance(result.content[0], TextContent)
687 | assert 'Operation patch is not allowed without write access' in result.content[0].text
688 |
689 | # Test with delete operation (should be rejected when write access is disabled)
690 | result = await handler.manage_k8s_resource(
691 | mock_context,
692 | operation='delete',
693 | cluster_name='test-cluster',
694 | kind='Pod',
695 | api_version='v1',
696 | name='test-pod',
697 | namespace='test-namespace',
698 | )
699 |
700 | # Verify the result
701 | assert result.isError
702 | assert result.operation == 'delete'
703 | assert isinstance(result.content[0], TextContent)
704 | assert 'Operation delete is not allowed without write access' in result.content[0].text
705 |
706 | # Test with read operation (should be allowed even when write access is disabled)
707 | mock_k8s_apis = MagicMock()
708 | mock_response = MagicMock()
709 | mock_response.to_dict.return_value = {
710 | 'kind': 'Pod',
711 | 'apiVersion': 'v1',
712 | 'metadata': {'name': 'test-pod', 'namespace': 'test-namespace'},
713 | }
714 | mock_k8s_apis.manage_resource.return_value = mock_response
715 |
716 | with patch.object(handler, 'get_client', return_value=mock_k8s_apis) as mock_client:
717 | result = await handler.manage_k8s_resource(
718 | mock_context,
719 | operation='read',
720 | cluster_name='test-cluster',
721 | kind='Pod',
722 | api_version='v1',
723 | name='test-pod',
724 | namespace='test-namespace',
725 | )
726 |
727 | # Verify that get_client was called
728 | mock_client.assert_called_once_with('test-cluster')
729 |
730 | # Verify that manage_resource was called
731 | mock_k8s_apis.manage_resource.assert_called_once()
732 |
733 | # Verify the result
734 | assert not result.isError
735 | assert result.kind == 'Pod'
736 | assert result.name == 'test-pod'
737 | assert result.namespace == 'test-namespace'
738 | assert result.api_version == 'v1'
739 | assert result.operation == 'read'
740 | assert isinstance(result.content[0], TextContent)
741 | assert 'Successfully retrieved Pod test-namespace/test-pod' in result.content[0].text
742 |
743 | @pytest.mark.asyncio
744 | async def test_list_k8s_resources_success(self, mock_context, mock_mcp, mock_client_cache):
745 | """Test list_k8s_resources method with successful listing."""
746 | # Initialize the K8s handler
747 | with patch(
748 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
749 | ):
750 | handler = K8sHandler(mock_mcp)
751 |
752 | # Mock get_client
753 | mock_k8s_apis = MagicMock()
754 |
755 | # Mock response with items
756 | mock_item1 = MagicMock()
757 | mock_item1.to_dict.return_value = {
758 | 'metadata': {
759 | 'name': 'test-pod-1',
760 | 'namespace': 'test-namespace',
761 | 'creation_timestamp': '2023-01-01T00:00:00Z',
762 | 'labels': {'app': 'test'},
763 | 'annotations': {'description': 'Test pod 1'},
764 | }
765 | }
766 | mock_item2 = MagicMock()
767 | mock_item2.to_dict.return_value = {
768 | 'metadata': {
769 | 'name': 'test-pod-2',
770 | 'namespace': 'test-namespace',
771 | 'creation_timestamp': '2023-01-02T00:00:00Z',
772 | 'labels': {'app': 'test'},
773 | 'annotations': {'description': 'Test pod 2'},
774 | }
775 | }
776 |
777 | mock_response = MagicMock()
778 | mock_response.items = [mock_item1, mock_item2]
779 | mock_k8s_apis.list_resources.return_value = mock_response
780 |
781 | with patch.object(handler, 'get_client', return_value=mock_k8s_apis) as mock_client:
782 | result = await handler.list_k8s_resources(
783 | mock_context,
784 | cluster_name='test-cluster',
785 | kind='Pod',
786 | api_version='v1',
787 | namespace='test-namespace',
788 | label_selector='app=test',
789 | )
790 |
791 | # Verify that get_client was called
792 | mock_client.assert_called_once_with('test-cluster')
793 |
794 | # Verify that list_resources was called once
795 | mock_k8s_apis.list_resources.assert_called_once()
796 |
797 | # Get the call args
798 | args, kwargs = mock_k8s_apis.list_resources.call_args
799 |
800 | # Verify the positional args
801 | assert args[0] == 'Pod'
802 | assert args[1] == 'v1'
803 |
804 | # Verify the keyword args
805 | assert kwargs['namespace'] == 'test-namespace'
806 | assert kwargs['label_selector'] == 'app=test'
807 |
808 | # Verify the result
809 | assert not result.isError
810 | assert result.kind == 'Pod'
811 | assert result.api_version == 'v1'
812 | assert result.namespace == 'test-namespace'
813 | assert result.count == 2
814 | assert len(result.items) == 2
815 | assert result.items[0].name == 'test-pod-1'
816 | assert result.items[0].namespace == 'test-namespace'
817 | # Don't check creation_timestamp as it might be None in the actual implementation
818 | assert result.items[0].labels == {'app': 'test'}
819 | assert result.items[0].annotations == {'description': 'Test pod 1'}
820 | assert result.items[1].name == 'test-pod-2'
821 | assert isinstance(result.content[0], TextContent)
822 | assert (
823 | 'Successfully listed 2 Pod resources in test-namespace/' in result.content[0].text
824 | )
825 |
826 | @pytest.mark.asyncio
827 | async def test_list_k8s_resources_empty(self, mock_context, mock_mcp, mock_client_cache):
828 | """Test list_k8s_resources method with empty result."""
829 | # Initialize the K8s handler
830 | with patch(
831 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
832 | ):
833 | handler = K8sHandler(mock_mcp)
834 |
835 | # Mock get_client
836 | mock_k8s_apis = MagicMock()
837 |
838 | # Mock response with no items
839 | mock_response = MagicMock()
840 | mock_response.items = []
841 | mock_k8s_apis.list_resources.return_value = mock_response
842 |
843 | with patch.object(handler, 'get_client', return_value=mock_k8s_apis):
844 | result = await handler.list_k8s_resources(
845 | mock_context,
846 | cluster_name='test-cluster',
847 | kind='Pod',
848 | api_version='v1',
849 | namespace='test-namespace',
850 | )
851 |
852 | # Verify the result
853 | assert not result.isError
854 | assert result.kind == 'Pod'
855 | assert result.count == 0
856 | assert len(result.items) == 0
857 | assert isinstance(result.content[0], TextContent)
858 | assert (
859 | 'Successfully listed 0 Pod resources in test-namespace/' in result.content[0].text
860 | )
861 |
862 | @pytest.mark.asyncio
863 | async def test_list_k8s_resources_error(self, mock_context, mock_mcp, mock_client_cache):
864 | """Test list_k8s_resources method with an error."""
865 | # Initialize the K8s handler
866 | with patch(
867 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
868 | ):
869 | handler = K8sHandler(mock_mcp)
870 |
871 | # Mock get_client
872 | mock_k8s_apis = MagicMock()
873 | mock_k8s_apis.list_resources.side_effect = Exception('Failed to list resources')
874 |
875 | with patch.object(handler, 'get_client', return_value=mock_k8s_apis):
876 | result = await handler.list_k8s_resources(
877 | mock_context,
878 | cluster_name='test-cluster',
879 | kind='Pod',
880 | api_version='v1',
881 | namespace='test-namespace',
882 | )
883 |
884 | # Verify the result
885 | assert result.isError
886 | assert result.kind == 'Pod'
887 | assert result.count == 0
888 | assert len(result.items) == 0
889 | assert isinstance(result.content[0], TextContent)
890 | assert (
891 | 'Failed to list Pod resources: Failed to list resources' in result.content[0].text
892 | )
893 |
894 | @pytest.mark.asyncio
895 | async def test_generate_app_manifest_write_access_disabled(
896 | self, mock_context, mock_mcp, mock_client_cache
897 | ):
898 | """Test generate_app_manifest method with write access disabled."""
899 | # Initialize the K8s handler with write access disabled
900 | with patch(
901 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
902 | ):
903 | handler = K8sHandler(mock_mcp, allow_write=False)
904 |
905 | # Generate manifest with write access disabled
906 | result = await handler.generate_app_manifest(
907 | mock_context,
908 | app_name='test-app',
909 | image_uri='123456789012.dkr.ecr.region.amazonaws.com/repo:tag',
910 | output_dir='/absolute/path/to/output',
911 | )
912 |
913 | # Verify the result
914 | assert result.isError
915 | assert isinstance(result.content[0], TextContent)
916 | assert (
917 | 'Operation generate_app_manifest is not allowed without write access'
918 | in result.content[0].text
919 | )
920 | assert result.output_file_path == ''
921 |
922 | @pytest.mark.asyncio
923 | async def test_generate_app_manifest_relative_path(
924 | self, mock_context, mock_mcp, mock_client_cache
925 | ):
926 | """Test generate_app_manifest method with a relative path."""
927 | # Initialize the K8s handler with write access enabled
928 | with patch(
929 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
930 | ):
931 | handler = K8sHandler(mock_mcp, allow_write=True)
932 |
933 | # Mock os.path.isabs to return False for relative paths
934 | with patch('os.path.isabs', return_value=False):
935 | # Generate manifest with a relative path
936 | result = await handler.generate_app_manifest(
937 | mock_context,
938 | app_name='test-app',
939 | image_uri='123456789012.dkr.ecr.region.amazonaws.com/repo:tag',
940 | output_dir='relative/path/to/output',
941 | )
942 |
943 | # Verify the result
944 | assert result.isError
945 | assert isinstance(result.content[0], TextContent)
946 | assert 'Output directory path must be absolute' in result.content[0].text
947 | assert 'relative/path/to/output' in result.content[0].text
948 |
949 | @pytest.mark.asyncio
950 | async def test_generate_app_manifest_success(self, mock_context, mock_mcp, mock_client_cache):
951 | """Test generate_app_manifest with successful creation."""
952 | # Initialize the K8s handler with write access enabled
953 | with patch(
954 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
955 | ):
956 | handler = K8sHandler(mock_mcp, allow_write=True)
957 |
958 | # Mock the _load_yaml_template method to avoid template loading issues
959 | with patch.object(handler, '_load_yaml_template', return_value='combined yaml content'):
960 | # Mock os.path.isabs to return True for absolute paths
961 | with patch('os.path.isabs', return_value=True):
962 | # Mock os.makedirs to avoid creating directories
963 | with patch('os.makedirs') as mock_makedirs:
964 | # Mock open for writing output
965 | with patch('builtins.open', mock_open()) as mocked_open:
966 | # Mock os.path.abspath to return a predictable absolute path
967 | with patch(
968 | 'os.path.abspath',
969 | return_value='/absolute/path/test-output/test-app-manifest.yaml',
970 | ):
971 | # Generate the manifest
972 | result = await handler.generate_app_manifest(
973 | mock_context,
974 | app_name='test-app',
975 | image_uri='123456789012.dkr.ecr.region.amazonaws.com/repo:tag',
976 | port=8080,
977 | replicas=3,
978 | cpu='250m',
979 | memory='256Mi',
980 | namespace='test-namespace',
981 | load_balancer_scheme='internet-facing',
982 | output_dir='/absolute/path/test-output',
983 | )
984 |
985 | # Verify that os.makedirs was called with exist_ok=True
986 | mock_makedirs.assert_called_once_with(
987 | '/absolute/path/test-output', exist_ok=True
988 | )
989 |
990 | # Verify that open was called for writing output
991 | mocked_open.assert_called_once_with(
992 | '/absolute/path/test-output/test-app-manifest.yaml', 'w'
993 | )
994 |
995 | # Verify the result
996 | assert not result.isError
997 | assert isinstance(result.content[0], TextContent)
998 | assert (
999 | 'Successfully generated YAML for test-app'
1000 | in result.content[0].text
1001 | )
1002 | assert (
1003 | 'with image 123456789012.dkr.ecr.region.amazonaws.com/repo:tag'
1004 | in result.content[0].text
1005 | )
1006 |
1007 | # Verify that the output path is absolute
1008 | assert os.path.isabs(result.output_file_path)
1009 | assert (
1010 | result.output_file_path
1011 | == '/absolute/path/test-output/test-app-manifest.yaml'
1012 | )
1013 |
1014 | @pytest.mark.asyncio
1015 | async def test_generate_app_manifest_error(self, mock_context, mock_mcp, mock_client_cache):
1016 | """Test generate_app_manifest with an error."""
1017 | # Initialize the K8s handler with write access enabled
1018 | with patch(
1019 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
1020 | ):
1021 | handler = K8sHandler(mock_mcp, allow_write=True)
1022 |
1023 | # Mock os.path.isabs to return True for absolute paths
1024 | with patch('os.path.isabs', return_value=True):
1025 | # Mock open function to raise an exception
1026 | with patch('builtins.open', side_effect=Exception('File error')):
1027 | # Generate the manifest with an absolute path
1028 | result = await handler.generate_app_manifest(
1029 | mock_context,
1030 | app_name='test-app',
1031 | image_uri='123456789012.dkr.ecr.region.amazonaws.com/repo:tag',
1032 | output_dir='/absolute/path/to/output', # Use an absolute path
1033 | )
1034 |
1035 | # Verify the result
1036 | assert result.isError
1037 | assert isinstance(result.content[0], TextContent)
1038 | assert 'Failed to generate YAML' in result.content[0].text
1039 | assert 'File error' in result.content[0].text
1040 | assert result.output_file_path == ''
1041 |
1042 | def test_load_yaml_template(self, mock_mcp, mock_client_cache):
1043 | """Test _load_yaml_template method."""
1044 | # Initialize the K8s handler
1045 | with patch(
1046 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
1047 | ):
1048 | handler = K8sHandler(mock_mcp)
1049 |
1050 | # Create mock file content for templates
1051 | template1 = 'kind: Deployment\nmetadata:\n name: APP_NAME'
1052 | template2 = 'kind: Service\nmetadata:\n name: APP_NAME'
1053 |
1054 | # Mock open to return our test templates
1055 | mock_open = MagicMock()
1056 | mock_file = MagicMock()
1057 | mock_file.__enter__.return_value.read.side_effect = [template1, template2]
1058 | mock_open.return_value = mock_file
1059 |
1060 | with patch('builtins.open', mock_open):
1061 | # Test loading and processing templates
1062 | template_files = ['file1.yaml', 'file2.yaml']
1063 | values = {'APP_NAME': 'test-app'}
1064 |
1065 | result = handler._load_yaml_template(template_files, values)
1066 |
1067 | # Verify open was called for each template
1068 | assert mock_open.call_count == 2
1069 |
1070 | # Verify template content was properly processed
1071 | assert 'kind: Deployment' in result
1072 | assert 'kind: Service' in result
1073 | assert 'name: test-app' in result
1074 | assert '---' in result
1075 |
1076 | @pytest.mark.asyncio
1077 | async def test_generate_app_manifest_with_absolute_path(
1078 | self, mock_context, mock_mcp, mock_client_cache
1079 | ):
1080 | """Test generate_app_manifest with an absolute path."""
1081 | # Initialize the K8s handler with write access enabled
1082 | with patch(
1083 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
1084 | ):
1085 | handler = K8sHandler(mock_mcp, allow_write=True)
1086 |
1087 | # Mock the _load_yaml_template method to avoid template loading issues
1088 | with patch.object(handler, '_load_yaml_template', return_value='combined yaml content'):
1089 | # Mock os.path.isabs to return True for absolute paths
1090 | with patch('os.path.isabs', return_value=True):
1091 | # Mock os.makedirs to avoid creating directories
1092 | with patch('os.makedirs') as mock_makedirs:
1093 | # Mock open for writing output
1094 | with patch('builtins.open', mock_open()) as mocked_open:
1095 | # Mock os.path.abspath to return a predictable absolute path
1096 | with patch(
1097 | 'os.path.abspath',
1098 | return_value='/path/to/output/test-app-manifest.yaml',
1099 | ):
1100 | # Generate the manifest with an absolute path
1101 | result = await handler.generate_app_manifest(
1102 | mock_context,
1103 | app_name='test-app',
1104 | image_uri='123456789012.dkr.ecr.region.amazonaws.com/repo:tag',
1105 | output_dir='/path/to/output',
1106 | )
1107 |
1108 | # Verify that os.makedirs was called with exist_ok=True
1109 | mock_makedirs.assert_called_once_with('/path/to/output', exist_ok=True)
1110 |
1111 | # Verify that open was called for writing output
1112 | mocked_open.assert_called_once_with(
1113 | '/path/to/output/test-app-manifest.yaml', 'w'
1114 | )
1115 |
1116 | # Verify the output file path is absolute
1117 | assert os.path.isabs(result.output_file_path)
1118 | assert (
1119 | result.output_file_path == '/path/to/output/test-app-manifest.yaml'
1120 | )
1121 |
1122 | # Verify the result is successful
1123 | assert not result.isError
1124 |
1125 | @pytest.mark.asyncio
1126 | async def test_generate_app_manifest_multiple_templates(
1127 | self, mock_context, mock_mcp, mock_client_cache
1128 | ):
1129 | """Test generate_app_manifest with multiple templates."""
1130 | # Initialize the K8s handler with write access enabled
1131 | with patch(
1132 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
1133 | ):
1134 | handler = K8sHandler(mock_mcp, allow_write=True)
1135 |
1136 | # Mock the _load_yaml_template method to avoid template loading issues
1137 | with patch.object(handler, '_load_yaml_template', return_value='combined yaml content'):
1138 | # Mock os.path.isabs to return True for absolute paths
1139 | with patch('os.path.isabs', return_value=True):
1140 | # Mock os.makedirs to avoid creating directories
1141 | with patch('os.makedirs') as mock_makedirs:
1142 | # Mock open for writing output
1143 | with patch('builtins.open', mock_open()) as mocked_open:
1144 | # Mock os.path.abspath to return a predictable absolute path
1145 | with patch(
1146 | 'os.path.abspath',
1147 | return_value='/absolute/path/output/test-app-manifest.yaml',
1148 | ):
1149 | # Generate the manifest with all required parameters explicitly specified
1150 | result = await handler.generate_app_manifest(
1151 | mock_context,
1152 | app_name='test-app',
1153 | image_uri='123456789012.dkr.ecr.region.amazonaws.com/repo:tag',
1154 | port=80,
1155 | replicas=2,
1156 | cpu='100m',
1157 | memory='128Mi',
1158 | namespace='default',
1159 | load_balancer_scheme='internal', # Using the default value
1160 | output_dir='/absolute/path/output',
1161 | )
1162 |
1163 | # Verify that os.makedirs was called with exist_ok=True
1164 | mock_makedirs.assert_called_once_with(
1165 | '/absolute/path/output', exist_ok=True
1166 | )
1167 |
1168 | # Verify that open was called for writing output
1169 | mocked_open.assert_called_once_with(
1170 | '/absolute/path/output/test-app-manifest.yaml', 'w'
1171 | )
1172 |
1173 | # Verify the output file path is absolute
1174 | assert os.path.isabs(result.output_file_path)
1175 | assert (
1176 | result.output_file_path
1177 | == '/absolute/path/output/test-app-manifest.yaml'
1178 | )
1179 |
1180 | # Verify the result is successful
1181 | assert not result.isError
1182 |
1183 | def test_init_with_get_pod_logs(self, mock_mcp, mock_client_cache):
1184 | """Test initialization of K8sHandler with get_pod_logs tool."""
1185 | # Initialize the K8s handler with both write and sensitive data access enabled
1186 | with patch(
1187 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
1188 | ):
1189 | K8sHandler(mock_mcp, allow_write=True, allow_sensitive_data_access=True)
1190 |
1191 | # Verify that the tools were registered
1192 | assert mock_mcp.tool.call_count == 7
1193 |
1194 | # Get all call args
1195 | call_args_list = mock_mcp.tool.call_args_list
1196 |
1197 | # Get all tool names that were registered
1198 | tool_names = [call_args[1]['name'] for call_args in call_args_list]
1199 |
1200 | # Verify that get_pod_logs and get_k8s_events were registered
1201 | assert 'get_pod_logs' in tool_names
1202 | assert 'get_k8s_events' in tool_names
1203 |
1204 | def test_init_write_access_disabled(self, mock_mcp, mock_client_cache):
1205 | """Test initialization of K8sHandler with write access disabled."""
1206 | # Initialize the K8s handler with write access disabled but sensitive data access enabled
1207 | with patch(
1208 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
1209 | ):
1210 | handler = K8sHandler(mock_mcp, allow_write=False, allow_sensitive_data_access=True)
1211 |
1212 | # Verify that allow_write is set
1213 | assert handler.allow_write is False
1214 |
1215 | # Verify that the tools were registered
1216 | assert mock_mcp.tool.call_count == 7
1217 |
1218 | # Get all call args
1219 | call_args_list = mock_mcp.tool.call_args_list
1220 |
1221 | # Get all tool names that were registered
1222 | tool_names = [call_args[1]['name'] for call_args in call_args_list]
1223 |
1224 | # Verify that all tools are registered
1225 | assert 'list_k8s_resources' in tool_names
1226 | assert 'get_pod_logs' in tool_names
1227 | assert 'get_k8s_events' in tool_names
1228 | assert 'manage_k8s_resource' in tool_names
1229 | assert 'apply_yaml' in tool_names
1230 | assert 'generate_app_manifest' in tool_names
1231 |
1232 | @pytest.mark.asyncio
1233 | async def test_get_pod_logs_success(self, mock_context, mock_mcp, mock_client_cache):
1234 | """Test get_pod_logs method with successful log retrieval."""
1235 | # Initialize the K8s handler with sensitive data access enabled
1236 | with patch(
1237 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
1238 | ):
1239 | handler = K8sHandler(mock_mcp, allow_sensitive_data_access=True)
1240 |
1241 | # Mock get_client
1242 | mock_k8s_apis = MagicMock()
1243 | mock_k8s_apis.get_pod_logs.return_value = 'log line 1\nlog line 2\n'
1244 |
1245 | with patch.object(handler, 'get_client', return_value=mock_k8s_apis) as mock_client:
1246 | # Get pod logs
1247 | result = await handler.get_pod_logs(
1248 | mock_context,
1249 | cluster_name='test-cluster',
1250 | namespace='test-namespace',
1251 | pod_name='test-pod',
1252 | container_name='test-container',
1253 | since_seconds=60,
1254 | tail_lines=100,
1255 | limit_bytes=1024,
1256 | previous=True,
1257 | )
1258 |
1259 | # Verify that get_client was called
1260 | mock_client.assert_called_once_with('test-cluster')
1261 |
1262 | # Verify that get_pod_logs was called with the correct parameters
1263 | mock_k8s_apis.get_pod_logs.assert_called_once_with(
1264 | pod_name='test-pod',
1265 | namespace='test-namespace',
1266 | container_name='test-container',
1267 | since_seconds=60,
1268 | tail_lines=100,
1269 | limit_bytes=1024,
1270 | previous=True,
1271 | )
1272 |
1273 | # Verify the result
1274 | assert not result.isError
1275 | assert result.pod_name == 'test-pod'
1276 | assert result.namespace == 'test-namespace'
1277 | assert result.container_name == 'test-container'
1278 | assert result.log_lines == ['log line 1', 'log line 2', '']
1279 | assert isinstance(result.content[0], TextContent)
1280 | assert (
1281 | 'Successfully retrieved 3 log lines from pod test-namespace/test-pod (container: test-container)'
1282 | in result.content[0].text
1283 | )
1284 |
1285 | @pytest.mark.asyncio
1286 | async def test_get_pod_logs_minimal(self, mock_context, mock_mcp, mock_client_cache):
1287 | """Test get_pod_logs method with minimal parameters."""
1288 | # Initialize the K8s handler with sensitive data access enabled
1289 | with patch(
1290 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
1291 | ):
1292 | handler = K8sHandler(mock_mcp, allow_sensitive_data_access=True)
1293 |
1294 | # Mock get_client
1295 | mock_k8s_apis = MagicMock()
1296 | mock_k8s_apis.get_pod_logs.return_value = 'log line 1\nlog line 2\n'
1297 |
1298 | with patch.object(handler, 'get_client', return_value=mock_k8s_apis) as mock_client:
1299 | # Get pod logs with minimal parameters - explicitly pass default values for non-optional parameters
1300 | result = await handler.get_pod_logs(
1301 | mock_context,
1302 | cluster_name='test-cluster',
1303 | namespace='test-namespace',
1304 | pod_name='test-pod',
1305 | container_name=None,
1306 | since_seconds=None,
1307 | tail_lines=100, # Default value
1308 | limit_bytes=10240, # Default value
1309 | previous=False, # Default value
1310 | )
1311 |
1312 | # Verify that get_client was called
1313 | mock_client.assert_called_once_with('test-cluster')
1314 |
1315 | # Verify that get_pod_logs was called
1316 | mock_k8s_apis.get_pod_logs.assert_called_once()
1317 |
1318 | # Get the call args
1319 | args, kwargs = mock_k8s_apis.get_pod_logs.call_args
1320 |
1321 | # Verify the keyword args
1322 | assert kwargs['pod_name'] == 'test-pod'
1323 | assert kwargs['namespace'] == 'test-namespace'
1324 | assert kwargs['container_name'] is None
1325 |
1326 | # Verify the result
1327 | assert not result.isError
1328 | assert result.pod_name == 'test-pod'
1329 | assert result.namespace == 'test-namespace'
1330 | assert result.container_name is None
1331 | assert result.log_lines == ['log line 1', 'log line 2', '']
1332 | assert isinstance(result.content[0], TextContent)
1333 | assert (
1334 | 'Successfully retrieved 3 log lines from pod test-namespace/test-pod'
1335 | in result.content[0].text
1336 | )
1337 |
1338 | @pytest.mark.asyncio
1339 | async def test_get_pod_logs_sensitive_data_access_disabled(
1340 | self, mock_context, mock_mcp, mock_client_cache
1341 | ):
1342 | """Test get_pod_logs method with sensitive data access disabled."""
1343 | # Initialize the K8s handler with sensitive data access disabled
1344 | with patch(
1345 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
1346 | ):
1347 | handler = K8sHandler(mock_mcp, allow_sensitive_data_access=False)
1348 |
1349 | # Get pod logs with sensitive data access disabled
1350 | result = await handler.get_pod_logs(
1351 | mock_context,
1352 | cluster_name='test-cluster',
1353 | namespace='test-namespace',
1354 | pod_name='test-pod',
1355 | container_name='test-container',
1356 | )
1357 |
1358 | # Verify the result
1359 | assert result.isError
1360 | assert result.pod_name == 'test-pod'
1361 | assert result.namespace == 'test-namespace'
1362 | assert result.container_name == 'test-container'
1363 | assert result.log_lines == []
1364 | assert isinstance(result.content[0], TextContent)
1365 | assert (
1366 | 'Access to pod logs requires --allow-sensitive-data-access flag'
1367 | in result.content[0].text
1368 | )
1369 |
1370 | @pytest.mark.asyncio
1371 | async def test_get_pod_logs_error(self, mock_context, mock_mcp, mock_client_cache):
1372 | """Test get_pod_logs method with an error."""
1373 | # Initialize the K8s handler with sensitive data access enabled
1374 | with patch(
1375 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
1376 | ):
1377 | handler = K8sHandler(mock_mcp, allow_sensitive_data_access=True)
1378 |
1379 | # Mock get_client
1380 | mock_k8s_apis = MagicMock()
1381 | mock_k8s_apis.get_pod_logs.side_effect = Exception('Pod not found')
1382 |
1383 | with patch.object(handler, 'get_client', return_value=mock_k8s_apis) as mock_client:
1384 | # Get pod logs with an error
1385 | result = await handler.get_pod_logs(
1386 | mock_context,
1387 | cluster_name='test-cluster',
1388 | namespace='test-namespace',
1389 | pod_name='test-pod',
1390 | container_name='test-container',
1391 | )
1392 |
1393 | # Verify that get_client was called
1394 | mock_client.assert_called_once_with('test-cluster')
1395 |
1396 | # Verify that get_pod_logs was called
1397 | mock_k8s_apis.get_pod_logs.assert_called_once()
1398 |
1399 | # Get the call args
1400 | args, kwargs = mock_k8s_apis.get_pod_logs.call_args
1401 |
1402 | # Verify the keyword args
1403 | assert kwargs['pod_name'] == 'test-pod'
1404 | assert kwargs['namespace'] == 'test-namespace'
1405 | assert kwargs['container_name'] == 'test-container'
1406 |
1407 | # Verify the result
1408 | assert result.isError
1409 | assert result.pod_name == 'test-pod'
1410 | assert result.namespace == 'test-namespace'
1411 | assert result.container_name == 'test-container'
1412 | assert result.log_lines == []
1413 | assert isinstance(result.content[0], TextContent)
1414 | assert (
1415 | 'Failed to get logs from pod test-namespace/test-pod (container: test-container): Pod not found'
1416 | in result.content[0].text
1417 | )
1418 |
1419 | @pytest.mark.asyncio
1420 | async def test_get_k8s_events_success(self, mock_context, mock_mcp, mock_client_cache):
1421 | """Test get_k8s_events method with successful event retrieval."""
1422 | # Initialize the K8s handler
1423 | with patch(
1424 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
1425 | ):
1426 | handler = K8sHandler(mock_mcp, allow_sensitive_data_access=True)
1427 |
1428 | # Mock get_client
1429 | mock_k8s_apis = MagicMock()
1430 | mock_k8s_apis.get_events.return_value = [
1431 | {
1432 | 'first_timestamp': '2023-01-01T00:00:00Z',
1433 | 'last_timestamp': '2023-01-01T00:05:00Z',
1434 | 'count': 5,
1435 | 'message': 'Container created',
1436 | 'reason': 'Created',
1437 | 'reporting_component': 'kubelet',
1438 | 'type': 'Normal',
1439 | },
1440 | {
1441 | 'first_timestamp': '2023-01-01T00:05:00Z',
1442 | 'last_timestamp': '2023-01-01T00:10:00Z',
1443 | 'count': 1,
1444 | 'message': 'Container started',
1445 | 'reason': 'Started',
1446 | 'reporting_component': 'kubelet',
1447 | 'type': 'Normal',
1448 | },
1449 | ]
1450 |
1451 | with patch.object(handler, 'get_client', return_value=mock_k8s_apis) as mock_client:
1452 | # Get events
1453 | result = await handler.get_k8s_events(
1454 | mock_context,
1455 | cluster_name='test-cluster',
1456 | kind='Pod',
1457 | name='test-pod',
1458 | namespace='test-namespace',
1459 | )
1460 |
1461 | # Verify that get_client was called
1462 | mock_client.assert_called_once_with('test-cluster')
1463 |
1464 | # Verify that get_events was called with the correct parameters
1465 | mock_k8s_apis.get_events.assert_called_once_with(
1466 | kind='Pod',
1467 | name='test-pod',
1468 | namespace='test-namespace',
1469 | )
1470 |
1471 | # Verify the result
1472 | assert not result.isError
1473 | assert result.involved_object_kind == 'Pod'
1474 | assert result.involved_object_name == 'test-pod'
1475 | assert result.involved_object_namespace == 'test-namespace'
1476 | assert result.count == 2
1477 | assert len(result.events) == 2
1478 |
1479 | # Check first event
1480 | assert result.events[0].first_timestamp == '2023-01-01T00:00:00Z'
1481 | assert result.events[0].last_timestamp == '2023-01-01T00:05:00Z'
1482 | assert result.events[0].count == 5
1483 | assert result.events[0].message == 'Container created'
1484 | assert result.events[0].reason == 'Created'
1485 | assert result.events[0].reporting_component == 'kubelet'
1486 | assert result.events[0].type == 'Normal'
1487 |
1488 | # Check second event
1489 | assert result.events[1].message == 'Container started'
1490 |
1491 | # Check content
1492 | assert isinstance(result.content[0], TextContent)
1493 | assert (
1494 | 'Successfully retrieved 2 events for Pod test-namespace/test-pod'
1495 | in result.content[0].text
1496 | )
1497 |
1498 | @pytest.mark.asyncio
1499 | async def test_get_k8s_events_empty(self, mock_context, mock_mcp, mock_client_cache):
1500 | """Test get_k8s_events method with no events found."""
1501 | # Initialize the K8s handler
1502 | with patch(
1503 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
1504 | ):
1505 | handler = K8sHandler(mock_mcp, allow_sensitive_data_access=True)
1506 |
1507 | # Mock get_client
1508 | mock_k8s_apis = MagicMock()
1509 | mock_k8s_apis.get_events.return_value = []
1510 |
1511 | with patch.object(handler, 'get_client', return_value=mock_k8s_apis) as mock_client:
1512 | # Get events
1513 | result = await handler.get_k8s_events(
1514 | mock_context,
1515 | cluster_name='test-cluster',
1516 | kind='Pod',
1517 | name='test-pod',
1518 | namespace='test-namespace',
1519 | )
1520 |
1521 | # Verify that get_client was called
1522 | mock_client.assert_called_once_with('test-cluster')
1523 |
1524 | # Verify that get_events was called
1525 | mock_k8s_apis.get_events.assert_called_once()
1526 |
1527 | # Verify the result
1528 | assert not result.isError
1529 | assert result.involved_object_kind == 'Pod'
1530 | assert result.involved_object_name == 'test-pod'
1531 | assert result.involved_object_namespace == 'test-namespace'
1532 | assert result.count == 0
1533 | assert len(result.events) == 0
1534 | assert isinstance(result.content[0], TextContent)
1535 | assert (
1536 | 'Successfully retrieved 0 events for Pod test-namespace/test-pod'
1537 | in result.content[0].text
1538 | )
1539 |
1540 | @pytest.mark.asyncio
1541 | async def test_get_k8s_events_sensitive_data_access_disabled(
1542 | self, mock_context, mock_mcp, mock_client_cache
1543 | ):
1544 | """Test get_k8s_events method with sensitive data access disabled."""
1545 | # Initialize the K8s handler with sensitive data access disabled
1546 | with patch(
1547 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
1548 | ):
1549 | handler = K8sHandler(mock_mcp, allow_sensitive_data_access=False)
1550 |
1551 | # Get events with sensitive data access disabled
1552 | result = await handler.get_k8s_events(
1553 | mock_context,
1554 | cluster_name='test-cluster',
1555 | kind='Pod',
1556 | name='test-pod',
1557 | namespace='test-namespace',
1558 | )
1559 |
1560 | # Verify the result
1561 | assert result.isError
1562 | assert result.involved_object_kind == 'Pod'
1563 | assert result.involved_object_name == 'test-pod'
1564 | assert result.involved_object_namespace == 'test-namespace'
1565 | assert result.count == 0
1566 | assert len(result.events) == 0
1567 | assert isinstance(result.content[0], TextContent)
1568 | assert (
1569 | 'Access to Kubernetes events requires --allow-sensitive-data-access flag'
1570 | in result.content[0].text
1571 | )
1572 |
1573 | @pytest.mark.asyncio
1574 | async def test_get_k8s_events_error(self, mock_context, mock_mcp, mock_client_cache):
1575 | """Test get_k8s_events method with an error."""
1576 | # Initialize the K8s handler with sensitive data access enabled
1577 | with patch(
1578 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
1579 | ):
1580 | handler = K8sHandler(mock_mcp, allow_sensitive_data_access=True)
1581 |
1582 | # Mock get_client
1583 | mock_k8s_apis = MagicMock()
1584 | mock_k8s_apis.get_events.side_effect = Exception('Failed to get events')
1585 |
1586 | with patch.object(handler, 'get_client', return_value=mock_k8s_apis) as mock_client:
1587 | # Get events with an error
1588 | result = await handler.get_k8s_events(
1589 | mock_context,
1590 | cluster_name='test-cluster',
1591 | kind='Pod',
1592 | name='test-pod',
1593 | namespace='test-namespace',
1594 | )
1595 |
1596 | # Verify that get_client was called
1597 | mock_client.assert_called_once_with('test-cluster')
1598 |
1599 | # Verify that get_events was called
1600 | mock_k8s_apis.get_events.assert_called_once()
1601 |
1602 | # Verify the result
1603 | assert result.isError
1604 | assert result.involved_object_kind == 'Pod'
1605 | assert result.involved_object_name == 'test-pod'
1606 | assert result.involved_object_namespace == 'test-namespace'
1607 | assert result.count == 0
1608 | assert len(result.events) == 0
1609 | assert isinstance(result.content[0], TextContent)
1610 | assert (
1611 | 'Failed to get events for Pod test-namespace/test-pod: Failed to get events'
1612 | in result.content[0].text
1613 | )
1614 |
1615 | def test_remove_managed_fields(self, mock_mcp, mock_client_cache):
1616 | """Test remove_managed_fields method for removing managed_fields from Kubernetes resources."""
1617 | # Initialize the K8s handler
1618 | with patch(
1619 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
1620 | ):
1621 | handler = K8sHandler(mock_mcp)
1622 |
1623 | # Create a deep copy of the input to avoid modifying the original
1624 | import copy
1625 |
1626 | # Test case 1: Resource with managedFields (camelCase as used by dynamic client)
1627 | resource_with_managed_fields = {
1628 | 'metadata': {
1629 | 'name': 'test-pod',
1630 | 'namespace': 'default',
1631 | 'managedFields': [
1632 | {
1633 | 'manager': 'kubectl',
1634 | 'operation': 'Update',
1635 | 'apiVersion': 'v1',
1636 | 'time': '2023-01-01T00:00:00Z',
1637 | 'fieldsType': 'FieldsV1',
1638 | 'fieldsV1': {'f:metadata': {'f:labels': {'.': {}, 'f:app': {}}}},
1639 | }
1640 | ],
1641 | },
1642 | 'spec': {'containers': [{'name': 'container1', 'image': 'nginx'}]},
1643 | }
1644 |
1645 | expected_result = {
1646 | 'metadata': {'name': 'test-pod', 'namespace': 'default'},
1647 | 'spec': {'containers': [{'name': 'container1', 'image': 'nginx'}]},
1648 | }
1649 |
1650 | # Make a deep copy to avoid modifying the original
1651 | input_copy = copy.deepcopy(resource_with_managed_fields)
1652 | result = handler.remove_managed_fields(input_copy)
1653 | assert result == expected_result
1654 |
1655 | # Test case 2: Resource without managedFields
1656 | resource_without_managed_fields = {
1657 | 'metadata': {'name': 'test-pod', 'namespace': 'default'},
1658 | 'spec': {'containers': [{'name': 'container1', 'image': 'nginx'}]},
1659 | }
1660 |
1661 | # The result should be the same as the input
1662 | result = handler.remove_managed_fields(resource_without_managed_fields)
1663 | assert result == resource_without_managed_fields
1664 |
1665 | # Test case 3: Resource without metadata
1666 | resource_without_metadata = {
1667 | 'kind': 'Pod',
1668 | 'apiVersion': 'v1',
1669 | 'spec': {'containers': [{'name': 'container1', 'image': 'nginx'}]},
1670 | }
1671 |
1672 | # The result should be the same as the input
1673 | result = handler.remove_managed_fields(resource_without_metadata)
1674 | assert result == resource_without_metadata
1675 |
1676 | # Test case 4: Non-dict input
1677 | non_dict_input = {} # Use empty dict instead of string to avoid type error
1678 | result = handler.remove_managed_fields(non_dict_input)
1679 | assert result == non_dict_input
1680 |
1681 | def test_cleanup_resource_response(self, mock_mcp, mock_client_cache):
1682 | """Test cleanup_resource_response method for removing managed fields and null values."""
1683 | # Initialize the K8s handler
1684 | with patch(
1685 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
1686 | ):
1687 | handler = K8sHandler(mock_mcp)
1688 |
1689 | # Create a complex resource with both managed_fields and null values
1690 | complex_resource = {
1691 | 'kind': 'Pod',
1692 | 'apiVersion': 'v1',
1693 | 'metadata': {
1694 | 'name': 'test-pod',
1695 | 'namespace': 'default',
1696 | 'managedFields': [
1697 | {
1698 | 'manager': 'kubectl',
1699 | 'operation': 'Update',
1700 | 'apiVersion': 'v1',
1701 | 'time': '2023-01-01T00:00:00Z',
1702 | 'fieldsType': 'FieldsV1',
1703 | 'fieldsV1': {'f:metadata': {'f:labels': {'.': {}, 'f:app': {}}}},
1704 | }
1705 | ],
1706 | 'labels': {'app': 'test', 'environment': None},
1707 | 'annotations': None,
1708 | },
1709 | 'spec': {
1710 | 'containers': [
1711 | {'name': 'container1', 'image': 'nginx', 'resources': None},
1712 | None,
1713 | {
1714 | 'name': 'container2',
1715 | 'image': 'redis',
1716 | 'ports': [{'containerPort': 6379}, {'containerPort': None}],
1717 | },
1718 | ],
1719 | 'volumes': None,
1720 | },
1721 | 'status': None,
1722 | }
1723 |
1724 | # Expected result after cleanup
1725 | expected_result = {
1726 | 'kind': 'Pod',
1727 | 'apiVersion': 'v1',
1728 | 'metadata': {'name': 'test-pod', 'namespace': 'default', 'labels': {'app': 'test'}},
1729 | 'spec': {
1730 | 'containers': [
1731 | {'name': 'container1', 'image': 'nginx'},
1732 | {
1733 | 'name': 'container2',
1734 | 'image': 'redis',
1735 | 'ports': [{'containerPort': 6379}, {}],
1736 | },
1737 | ]
1738 | },
1739 | }
1740 |
1741 | # Test with spies to verify both methods are called
1742 | with patch.object(
1743 | handler, 'remove_managed_fields', wraps=handler.remove_managed_fields
1744 | ) as mock_remove:
1745 | with patch.object(
1746 | handler, 'filter_null_values', wraps=handler.filter_null_values
1747 | ) as mock_filter:
1748 | result = handler.cleanup_resource_response(complex_resource)
1749 |
1750 | # Verify that both methods were called
1751 | mock_remove.assert_called_once_with(complex_resource)
1752 | # We don't check the exact number of calls for filter_null_values since it's recursive
1753 | # and will be called for each nested element
1754 | assert mock_filter.called
1755 |
1756 | # Just verify that filter_null_values was called at least once
1757 | # We can't easily check the exact argument since remove_managed_fields modifies the input
1758 |
1759 | # Verify the final result
1760 | assert result == expected_result
1761 |
1762 | # Test with a simple string input
1763 | simple_input = 'simple string'
1764 | result = handler.cleanup_resource_response(simple_input)
1765 | assert result == simple_input
1766 |
1767 | def test_remove_checkov_skip_annotations(self, mock_mcp, mock_client_cache):
1768 | """Test _remove_checkov_skip_annotations method directly to ensure line 807 is covered."""
1769 | # Initialize the K8s handler
1770 | with patch(
1771 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
1772 | ):
1773 | handler = K8sHandler(mock_mcp)
1774 |
1775 | # Test case 1: YAML with only checkov skip annotations (should remove annotations completely)
1776 | yaml_content = """apiVersion: apps/v1
1777 | kind: Deployment
1778 | metadata:
1779 | name: test-app
1780 | namespace: default
1781 | annotations:
1782 | checkov.io/skip1: "CKV_K8S_14=We're using a specific image version"
1783 | checkov.io/skip2: "CKV_K8S_43=Resource limits are set appropriately"
1784 | spec:
1785 | replicas: 3"""
1786 |
1787 | expected_result = """apiVersion: apps/v1
1788 | kind: Deployment
1789 | metadata:
1790 | name: test-app
1791 | namespace: default
1792 | spec:
1793 | replicas: 3
1794 | """
1795 |
1796 | result = handler._remove_checkov_skip_annotations(yaml_content)
1797 | # Normalize whitespace for comparison
1798 | result = result.replace(' ', '').replace('\n', '')
1799 | expected_result = expected_result.replace(' ', '').replace('\n', '')
1800 | assert result == expected_result
1801 |
1802 | # Test case 2: YAML with mixed annotations (should keep non-checkov annotations)
1803 | yaml_content = """apiVersion: apps/v1
1804 | kind: Deployment
1805 | metadata:
1806 | name: test-app
1807 | namespace: default
1808 | annotations:
1809 | checkov.io/skip1: "CKV_K8S_14=We're using a specific image version"
1810 | other-annotation: "This should be preserved"
1811 | spec:
1812 | replicas: 3"""
1813 |
1814 | expected_result = """apiVersion: apps/v1
1815 | kind: Deployment
1816 | metadata:
1817 | name: test-app
1818 | namespace: default
1819 | annotations:
1820 | other-annotation: This should be preserved
1821 | spec:
1822 | replicas: 3
1823 | """
1824 |
1825 | result = handler._remove_checkov_skip_annotations(yaml_content)
1826 | assert 'other-annotation: This should be preserved' in result
1827 |
1828 | def test_filter_null_values(self, mock_mcp, mock_client_cache):
1829 | """Test filter_null_values method for removing null values from data structures."""
1830 | # Initialize the K8s handler
1831 | with patch(
1832 | 'awslabs.eks_mcp_server.k8s_handler.K8sClientCache', return_value=mock_client_cache
1833 | ):
1834 | handler = K8sHandler(mock_mcp)
1835 |
1836 | # Test case 1: Simple dictionary with null values
1837 | input_dict = {'key1': 'value1', 'key2': None, 'key3': 'value3', 'key4': None}
1838 | expected_dict = {'key1': 'value1', 'key3': 'value3'}
1839 | result = handler.filter_null_values(input_dict)
1840 | assert result == expected_dict
1841 |
1842 | # Test case 2: Nested dictionary with null values
1843 | input_nested_dict = {
1844 | 'key1': 'value1',
1845 | 'key2': None,
1846 | 'key3': {
1847 | 'nested1': 'nested_value1',
1848 | 'nested2': None,
1849 | 'nested3': {'deep1': 'deep_value1', 'deep2': None},
1850 | },
1851 | }
1852 | expected_nested_dict = {
1853 | 'key1': 'value1',
1854 | 'key3': {'nested1': 'nested_value1', 'nested3': {'deep1': 'deep_value1'}},
1855 | }
1856 | result = handler.filter_null_values(input_nested_dict)
1857 | assert result == expected_nested_dict
1858 |
1859 | # Test case 3: List with null values
1860 | input_list = ['item1', None, 'item2', None, 'item3']
1861 | expected_list = ['item1', 'item2', 'item3']
1862 | result = handler.filter_null_values(input_list)
1863 | assert result == expected_list
1864 |
1865 | # Test case 4: List of dictionaries with null values
1866 | input_list_of_dicts = [
1867 | {'key1': 'value1', 'key2': None},
1868 | None,
1869 | {'key3': None, 'key4': 'value4'},
1870 | ]
1871 | expected_list_of_dicts = [{'key1': 'value1'}, {'key4': 'value4'}]
1872 | result = handler.filter_null_values(input_list_of_dicts)
1873 | assert result == expected_list_of_dicts
1874 |
1875 | # Test case 5: Primitive values (non-dict, non-list)
1876 | assert handler.filter_null_values('string') == 'string'
1877 | assert handler.filter_null_values(123) == 123
1878 | assert handler.filter_null_values(True)
1879 | assert handler.filter_null_values(None) is None # None input should return None
1880 |
1881 | # Test case 6: Empty containers
1882 | assert handler.filter_null_values({}) == {}
1883 | assert handler.filter_null_values([]) == []
1884 |
1885 | # Test case 7: Complex nested structure
1886 | complex_input = {
1887 | 'metadata': {
1888 | 'name': 'test-pod',
1889 | 'namespace': 'default',
1890 | 'labels': {'app': 'test', 'environment': None},
1891 | 'annotations': None,
1892 | },
1893 | 'spec': {
1894 | 'containers': [
1895 | {'name': 'container1', 'image': 'nginx', 'resources': None},
1896 | None,
1897 | {
1898 | 'name': 'container2',
1899 | 'image': 'redis',
1900 | 'ports': [{'containerPort': 6379}, {'containerPort': None}],
1901 | },
1902 | ],
1903 | 'volumes': None,
1904 | },
1905 | 'status': None,
1906 | }
1907 |
1908 | expected_complex = {
1909 | 'metadata': {'name': 'test-pod', 'namespace': 'default', 'labels': {'app': 'test'}},
1910 | 'spec': {
1911 | 'containers': [
1912 | {'name': 'container1', 'image': 'nginx'},
1913 | {
1914 | 'name': 'container2',
1915 | 'image': 'redis',
1916 | 'ports': [{'containerPort': 6379}, {}],
1917 | },
1918 | ]
1919 | },
1920 | }
1921 |
1922 | result = handler.filter_null_values(complex_input)
1923 | assert result == expected_complex
1924 |
```