This is page 499 of 503. Use http://codebase.md/awslabs/mcp?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .devcontainer
│ └── devcontainer.json
├── .github
│ ├── actions
│ │ ├── build-and-push-container-image
│ │ │ └── action.yml
│ │ └── clear-space-ubuntu-latest-agressively
│ │ └── action.yml
│ ├── codecov.yml
│ ├── CODEOWNERS
│ ├── dependabot.yml
│ ├── ISSUE_TEMPLATE
│ │ ├── bug_report.yml
│ │ ├── documentation.yml
│ │ ├── feature_request.yml
│ │ ├── rfc.yml
│ │ └── support_awslabs_mcp_servers.yml
│ ├── pull_request_template.md
│ ├── SECURITY
│ ├── SUPPORT
│ └── workflows
│ ├── aws-api-mcp-upgrade-version.yml
│ ├── bandit-requirements.txt
│ ├── bandit.yml
│ ├── cfn_nag.yml
│ ├── check-gh-pages-builds.yml
│ ├── check-license-header-hash.txt
│ ├── check-license-header.json
│ ├── check-license-header.yml
│ ├── checkov.yml
│ ├── codeql.yml
│ ├── dependency-review-action.yml
│ ├── detect-secrets-requirements.txt
│ ├── gh-pages.yml
│ ├── merge-prevention.yml
│ ├── powershell.yml
│ ├── pre-commit-requirements.txt
│ ├── pre-commit.yml
│ ├── pull-request-lint.yml
│ ├── python.yml
│ ├── RELEASE_INSTRUCTIONS.md
│ ├── release-initiate-branch.yml
│ ├── release-merge-tag.yml
│ ├── release.py
│ ├── release.yml
│ ├── scanners.yml
│ ├── scorecard-analysis.yml
│ ├── semgrep-requirements.txt
│ ├── semgrep.yml
│ ├── stale.yml
│ ├── trivy.yml
│ └── typescript.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .python-version
├── .ruff.toml
├── .secrets.baseline
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── DESIGN_GUIDELINES.md
├── DEVELOPER_GUIDE.md
├── docs
│ └── images
│ └── root-readme
│ ├── cline-api-provider-filled.png
│ ├── cline-chat-interface.png
│ ├── cline-custom-instructions.png
│ ├── cline-select-aws-profile.png
│ ├── cline-select-bedrock.png
│ ├── configure-mcp-servers.png
│ ├── install-cline-extension.png
│ ├── mcp-servers-installed.png
│ └── select-mcp-servers.png
├── docusaurus
│ ├── .gitignore
│ ├── docs
│ │ ├── installation.md
│ │ ├── intro.md
│ │ ├── samples
│ │ │ ├── index.md
│ │ │ ├── mcp-integration-with-kb.md
│ │ │ ├── mcp-integration-with-nova-canvas.md
│ │ │ └── stepfunctions-tool-mcp-server.md
│ │ ├── servers
│ │ │ ├── amazon-bedrock-agentcore-mcp-server.md
│ │ │ ├── amazon-keyspaces-mcp-server.md
│ │ │ ├── amazon-mq-mcp-server.md
│ │ │ ├── amazon-neptune-mcp-server.md
│ │ │ ├── amazon-qbusiness-anonymous-mcp-server.md
│ │ │ ├── amazon-qindex-mcp-server.md
│ │ │ ├── amazon-sns-sqs-mcp-server.md
│ │ │ ├── aurora-dsql-mcp-server.md
│ │ │ ├── aws-api-mcp-server.md
│ │ │ ├── aws-appsync-mcp-server.md
│ │ │ ├── aws-bedrock-custom-model-import-mcp-server.md
│ │ │ ├── aws-bedrock-data-automation-mcp-server.md
│ │ │ ├── aws-dataprocessing-mcp-server.md
│ │ │ ├── aws-diagram-mcp-server.md
│ │ │ ├── aws-documentation-mcp-server.md
│ │ │ ├── aws-healthomics-mcp-server.md
│ │ │ ├── aws-iot-sitewise-mcp-server.md
│ │ │ ├── aws-knowledge-mcp-server.md
│ │ │ ├── aws-location-mcp-server.md
│ │ │ ├── aws-msk-mcp-server.md
│ │ │ ├── aws-pricing-mcp-server.md
│ │ │ ├── aws-serverless-mcp-server.md
│ │ │ ├── aws-support-mcp-server.md
│ │ │ ├── bedrock-kb-retrieval-mcp-server.md
│ │ │ ├── billing-cost-management-mcp-server.md
│ │ │ ├── ccapi-mcp-server.md
│ │ │ ├── cdk-mcp-server.md
│ │ │ ├── cfn-mcp-server.md
│ │ │ ├── cloudtrail-mcp-server.md
│ │ │ ├── cloudwatch-appsignals-mcp-server.md
│ │ │ ├── cloudwatch-mcp-server.md
│ │ │ ├── code-doc-gen-mcp-server.md
│ │ │ ├── core-mcp-server.md
│ │ │ ├── cost-explorer-mcp-server.md
│ │ │ ├── documentdb-mcp-server.md
│ │ │ ├── dynamodb-mcp-server.md
│ │ │ ├── ecs-mcp-server.md
│ │ │ ├── eks-mcp-server.md
│ │ │ ├── elasticache-mcp-server.md
│ │ │ ├── finch-mcp-server.md
│ │ │ ├── frontend-mcp-server.md
│ │ │ ├── git-repo-research-mcp-server.md
│ │ │ ├── healthlake-mcp-server.md
│ │ │ ├── iam-mcp-server.md
│ │ │ ├── kendra-index-mcp-server.md
│ │ │ ├── lambda-tool-mcp-server.md
│ │ │ ├── memcached-mcp-server.md
│ │ │ ├── mysql-mcp-server.md
│ │ │ ├── nova-canvas-mcp-server.md
│ │ │ ├── openapi-mcp-server.md
│ │ │ ├── postgres-mcp-server.md
│ │ │ ├── prometheus-mcp-server.md
│ │ │ ├── redshift-mcp-server.md
│ │ │ ├── s3-tables-mcp-server.md
│ │ │ ├── stepfunctions-tool-mcp-server.md
│ │ │ ├── syntheticdata-mcp-server.md
│ │ │ ├── terraform-mcp-server.md
│ │ │ ├── timestream-for-influxdb-mcp-server.md
│ │ │ ├── valkey-mcp-server.md
│ │ │ └── well-architected-security-mcp-server.mdx
│ │ └── vibe_coding.md
│ ├── docusaurus.config.ts
│ ├── package-lock.json
│ ├── package.json
│ ├── README.md
│ ├── sidebars.ts
│ ├── src
│ │ ├── components
│ │ │ ├── HomepageFeatures
│ │ │ │ └── styles.module.css
│ │ │ └── ServerCards
│ │ │ ├── index.tsx
│ │ │ └── styles.module.css
│ │ ├── css
│ │ │ ├── custom.css
│ │ │ └── doc-override.css
│ │ └── pages
│ │ ├── index.module.css
│ │ └── servers.tsx
│ ├── static
│ │ ├── .nojekyll
│ │ ├── assets
│ │ │ ├── icons
│ │ │ │ ├── activity.svg
│ │ │ │ ├── book-open.svg
│ │ │ │ ├── cpu.svg
│ │ │ │ ├── database.svg
│ │ │ │ ├── dollar-sign.svg
│ │ │ │ ├── help-circle.svg
│ │ │ │ ├── key.svg
│ │ │ │ ├── server.svg
│ │ │ │ ├── share-2.svg
│ │ │ │ ├── tool.svg
│ │ │ │ └── zap.svg
│ │ │ └── server-cards.json
│ │ └── img
│ │ ├── aws-logo.svg
│ │ └── logo.png
│ └── tsconfig.json
├── LICENSE
├── NOTICE
├── README.md
├── samples
│ ├── mcp-integration-with-kb
│ │ ├── .env.example
│ │ ├── .python-version
│ │ ├── assets
│ │ │ └── simplified-mcp-flow-diagram.png
│ │ ├── clients
│ │ │ └── client_server.py
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── user_interfaces
│ │ │ └── chat_bedrock_st.py
│ │ └── uv.lock
│ ├── mcp-integration-with-nova-canvas
│ │ ├── .env.example
│ │ ├── .python-version
│ │ ├── clients
│ │ │ └── client_server.py
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── user_interfaces
│ │ │ └── image_generator_st.py
│ │ └── uv.lock
│ ├── README.md
│ └── stepfunctions-tool-mcp-server
│ ├── README.md
│ └── sample_state_machines
│ ├── customer-create
│ │ └── app.py
│ ├── customer-id-from-email
│ │ └── app.py
│ ├── customer-info-from-id
│ │ └── app.py
│ └── template.yml
├── src
│ ├── amazon-bedrock-agentcore-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── amazon_bedrock_agentcore_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── config.py
│ │ │ ├── server.py
│ │ │ └── utils
│ │ │ ├── __init__.py
│ │ │ ├── cache.py
│ │ │ ├── doc_fetcher.py
│ │ │ ├── indexer.py
│ │ │ ├── text_processor.py
│ │ │ └── url_validator.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── SECURITY.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── test_cache.py
│ │ │ ├── test_config.py
│ │ │ ├── test_doc_fetcher.py
│ │ │ ├── test_indexer.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_server.py
│ │ │ ├── test_text_processor.py
│ │ │ └── test_url_validator.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── amazon-kendra-index-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── amazon_kendra_index_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── server.py
│ │ │ └── util.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── amazon-keyspaces-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── amazon_keyspaces_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── client.py
│ │ │ ├── config.py
│ │ │ ├── consts.py
│ │ │ ├── llm_context.py
│ │ │ ├── models.py
│ │ │ ├── server.py
│ │ │ └── services.py
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── run_tests.sh
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── test_client.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_query_analysis_service.py
│ │ │ ├── test_server.py
│ │ │ └── test_services.py
│ │ └── uv.lock
│ ├── amazon-mq-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── amazon_mq_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── aws_service_mcp_generator.py
│ │ │ ├── consts.py
│ │ │ ├── rabbitmq
│ │ │ │ ├── __init__.py
│ │ │ │ ├── admin.py
│ │ │ │ ├── connection.py
│ │ │ │ ├── doc
│ │ │ │ │ ├── rabbitmq_broker_sizing_guide.md
│ │ │ │ │ ├── rabbitmq_performance_optimization_best_practice.md
│ │ │ │ │ ├── rabbitmq_production_deployment_guidelines.md
│ │ │ │ │ ├── rabbitmq_quorum_queue_migration_guide.md
│ │ │ │ │ └── rabbitmq_setup_best_practice.md
│ │ │ │ ├── handlers.py
│ │ │ │ └── module.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── example
│ │ │ └── sample_mcp_q_cli.json
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── .gitignore
│ │ │ ├── rabbitmq
│ │ │ │ ├── __init__.py
│ │ │ │ ├── conftest.py
│ │ │ │ ├── test_admin.py
│ │ │ │ ├── test_connection.py
│ │ │ │ ├── test_handlers.py
│ │ │ │ └── test_module.py
│ │ │ ├── test_aws_service_mcp_generator.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── amazon-neptune-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── amazon_neptune_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── exceptions.py
│ │ │ ├── graph_store
│ │ │ │ ├── __init__.py
│ │ │ │ ├── analytics.py
│ │ │ │ ├── base.py
│ │ │ │ └── database.py
│ │ │ ├── models.py
│ │ │ ├── neptune.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── test_analytics.py
│ │ │ ├── test_database.py
│ │ │ ├── test_exceptions.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_models.py
│ │ │ ├── test_neptune.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── amazon-qbusiness-anonymous-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── amazon_qbusiness_anonymous_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── clients.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── amazon-qindex-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── amazon_qindex_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── clients.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── test_clients.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ └── test_server.py
│ │ └── uv.lock
│ ├── amazon-sns-sqs-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── amazon_sns_sqs_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── common.py
│ │ │ ├── consts.py
│ │ │ ├── generator.py
│ │ │ ├── server.py
│ │ │ ├── sns.py
│ │ │ └── sqs.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── print_tools.py
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── run_tests.sh
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── .gitignore
│ │ │ ├── README.md
│ │ │ ├── test_common.py
│ │ │ ├── test_generator.py
│ │ │ ├── test_server.py
│ │ │ ├── test_sns.py
│ │ │ └── test_sqs.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── aurora-dsql-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aurora_dsql_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── consts.py
│ │ │ ├── mutable_sql_detector.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── test_connection_reuse.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_profile_option.py
│ │ │ ├── test_readonly_enforcement.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── aws-api-mcp-server
│ │ ├── .gitattributes
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aws_api_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── core
│ │ │ │ ├── __init__.py
│ │ │ │ ├── agent_scripts
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── manager.py
│ │ │ │ │ ├── models.py
│ │ │ │ │ └── registry
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── application-failure-troubleshooting.script.md
│ │ │ │ │ ├── cloudtral-mutli-region-setup.script.md
│ │ │ │ │ ├── create_amazon_aurora_db_cluster_with_instances.script.md
│ │ │ │ │ ├── lambda-timeout-debugging.script.md
│ │ │ │ │ ├── scripts_format.md
│ │ │ │ │ └── troubleshoot-permissions-with-cloudtrail-events.script.md
│ │ │ │ ├── aws
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── driver.py
│ │ │ │ │ ├── pagination.py
│ │ │ │ │ ├── regions.py
│ │ │ │ │ ├── service.py
│ │ │ │ │ └── services.py
│ │ │ │ ├── common
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── command_metadata.py
│ │ │ │ │ ├── command.py
│ │ │ │ │ ├── config.py
│ │ │ │ │ ├── errors.py
│ │ │ │ │ ├── file_operations.py
│ │ │ │ │ ├── file_system_controls.py
│ │ │ │ │ ├── helpers.py
│ │ │ │ │ ├── models.py
│ │ │ │ │ └── py.typed
│ │ │ │ ├── data
│ │ │ │ │ └── api_metadata.json
│ │ │ │ ├── metadata
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ └── read_only_operations_list.py
│ │ │ │ ├── parser
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── custom_validators
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── botocore_param_validator.py
│ │ │ │ │ │ ├── ec2_validator.py
│ │ │ │ │ │ └── ssm_validator.py
│ │ │ │ │ ├── interpretation.py
│ │ │ │ │ ├── lexer.py
│ │ │ │ │ └── parser.py
│ │ │ │ ├── py.typed
│ │ │ │ └── security
│ │ │ │ ├── __init__.py
│ │ │ │ ├── aws_api_customization.json
│ │ │ │ └── policy.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── CONTRIBUTING.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── agent_scripts
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_manager.py
│ │ │ │ └── test_registry
│ │ │ │ ├── another_valid_script.script.md
│ │ │ │ ├── test_script.script.md
│ │ │ │ └── valid_script.script.md
│ │ │ ├── aws
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_driver.py
│ │ │ │ ├── test_pagination.py
│ │ │ │ ├── test_service.py
│ │ │ │ └── test_services.py
│ │ │ ├── common
│ │ │ │ ├── test_command.py
│ │ │ │ ├── test_config.py
│ │ │ │ ├── test_file_operations.py
│ │ │ │ ├── test_file_system_controls.py
│ │ │ │ ├── test_file_validation.py
│ │ │ │ └── test_helpers.py
│ │ │ ├── fixtures.py
│ │ │ ├── history_handler.py
│ │ │ ├── metadata
│ │ │ │ ├── __init__.py
│ │ │ │ └── test_read_only_operations_list.py
│ │ │ ├── parser
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_file_path_detection.py
│ │ │ │ ├── test_lexer.py
│ │ │ │ ├── test_parser_customizations.py
│ │ │ │ └── test_parser.py
│ │ │ ├── test_security_policy.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── aws-appsync-mcp-server
│ │ ├── .dockerignore
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aws_appsync_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── decorators.py
│ │ │ ├── helpers.py
│ │ │ ├── operations
│ │ │ │ ├── __init__.py
│ │ │ │ ├── create_api_cache.py
│ │ │ │ ├── create_api_key.py
│ │ │ │ ├── create_api.py
│ │ │ │ ├── create_channel_namespace.py
│ │ │ │ ├── create_datasource.py
│ │ │ │ ├── create_domain_name.py
│ │ │ │ ├── create_function.py
│ │ │ │ ├── create_graphql_api.py
│ │ │ │ ├── create_resolver.py
│ │ │ │ └── create_schema.py
│ │ │ ├── server.py
│ │ │ ├── tools
│ │ │ │ ├── __init__.py
│ │ │ │ ├── create_api_cache.py
│ │ │ │ ├── create_api_key.py
│ │ │ │ ├── create_api.py
│ │ │ │ ├── create_channel_namespace.py
│ │ │ │ ├── create_datasource.py
│ │ │ │ ├── create_domain_name.py
│ │ │ │ ├── create_function.py
│ │ │ │ ├── create_graphql_api.py
│ │ │ │ ├── create_resolver.py
│ │ │ │ └── create_schema.py
│ │ │ └── validators.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── test_all_create_tools_write_protection.py
│ │ │ ├── test_create_api_cache.py
│ │ │ ├── test_create_api_key.py
│ │ │ ├── test_create_api.py
│ │ │ ├── test_create_channel_namespace.py
│ │ │ ├── test_create_datasource_tool.py
│ │ │ ├── test_create_datasource.py
│ │ │ ├── test_create_domain_name.py
│ │ │ ├── test_create_function.py
│ │ │ ├── test_create_graphql_api.py
│ │ │ ├── test_create_resolver.py
│ │ │ ├── test_create_schema_tool.py
│ │ │ ├── test_create_schema.py
│ │ │ ├── test_helpers.py
│ │ │ ├── test_server.py
│ │ │ ├── test_validators.py
│ │ │ └── test_write_operation.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── aws-bedrock-custom-model-import-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aws_bedrock_custom_model_import_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── client.py
│ │ │ ├── llm_context.py
│ │ │ ├── models.py
│ │ │ ├── prompts.py
│ │ │ ├── server.py
│ │ │ ├── services
│ │ │ │ ├── __init__.py
│ │ │ │ ├── imported_model_service.py
│ │ │ │ └── model_import_service.py
│ │ │ ├── tools
│ │ │ │ ├── create_model_import_job.py
│ │ │ │ ├── delete_imported_model.py
│ │ │ │ ├── get_imported_model.py
│ │ │ │ ├── get_model_import_job.py
│ │ │ │ ├── list_imported_models.py
│ │ │ │ └── list_model_import_jobs.py
│ │ │ └── utils
│ │ │ ├── __init__.py
│ │ │ ├── aws.py
│ │ │ ├── config.py
│ │ │ ├── consts.py
│ │ │ └── matching.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── services
│ │ │ │ ├── test_imported_model_service.py
│ │ │ │ └── test_model_import_service.py
│ │ │ ├── test_client.py
│ │ │ ├── test_init.py
│ │ │ ├── test_llm_context.py
│ │ │ ├── test_prompts.py
│ │ │ ├── test_server.py
│ │ │ ├── tools
│ │ │ │ ├── test_create_model_import_job.py
│ │ │ │ ├── test_delete_imported_model.py
│ │ │ │ ├── test_get_imported_model.py
│ │ │ │ ├── test_get_model_import_job.py
│ │ │ │ ├── test_list_imported_models.py
│ │ │ │ └── test_list_model_import_jobs.py
│ │ │ └── utils
│ │ │ ├── test_aws.py
│ │ │ ├── test_config.py
│ │ │ └── test_matching.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── aws-bedrock-data-automation-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aws_bedrock_data_automation_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── helpers.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── test_helpers.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── aws-dataprocessing-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aws_dataprocessing_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── core
│ │ │ │ ├── __init__.py
│ │ │ │ └── glue_data_catalog
│ │ │ │ ├── __init__.py
│ │ │ │ ├── data_catalog_database_manager.py
│ │ │ │ ├── data_catalog_handler.py
│ │ │ │ └── data_catalog_table_manager.py
│ │ │ ├── handlers
│ │ │ │ ├── __init__.py
│ │ │ │ ├── athena
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── athena_data_catalog_handler.py
│ │ │ │ │ ├── athena_query_handler.py
│ │ │ │ │ └── athena_workgroup_handler.py
│ │ │ │ ├── commons
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ └── common_resource_handler.py
│ │ │ │ ├── emr
│ │ │ │ │ ├── emr_ec2_cluster_handler.py
│ │ │ │ │ ├── emr_ec2_instance_handler.py
│ │ │ │ │ └── emr_ec2_steps_handler.py
│ │ │ │ └── glue
│ │ │ │ ├── __init__.py
│ │ │ │ ├── crawler_handler.py
│ │ │ │ ├── data_catalog_handler.py
│ │ │ │ ├── glue_commons_handler.py
│ │ │ │ ├── glue_etl_handler.py
│ │ │ │ ├── interactive_sessions_handler.py
│ │ │ │ └── worklows_handler.py
│ │ │ ├── models
│ │ │ │ ├── __init__.py
│ │ │ │ ├── athena_models.py
│ │ │ │ ├── common_resource_models.py
│ │ │ │ ├── data_catalog_models.py
│ │ │ │ ├── emr_models.py
│ │ │ │ └── glue_models.py
│ │ │ ├── server.py
│ │ │ └── utils
│ │ │ ├── __init__.py
│ │ │ ├── aws_helper.py
│ │ │ ├── consts.py
│ │ │ ├── logging_helper.py
│ │ │ └── mutable_sql_detector.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── core
│ │ │ │ ├── __init__.py
│ │ │ │ └── glue_data_catalog
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_data_catalog_database_manager.py
│ │ │ │ ├── test_data_catalog_handler.py
│ │ │ │ └── test_data_catalog_table_manager.py
│ │ │ ├── handlers
│ │ │ │ ├── __init__.py
│ │ │ │ ├── athena
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── test_athena_data_catalog_handler.py
│ │ │ │ │ ├── test_athena_query_handler.py
│ │ │ │ │ ├── test_athena_workgroup_handler.py
│ │ │ │ │ └── test_custom_tags_athena.py
│ │ │ │ ├── commons
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ └── test_common_resource_handler.py
│ │ │ │ ├── emr
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── test_custom_tags_emr.py
│ │ │ │ │ ├── test_emr_ec2_cluster_handler.py
│ │ │ │ │ ├── test_emr_ec2_instance_handler.py
│ │ │ │ │ └── test_emr_ec2_steps_handler.py
│ │ │ │ └── glue
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_crawler_handler.py
│ │ │ │ ├── test_custom_tags_glue.py
│ │ │ │ ├── test_data_catalog_handler.py
│ │ │ │ ├── test_glue_commons_handler.py
│ │ │ │ ├── test_glue_etl_handler.py
│ │ │ │ ├── test_glue_interactive_sessions_handler.py
│ │ │ │ └── test_glue_workflows_handler.py
│ │ │ ├── models
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_athena_models.py
│ │ │ │ ├── test_common_resource_models.py
│ │ │ │ ├── test_data_catalog_models.py
│ │ │ │ ├── test_emr_models.py
│ │ │ │ ├── test_glue_models.py
│ │ │ │ ├── test_interactive_sessions_models.py
│ │ │ │ └── test_workflows_models.py
│ │ │ ├── test_init.py
│ │ │ ├── test_server.py
│ │ │ └── utils
│ │ │ ├── __init__.py
│ │ │ ├── test_aws_helper.py
│ │ │ ├── test_custom_tags.py
│ │ │ └── test_logging_helper.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── aws-diagram-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aws_diagram_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── diagrams_tools.py
│ │ │ ├── models.py
│ │ │ ├── scanner.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── .gitignore
│ │ │ ├── conftest.py
│ │ │ ├── README.md
│ │ │ ├── resources
│ │ │ │ ├── __init__.py
│ │ │ │ └── example_diagrams
│ │ │ │ ├── __init__.py
│ │ │ │ ├── aws_example.py
│ │ │ │ ├── flow_example.py
│ │ │ │ └── sequence_example.py
│ │ │ ├── test_diagrams.py
│ │ │ ├── test_models.py
│ │ │ ├── test_sarif_fix.py
│ │ │ ├── test_scanner.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── aws-documentation-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aws_documentation_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── models.py
│ │ │ ├── server_aws_cn.py
│ │ │ ├── server_aws.py
│ │ │ ├── server_utils.py
│ │ │ ├── server.py
│ │ │ └── util.py
│ │ ├── basic-usage.gif
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── constants.py
│ │ │ ├── resources
│ │ │ │ └── lambda_sns_raw.html
│ │ │ ├── test_aws_cn_get_available_services_live.py
│ │ │ ├── test_aws_cn_read_documentation_live.py
│ │ │ ├── test_aws_read_documentation_live.py
│ │ │ ├── test_aws_recommend_live.py
│ │ │ ├── test_aws_search_live.py
│ │ │ ├── test_metadata_handling.py
│ │ │ ├── test_models.py
│ │ │ ├── test_server_aws_cn.py
│ │ │ ├── test_server_aws.py
│ │ │ ├── test_server_utils.py
│ │ │ ├── test_server.py
│ │ │ └── test_util.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── aws-healthomics-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aws_healthomics_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── consts.py
│ │ │ ├── models.py
│ │ │ ├── server.py
│ │ │ ├── tools
│ │ │ │ ├── __init__.py
│ │ │ │ ├── helper_tools.py
│ │ │ │ ├── run_analysis.py
│ │ │ │ ├── troubleshooting.py
│ │ │ │ ├── workflow_analysis.py
│ │ │ │ ├── workflow_execution.py
│ │ │ │ ├── workflow_linting.py
│ │ │ │ └── workflow_management.py
│ │ │ └── utils
│ │ │ ├── __init__.py
│ │ │ ├── aws_utils.py
│ │ │ ├── s3_utils.py
│ │ │ └── validation_utils.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── docs
│ │ │ └── workflow_linting.md
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── conftest.py
│ │ │ ├── test_aws_utils.py
│ │ │ ├── test_consts.py
│ │ │ ├── test_helper_tools.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_models.py
│ │ │ ├── test_run_analysis.py
│ │ │ ├── test_s3_utils.py
│ │ │ ├── test_server.py
│ │ │ ├── test_troubleshooting.py
│ │ │ ├── test_workflow_analysis.py
│ │ │ ├── test_workflow_execution.py
│ │ │ ├── test_workflow_linting.py
│ │ │ ├── test_workflow_management.py
│ │ │ └── test_workflow_tools.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── aws-iot-sitewise-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aws_iot_sitewise_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── client.py
│ │ │ ├── prompts
│ │ │ │ ├── __init__.py
│ │ │ │ ├── asset_hierarchy.py
│ │ │ │ ├── data_exploration.py
│ │ │ │ └── data_ingestion.py
│ │ │ ├── server.py
│ │ │ ├── tool_metadata.py
│ │ │ ├── tools
│ │ │ │ ├── __init__.py
│ │ │ │ ├── sitewise_access.py
│ │ │ │ ├── sitewise_asset_models.py
│ │ │ │ ├── sitewise_assets.py
│ │ │ │ ├── sitewise_data.py
│ │ │ │ └── sitewise_gateways.py
│ │ │ └── validation.py
│ │ ├── CHANGELOG.md
│ │ ├── DEVELOPMENT.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── examples
│ │ │ └── wind_farm_example.py
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── run_server.py
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_server.py
│ │ │ ├── test_sitewise_access.py
│ │ │ ├── test_sitewise_asset_models.py
│ │ │ ├── test_sitewise_assets.py
│ │ │ ├── test_sitewise_data.py
│ │ │ ├── test_sitewise_gateways.py
│ │ │ └── test_validation.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── aws-knowledge-mcp-server
│ │ └── README.md
│ ├── aws-location-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aws_location_server
│ │ │ ├── __init__.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── test_server_integration.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── aws-msk-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aws_msk_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── server.py
│ │ │ └── tools
│ │ │ ├── __init__.py
│ │ │ ├── common_functions
│ │ │ │ ├── __init__.py
│ │ │ │ ├── client_manager.py
│ │ │ │ └── common_functions.py
│ │ │ ├── logs_and_telemetry
│ │ │ │ ├── __init__.py
│ │ │ │ ├── cluster_metrics_tools.py
│ │ │ │ ├── list_customer_iam_access.py
│ │ │ │ └── metric_config.py
│ │ │ ├── mutate_cluster
│ │ │ │ ├── __init__.py
│ │ │ │ ├── batch_associate_scram_secret.py
│ │ │ │ ├── batch_disassociate_scram_secret.py
│ │ │ │ ├── create_cluster_v2.py
│ │ │ │ ├── put_cluster_policy.py
│ │ │ │ ├── reboot_broker.py
│ │ │ │ ├── update_broker_count.py
│ │ │ │ ├── update_broker_storage.py
│ │ │ │ ├── update_broker_type.py
│ │ │ │ ├── update_cluster_configuration.py
│ │ │ │ ├── update_monitoring.py
│ │ │ │ └── update_security.py
│ │ │ ├── mutate_config
│ │ │ │ ├── __init__.py
│ │ │ │ ├── create_configuration.py
│ │ │ │ ├── tag_resource.py
│ │ │ │ ├── untag_resource.py
│ │ │ │ └── update_configuration.py
│ │ │ ├── mutate_vpc
│ │ │ │ ├── __init__.py
│ │ │ │ ├── create_vpc_connection.py
│ │ │ │ ├── delete_vpc_connection.py
│ │ │ │ └── reject_client_vpc_connection.py
│ │ │ ├── read_cluster
│ │ │ │ ├── __init__.py
│ │ │ │ ├── describe_cluster_operation.py
│ │ │ │ ├── describe_cluster.py
│ │ │ │ ├── get_bootstrap_brokers.py
│ │ │ │ ├── get_cluster_policy.py
│ │ │ │ ├── get_compatible_kafka_versions.py
│ │ │ │ ├── list_client_vpc_connections.py
│ │ │ │ ├── list_cluster_operations.py
│ │ │ │ ├── list_nodes.py
│ │ │ │ └── list_scram_secrets.py
│ │ │ ├── read_config
│ │ │ │ ├── __init__.py
│ │ │ │ ├── describe_configuration_revision.py
│ │ │ │ ├── describe_configuration.py
│ │ │ │ ├── list_configuration_revisions.py
│ │ │ │ └── list_tags_for_resource.py
│ │ │ ├── read_global
│ │ │ │ ├── __init__.py
│ │ │ │ ├── list_clusters.py
│ │ │ │ ├── list_configurations.py
│ │ │ │ ├── list_kafka_versions.py
│ │ │ │ └── list_vpc_connections.py
│ │ │ ├── read_vpc
│ │ │ │ ├── __init__.py
│ │ │ │ └── describe_vpc_connection.py
│ │ │ └── static_tools
│ │ │ ├── __init__.py
│ │ │ └── cluster_best_practices.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── test_client_manager.py
│ │ │ ├── test_cluster_metrics_tools.py
│ │ │ ├── test_common_functions.py
│ │ │ ├── test_create_cluster_v2.py
│ │ │ ├── test_create_configuration.py
│ │ │ ├── test_create_vpc_connection.py
│ │ │ ├── test_delete_vpc_connection.py
│ │ │ ├── test_describe_cluster_operation.py
│ │ │ ├── test_describe_cluster.py
│ │ │ ├── test_describe_configuration_revision.py
│ │ │ ├── test_describe_configuration.py
│ │ │ ├── test_describe_vpc_connection.py
│ │ │ ├── test_get_bootstrap_brokers.py
│ │ │ ├── test_get_cluster_policy.py
│ │ │ ├── test_get_compatible_kafka_versions.py
│ │ │ ├── test_init.py
│ │ │ ├── test_list_client_vpc_connections.py
│ │ │ ├── test_list_cluster_operations.py
│ │ │ ├── test_list_clusters.py
│ │ │ ├── test_list_configuration_revisions.py
│ │ │ ├── test_list_configurations.py
│ │ │ ├── test_list_customer_iam_access.py
│ │ │ ├── test_list_kafka_versions.py
│ │ │ ├── test_list_nodes.py
│ │ │ ├── test_list_scram_secrets.py
│ │ │ ├── test_list_tags_for_resource.py
│ │ │ ├── test_list_vpc_connections.py
│ │ │ ├── test_logs_and_telemetry.py
│ │ │ ├── test_main.py
│ │ │ ├── test_mutate_cluster_init.py
│ │ │ ├── test_mutate_cluster_success_cases.py
│ │ │ ├── test_mutate_cluster.py
│ │ │ ├── test_mutate_config_init.py
│ │ │ ├── test_mutate_vpc_init.py
│ │ │ ├── test_read_cluster_init_updated.py
│ │ │ ├── test_read_cluster_init.py
│ │ │ ├── test_read_config_init.py
│ │ │ ├── test_read_global_init.py
│ │ │ ├── test_read_vpc_init.py
│ │ │ ├── test_reject_client_vpc_connection.py
│ │ │ ├── test_server.py
│ │ │ ├── test_static_tools_init.py
│ │ │ ├── test_tag_resource.py
│ │ │ ├── test_tool_descriptions.py
│ │ │ ├── test_untag_resource.py
│ │ │ └── test_update_configuration.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── aws-pricing-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aws_pricing_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── cdk_analyzer.py
│ │ │ ├── consts.py
│ │ │ ├── helpers.py
│ │ │ ├── models.py
│ │ │ ├── pricing_client.py
│ │ │ ├── pricing_transformer.py
│ │ │ ├── report_generator.py
│ │ │ ├── server.py
│ │ │ ├── static
│ │ │ │ ├── __init__.py
│ │ │ │ ├── COST_REPORT_TEMPLATE.md
│ │ │ │ └── patterns
│ │ │ │ ├── __init__.py
│ │ │ │ └── BEDROCK.md
│ │ │ └── terraform_analyzer.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── test_cdk_analyzer.py
│ │ │ ├── test_helpers.py
│ │ │ ├── test_pricing_client.py
│ │ │ ├── test_pricing_transformer.py
│ │ │ ├── test_report_generator.py
│ │ │ ├── test_server.py
│ │ │ └── test_terraform_analyzer.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── aws-serverless-mcp-server
│ │ ├── .pre-commit.config.yaml
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aws_serverless_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── models.py
│ │ │ ├── resources
│ │ │ │ ├── __init__.py
│ │ │ │ ├── deployment_details.py
│ │ │ │ ├── deployment_list.py
│ │ │ │ ├── template_details.py
│ │ │ │ └── template_list.py
│ │ │ ├── server.py
│ │ │ ├── template
│ │ │ │ ├── __init__.py
│ │ │ │ ├── registry.py
│ │ │ │ ├── renderer.py
│ │ │ │ └── templates
│ │ │ │ ├── backend.j2
│ │ │ │ ├── frontend.j2
│ │ │ │ ├── fullstack.j2
│ │ │ │ └── README.md
│ │ │ ├── tools
│ │ │ │ ├── common
│ │ │ │ │ └── base_tool.py
│ │ │ │ ├── guidance
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── deploy_serverless_app_help.py
│ │ │ │ │ ├── get_iac_guidance.py
│ │ │ │ │ ├── get_lambda_event_schemas.py
│ │ │ │ │ ├── get_lambda_guidance.py
│ │ │ │ │ └── get_serverless_templates.py
│ │ │ │ ├── sam
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── sam_build.py
│ │ │ │ │ ├── sam_deploy.py
│ │ │ │ │ ├── sam_init.py
│ │ │ │ │ ├── sam_local_invoke.py
│ │ │ │ │ └── sam_logs.py
│ │ │ │ ├── schemas
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── describe_schema.py
│ │ │ │ │ ├── list_registries.py
│ │ │ │ │ └── search_schema.py
│ │ │ │ └── webapps
│ │ │ │ ├── __init__.py
│ │ │ │ ├── configure_domain.py
│ │ │ │ ├── deploy_webapp.py
│ │ │ │ ├── get_metrics.py
│ │ │ │ ├── update_webapp_frontend.py
│ │ │ │ ├── utils
│ │ │ │ │ ├── deploy_service.py
│ │ │ │ │ ├── frontend_uploader.py
│ │ │ │ │ └── startup_script_generator.py
│ │ │ │ └── webapp_deployment_help.py
│ │ │ └── utils
│ │ │ ├── __init__.py
│ │ │ ├── aws_client_helper.py
│ │ │ ├── cloudformation.py
│ │ │ ├── const.py
│ │ │ ├── deployment_manager.py
│ │ │ ├── github.py
│ │ │ └── process.py
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── README.md
│ │ │ ├── test_cloudformation.py
│ │ │ ├── test_configure_domain.py
│ │ │ ├── test_deploy_serverless_app_help.py
│ │ │ ├── test_deploy_service.py
│ │ │ ├── test_deploy_webapp.py
│ │ │ ├── test_deployment_details.py
│ │ │ ├── test_deployment_help.py
│ │ │ ├── test_deployment_list.py
│ │ │ ├── test_deployment_manager.py
│ │ │ ├── test_frontend_uploader.py
│ │ │ ├── test_get_iac_guidance.py
│ │ │ ├── test_get_lambda_event_schemas.py
│ │ │ ├── test_get_lambda_guidance.py
│ │ │ ├── test_get_metrics.py
│ │ │ ├── test_get_serverless_templates.py
│ │ │ ├── test_github.py
│ │ │ ├── test_models.py
│ │ │ ├── test_process.py
│ │ │ ├── test_sam_build.py
│ │ │ ├── test_sam_deploy.py
│ │ │ ├── test_sam_init.py
│ │ │ ├── test_sam_local_invoke.py
│ │ │ ├── test_sam_logs.py
│ │ │ ├── test_schemas.py
│ │ │ ├── test_server.py
│ │ │ ├── test_startup_script_generator.py
│ │ │ ├── test_template_details.py
│ │ │ ├── test_template_list.py
│ │ │ ├── test_template_registry.py
│ │ │ ├── test_template_renderer.py
│ │ │ └── test_update_webapp_frontend.py
│ │ └── uv.lock
│ ├── aws-support-mcp-server
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── aws_support_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── client.py
│ │ │ ├── consts.py
│ │ │ ├── debug_helper.py
│ │ │ ├── errors.py
│ │ │ ├── formatters.py
│ │ │ ├── models.py
│ │ │ └── server.py
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftests.py
│ │ │ ├── test_aws_support_mcp_server.py
│ │ │ └── test_models.py
│ │ └── uv.lock
│ ├── bedrock-kb-retrieval-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── bedrock_kb_retrieval_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── knowledgebases
│ │ │ │ ├── __init__.py
│ │ │ │ ├── clients.py
│ │ │ │ ├── discovery.py
│ │ │ │ └── retrieval.py
│ │ │ ├── models.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── run_tests.sh
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── .gitignore
│ │ │ ├── conftest.py
│ │ │ ├── README.md
│ │ │ ├── test_clients.py
│ │ │ ├── test_discovery.py
│ │ │ ├── test_env_config.py
│ │ │ ├── test_models.py
│ │ │ ├── test_retrieval.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── billing-cost-management-mcp-server
│ │ ├── __init__.py
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── billing_cost_management_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── models.py
│ │ │ ├── prompts
│ │ │ │ ├── __init__.py
│ │ │ │ ├── decorator.py
│ │ │ │ ├── graviton_migration.py
│ │ │ │ ├── README.md
│ │ │ │ ├── savings_plans.py
│ │ │ │ └── types.py
│ │ │ ├── server.py
│ │ │ ├── templates
│ │ │ │ └── recommendation_templates
│ │ │ │ ├── ebs_volume.template
│ │ │ │ ├── ec2_asg.template
│ │ │ │ ├── ec2_instance.template
│ │ │ │ ├── ecs_service.template
│ │ │ │ ├── idle.template
│ │ │ │ ├── lambda_function.template
│ │ │ │ ├── rds_database.template
│ │ │ │ ├── reserved_instances.template
│ │ │ │ └── savings_plans.template
│ │ │ ├── tools
│ │ │ │ ├── __init__.py
│ │ │ │ ├── aws_pricing_operations.py
│ │ │ │ ├── aws_pricing_tools.py
│ │ │ │ ├── budget_tools.py
│ │ │ │ ├── compute_optimizer_tools.py
│ │ │ │ ├── cost_anomaly_tools.py
│ │ │ │ ├── cost_comparison_tools.py
│ │ │ │ ├── cost_explorer_operations.py
│ │ │ │ ├── cost_explorer_tools.py
│ │ │ │ ├── cost_optimization_hub_helpers.py
│ │ │ │ ├── cost_optimization_hub_tools.py
│ │ │ │ ├── free_tier_usage_tools.py
│ │ │ │ ├── recommendation_details_tools.py
│ │ │ │ ├── ri_performance_tools.py
│ │ │ │ ├── sp_performance_tools.py
│ │ │ │ ├── storage_lens_tools.py
│ │ │ │ └── unified_sql_tools.py
│ │ │ └── utilities
│ │ │ ├── __init__.py
│ │ │ ├── aws_service_base.py
│ │ │ ├── constants.py
│ │ │ ├── logging_utils.py
│ │ │ └── sql_utils.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── requirements.txt
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── prompts
│ │ │ │ ├── __init__.py
│ │ │ │ └── test_prompts.py
│ │ │ ├── README.md
│ │ │ ├── test_models.py
│ │ │ ├── test_server.py
│ │ │ ├── tools
│ │ │ │ ├── __init__.py
│ │ │ │ ├── fixtures.py
│ │ │ │ ├── test_aws_pricing_tools.py
│ │ │ │ ├── test_budget_tools.py
│ │ │ │ ├── test_compute_optimizer_tools.py
│ │ │ │ ├── test_cost_anomaly_tools_enhanced.py
│ │ │ │ ├── test_cost_anomaly_tools.py
│ │ │ │ ├── test_cost_comparison_tools.py
│ │ │ │ ├── test_cost_explorer_operations.py
│ │ │ │ ├── test_cost_explorer_tools.py
│ │ │ │ ├── test_cost_optimization_hub_helpers.py
│ │ │ │ ├── test_cost_optimization_hub_tools.py
│ │ │ │ ├── test_free_tier_usage_tools_new.py
│ │ │ │ ├── test_recommendation_details_tools.py
│ │ │ │ ├── test_ri_performance_tools.py
│ │ │ │ ├── test_sp_performance_tools.py
│ │ │ │ ├── test_storage_lens_tools.py
│ │ │ │ └── test_unified_sql_tools.py
│ │ │ └── utilities
│ │ │ ├── test_aws_service_base.py
│ │ │ └── test_sql_utils.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── ccapi-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── ccapi_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── aws_client.py
│ │ │ ├── cloud_control_utils.py
│ │ │ ├── context.py
│ │ │ ├── errors.py
│ │ │ ├── iac_generator.py
│ │ │ ├── impl
│ │ │ │ ├── __init__.py
│ │ │ │ ├── tools
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── explanation.py
│ │ │ │ │ ├── infrastructure_generation.py
│ │ │ │ │ ├── resource_operations.py
│ │ │ │ │ ├── security_scanning.py
│ │ │ │ │ └── session_management.py
│ │ │ │ └── utils
│ │ │ │ ├── __init__.py
│ │ │ │ └── validation.py
│ │ │ ├── infrastructure_generator.py
│ │ │ ├── models
│ │ │ │ ├── __init__.py
│ │ │ │ └── models.py
│ │ │ ├── schema_manager.py
│ │ │ ├── server.py
│ │ │ └── static
│ │ │ └── __init__.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── run_tests.sh
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── test_aws_client.py
│ │ │ ├── test_checkov_install.py
│ │ │ ├── test_cloud_control_utils.py
│ │ │ ├── test_context.py
│ │ │ ├── test_errors.py
│ │ │ ├── test_explanation.py
│ │ │ ├── test_iac_generator.py
│ │ │ ├── test_infrastructure_generation.py
│ │ │ ├── test_infrastructure_generator.py
│ │ │ ├── test_models.py
│ │ │ ├── test_resource_operations.py
│ │ │ ├── test_schema_manager.py
│ │ │ ├── test_security_scanning.py
│ │ │ ├── test_server.py
│ │ │ ├── test_session_management.py
│ │ │ └── test_validation.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── cdk-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── cdk_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── core
│ │ │ │ ├── __init__.py
│ │ │ │ ├── resources.py
│ │ │ │ ├── search_utils.py
│ │ │ │ ├── server.py
│ │ │ │ └── tools.py
│ │ │ ├── data
│ │ │ │ ├── __init__.py
│ │ │ │ ├── cdk_nag_parser.py
│ │ │ │ ├── construct_descriptions.py
│ │ │ │ ├── genai_cdk_loader.py
│ │ │ │ ├── lambda_layer_parser.py
│ │ │ │ ├── lambda_powertools_loader.py
│ │ │ │ ├── schema_generator.py
│ │ │ │ └── solutions_constructs_parser.py
│ │ │ ├── server.py
│ │ │ └── static
│ │ │ ├── __init__.py
│ │ │ ├── CDK_GENERAL_GUIDANCE.md
│ │ │ ├── CDK_NAG_GUIDANCE.md
│ │ │ └── lambda_powertools
│ │ │ ├── bedrock.md
│ │ │ ├── cdk.md
│ │ │ ├── dependencies.md
│ │ │ ├── index.md
│ │ │ ├── insights.md
│ │ │ ├── logging.md
│ │ │ ├── metrics.md
│ │ │ └── tracing.md
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── core
│ │ │ │ ├── test_resources_enhanced.py
│ │ │ │ ├── test_resources.py
│ │ │ │ ├── test_search_utils.py
│ │ │ │ ├── test_server.py
│ │ │ │ └── test_tools.py
│ │ │ └── data
│ │ │ ├── test_cdk_nag_parser.py
│ │ │ ├── test_genai_cdk_loader.py
│ │ │ ├── test_lambda_powertools_loader.py
│ │ │ ├── test_schema_generator.py
│ │ │ └── test_solutions_constructs_parser.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── cfn-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── cfn_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── aws_client.py
│ │ │ ├── cloud_control_utils.py
│ │ │ ├── context.py
│ │ │ ├── errors.py
│ │ │ ├── iac_generator.py
│ │ │ ├── schema_manager.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── run_tests.sh
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── test_aws_client.py
│ │ │ ├── test_cloud_control_utils.py
│ │ │ ├── test_errors.py
│ │ │ ├── test_iac_generator.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_schema_manager.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── cloudtrail-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── cloudtrail_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── common.py
│ │ │ ├── models.py
│ │ │ ├── server.py
│ │ │ └── tools.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_models.py
│ │ │ ├── test_server.py
│ │ │ └── test_tools.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── cloudwatch-appsignals-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── cloudwatch_appsignals_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── audit_presentation_utils.py
│ │ │ ├── audit_utils.py
│ │ │ ├── aws_clients.py
│ │ │ ├── canary_utils.py
│ │ │ ├── server.py
│ │ │ ├── service_audit_utils.py
│ │ │ ├── service_tools.py
│ │ │ ├── sli_report_client.py
│ │ │ ├── slo_tools.py
│ │ │ ├── trace_tools.py
│ │ │ └── utils.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── conftest.py
│ │ │ ├── test_audit_presentation_utils.py
│ │ │ ├── test_audit_utils.py
│ │ │ ├── test_aws_profile.py
│ │ │ ├── test_canary_utils.py
│ │ │ ├── test_initialization.py
│ │ │ ├── test_server_audit_functions.py
│ │ │ ├── test_server_audit_tools.py
│ │ │ ├── test_server.py
│ │ │ ├── test_service_audit_utils.py
│ │ │ ├── test_service_tools_operations.py
│ │ │ ├── test_sli_report_client.py
│ │ │ ├── test_slo_tools.py
│ │ │ └── test_utils.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── cloudwatch-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── cloudwatch_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── cloudwatch_alarms
│ │ │ │ ├── models.py
│ │ │ │ └── tools.py
│ │ │ ├── cloudwatch_logs
│ │ │ │ ├── models.py
│ │ │ │ └── tools.py
│ │ │ ├── cloudwatch_metrics
│ │ │ │ ├── data
│ │ │ │ │ └── metric_metadata.json
│ │ │ │ ├── models.py
│ │ │ │ └── tools.py
│ │ │ ├── common.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── cloudwatch_alarms
│ │ │ │ ├── test_active_alarms.py
│ │ │ │ ├── test_alarm_history_integration.py
│ │ │ │ ├── test_alarm_history.py
│ │ │ │ └── test_alarms_error_handling.py
│ │ │ ├── cloudwatch_logs
│ │ │ │ ├── test_logs_error_handling.py
│ │ │ │ ├── test_logs_models.py
│ │ │ │ └── test_logs_server.py
│ │ │ ├── cloudwatch_metrics
│ │ │ │ ├── test_metrics_error_handling.py
│ │ │ │ ├── test_metrics_models.py
│ │ │ │ ├── test_metrics_server.py
│ │ │ │ └── test_validation_error.py
│ │ │ ├── test_common_and_server.py
│ │ │ ├── test_init.py
│ │ │ └── test_main.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── code-doc-gen-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── code_doc_gen_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── server.py
│ │ │ └── utils
│ │ │ ├── doc_generator.py
│ │ │ ├── models.py
│ │ │ ├── repomix_manager.py
│ │ │ └── templates.py
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── test_doc_generator_edge_cases.py
│ │ │ ├── test_doc_generator.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_repomix_manager_scenarios.py
│ │ │ ├── test_repomix_manager.py
│ │ │ ├── test_repomix_statistics.py
│ │ │ ├── test_server_extended.py
│ │ │ ├── test_server.py
│ │ │ └── test_templates.py
│ │ └── uv.lock
│ ├── core-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── core_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── server.py
│ │ │ └── static
│ │ │ ├── __init__.py
│ │ │ └── PROMPT_UNDERSTANDING.md
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── README.md
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_response_types.py
│ │ │ ├── test_server.py
│ │ │ └── test_static.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── cost-explorer-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── cost_explorer_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── comparison_handler.py
│ │ │ ├── constants.py
│ │ │ ├── cost_usage_handler.py
│ │ │ ├── forecasting_handler.py
│ │ │ ├── helpers.py
│ │ │ ├── metadata_handler.py
│ │ │ ├── models.py
│ │ │ ├── server.py
│ │ │ └── utility_handler.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── test_comparison_handler.py
│ │ │ ├── test_cost_usage_handler.py
│ │ │ ├── test_forecasting_handler.py
│ │ │ ├── test_helpers.py
│ │ │ ├── test_metadata_handler.py
│ │ │ ├── test_models.py
│ │ │ ├── test_server.py
│ │ │ └── test_utility_handler.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── documentdb-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ └── documentdb_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── analytic_tools.py
│ │ │ ├── config.py
│ │ │ ├── connection_tools.py
│ │ │ ├── db_management_tools.py
│ │ │ ├── query_tools.py
│ │ │ ├── server.py
│ │ │ └── write_tools.py
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── conftest.py
│ │ │ ├── test_analytic_tools.py
│ │ │ ├── test_connection_tools.py
│ │ │ ├── test_db_management_tools.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_query_tools.py
│ │ │ └── test_write_tools.py
│ │ └── uv.lock
│ ├── dynamodb-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── dynamodb_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── common.py
│ │ │ ├── database_analysis_queries.py
│ │ │ ├── database_analyzers.py
│ │ │ ├── prompts
│ │ │ │ └── dynamodb_architect.md
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── evals
│ │ │ │ ├── dynamic_evaluators.py
│ │ │ │ ├── evaluation_registry.py
│ │ │ │ ├── logging_config.py
│ │ │ │ ├── multiturn_evaluator.py
│ │ │ │ ├── README.md
│ │ │ │ ├── scenarios.py
│ │ │ │ └── test_dspy_evals.py
│ │ │ ├── test_dynamodb_server.py
│ │ │ └── test_source_db_integration.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── ecs-mcp-server
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── ecs_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── api
│ │ │ │ ├── __init__.py
│ │ │ │ ├── containerize.py
│ │ │ │ ├── delete.py
│ │ │ │ ├── ecs_troubleshooting.py
│ │ │ │ ├── infrastructure.py
│ │ │ │ ├── resource_management.py
│ │ │ │ ├── status.py
│ │ │ │ └── troubleshooting_tools
│ │ │ │ ├── __init__.py
│ │ │ │ ├── detect_image_pull_failures.py
│ │ │ │ ├── fetch_cloudformation_status.py
│ │ │ │ ├── fetch_network_configuration.py
│ │ │ │ ├── fetch_service_events.py
│ │ │ │ ├── fetch_task_failures.py
│ │ │ │ ├── fetch_task_logs.py
│ │ │ │ ├── get_ecs_troubleshooting_guidance.py
│ │ │ │ └── utils.py
│ │ │ ├── main.py
│ │ │ ├── modules
│ │ │ │ ├── __init__.py
│ │ │ │ ├── aws_knowledge_proxy.py
│ │ │ │ ├── containerize.py
│ │ │ │ ├── delete.py
│ │ │ │ ├── deployment_status.py
│ │ │ │ ├── infrastructure.py
│ │ │ │ ├── resource_management.py
│ │ │ │ └── troubleshooting.py
│ │ │ ├── templates
│ │ │ │ ├── ecr_infrastructure.json
│ │ │ │ └── ecs_infrastructure.json
│ │ │ └── utils
│ │ │ ├── arn_parser.py
│ │ │ ├── aws.py
│ │ │ ├── config.py
│ │ │ ├── docker.py
│ │ │ ├── security.py
│ │ │ ├── templates.py
│ │ │ └── time_utils.py
│ │ ├── DEVELOPMENT.md
│ │ ├── pyproject.toml
│ │ ├── pyrightconfig.json
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── integ
│ │ │ │ └── mcp-inspector
│ │ │ │ ├── .gitignore
│ │ │ │ ├── README.md
│ │ │ │ ├── run-tests.sh
│ │ │ │ └── scenarios
│ │ │ │ ├── 01_comprehensive_troubleshooting
│ │ │ │ │ ├── 01_create.sh
│ │ │ │ │ ├── 02_validate.sh
│ │ │ │ │ ├── 03_cleanup.sh
│ │ │ │ │ ├── description.txt
│ │ │ │ │ └── utils
│ │ │ │ │ ├── mcp_helpers.sh
│ │ │ │ │ └── validation_helpers.sh
│ │ │ │ └── 02_test_knowledge_proxy_tools
│ │ │ │ ├── 01_create.sh
│ │ │ │ ├── 02_validate.sh
│ │ │ │ ├── 03_cleanup.sh
│ │ │ │ ├── description.txt
│ │ │ │ └── utils
│ │ │ │ ├── knowledge_validation_helpers.sh
│ │ │ │ └── mcp_knowledge_helpers.sh
│ │ │ ├── llm_testing
│ │ │ │ ├── invalid_cfn_template.yaml
│ │ │ │ ├── README.md
│ │ │ │ ├── run_tests.sh
│ │ │ │ ├── scenarios
│ │ │ │ │ ├── 01_cloudformation_failure
│ │ │ │ │ │ ├── 01_create.sh
│ │ │ │ │ │ ├── 02_validate.sh
│ │ │ │ │ │ ├── 03_prompts.txt
│ │ │ │ │ │ ├── 04_evaluation.md
│ │ │ │ │ │ ├── 05_cleanup.sh
│ │ │ │ │ │ └── description.txt
│ │ │ │ │ ├── 02_service_failure
│ │ │ │ │ │ ├── 01_create.sh
│ │ │ │ │ │ ├── 02_validate.sh
│ │ │ │ │ │ ├── 03_prompts.txt
│ │ │ │ │ │ ├── 04_evaluation.md
│ │ │ │ │ │ ├── 05_cleanup.sh
│ │ │ │ │ │ └── description.txt
│ │ │ │ │ ├── 03_task_exit_failure
│ │ │ │ │ │ ├── 01_create.sh
│ │ │ │ │ │ ├── 02_validate.sh
│ │ │ │ │ │ ├── 03_prompts.txt
│ │ │ │ │ │ ├── 04_evaluation.md
│ │ │ │ │ │ ├── 05_cleanup.sh
│ │ │ │ │ │ └── description.txt
│ │ │ │ │ ├── 04_network_configuration_failure
│ │ │ │ │ │ ├── 01_create.sh
│ │ │ │ │ │ ├── 02_validate.sh
│ │ │ │ │ │ ├── 03_prompts.txt
│ │ │ │ │ │ ├── 05_cleanup.sh
│ │ │ │ │ │ └── description.txt
│ │ │ │ │ ├── 05_resource_constraint_failure
│ │ │ │ │ │ ├── 01_create.sh
│ │ │ │ │ │ ├── 02_validate.sh
│ │ │ │ │ │ ├── 03_prompts.txt
│ │ │ │ │ │ ├── 05_cleanup.sh
│ │ │ │ │ │ └── description.txt
│ │ │ │ │ └── 06_load_balancer_failure
│ │ │ │ │ ├── 01_create.sh
│ │ │ │ │ ├── 02_validate.sh
│ │ │ │ │ ├── 03_prompts.txt
│ │ │ │ │ ├── 05_cleanup.sh
│ │ │ │ │ └── description.txt
│ │ │ │ ├── SCRIPT_IMPROVEMENTS.md
│ │ │ │ └── utils
│ │ │ │ ├── aws_helpers.sh
│ │ │ │ └── evaluation_template.md
│ │ │ └── unit
│ │ │ ├── __init__.py
│ │ │ ├── api
│ │ │ │ ├── conftest.py
│ │ │ │ ├── test_delete_api.py
│ │ │ │ ├── test_ecs_troubleshooting.py
│ │ │ │ ├── test_resource_management_api.py
│ │ │ │ └── troubleshooting_tools
│ │ │ │ └── test_fetch_network_configuration.py
│ │ │ ├── conftest.py
│ │ │ ├── modules
│ │ │ │ ├── test_aws_knowledge_proxy.py
│ │ │ │ └── test_resource_management_module.py
│ │ │ ├── test_aws_role_utils.py
│ │ │ ├── test_aws_utils.py
│ │ │ ├── test_containerize.py
│ │ │ ├── test_delete.py
│ │ │ ├── test_docker_utils.py
│ │ │ ├── test_docker_with_role.py
│ │ │ ├── test_image_pull_failure_extended.py
│ │ │ ├── test_image_pull_failure.py
│ │ │ ├── test_infrastructure_role.py
│ │ │ ├── test_infrastructure.py
│ │ │ ├── test_integration.py
│ │ │ ├── test_main.py
│ │ │ ├── test_resource_management_api_operation.py
│ │ │ ├── test_resource_management_tool.py
│ │ │ ├── test_resource_management.py
│ │ │ ├── test_security_integration.py
│ │ │ ├── test_status_pytest.py
│ │ │ ├── test_status.py
│ │ │ ├── troubleshooting_tools
│ │ │ │ ├── __init__.py
│ │ │ │ ├── conftest.py
│ │ │ │ ├── test_detect_image_pull_failures.py
│ │ │ │ ├── test_fetch_cloudformation_status.py
│ │ │ │ ├── test_fetch_service_events.py
│ │ │ │ ├── test_fetch_task_failures.py
│ │ │ │ ├── test_fetch_task_logs.py
│ │ │ │ ├── test_get_ecs_troubleshooting_guidance.py
│ │ │ │ ├── test_is_ecr_image_security.py
│ │ │ │ └── test_utils.py
│ │ │ └── utils
│ │ │ ├── __init__.py
│ │ │ ├── async_test_utils.py
│ │ │ ├── test_arn_parser.py
│ │ │ ├── test_config.py
│ │ │ ├── test_docker.py
│ │ │ ├── test_response_sanitization.py
│ │ │ ├── test_security_extended.py
│ │ │ ├── test_security.py
│ │ │ ├── test_templates.py
│ │ │ └── test_time_utils.py
│ │ └── uv.lock
│ ├── eks-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── eks_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── aws_helper.py
│ │ │ ├── cloudwatch_handler.py
│ │ │ ├── cloudwatch_metrics_guidance_handler.py
│ │ │ ├── consts.py
│ │ │ ├── data
│ │ │ │ └── eks_cloudwatch_metrics_guidance.json
│ │ │ ├── eks_kb_handler.py
│ │ │ ├── eks_stack_handler.py
│ │ │ ├── iam_handler.py
│ │ │ ├── insights_handler.py
│ │ │ ├── k8s_apis.py
│ │ │ ├── k8s_client_cache.py
│ │ │ ├── k8s_handler.py
│ │ │ ├── logging_helper.py
│ │ │ ├── models.py
│ │ │ ├── scripts
│ │ │ │ └── update_eks_cloudwatch_metrics_guidance.py
│ │ │ ├── server.py
│ │ │ ├── templates
│ │ │ │ ├── eks-templates
│ │ │ │ │ └── eks-with-vpc.yaml
│ │ │ │ └── k8s-templates
│ │ │ │ ├── deployment.yaml
│ │ │ │ └── service.yaml
│ │ │ └── vpc_config_handler.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── test_aws_helper.py
│ │ │ ├── test_cloudwatch_handler.py
│ │ │ ├── test_cloudwatch_metrics_guidance_handler.py
│ │ │ ├── test_eks_kb_handler.py
│ │ │ ├── test_eks_stack_handler.py
│ │ │ ├── test_iam_handler.py
│ │ │ ├── test_init.py
│ │ │ ├── test_insights_handler.py
│ │ │ ├── test_k8s_apis.py
│ │ │ ├── test_k8s_client_cache.py
│ │ │ ├── test_k8s_handler.py
│ │ │ ├── test_logging_helper.py
│ │ │ ├── test_main.py
│ │ │ ├── test_models.py
│ │ │ ├── test_server.py
│ │ │ └── test_vpc_config_handler.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── elasticache-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── elasticache_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── common
│ │ │ │ ├── __init__.py
│ │ │ │ ├── connection.py
│ │ │ │ ├── decorators.py
│ │ │ │ └── server.py
│ │ │ ├── context.py
│ │ │ ├── main.py
│ │ │ └── tools
│ │ │ ├── __init__.py
│ │ │ ├── cc
│ │ │ │ ├── __init__.py
│ │ │ │ ├── connect.py
│ │ │ │ ├── create.py
│ │ │ │ ├── delete.py
│ │ │ │ ├── describe.py
│ │ │ │ ├── modify.py
│ │ │ │ ├── parsers.py
│ │ │ │ └── processors.py
│ │ │ ├── ce
│ │ │ │ ├── __init__.py
│ │ │ │ └── get_cost_and_usage.py
│ │ │ ├── cw
│ │ │ │ ├── __init__.py
│ │ │ │ └── get_metric_statistics.py
│ │ │ ├── cwlogs
│ │ │ │ ├── __init__.py
│ │ │ │ ├── create_log_group.py
│ │ │ │ ├── describe_log_groups.py
│ │ │ │ ├── describe_log_streams.py
│ │ │ │ ├── filter_log_events.py
│ │ │ │ └── get_log_events.py
│ │ │ ├── firehose
│ │ │ │ ├── __init__.py
│ │ │ │ └── list_delivery_streams.py
│ │ │ ├── misc
│ │ │ │ ├── __init__.py
│ │ │ │ ├── batch_apply_update_action.py
│ │ │ │ ├── batch_stop_update_action.py
│ │ │ │ ├── describe_cache_engine_versions.py
│ │ │ │ ├── describe_engine_default_parameters.py
│ │ │ │ ├── describe_events.py
│ │ │ │ └── describe_service_updates.py
│ │ │ ├── rg
│ │ │ │ ├── __init__.py
│ │ │ │ ├── complete_migration.py
│ │ │ │ ├── connect.py
│ │ │ │ ├── create.py
│ │ │ │ ├── delete.py
│ │ │ │ ├── describe.py
│ │ │ │ ├── modify.py
│ │ │ │ ├── parsers.py
│ │ │ │ ├── processors.py
│ │ │ │ ├── start_migration.py
│ │ │ │ └── test_migration.py
│ │ │ └── serverless
│ │ │ ├── __init__.py
│ │ │ ├── connect.py
│ │ │ ├── create.py
│ │ │ ├── delete.py
│ │ │ ├── describe.py
│ │ │ ├── models.py
│ │ │ └── modify.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── test_connection.py
│ │ │ ├── test_decorators.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ └── tools
│ │ │ ├── cc
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_connect_additional.py
│ │ │ │ ├── test_connect_coverage_additional.py
│ │ │ │ ├── test_connect_coverage.py
│ │ │ │ ├── test_connect.py
│ │ │ │ ├── test_create_additional.py
│ │ │ │ ├── test_create.py
│ │ │ │ ├── test_delete.py
│ │ │ │ ├── test_describe.py
│ │ │ │ ├── test_modify.py
│ │ │ │ ├── test_parsers.py
│ │ │ │ └── test_processors.py
│ │ │ ├── ce
│ │ │ │ ├── __init__.py
│ │ │ │ └── test_get_cost_and_usage.py
│ │ │ ├── cw
│ │ │ │ └── test_get_metric_statistics.py
│ │ │ ├── cwlogs
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_create_log_group.py
│ │ │ │ ├── test_describe_log_groups.py
│ │ │ │ ├── test_describe_log_streams.py
│ │ │ │ ├── test_filter_log_events.py
│ │ │ │ └── test_get_log_events.py
│ │ │ ├── firehose
│ │ │ │ └── test_list_delivery_streams.py
│ │ │ ├── misc
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_batch_apply_update_action.py
│ │ │ │ ├── test_batch_stop_update_action.py
│ │ │ │ ├── test_describe_cache_engine_versions.py
│ │ │ │ ├── test_describe_engine_default_parameters.py
│ │ │ │ ├── test_describe_events.py
│ │ │ │ └── test_describe_service_updates.py
│ │ │ ├── rg
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_complete_migration.py
│ │ │ │ ├── test_connect_additional.py
│ │ │ │ ├── test_connect_coverage_additional.py
│ │ │ │ ├── test_connect_optional_fields.py
│ │ │ │ ├── test_connect_partial_coverage.py
│ │ │ │ ├── test_connect.py
│ │ │ │ ├── test_create.py
│ │ │ │ ├── test_delete.py
│ │ │ │ ├── test_describe.py
│ │ │ │ ├── test_modify.py
│ │ │ │ ├── test_parsers.py
│ │ │ │ ├── test_processors.py
│ │ │ │ ├── test_start_migration.py
│ │ │ │ └── test_test_migration.py
│ │ │ └── serverless
│ │ │ ├── test_connect_additional.py
│ │ │ ├── test_connect_coverage_additional.py
│ │ │ ├── test_connect_optional_fields.py
│ │ │ ├── test_connect.py
│ │ │ ├── test_create.py
│ │ │ ├── test_delete.py
│ │ │ ├── test_describe.py
│ │ │ └── test_modify.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── finch-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── finch_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── consts.py
│ │ │ ├── models.py
│ │ │ ├── server.py
│ │ │ └── utils
│ │ │ ├── __init__.py
│ │ │ ├── build.py
│ │ │ ├── common.py
│ │ │ ├── ecr.py
│ │ │ ├── push.py
│ │ │ └── vm.py
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── test_cli_flags.py
│ │ │ ├── test_logging_configuration.py
│ │ │ ├── test_server.py
│ │ │ ├── test_utils_build.py
│ │ │ ├── test_utils_common.py
│ │ │ ├── test_utils_ecr.py
│ │ │ ├── test_utils_push.py
│ │ │ └── test_utils_vm.py
│ │ └── uv.lock
│ ├── frontend-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── frontend_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── server.py
│ │ │ ├── static
│ │ │ │ └── react
│ │ │ │ ├── essential-knowledge.md
│ │ │ │ └── troubleshooting.md
│ │ │ └── utils
│ │ │ ├── __init__.py
│ │ │ └── file_utils.py
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── test_file_utils.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ └── test_server.py
│ │ └── uv.lock
│ ├── git-repo-research-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── git_repo_research_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── defaults.py
│ │ │ ├── embeddings.py
│ │ │ ├── github_search.py
│ │ │ ├── indexer.py
│ │ │ ├── models.py
│ │ │ ├── repository.py
│ │ │ ├── search.py
│ │ │ ├── server.py
│ │ │ └── utils.py
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── run_tests.sh
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── test_errors_repository.py
│ │ │ ├── test_github_search_edge_cases.py
│ │ │ ├── test_graphql_github_search.py
│ │ │ ├── test_local_repository.py
│ │ │ ├── test_repository_utils.py
│ │ │ ├── test_rest_github_search.py
│ │ │ ├── test_search.py
│ │ │ ├── test_server.py
│ │ │ └── test_url_repository.py
│ │ └── uv.lock
│ ├── healthlake-mcp-server
│ │ ├── .dockerignore
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── healthlake_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── fhir_operations.py
│ │ │ ├── main.py
│ │ │ ├── models.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── CONTRIBUTING.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── examples
│ │ │ ├── mcp_config.json
│ │ │ └── README.md
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── conftest.py
│ │ │ ├── test_fhir_client_comprehensive.py
│ │ │ ├── test_fhir_error_scenarios.py
│ │ │ ├── test_fhir_operations.py
│ │ │ ├── test_integration_mock_based.py
│ │ │ ├── test_main_edge_cases.py
│ │ │ ├── test_main.py
│ │ │ ├── test_mcp_integration_coverage.py
│ │ │ ├── test_models_edge_cases.py
│ │ │ ├── test_models.py
│ │ │ ├── test_readonly_mode.py
│ │ │ ├── test_server_core.py
│ │ │ ├── test_server_error_handling.py
│ │ │ ├── test_server_mcp_handlers.py
│ │ │ ├── test_server_toolhandler.py
│ │ │ └── test_server_validation.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── iam-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── iam_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── aws_client.py
│ │ │ ├── context.py
│ │ │ ├── errors.py
│ │ │ ├── models.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── DESIGN_COMPLIANCE.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── examples
│ │ │ ├── get_policy_document_example.py
│ │ │ └── inline_policy_demo.py
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── run_tests.sh
│ │ ├── tests
│ │ │ ├── test_context.py
│ │ │ ├── test_errors.py
│ │ │ ├── test_inline_policies.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── lambda-tool-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── lambda_tool_mcp_server
│ │ │ ├── __init__.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── examples
│ │ │ ├── README.md
│ │ │ └── sample_functions
│ │ │ ├── customer-create
│ │ │ │ └── app.py
│ │ │ ├── customer-id-from-email
│ │ │ │ └── app.py
│ │ │ ├── customer-info-from-id
│ │ │ │ └── app.py
│ │ │ └── template.yml
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── .gitignore
│ │ │ ├── conftest.py
│ │ │ ├── README.md
│ │ │ ├── test_format_lambda_response.py
│ │ │ ├── test_integration_coverage.py
│ │ │ ├── test_integration.py
│ │ │ ├── test_register_lambda_functions.py
│ │ │ ├── test_schema_integration.py
│ │ │ ├── test_server_coverage_additional.py
│ │ │ ├── test_server_coverage.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── mcp-lambda-handler
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ └── mcp_lambda_handler
│ │ │ ├── __init__.py
│ │ │ ├── mcp_lambda_handler.py
│ │ │ ├── session.py
│ │ │ └── types.py
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ └── test_lambda_handler.py
│ │ └── uv.lock
│ ├── memcached-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── memcached_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── common
│ │ │ │ ├── config.py
│ │ │ │ ├── connection.py
│ │ │ │ └── server.py
│ │ │ ├── context.py
│ │ │ ├── main.py
│ │ │ └── tools
│ │ │ └── cache.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── ELASTICACHECONNECT.md
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── test_cache_readonly.py
│ │ │ ├── test_cache.py
│ │ │ ├── test_connection.py
│ │ │ ├── test_init.py
│ │ │ └── test_main.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── mysql-mcp-server
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── mysql_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── mutable_sql_detector.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── conftest.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── nova-canvas-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── nova_canvas_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── consts.py
│ │ │ ├── models.py
│ │ │ ├── novacanvas.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── .gitignore
│ │ │ ├── conftest.py
│ │ │ ├── README.md
│ │ │ ├── test_models.py
│ │ │ ├── test_novacanvas.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── openapi-mcp-server
│ │ ├── .coveragerc
│ │ ├── .dockerignore
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── AUTHENTICATION.md
│ │ ├── AWS_BEST_PRACTICES.md
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── openapi_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── api
│ │ │ │ ├── __init__.py
│ │ │ │ └── config.py
│ │ │ ├── auth
│ │ │ │ ├── __init__.py
│ │ │ │ ├── api_key_auth.py
│ │ │ │ ├── auth_cache.py
│ │ │ │ ├── auth_errors.py
│ │ │ │ ├── auth_factory.py
│ │ │ │ ├── auth_protocol.py
│ │ │ │ ├── auth_provider.py
│ │ │ │ ├── base_auth.py
│ │ │ │ ├── basic_auth.py
│ │ │ │ ├── bearer_auth.py
│ │ │ │ ├── cognito_auth.py
│ │ │ │ └── register.py
│ │ │ ├── patch
│ │ │ │ └── __init__.py
│ │ │ ├── prompts
│ │ │ │ ├── __init__.py
│ │ │ │ ├── generators
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── operation_prompts.py
│ │ │ │ │ └── workflow_prompts.py
│ │ │ │ ├── models.py
│ │ │ │ └── prompt_manager.py
│ │ │ ├── server.py
│ │ │ └── utils
│ │ │ ├── __init__.py
│ │ │ ├── cache_provider.py
│ │ │ ├── config.py
│ │ │ ├── error_handler.py
│ │ │ ├── http_client.py
│ │ │ ├── metrics_provider.py
│ │ │ ├── openapi_validator.py
│ │ │ └── openapi.py
│ │ ├── CHANGELOG.md
│ │ ├── DEPLOYMENT.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── OBSERVABILITY.md
│ │ ├── pyproject.toml
│ │ ├── pyrightconfig.json
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── api
│ │ │ │ └── test_config.py
│ │ │ ├── auth
│ │ │ │ ├── test_api_key_auth.py
│ │ │ │ ├── test_auth_cache.py
│ │ │ │ ├── test_auth_errors.py
│ │ │ │ ├── test_auth_factory_caching.py
│ │ │ │ ├── test_auth_factory_coverage.py
│ │ │ │ ├── test_auth_factory.py
│ │ │ │ ├── test_auth_protocol_additional.py
│ │ │ │ ├── test_auth_protocol_boost.py
│ │ │ │ ├── test_auth_protocol_coverage.py
│ │ │ │ ├── test_auth_protocol_extended.py
│ │ │ │ ├── test_auth_protocol_improved.py
│ │ │ │ ├── test_auth_protocol.py
│ │ │ │ ├── test_auth_provider_additional.py
│ │ │ │ ├── test_base_auth_coverage.py
│ │ │ │ ├── test_base_auth.py
│ │ │ │ ├── test_basic_auth.py
│ │ │ │ ├── test_bearer_auth.py
│ │ │ │ ├── test_cognito_auth_additional_coverage.py
│ │ │ │ ├── test_cognito_auth_boost_coverage.py
│ │ │ │ ├── test_cognito_auth_client_credentials.py
│ │ │ │ ├── test_cognito_auth_coverage_boost.py
│ │ │ │ ├── test_cognito_auth_exceptions.py
│ │ │ │ ├── test_cognito_auth.py
│ │ │ │ ├── test_register_coverage.py
│ │ │ │ └── test_register.py
│ │ │ ├── prompts
│ │ │ │ ├── standalone
│ │ │ │ │ ├── test_operation_prompt.py
│ │ │ │ │ ├── test_prompt_arguments.py
│ │ │ │ │ └── test_secure_operation_prompt.py
│ │ │ │ ├── test_mcp_prompt_manager_integration.py
│ │ │ │ ├── test_mcp_prompt_manager.py
│ │ │ │ ├── test_models_dict_method.py
│ │ │ │ ├── test_operation_prompts_extended.py
│ │ │ │ ├── test_prompt_manager_additional.py
│ │ │ │ ├── test_prompt_manager_comprehensive.py
│ │ │ │ ├── test_prompt_manager_coverage.py
│ │ │ │ └── test_prompt_registration.py
│ │ │ ├── README.md
│ │ │ ├── test_api_name.py
│ │ │ ├── test_cache_coverage_89.py
│ │ │ ├── test_client.py
│ │ │ ├── test_coverage_boost.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main_extended.py
│ │ │ ├── test_main.py
│ │ │ ├── test_openapi_coverage_89.py
│ │ │ ├── test_server_auth_errors.py
│ │ │ ├── test_server_coverage_boost_2.py
│ │ │ ├── test_server_coverage_boost.py
│ │ │ ├── test_server_exception_handling.py
│ │ │ ├── test_server_extended.py
│ │ │ ├── test_server_httpx_version.py
│ │ │ ├── test_server_part1.py
│ │ │ ├── test_server_route_logging.py
│ │ │ ├── test_server_signal_handlers.py
│ │ │ ├── test_server.py
│ │ │ └── utils
│ │ │ ├── test_cache_provider.py
│ │ │ ├── test_error_handler_boost.py
│ │ │ ├── test_error_handler_extended.py
│ │ │ ├── test_error_handler_fix.py
│ │ │ ├── test_error_handler.py
│ │ │ ├── test_http_client_comprehensive.py
│ │ │ ├── test_http_client_extended.py
│ │ │ ├── test_http_client_extended2.py
│ │ │ ├── test_http_client_import_error.py
│ │ │ ├── test_http_client.py
│ │ │ ├── test_metrics_provider_decorators.py
│ │ │ ├── test_metrics_provider_extended2.py
│ │ │ ├── test_metrics_provider_prometheus.py
│ │ │ ├── test_metrics_provider.py
│ │ │ ├── test_openapi_validator.py
│ │ │ └── test_openapi.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── postgres-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── postgres_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── connection
│ │ │ │ ├── __init__.py
│ │ │ │ ├── abstract_db_connection.py
│ │ │ │ ├── db_connection_singleton.py
│ │ │ │ ├── psycopg_pool_connection.py
│ │ │ │ └── rds_api_connection.py
│ │ │ ├── mutable_sql_detector.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── conftest.py
│ │ │ ├── test_psycopg_connector.py
│ │ │ ├── test_server.py
│ │ │ └── test_singleton.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── prometheus-mcp-server
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── prometheus_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── consts.py
│ │ │ ├── models.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── conftest.py
│ │ │ ├── test_aws_credentials.py
│ │ │ ├── test_config_manager.py
│ │ │ ├── test_consts.py
│ │ │ ├── test_coverage_gaps.py
│ │ │ ├── test_coverage_improvement.py
│ │ │ ├── test_final_coverage.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_models.py
│ │ │ ├── test_prometheus_client.py
│ │ │ ├── test_prometheus_connection.py
│ │ │ ├── test_security_validator.py
│ │ │ ├── test_server_coverage.py
│ │ │ ├── test_tools.py
│ │ │ └── test_workspace_config.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── redshift-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── redshift_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── consts.py
│ │ │ ├── models.py
│ │ │ ├── redshift.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_redshift.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── s3-tables-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── s3_tables_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── constants.py
│ │ │ ├── database.py
│ │ │ ├── engines
│ │ │ │ ├── __init__.py
│ │ │ │ └── pyiceberg.py
│ │ │ ├── file_processor
│ │ │ │ ├── __init__.py
│ │ │ │ ├── csv.py
│ │ │ │ ├── parquet.py
│ │ │ │ └── utils.py
│ │ │ ├── models.py
│ │ │ ├── namespaces.py
│ │ │ ├── resources.py
│ │ │ ├── s3_operations.py
│ │ │ ├── server.py
│ │ │ ├── table_buckets.py
│ │ │ ├── tables.py
│ │ │ └── utils.py
│ │ ├── CHANGELOG.md
│ │ ├── CONTEXT.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── test_csv.py
│ │ │ ├── test_database.py
│ │ │ ├── test_file_processor_utils.py
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ ├── test_namespaces.py
│ │ │ ├── test_parquet.py
│ │ │ ├── test_pyiceberg.py
│ │ │ ├── test_resources.py
│ │ │ ├── test_s3_operations.py
│ │ │ ├── test_server.py
│ │ │ ├── test_table_buckets.py
│ │ │ ├── test_tables.py
│ │ │ └── test_utils.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── stepfunctions-tool-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── stepfunctions_tool_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── aws_helper.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── .gitignore
│ │ │ ├── README.md
│ │ │ ├── test_aws_helper.py
│ │ │ ├── test_create_state_machine_tool.py
│ │ │ ├── test_filter_state_machines_by_tag.py
│ │ │ ├── test_format_state_machine_response.py
│ │ │ ├── test_get_schema_arn_from_state_machine_arn.py
│ │ │ ├── test_get_schema_from_registry.py
│ │ │ ├── test_invoke_express_state_machine_impl.py
│ │ │ ├── test_invoke_standard_state_machine_impl.py
│ │ │ ├── test_main.py
│ │ │ ├── test_register_state_machines.py
│ │ │ ├── test_sanitize_tool_name.py
│ │ │ ├── test_server.py
│ │ │ └── test_validate_state_machine_name.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── syntheticdata-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── syntheticdata_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── pandas_interpreter.py
│ │ │ ├── server.py
│ │ │ └── storage
│ │ │ ├── __init__.py
│ │ │ ├── base.py
│ │ │ ├── loader.py
│ │ │ └── s3.py
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ ├── test_constants.py
│ │ │ ├── test_pandas_interpreter.py
│ │ │ ├── test_server.py
│ │ │ └── test_storage
│ │ │ ├── __init__.py
│ │ │ ├── test_loader.py
│ │ │ └── test_s3.py
│ │ └── uv.lock
│ ├── terraform-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── terraform_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── impl
│ │ │ │ ├── resources
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── terraform_aws_provider_resources_listing.py
│ │ │ │ │ └── terraform_awscc_provider_resources_listing.py
│ │ │ │ └── tools
│ │ │ │ ├── __init__.py
│ │ │ │ ├── execute_terraform_command.py
│ │ │ │ ├── execute_terragrunt_command.py
│ │ │ │ ├── run_checkov_scan.py
│ │ │ │ ├── search_aws_provider_docs.py
│ │ │ │ ├── search_awscc_provider_docs.py
│ │ │ │ ├── search_specific_aws_ia_modules.py
│ │ │ │ ├── search_user_provided_module.py
│ │ │ │ └── utils.py
│ │ │ ├── models
│ │ │ │ ├── __init__.py
│ │ │ │ └── models.py
│ │ │ ├── scripts
│ │ │ │ ├── generate_aws_provider_resources.py
│ │ │ │ ├── generate_awscc_provider_resources.py
│ │ │ │ └── scrape_aws_terraform_best_practices.py
│ │ │ ├── server.py
│ │ │ └── static
│ │ │ ├── __init__.py
│ │ │ ├── AWS_PROVIDER_RESOURCES.md
│ │ │ ├── AWS_TERRAFORM_BEST_PRACTICES.md
│ │ │ ├── AWSCC_PROVIDER_RESOURCES.md
│ │ │ ├── MCP_INSTRUCTIONS.md
│ │ │ └── TERRAFORM_WORKFLOW_GUIDE.md
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── .gitignore
│ │ │ ├── conftest.py
│ │ │ ├── README.md
│ │ │ ├── test_command_impl.py
│ │ │ ├── test_execute_terraform_command.py
│ │ │ ├── test_execute_terragrunt_command.py
│ │ │ ├── test_models.py
│ │ │ ├── test_parameter_annotations.py
│ │ │ ├── test_resources.py
│ │ │ ├── test_run_checkov_scan.py
│ │ │ ├── test_search_user_provided_module.py
│ │ │ ├── test_server.py
│ │ │ ├── test_tool_implementations.py
│ │ │ ├── test_utils_additional.py
│ │ │ └── test_utils.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── timestream-for-influxdb-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── timestream_for_influxdb_mcp_server
│ │ │ ├── __init__.py
│ │ │ └── server.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── test_init.py
│ │ │ ├── test_main.py
│ │ │ └── test_server.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ ├── valkey-mcp-server
│ │ ├── .gitignore
│ │ ├── .python-version
│ │ ├── awslabs
│ │ │ ├── __init__.py
│ │ │ └── valkey_mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── common
│ │ │ │ ├── __init__.py
│ │ │ │ ├── config.py
│ │ │ │ ├── connection.py
│ │ │ │ └── server.py
│ │ │ ├── context.py
│ │ │ ├── main.py
│ │ │ ├── tools
│ │ │ │ ├── __init__.py
│ │ │ │ ├── bitmap.py
│ │ │ │ ├── hash.py
│ │ │ │ ├── hyperloglog.py
│ │ │ │ ├── json.py
│ │ │ │ ├── list.py
│ │ │ │ ├── misc.py
│ │ │ │ ├── server_management.py
│ │ │ │ ├── set.py
│ │ │ │ ├── sorted_set.py
│ │ │ │ ├── stream.py
│ │ │ │ └── string.py
│ │ │ └── version.py
│ │ ├── CHANGELOG.md
│ │ ├── docker-healthcheck.sh
│ │ ├── Dockerfile
│ │ ├── ELASTICACHECONNECT.md
│ │ ├── LICENSE
│ │ ├── NOTICE
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── test_bitmap.py
│ │ │ ├── test_config.py
│ │ │ ├── test_connection.py
│ │ │ ├── test_hash.py
│ │ │ ├── test_hyperloglog.py
│ │ │ ├── test_init.py
│ │ │ ├── test_json_additional.py
│ │ │ ├── test_json_readonly.py
│ │ │ ├── test_json.py
│ │ │ ├── test_list_additional.py
│ │ │ ├── test_list_readonly.py
│ │ │ ├── test_list.py
│ │ │ ├── test_main.py
│ │ │ ├── test_misc.py
│ │ │ ├── test_server_management.py
│ │ │ ├── test_set_readonly.py
│ │ │ ├── test_set.py
│ │ │ ├── test_sorted_set_additional.py
│ │ │ ├── test_sorted_set_readonly.py
│ │ │ ├── test_sorted_set.py
│ │ │ ├── test_stream_additional.py
│ │ │ ├── test_stream_readonly.py
│ │ │ ├── test_stream.py
│ │ │ └── test_string.py
│ │ ├── uv-requirements.txt
│ │ └── uv.lock
│ └── well-architected-security-mcp-server
│ ├── .python-version
│ ├── awslabs
│ │ └── well_architected_security_mcp_server
│ │ ├── __init__.py
│ │ ├── consts.py
│ │ ├── server.py
│ │ └── util
│ │ ├── __init__.py
│ │ ├── network_security.py
│ │ ├── prompt_utils.py
│ │ ├── resource_utils.py
│ │ ├── security_services.py
│ │ └── storage_security.py
│ ├── PROMPT_TEMPLATE.md
│ ├── pyproject.toml
│ ├── README.md
│ ├── tests
│ │ ├── __init__.py
│ │ ├── conftest.py
│ │ ├── README.md
│ │ ├── test_access_analyzer_fix.py
│ │ ├── test_network_security_additional.py
│ │ ├── test_network_security.py
│ │ ├── test_prompt_utils_coverage.py
│ │ ├── test_prompt_utils.py
│ │ ├── test_resource_utils_fix.py
│ │ ├── test_resource_utils.py
│ │ ├── test_security_services_additional.py
│ │ ├── test_security_services_coverage.py
│ │ ├── test_security_services.py
│ │ ├── test_server_additional.py
│ │ ├── test_server_coverage.py
│ │ ├── test_server_prompts.py
│ │ ├── test_server_security_findings.py
│ │ ├── test_server.py
│ │ ├── test_storage_security_additional.py
│ │ ├── test_storage_security_comprehensive.py
│ │ ├── test_storage_security_edge_cases.py
│ │ ├── test_storage_security_recommendations.py
│ │ ├── test_storage_security.py
│ │ └── test_user_agent_config.py
│ └── uv.lock
└── VIBE_CODING_TIPS_TRICKS.md
```
# Files
--------------------------------------------------------------------------------
/src/aws-dataprocessing-mcp-server/tests/handlers/glue/test_glue_workflows_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"). You may not use this file except in compliance
4 | # with the License. A copy of the License is located at
5 | #
6 | # http://www.apache.org/licenses/LICENSE-2.0
7 | #
8 | # or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES
9 | # OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
10 | # and limitations under the License.
11 | # ruff: noqa: D101, D102, D103
12 | """Tests for the Glue Workflows and Triggers handler."""
13 |
14 | import pytest
15 | from awslabs.aws_dataprocessing_mcp_server.handlers.glue.worklows_handler import (
16 | GlueWorkflowAndTriggerHandler,
17 | )
18 | from botocore.exceptions import ClientError
19 | from mcp.server.fastmcp import Context
20 | from unittest.mock import MagicMock, patch
21 |
22 |
23 | @pytest.mark.asyncio
24 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
25 | async def test_glue_workflow_handler_initialization(mock_create_client):
26 | # Create a mock Glue client
27 | mock_glue_client = MagicMock()
28 | mock_create_client.return_value = mock_glue_client
29 |
30 | # Create a mock MCP server
31 | mock_mcp = MagicMock()
32 |
33 | # Initialize the Glue Workflow handler with the mock MCP server
34 | GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
35 |
36 | # Verify that create_boto3_client was called with 'glue'
37 | mock_create_client.assert_called_once_with('glue')
38 |
39 | # Verify that all tools were registered
40 | assert mock_mcp.tool.call_count == 2
41 |
42 | # Get all call args
43 | call_args_list = mock_mcp.tool.call_args_list
44 |
45 | # Get all tool names that were registered
46 | tool_names = [call_args[1]['name'] for call_args in call_args_list]
47 |
48 | # Verify that all expected tools were registered
49 | assert 'manage_aws_glue_workflows' in tool_names
50 | assert 'manage_aws_glue_triggers' in tool_names
51 |
52 |
53 | # Tests for manage_aws_glue_workflows method
54 |
55 |
56 | @pytest.mark.asyncio
57 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
58 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.prepare_resource_tags')
59 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_region')
60 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_account_id')
61 | async def test_create_workflow_success(
62 | mock_get_account_id, mock_get_region, mock_prepare_tags, mock_create_client
63 | ):
64 | # Create a mock Glue client
65 | mock_glue_client = MagicMock()
66 | mock_create_client.return_value = mock_glue_client
67 |
68 | # Mock the resource tags
69 | mock_prepare_tags.return_value = {'ManagedBy': 'MCP'}
70 |
71 | # Create a mock MCP server
72 | mock_mcp = MagicMock()
73 |
74 | # Initialize the Glue Workflow handler with the mock MCP server
75 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
76 | handler.glue_client = mock_glue_client
77 |
78 | # Create a mock context
79 | mock_ctx = MagicMock(spec=Context)
80 |
81 | # Mock the create_workflow response
82 | mock_glue_client.create_workflow.return_value = {'Name': 'test-workflow'}
83 |
84 | # Call the manage_aws_glue_workflows method with create-workflow operation
85 | result = await handler.manage_aws_glue_workflows(
86 | mock_ctx,
87 | operation='create-workflow',
88 | workflow_name='test-workflow',
89 | workflow_definition={
90 | 'Description': 'Test workflow',
91 | 'DefaultRunProperties': {'ENV': 'test'},
92 | 'MaxConcurrentRuns': 1,
93 | },
94 | )
95 |
96 | # Verify the result
97 | assert not result.isError
98 | assert len(result.content) == 1
99 | assert result.content[0].type == 'text'
100 | assert 'Successfully created workflow test-workflow' in result.content[0].text
101 | assert result.workflow_name == 'test-workflow'
102 |
103 | # Verify that create_workflow was called with the correct parameters
104 | mock_glue_client.create_workflow.assert_called_once()
105 | args, kwargs = mock_glue_client.create_workflow.call_args
106 | assert kwargs['Name'] == 'test-workflow'
107 | assert kwargs['Description'] == 'Test workflow'
108 | assert kwargs['DefaultRunProperties'] == {'ENV': 'test'}
109 | assert kwargs['MaxConcurrentRuns'] == 1
110 | assert kwargs['Tags'] == {'ManagedBy': 'MCP'}
111 |
112 |
113 | @pytest.mark.asyncio
114 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
115 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.prepare_resource_tags')
116 | async def test_create_workflow_with_user_tags(mock_prepare_tags, mock_create_client):
117 | """Test creating a workflow with user-provided tags."""
118 | # Create a mock Glue client
119 | mock_glue_client = MagicMock()
120 | mock_create_client.return_value = mock_glue_client
121 |
122 | # Mock the resource tags
123 | mock_prepare_tags.return_value = {'ManagedBy': 'MCP'}
124 |
125 | # Create a mock MCP server
126 | mock_mcp = MagicMock()
127 |
128 | # Initialize the Glue Workflow handler with the mock MCP server
129 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
130 | handler.glue_client = mock_glue_client
131 |
132 | # Create a mock context
133 | mock_ctx = MagicMock(spec=Context)
134 |
135 | # Mock the create_workflow response
136 | mock_glue_client.create_workflow.return_value = {'Name': 'test-workflow'}
137 |
138 | # Call the manage_aws_glue_workflows method with create-workflow operation and user tags
139 | result = await handler.manage_aws_glue_workflows(
140 | mock_ctx,
141 | operation='create-workflow',
142 | workflow_name='test-workflow',
143 | workflow_definition={
144 | 'Description': 'Test workflow',
145 | 'Tags': {'Environment': 'Test', 'Project': 'UnitTest'},
146 | },
147 | )
148 |
149 | # Verify the result
150 | assert not result.isError
151 | assert result.workflow_name == 'test-workflow'
152 |
153 | # Verify that create_workflow was called with merged tags
154 | mock_glue_client.create_workflow.assert_called_once()
155 | args, kwargs = mock_glue_client.create_workflow.call_args
156 | assert kwargs['Tags'] == {'Environment': 'Test', 'Project': 'UnitTest', 'ManagedBy': 'MCP'}
157 |
158 |
159 | @pytest.mark.asyncio
160 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
161 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.prepare_resource_tags')
162 | async def test_create_workflow_with_only_description(mock_prepare_tags, mock_create_client):
163 | """Test creating a workflow with only description parameter."""
164 | # Create a mock Glue client
165 | mock_glue_client = MagicMock()
166 | mock_create_client.return_value = mock_glue_client
167 |
168 | # Mock the resource tags
169 | mock_prepare_tags.return_value = {'ManagedBy': 'MCP'}
170 |
171 | # Create a mock MCP server
172 | mock_mcp = MagicMock()
173 |
174 | # Initialize the Glue Workflow handler with the mock MCP server
175 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
176 | handler.glue_client = mock_glue_client
177 |
178 | # Create a mock context
179 | mock_ctx = MagicMock(spec=Context)
180 |
181 | # Mock the create_workflow response
182 | mock_glue_client.create_workflow.return_value = {'Name': 'test-workflow'}
183 |
184 | # Call the manage_aws_glue_workflows method with create-workflow operation and only description
185 | result = await handler.manage_aws_glue_workflows(
186 | mock_ctx,
187 | operation='create-workflow',
188 | workflow_name='test-workflow',
189 | workflow_definition={
190 | 'Description': 'Test workflow',
191 | },
192 | )
193 |
194 | # Verify the result
195 | assert not result.isError
196 | assert result.workflow_name == 'test-workflow'
197 |
198 | # Verify that create_workflow was called with the correct parameters
199 | mock_glue_client.create_workflow.assert_called_once()
200 | args, kwargs = mock_glue_client.create_workflow.call_args
201 | assert kwargs['Description'] == 'Test workflow'
202 | assert 'DefaultRunProperties' not in kwargs
203 | assert kwargs['Tags'] == {'ManagedBy': 'MCP'}
204 |
205 |
206 | @pytest.mark.asyncio
207 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
208 | async def test_create_workflow_missing_parameters(mock_create_client):
209 | """Test creating a workflow with missing required parameters."""
210 | # Create a mock Glue client
211 | mock_glue_client = MagicMock()
212 | mock_create_client.return_value = mock_glue_client
213 |
214 | # Create a mock MCP server
215 | mock_mcp = MagicMock()
216 |
217 | # Initialize the Glue Workflow handler with the mock MCP server
218 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
219 | handler.glue_client = mock_glue_client
220 |
221 | # Create a mock context
222 | mock_ctx = MagicMock(spec=Context)
223 |
224 | # Test missing workflow_definition
225 | with pytest.raises(ValueError) as excinfo:
226 | await handler.manage_aws_glue_workflows(
227 | mock_ctx,
228 | operation='create-workflow',
229 | workflow_name='test-workflow',
230 | workflow_definition=None,
231 | )
232 | assert 'workflow_name and workflow_definition are required' in str(excinfo.value)
233 |
234 | # Test missing workflow_name
235 | with pytest.raises(ValueError) as excinfo:
236 | await handler.manage_aws_glue_workflows(
237 | mock_ctx,
238 | operation='create-workflow',
239 | workflow_name=None,
240 | workflow_definition={'Description': 'Test workflow'},
241 | )
242 | assert 'workflow_name and workflow_definition are required' in str(excinfo.value)
243 |
244 |
245 | @pytest.mark.asyncio
246 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
247 | async def test_get_workflow_with_include_graph_false(mock_create_client):
248 | """Test getting a workflow with include_graph parameter set to False."""
249 | # Create a mock Glue client
250 | mock_glue_client = MagicMock()
251 | mock_create_client.return_value = mock_glue_client
252 |
253 | # Create a mock MCP server
254 | mock_mcp = MagicMock()
255 |
256 | # Initialize the Glue Workflow handler with the mock MCP server
257 | handler = GlueWorkflowAndTriggerHandler(mock_mcp)
258 | handler.glue_client = mock_glue_client
259 |
260 | # Create a mock context
261 | mock_ctx = MagicMock(spec=Context)
262 |
263 | # Mock the get_workflow response
264 | mock_workflow_details = {
265 | 'Name': 'test-workflow',
266 | 'Description': 'Test workflow',
267 | 'CreatedOn': '2023-01-01T00:00:00Z',
268 | }
269 | mock_glue_client.get_workflow.return_value = {'Workflow': mock_workflow_details}
270 |
271 | # Call the manage_aws_glue_workflows method with get-workflow operation and include_graph=False
272 | result = await handler.manage_aws_glue_workflows(
273 | mock_ctx,
274 | operation='get-workflow',
275 | workflow_name='test-workflow',
276 | workflow_definition={'include_graph': False},
277 | )
278 |
279 | # Verify the result
280 | assert not result.isError
281 | assert result.workflow_name == 'test-workflow'
282 | assert result.workflow_details == mock_workflow_details
283 |
284 | # Verify that get_workflow was called without IncludeGraph parameter
285 | mock_glue_client.get_workflow.assert_called_once_with(Name='test-workflow')
286 |
287 |
288 | @pytest.mark.asyncio
289 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
290 | async def test_create_workflow_no_write_access(mock_create_client):
291 | # Create a mock Glue client
292 | mock_glue_client = MagicMock()
293 | mock_create_client.return_value = mock_glue_client
294 |
295 | # Create a mock MCP server
296 | mock_mcp = MagicMock()
297 |
298 | # Initialize the Glue Workflow handler with the mock MCP server without write access
299 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=False)
300 | handler.glue_client = mock_glue_client
301 |
302 | # Create a mock context
303 | mock_ctx = MagicMock(spec=Context)
304 |
305 | # Call the manage_aws_glue_workflows method with create-workflow operation
306 | result = await handler.manage_aws_glue_workflows(
307 | mock_ctx,
308 | operation='create-workflow',
309 | workflow_name='test-workflow',
310 | workflow_definition={'Description': 'Test workflow'},
311 | )
312 |
313 | # Verify the result indicates an error due to no write access
314 | assert result.isError
315 | assert len(result.content) == 1
316 | assert result.content[0].type == 'text'
317 | assert (
318 | 'Operation create-workflow is not allowed without write access' in result.content[0].text
319 | )
320 | assert result.workflow_name == ''
321 |
322 | # Verify that create_workflow was NOT called
323 | mock_glue_client.create_workflow.assert_not_called()
324 |
325 |
326 | @pytest.mark.asyncio
327 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
328 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_region')
329 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_account_id')
330 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.is_resource_mcp_managed')
331 | async def test_delete_workflow_success(
332 | mock_is_mcp_managed, mock_get_account_id, mock_get_region, mock_create_client
333 | ):
334 | # Create a mock Glue client
335 | mock_glue_client = MagicMock()
336 | mock_create_client.return_value = mock_glue_client
337 |
338 | # Mock the region and account ID
339 | mock_get_region.return_value = 'us-east-1'
340 | mock_get_account_id.return_value = '123456789012'
341 |
342 | # Mock the is_resource_mcp_managed to return True
343 | mock_is_mcp_managed.return_value = True
344 |
345 | # Create a mock MCP server
346 | mock_mcp = MagicMock()
347 |
348 | # Initialize the Glue Workflow handler with the mock MCP server
349 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
350 | handler.glue_client = mock_glue_client
351 |
352 | # Create a mock context
353 | mock_ctx = MagicMock(spec=Context)
354 |
355 | # Mock the get_workflow response
356 | mock_glue_client.get_workflow.return_value = {
357 | 'Workflow': {'Name': 'test-workflow', 'Tags': {'ManagedBy': 'MCP'}}
358 | }
359 |
360 | # Call the manage_aws_glue_workflows method with delete-workflow operation
361 | result = await handler.manage_aws_glue_workflows(
362 | mock_ctx, operation='delete-workflow', workflow_name='test-workflow'
363 | )
364 |
365 | # Verify the result
366 | assert not result.isError
367 | assert len(result.content) == 1
368 | assert result.content[0].type == 'text'
369 | assert 'Successfully deleted workflow test-workflow' in result.content[0].text
370 | assert result.workflow_name == 'test-workflow'
371 |
372 | # Verify that delete_workflow was called with the correct parameters
373 | mock_glue_client.delete_workflow.assert_called_once_with(Name='test-workflow')
374 |
375 |
376 | @pytest.mark.asyncio
377 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
378 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_region')
379 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_account_id')
380 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.is_resource_mcp_managed')
381 | async def test_delete_workflow_not_mcp_managed(
382 | mock_is_mcp_managed, mock_get_account_id, mock_get_region, mock_create_client
383 | ):
384 | # Create a mock Glue client
385 | mock_glue_client = MagicMock()
386 | mock_create_client.return_value = mock_glue_client
387 |
388 | # Mock the region and account ID
389 | mock_get_region.return_value = 'us-east-1'
390 | mock_get_account_id.return_value = '123456789012'
391 |
392 | # Mock the is_resource_mcp_managed to return False
393 | mock_is_mcp_managed.return_value = False
394 |
395 | # Create a mock MCP server
396 | mock_mcp = MagicMock()
397 |
398 | # Initialize the Glue Workflow handler with the mock MCP server
399 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
400 | handler.glue_client = mock_glue_client
401 |
402 | # Create a mock context
403 | mock_ctx = MagicMock(spec=Context)
404 |
405 | # Mock the get_workflow response
406 | mock_glue_client.get_workflow.return_value = {
407 | 'Workflow': {
408 | 'Name': 'test-workflow',
409 | 'Tags': {}, # No MCP tags
410 | }
411 | }
412 |
413 | # Call the manage_aws_glue_workflows method with delete-workflow operation
414 | result = await handler.manage_aws_glue_workflows(
415 | mock_ctx, operation='delete-workflow', workflow_name='test-workflow'
416 | )
417 |
418 | # Verify the result indicates an error because the workflow is not MCP managed
419 | assert result.isError
420 | assert len(result.content) == 1
421 | assert result.content[0].type == 'text'
422 | assert (
423 | 'Cannot delete workflow test-workflow - it is not managed by the MCP server'
424 | in result.content[0].text
425 | )
426 | assert result.workflow_name == 'test-workflow'
427 |
428 | # Verify that delete_workflow was NOT called
429 | mock_glue_client.delete_workflow.assert_not_called()
430 |
431 |
432 | @pytest.mark.asyncio
433 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
434 | async def test_get_workflow_success(mock_create_client):
435 | # Create a mock Glue client
436 | mock_glue_client = MagicMock()
437 | mock_create_client.return_value = mock_glue_client
438 |
439 | # Create a mock MCP server
440 | mock_mcp = MagicMock()
441 |
442 | # Initialize the Glue Workflow handler with the mock MCP server
443 | handler = GlueWorkflowAndTriggerHandler(mock_mcp)
444 | handler.glue_client = mock_glue_client
445 |
446 | # Create a mock context
447 | mock_ctx = MagicMock(spec=Context)
448 |
449 | # Mock the get_workflow response
450 | mock_workflow_details = {
451 | 'Name': 'test-workflow',
452 | 'Description': 'Test workflow',
453 | 'CreatedOn': '2023-01-01T00:00:00Z',
454 | }
455 | mock_glue_client.get_workflow.return_value = {'Workflow': mock_workflow_details}
456 |
457 | # Call the manage_aws_glue_workflows method with get-workflow operation
458 | result = await handler.manage_aws_glue_workflows(
459 | mock_ctx, operation='get-workflow', workflow_name='test-workflow'
460 | )
461 |
462 | # Verify the result
463 | assert not result.isError
464 | assert len(result.content) == 1
465 | assert result.content[0].type == 'text'
466 | assert 'Successfully retrieved workflow test-workflow' in result.content[0].text
467 | assert result.workflow_name == 'test-workflow'
468 | assert result.workflow_details == mock_workflow_details
469 |
470 | # Verify that get_workflow was called with the correct parameters
471 | mock_glue_client.get_workflow.assert_called_once_with(Name='test-workflow')
472 |
473 |
474 | @pytest.mark.asyncio
475 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
476 | async def test_get_workflow_with_include_graph(mock_create_client):
477 | # Create a mock Glue client
478 | mock_glue_client = MagicMock()
479 | mock_create_client.return_value = mock_glue_client
480 |
481 | # Create a mock MCP server
482 | mock_mcp = MagicMock()
483 |
484 | # Initialize the Glue Workflow handler with the mock MCP server
485 | handler = GlueWorkflowAndTriggerHandler(mock_mcp)
486 | handler.glue_client = mock_glue_client
487 |
488 | # Create a mock context
489 | mock_ctx = MagicMock(spec=Context)
490 |
491 | # Mock the get_workflow response
492 | mock_workflow_details = {
493 | 'Name': 'test-workflow',
494 | 'Description': 'Test workflow',
495 | 'CreatedOn': '2023-01-01T00:00:00Z',
496 | 'Graph': {'Nodes': [{'Type': 'JOB', 'Name': 'test-job'}], 'Edges': []},
497 | }
498 | mock_glue_client.get_workflow.return_value = {'Workflow': mock_workflow_details}
499 |
500 | # Call the manage_aws_glue_workflows method with get-workflow operation and include_graph
501 | result = await handler.manage_aws_glue_workflows(
502 | mock_ctx,
503 | operation='get-workflow',
504 | workflow_name='test-workflow',
505 | workflow_definition={'include_graph': True},
506 | )
507 |
508 | # Verify the result
509 | assert not result.isError
510 | assert len(result.content) == 1
511 | assert result.content[0].type == 'text'
512 | assert 'Successfully retrieved workflow test-workflow' in result.content[0].text
513 | assert result.workflow_name == 'test-workflow'
514 | assert result.workflow_details == mock_workflow_details
515 |
516 | # Verify that get_workflow was called with the correct parameters
517 | mock_glue_client.get_workflow.assert_called_once_with(Name='test-workflow', IncludeGraph=True)
518 |
519 |
520 | @pytest.mark.asyncio
521 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
522 | async def test_list_workflows_success(mock_create_client):
523 | # Create a mock Glue client
524 | mock_glue_client = MagicMock()
525 | mock_create_client.return_value = mock_glue_client
526 |
527 | # Create a mock MCP server
528 | mock_mcp = MagicMock()
529 |
530 | # Initialize the Glue Workflow handler with the mock MCP server
531 | handler = GlueWorkflowAndTriggerHandler(mock_mcp)
532 | handler.glue_client = mock_glue_client
533 |
534 | # Create a mock context
535 | mock_ctx = MagicMock(spec=Context)
536 |
537 | # Mock the list_workflows response - AWS API returns workflow names as strings
538 | mock_glue_client.list_workflows.return_value = {
539 | 'Workflows': ['workflow1', 'workflow2'],
540 | 'NextToken': 'next-token',
541 | }
542 |
543 | # Call the manage_aws_glue_workflows method with list-workflows operation
544 | result = await handler.manage_aws_glue_workflows(
545 | mock_ctx, operation='list-workflows', max_results=10, next_token='token'
546 | )
547 |
548 | # Verify the result
549 | assert not result.isError
550 | assert len(result.content) == 1
551 | assert result.content[0].type == 'text'
552 | assert 'Successfully retrieved workflows' in result.content[0].text
553 | assert len(result.workflows) == 2
554 | assert result.workflows[0]['Name'] == 'workflow1'
555 | assert result.workflows[1]['Name'] == 'workflow2'
556 | assert result.next_token == 'next-token'
557 |
558 | # Verify that list_workflows was called with the correct parameters
559 | mock_glue_client.list_workflows.assert_called_once_with(MaxResults=10, NextToken='token')
560 |
561 |
562 | @pytest.mark.asyncio
563 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
564 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_region')
565 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_account_id')
566 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.is_resource_mcp_managed')
567 | async def test_start_workflow_run_success(
568 | mock_is_mcp_managed, mock_get_account_id, mock_get_region, mock_create_client
569 | ):
570 | # Create a mock Glue client
571 | mock_glue_client = MagicMock()
572 | mock_create_client.return_value = mock_glue_client
573 |
574 | # Mock the region and account ID
575 | mock_get_region.return_value = 'us-east-1'
576 | mock_get_account_id.return_value = '123456789012'
577 |
578 | # Mock the is_resource_mcp_managed to return True
579 | mock_is_mcp_managed.return_value = True
580 |
581 | # Create a mock MCP server
582 | mock_mcp = MagicMock()
583 |
584 | # Initialize the Glue Workflow handler with the mock MCP server
585 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
586 | handler.glue_client = mock_glue_client
587 |
588 | # Create a mock context
589 | mock_ctx = MagicMock(spec=Context)
590 |
591 | # Mock the get_workflow response
592 | mock_glue_client.get_workflow.return_value = {
593 | 'Workflow': {'Name': 'test-workflow', 'Tags': {'ManagedBy': 'MCP'}}
594 | }
595 |
596 | # Mock the start_workflow_run response
597 | mock_glue_client.start_workflow_run.return_value = {'RunId': 'run-123'}
598 |
599 | # Call the manage_aws_glue_workflows method with start-workflow-run operation
600 | result = await handler.manage_aws_glue_workflows(
601 | mock_ctx,
602 | operation='start-workflow-run',
603 | workflow_name='test-workflow',
604 | workflow_definition={'run_properties': {'ENV': 'test'}},
605 | )
606 |
607 | # Verify the result
608 | assert not result.isError
609 | assert len(result.content) == 1
610 | assert result.content[0].type == 'text'
611 | assert 'Successfully started workflow run for test-workflow' in result.content[0].text
612 | assert result.workflow_name == 'test-workflow'
613 | assert result.run_id == 'run-123'
614 |
615 | # Verify that start_workflow_run was called with the correct parameters
616 | mock_glue_client.start_workflow_run.assert_called_once_with(
617 | Name='test-workflow', RunProperties={'ENV': 'test'}
618 | )
619 |
620 |
621 | @pytest.mark.asyncio
622 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
623 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_region')
624 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_account_id')
625 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.is_resource_mcp_managed')
626 | async def test_start_workflow_run_not_mcp_managed(
627 | mock_is_mcp_managed, mock_get_account_id, mock_get_region, mock_create_client
628 | ):
629 | """Test starting a workflow run for a workflow that is not MCP managed."""
630 | # Create a mock Glue client
631 | mock_glue_client = MagicMock()
632 | mock_create_client.return_value = mock_glue_client
633 |
634 | # Mock the region and account ID
635 | mock_get_region.return_value = 'us-east-1'
636 | mock_get_account_id.return_value = '123456789012'
637 |
638 | # Mock the is_resource_mcp_managed to return False
639 | mock_is_mcp_managed.return_value = False
640 |
641 | # Create a mock MCP server
642 | mock_mcp = MagicMock()
643 |
644 | # Initialize the Glue Workflow handler with the mock MCP server
645 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
646 | handler.glue_client = mock_glue_client
647 |
648 | # Create a mock context
649 | mock_ctx = MagicMock(spec=Context)
650 |
651 | # Mock the get_workflow response
652 | mock_glue_client.get_workflow.return_value = {
653 | 'Workflow': {'Name': 'test-workflow', 'Tags': {}} # No MCP tags
654 | }
655 |
656 | # Call the manage_aws_glue_workflows method with start-workflow-run operation
657 | result = await handler.manage_aws_glue_workflows(
658 | mock_ctx,
659 | operation='start-workflow-run',
660 | workflow_name='test-workflow',
661 | )
662 |
663 | # Verify the result indicates an error because the workflow is not MCP managed
664 | assert result.isError
665 | assert len(result.content) == 1
666 | assert result.content[0].type == 'text'
667 | assert (
668 | 'Cannot start workflow run for test-workflow - it is not managed by the MCP server'
669 | in result.content[0].text
670 | )
671 |
672 | # Verify that start_workflow_run was NOT called
673 | mock_glue_client.start_workflow_run.assert_not_called()
674 |
675 |
676 | @pytest.mark.asyncio
677 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
678 | async def test_start_workflow_run_no_write_access(mock_create_client):
679 | """Test starting a workflow run without write access."""
680 | # Create a mock Glue client
681 | mock_glue_client = MagicMock()
682 | mock_create_client.return_value = mock_glue_client
683 |
684 | # Create a mock MCP server
685 | mock_mcp = MagicMock()
686 |
687 | # Initialize the Glue Workflow handler with the mock MCP server without write access
688 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=False)
689 | handler.glue_client = mock_glue_client
690 |
691 | # Create a mock context
692 | mock_ctx = MagicMock(spec=Context)
693 |
694 | # Call the manage_aws_glue_workflows method with start-workflow-run operation
695 | result = await handler.manage_aws_glue_workflows(
696 | mock_ctx,
697 | operation='start-workflow-run',
698 | workflow_name='test-workflow',
699 | )
700 |
701 | # Verify the result indicates an error due to no write access
702 | assert result.isError
703 | assert len(result.content) == 1
704 | assert result.content[0].type == 'text'
705 | assert (
706 | 'Operation start-workflow-run is not allowed without write access'
707 | in result.content[0].text
708 | )
709 |
710 | # Verify that start_workflow_run was NOT called
711 | mock_glue_client.start_workflow_run.assert_not_called()
712 |
713 |
714 | @pytest.mark.asyncio
715 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
716 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_region')
717 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_account_id')
718 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.is_resource_mcp_managed')
719 | async def test_start_workflow_run_not_found(
720 | mock_is_mcp_managed, mock_get_account_id, mock_get_region, mock_create_client
721 | ):
722 | """Test starting a workflow run for a workflow that doesn't exist."""
723 | # Create a mock Glue client
724 | mock_glue_client = MagicMock()
725 | mock_create_client.return_value = mock_glue_client
726 |
727 | # Mock the region and account ID
728 | mock_get_region.return_value = 'us-east-1'
729 | mock_get_account_id.return_value = '123456789012'
730 |
731 | # Create a mock MCP server
732 | mock_mcp = MagicMock()
733 |
734 | # Initialize the Glue Workflow handler with the mock MCP server
735 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
736 | handler.glue_client = mock_glue_client
737 |
738 | # Create a mock context
739 | mock_ctx = MagicMock(spec=Context)
740 |
741 | # Mock the get_workflow to raise EntityNotFoundException
742 | mock_glue_client.exceptions.EntityNotFoundException = ClientError(
743 | {'Error': {'Code': 'EntityNotFoundException', 'Message': 'Workflow not found'}},
744 | 'get_workflow',
745 | )
746 | mock_glue_client.get_workflow.side_effect = mock_glue_client.exceptions.EntityNotFoundException
747 |
748 | # Call the manage_aws_glue_workflows method with start-workflow-run operation
749 | result = await handler.manage_aws_glue_workflows(
750 | mock_ctx,
751 | operation='start-workflow-run',
752 | workflow_name='test-workflow',
753 | )
754 |
755 | # Verify the result indicates an error because the workflow was not found
756 | assert result.isError
757 | assert len(result.content) == 1
758 | assert result.content[0].type == 'text'
759 | assert 'Workflow test-workflow not found' in result.content[0].text
760 |
761 | # Verify that start_workflow_run was NOT called
762 | mock_glue_client.start_workflow_run.assert_not_called()
763 |
764 |
765 | @pytest.mark.asyncio
766 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
767 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_region')
768 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_account_id')
769 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.is_resource_mcp_managed')
770 | async def test_start_workflow_run_without_run_properties(
771 | mock_is_mcp_managed, mock_get_account_id, mock_get_region, mock_create_client
772 | ):
773 | """Test starting a workflow run without run properties."""
774 | # Create a mock Glue client
775 | mock_glue_client = MagicMock()
776 | mock_create_client.return_value = mock_glue_client
777 |
778 | # Mock the region and account ID
779 | mock_get_region.return_value = 'us-east-1'
780 | mock_get_account_id.return_value = '123456789012'
781 |
782 | # Mock the is_resource_mcp_managed to return True
783 | mock_is_mcp_managed.return_value = True
784 |
785 | # Create a mock MCP server
786 | mock_mcp = MagicMock()
787 |
788 | # Initialize the Glue Workflow handler with the mock MCP server
789 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
790 | handler.glue_client = mock_glue_client
791 |
792 | # Create a mock context
793 | mock_ctx = MagicMock(spec=Context)
794 |
795 | # Mock the get_workflow response
796 | mock_glue_client.get_workflow.return_value = {
797 | 'Workflow': {'Name': 'test-workflow', 'Tags': {'ManagedBy': 'MCP'}}
798 | }
799 |
800 | # Mock the start_workflow_run response
801 | mock_glue_client.start_workflow_run.return_value = {'RunId': 'run-123'}
802 |
803 | # Call the manage_aws_glue_workflows method with start-workflow-run operation without run_properties
804 | result = await handler.manage_aws_glue_workflows(
805 | mock_ctx,
806 | operation='start-workflow-run',
807 | workflow_name='test-workflow',
808 | workflow_definition={}, # Empty definition, no run_properties
809 | )
810 |
811 | # Verify the result
812 | assert not result.isError
813 | assert result.workflow_name == 'test-workflow'
814 | assert result.run_id == 'run-123'
815 |
816 | # Verify that start_workflow_run was called with just the Name parameter
817 | mock_glue_client.start_workflow_run.assert_called_once_with(Name='test-workflow')
818 |
819 |
820 | @pytest.mark.asyncio
821 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
822 | async def test_manage_aws_glue_workflows_general_exception(mock_create_client):
823 | """Test handling of general exceptions in manage_aws_glue_workflows."""
824 | # Create a mock Glue client that raises an exception
825 | mock_glue_client = MagicMock()
826 | mock_glue_client.get_workflow.side_effect = Exception('Test exception')
827 | mock_create_client.return_value = mock_glue_client
828 |
829 | # Create a mock MCP server
830 | mock_mcp = MagicMock()
831 |
832 | # Initialize the Glue Workflow handler with the mock MCP server
833 | handler = GlueWorkflowAndTriggerHandler(mock_mcp)
834 | handler.glue_client = mock_glue_client
835 |
836 | # Create a mock context
837 | mock_ctx = MagicMock(spec=Context)
838 |
839 | # Call the manage_aws_glue_workflows method with get-workflow operation
840 | result = await handler.manage_aws_glue_workflows(
841 | mock_ctx, operation='get-workflow', workflow_name='test-workflow'
842 | )
843 |
844 | # Verify the result indicates an error
845 | assert result.isError
846 | assert len(result.content) == 1
847 | assert result.content[0].type == 'text'
848 | assert 'Error in manage_aws_glue_workflows: Test exception' in result.content[0].text
849 |
850 |
851 | @pytest.mark.asyncio
852 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
853 | async def test_invalid_operation(mock_create_client):
854 | # Create a mock Glue client
855 | mock_glue_client = MagicMock()
856 | mock_create_client.return_value = mock_glue_client
857 |
858 | # Create a mock MCP server
859 | mock_mcp = MagicMock()
860 |
861 | # Initialize the Glue Workflow handler with the mock MCP server
862 | handler = GlueWorkflowAndTriggerHandler(mock_mcp)
863 | handler.glue_client = mock_glue_client
864 |
865 | # Create a mock context
866 | mock_ctx = MagicMock(spec=Context)
867 |
868 | # Call the manage_aws_glue_workflows method with an invalid operation
869 | result = await handler.manage_aws_glue_workflows(
870 | mock_ctx, operation='invalid-operation', workflow_name='test-workflow'
871 | )
872 |
873 | # Verify the result indicates an error due to invalid operation
874 | assert result.isError
875 | assert len(result.content) == 1
876 | assert result.content[0].type == 'text'
877 | assert 'Invalid operation: invalid-operation' in result.content[0].text
878 | assert result.workflow_name == 'test-workflow'
879 |
880 |
881 | @pytest.mark.asyncio
882 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
883 | async def test_workflow_not_found(mock_create_client):
884 | # Create a mock Glue client
885 | mock_glue_client = MagicMock()
886 | mock_create_client.return_value = mock_glue_client
887 |
888 | # Create a mock MCP server
889 | mock_mcp = MagicMock()
890 |
891 | # Initialize the Glue Workflow handler with the mock MCP server
892 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
893 | handler.glue_client = mock_glue_client
894 |
895 | # Create a mock context
896 | mock_ctx = MagicMock(spec=Context)
897 |
898 | # Mock the get_workflow to raise EntityNotFoundException
899 | mock_glue_client.exceptions.EntityNotFoundException = ClientError(
900 | {'Error': {'Code': 'EntityNotFoundException', 'Message': 'Workflow not found'}},
901 | 'get_workflow',
902 | )
903 | mock_glue_client.get_workflow.side_effect = mock_glue_client.exceptions.EntityNotFoundException
904 |
905 | # Call the manage_aws_glue_workflows method with delete-workflow operation
906 | result = await handler.manage_aws_glue_workflows(
907 | mock_ctx, operation='delete-workflow', workflow_name='test-workflow'
908 | )
909 |
910 | # Verify the result indicates an error because the workflow was not found
911 | assert result.isError
912 | assert len(result.content) == 1
913 | assert result.content[0].type == 'text'
914 | assert 'Workflow test-workflow not found' in result.content[0].text
915 | assert result.workflow_name == 'test-workflow'
916 |
917 | # Verify that delete_workflow was NOT called
918 | mock_glue_client.delete_workflow.assert_not_called()
919 |
920 |
921 | # Tests for manage_aws_glue_triggers method
922 |
923 |
924 | @pytest.mark.asyncio
925 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
926 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.prepare_resource_tags')
927 | async def test_create_trigger_success(mock_prepare_tags, mock_create_client):
928 | # Create a mock Glue client
929 | mock_glue_client = MagicMock()
930 | mock_create_client.return_value = mock_glue_client
931 |
932 | # Mock the resource tags
933 | mock_prepare_tags.return_value = {'ManagedBy': 'MCP'}
934 |
935 | # Create a mock MCP server
936 | mock_mcp = MagicMock()
937 |
938 | # Initialize the Glue Workflow handler with the mock MCP server
939 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
940 | handler.glue_client = mock_glue_client
941 |
942 | # Create a mock context
943 | mock_ctx = MagicMock(spec=Context)
944 |
945 | # Mock the create_trigger response
946 | mock_glue_client.create_trigger.return_value = {'Name': 'test-trigger'}
947 |
948 | # Call the manage_aws_glue_triggers method with create-trigger operation
949 | result = await handler.manage_aws_glue_triggers(
950 | mock_ctx,
951 | operation='create-trigger',
952 | trigger_name='test-trigger',
953 | trigger_definition={
954 | 'Type': 'SCHEDULED',
955 | 'Schedule': 'cron(0 12 * * ? *)',
956 | 'Actions': [{'JobName': 'test-job'}],
957 | 'Description': 'Test trigger',
958 | 'StartOnCreation': True,
959 | },
960 | )
961 |
962 | # Verify the result
963 | assert not result.isError
964 | assert len(result.content) == 1
965 | assert result.content[0].type == 'text'
966 | assert 'Successfully created trigger test-trigger' in result.content[0].text
967 | assert result.trigger_name == 'test-trigger'
968 |
969 | # Verify that create_trigger was called with the correct parameters
970 | mock_glue_client.create_trigger.assert_called_once()
971 | args, kwargs = mock_glue_client.create_trigger.call_args
972 | assert kwargs['Name'] == 'test-trigger'
973 | assert kwargs['Type'] == 'SCHEDULED'
974 | assert kwargs['Schedule'] == 'cron(0 12 * * ? *)'
975 | assert kwargs['Actions'] == [{'JobName': 'test-job'}]
976 | assert kwargs['Description'] == 'Test trigger'
977 | assert kwargs['StartOnCreation']
978 | assert kwargs['Tags'] == {'ManagedBy': 'MCP'}
979 |
980 |
981 | @pytest.mark.asyncio
982 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
983 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.prepare_resource_tags')
984 | async def test_create_trigger_with_user_tags(mock_prepare_tags, mock_create_client):
985 | """Test creating a trigger with user-provided tags."""
986 | # Create a mock Glue client
987 | mock_glue_client = MagicMock()
988 | mock_create_client.return_value = mock_glue_client
989 |
990 | # Mock the resource tags
991 | mock_prepare_tags.return_value = {'ManagedBy': 'MCP'}
992 |
993 | # Create a mock MCP server
994 | mock_mcp = MagicMock()
995 |
996 | # Initialize the Glue Workflow handler with the mock MCP server
997 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
998 | handler.glue_client = mock_glue_client
999 |
1000 | # Create a mock context
1001 | mock_ctx = MagicMock(spec=Context)
1002 |
1003 | # Mock the create_trigger response
1004 | mock_glue_client.create_trigger.return_value = {'Name': 'test-trigger'}
1005 |
1006 | # Call the manage_aws_glue_triggers method with create-trigger operation and user tags
1007 | result = await handler.manage_aws_glue_triggers(
1008 | mock_ctx,
1009 | operation='create-trigger',
1010 | trigger_name='test-trigger',
1011 | trigger_definition={
1012 | 'Type': 'SCHEDULED',
1013 | 'Actions': [{'JobName': 'test-job'}],
1014 | 'Tags': {'Environment': 'Test', 'Project': 'UnitTest'},
1015 | },
1016 | )
1017 |
1018 | # Verify the result
1019 | assert not result.isError
1020 | assert result.trigger_name == 'test-trigger'
1021 |
1022 | # Verify that create_trigger was called with merged tags
1023 | mock_glue_client.create_trigger.assert_called_once()
1024 | args, kwargs = mock_glue_client.create_trigger.call_args
1025 | assert kwargs['Tags'] == {'Environment': 'Test', 'Project': 'UnitTest', 'ManagedBy': 'MCP'}
1026 |
1027 |
1028 | @pytest.mark.asyncio
1029 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
1030 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.prepare_resource_tags')
1031 | async def test_create_trigger_with_workflow_name(mock_prepare_tags, mock_create_client):
1032 | """Test creating a trigger with workflow_name parameter."""
1033 | # Create a mock Glue client
1034 | mock_glue_client = MagicMock()
1035 | mock_create_client.return_value = mock_glue_client
1036 |
1037 | # Mock the resource tags
1038 | mock_prepare_tags.return_value = {'ManagedBy': 'MCP'}
1039 |
1040 | # Create a mock MCP server
1041 | mock_mcp = MagicMock()
1042 |
1043 | # Initialize the Glue Workflow handler with the mock MCP server
1044 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
1045 | handler.glue_client = mock_glue_client
1046 |
1047 | # Create a mock context
1048 | mock_ctx = MagicMock(spec=Context)
1049 |
1050 | # Mock the create_trigger response
1051 | mock_glue_client.create_trigger.return_value = {'Name': 'test-trigger'}
1052 |
1053 | # Call the manage_aws_glue_triggers method with create-trigger operation and workflow_name
1054 | result = await handler.manage_aws_glue_triggers(
1055 | mock_ctx,
1056 | operation='create-trigger',
1057 | trigger_name='test-trigger',
1058 | trigger_definition={
1059 | 'Type': 'SCHEDULED',
1060 | 'Actions': [{'JobName': 'test-job'}],
1061 | 'WorkflowName': 'test-workflow',
1062 | },
1063 | )
1064 |
1065 | # Verify the result
1066 | assert not result.isError
1067 | assert result.trigger_name == 'test-trigger'
1068 |
1069 | # Verify that create_trigger was called with workflow_name
1070 | mock_glue_client.create_trigger.assert_called_once()
1071 | args, kwargs = mock_glue_client.create_trigger.call_args
1072 | assert kwargs['WorkflowName'] == 'test-workflow'
1073 |
1074 |
1075 | @pytest.mark.asyncio
1076 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
1077 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.prepare_resource_tags')
1078 | async def test_create_trigger_with_predicate(mock_prepare_tags, mock_create_client):
1079 | """Test creating a trigger with predicate parameter."""
1080 | # Create a mock Glue client
1081 | mock_glue_client = MagicMock()
1082 | mock_create_client.return_value = mock_glue_client
1083 |
1084 | # Mock the resource tags
1085 | mock_prepare_tags.return_value = {'ManagedBy': 'MCP'}
1086 |
1087 | # Create a mock MCP server
1088 | mock_mcp = MagicMock()
1089 |
1090 | # Initialize the Glue Workflow handler with the mock MCP server
1091 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
1092 | handler.glue_client = mock_glue_client
1093 |
1094 | # Create a mock context
1095 | mock_ctx = MagicMock(spec=Context)
1096 |
1097 | # Mock the create_trigger response
1098 | mock_glue_client.create_trigger.return_value = {'Name': 'test-trigger'}
1099 |
1100 | # Call the manage_aws_glue_triggers method with create-trigger operation and predicate
1101 | result = await handler.manage_aws_glue_triggers(
1102 | mock_ctx,
1103 | operation='create-trigger',
1104 | trigger_name='test-trigger',
1105 | trigger_definition={
1106 | 'Type': 'CONDITIONAL',
1107 | 'Actions': [{'JobName': 'test-job'}],
1108 | 'Predicate': {
1109 | 'Conditions': [
1110 | {
1111 | 'LogicalOperator': 'EQUALS',
1112 | 'JobName': 'crawl-job',
1113 | 'State': 'SUCCEEDED',
1114 | }
1115 | ]
1116 | },
1117 | },
1118 | )
1119 |
1120 | # Verify the result
1121 | assert not result.isError
1122 | assert result.trigger_name == 'test-trigger'
1123 |
1124 | # Verify that create_trigger was called with predicate
1125 | mock_glue_client.create_trigger.assert_called_once()
1126 | args, kwargs = mock_glue_client.create_trigger.call_args
1127 | assert kwargs['Predicate']['Conditions'][0]['LogicalOperator'] == 'EQUALS'
1128 | assert kwargs['Predicate']['Conditions'][0]['JobName'] == 'crawl-job'
1129 | assert kwargs['Predicate']['Conditions'][0]['State'] == 'SUCCEEDED'
1130 |
1131 |
1132 | @pytest.mark.asyncio
1133 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
1134 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.prepare_resource_tags')
1135 | async def test_create_trigger_with_event_batching_condition(mock_prepare_tags, mock_create_client):
1136 | """Test creating a trigger with event_batching_condition parameter."""
1137 | # Create a mock Glue client
1138 | mock_glue_client = MagicMock()
1139 | mock_create_client.return_value = mock_glue_client
1140 |
1141 | # Mock the resource tags
1142 | mock_prepare_tags.return_value = {'ManagedBy': 'MCP'}
1143 |
1144 | # Create a mock MCP server
1145 | mock_mcp = MagicMock()
1146 |
1147 | # Initialize the Glue Workflow handler with the mock MCP server
1148 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
1149 | handler.glue_client = mock_glue_client
1150 |
1151 | # Create a mock context
1152 | mock_ctx = MagicMock(spec=Context)
1153 |
1154 | # Mock the create_trigger response
1155 | mock_glue_client.create_trigger.return_value = {'Name': 'test-trigger'}
1156 |
1157 | # Call the manage_aws_glue_triggers method with create-trigger operation and event_batching_condition
1158 | result = await handler.manage_aws_glue_triggers(
1159 | mock_ctx,
1160 | operation='create-trigger',
1161 | trigger_name='test-trigger',
1162 | trigger_definition={
1163 | 'Type': 'EVENT',
1164 | 'Actions': [{'JobName': 'test-job'}],
1165 | 'EventBatchingCondition': {'BatchSize': 5, 'BatchWindow': 900},
1166 | },
1167 | )
1168 |
1169 | # Verify the result
1170 | assert not result.isError
1171 | assert result.trigger_name == 'test-trigger'
1172 |
1173 | # Verify that create_trigger was called with event_batching_condition
1174 | mock_glue_client.create_trigger.assert_called_once()
1175 | args, kwargs = mock_glue_client.create_trigger.call_args
1176 | assert kwargs['EventBatchingCondition']['BatchSize'] == 5
1177 | assert kwargs['EventBatchingCondition']['BatchWindow'] == 900
1178 |
1179 |
1180 | @pytest.mark.asyncio
1181 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
1182 | async def test_create_trigger_missing_parameters(mock_create_client):
1183 | """Test creating a trigger with missing required parameters."""
1184 | # Create a mock Glue client
1185 | mock_glue_client = MagicMock()
1186 | mock_create_client.return_value = mock_glue_client
1187 |
1188 | # Create a mock MCP server
1189 | mock_mcp = MagicMock()
1190 |
1191 | # Initialize the Glue Workflow handler with the mock MCP server
1192 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
1193 | handler.glue_client = mock_glue_client
1194 |
1195 | # Create a mock context
1196 | mock_ctx = MagicMock(spec=Context)
1197 |
1198 | # Test missing trigger_definition
1199 | with pytest.raises(ValueError) as excinfo:
1200 | await handler.manage_aws_glue_triggers(
1201 | mock_ctx,
1202 | operation='create-trigger',
1203 | trigger_name='test-trigger',
1204 | trigger_definition=None,
1205 | )
1206 | assert 'trigger_name and trigger_definition are required' in str(excinfo.value)
1207 |
1208 | # Test missing trigger_name
1209 | with pytest.raises(ValueError) as excinfo:
1210 | await handler.manage_aws_glue_triggers(
1211 | mock_ctx,
1212 | operation='create-trigger',
1213 | trigger_name=None,
1214 | trigger_definition={'Type': 'SCHEDULED', 'Actions': [{'JobName': 'test-job'}]},
1215 | )
1216 | assert 'trigger_name and trigger_definition are required' in str(excinfo.value)
1217 |
1218 |
1219 | @pytest.mark.asyncio
1220 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
1221 | async def test_create_trigger_no_write_access(mock_create_client):
1222 | # Create a mock Glue client
1223 | mock_glue_client = MagicMock()
1224 | mock_create_client.return_value = mock_glue_client
1225 |
1226 | # Create a mock MCP server
1227 | mock_mcp = MagicMock()
1228 |
1229 | # Initialize the Glue Workflow handler with the mock MCP server without write access
1230 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=False)
1231 | handler.glue_client = mock_glue_client
1232 |
1233 | # Create a mock context
1234 | mock_ctx = MagicMock(spec=Context)
1235 |
1236 | # Call the manage_aws_glue_triggers method with create-trigger operation
1237 | result = await handler.manage_aws_glue_triggers(
1238 | mock_ctx,
1239 | operation='create-trigger',
1240 | trigger_name='test-trigger',
1241 | trigger_definition={'Type': 'SCHEDULED', 'Actions': [{'JobName': 'test-job'}]},
1242 | )
1243 |
1244 | # Verify the result indicates an error due to no write access
1245 | assert result.isError
1246 | assert len(result.content) == 1
1247 | assert result.content[0].type == 'text'
1248 | assert 'Operation create-trigger is not allowed without write access' in result.content[0].text
1249 | assert result.trigger_name == ''
1250 |
1251 | # Verify that create_trigger was NOT called
1252 | mock_glue_client.create_trigger.assert_not_called()
1253 |
1254 |
1255 | @pytest.mark.asyncio
1256 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
1257 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_region')
1258 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_account_id')
1259 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.is_resource_mcp_managed')
1260 | async def test_delete_trigger_success(
1261 | mock_is_mcp_managed, mock_get_account_id, mock_get_region, mock_create_client
1262 | ):
1263 | # Create a mock Glue client
1264 | mock_glue_client = MagicMock()
1265 | mock_create_client.return_value = mock_glue_client
1266 |
1267 | # Mock the region and account ID
1268 | mock_get_region.return_value = 'us-east-1'
1269 | mock_get_account_id.return_value = '123456789012'
1270 |
1271 | # Mock the is_resource_mcp_managed to return True
1272 | mock_is_mcp_managed.return_value = True
1273 |
1274 | # Create a mock MCP server
1275 | mock_mcp = MagicMock()
1276 |
1277 | # Initialize the Glue Workflow handler with the mock MCP server
1278 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
1279 | handler.glue_client = mock_glue_client
1280 |
1281 | # Create a mock context
1282 | mock_ctx = MagicMock(spec=Context)
1283 |
1284 | # Mock the get_trigger response
1285 | mock_glue_client.get_trigger.return_value = {
1286 | 'Trigger': {'Name': 'test-trigger', 'Tags': {'ManagedBy': 'MCP'}}
1287 | }
1288 |
1289 | # Call the manage_aws_glue_triggers method with delete-trigger operation
1290 | result = await handler.manage_aws_glue_triggers(
1291 | mock_ctx, operation='delete-trigger', trigger_name='test-trigger'
1292 | )
1293 |
1294 | # Verify the result
1295 | assert not result.isError
1296 | assert len(result.content) == 1
1297 | assert result.content[0].type == 'text'
1298 | assert 'Successfully deleted trigger test-trigger' in result.content[0].text
1299 | assert result.trigger_name == 'test-trigger'
1300 |
1301 | # Verify that delete_trigger was called with the correct parameters
1302 | mock_glue_client.delete_trigger.assert_called_once_with(Name='test-trigger')
1303 |
1304 |
1305 | @pytest.mark.asyncio
1306 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
1307 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_region')
1308 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_account_id')
1309 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.is_resource_mcp_managed')
1310 | async def test_delete_trigger_not_mcp_managed(
1311 | mock_is_mcp_managed, mock_get_account_id, mock_get_region, mock_create_client
1312 | ):
1313 | # Create a mock Glue client
1314 | mock_glue_client = MagicMock()
1315 | mock_create_client.return_value = mock_glue_client
1316 |
1317 | # Mock the region and account ID
1318 | mock_get_region.return_value = 'us-east-1'
1319 | mock_get_account_id.return_value = '123456789012'
1320 |
1321 | # Mock the is_resource_mcp_managed to return False
1322 | mock_is_mcp_managed.return_value = False
1323 |
1324 | # Create a mock MCP server
1325 | mock_mcp = MagicMock()
1326 |
1327 | # Initialize the Glue Workflow handler with the mock MCP server
1328 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
1329 | handler.glue_client = mock_glue_client
1330 |
1331 | # Create a mock context
1332 | mock_ctx = MagicMock(spec=Context)
1333 |
1334 | # Mock the get_trigger response
1335 | mock_glue_client.get_trigger.return_value = {
1336 | 'Trigger': {
1337 | 'Name': 'test-trigger',
1338 | 'Tags': {}, # No MCP tags
1339 | }
1340 | }
1341 |
1342 | # Call the manage_aws_glue_triggers method with delete-trigger operation
1343 | result = await handler.manage_aws_glue_triggers(
1344 | mock_ctx, operation='delete-trigger', trigger_name='test-trigger'
1345 | )
1346 |
1347 | # Verify the result indicates an error because the trigger is not MCP managed
1348 | assert result.isError
1349 | assert len(result.content) == 1
1350 | assert result.content[0].type == 'text'
1351 | assert (
1352 | 'Cannot delete trigger test-trigger - it is not managed by the MCP server'
1353 | in result.content[0].text
1354 | )
1355 | assert result.trigger_name == 'test-trigger'
1356 |
1357 | # Verify that delete_trigger was NOT called
1358 | mock_glue_client.delete_trigger.assert_not_called()
1359 |
1360 |
1361 | @pytest.mark.asyncio
1362 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
1363 | async def test_get_trigger_success(mock_create_client):
1364 | # Create a mock Glue client
1365 | mock_glue_client = MagicMock()
1366 | mock_create_client.return_value = mock_glue_client
1367 |
1368 | # Create a mock MCP server
1369 | mock_mcp = MagicMock()
1370 |
1371 | # Initialize the Glue Workflow handler with the mock MCP server
1372 | handler = GlueWorkflowAndTriggerHandler(mock_mcp)
1373 | handler.glue_client = mock_glue_client
1374 |
1375 | # Create a mock context
1376 | mock_ctx = MagicMock(spec=Context)
1377 |
1378 | # Mock the get_trigger response
1379 | mock_trigger_details = {
1380 | 'Name': 'test-trigger',
1381 | 'Type': 'SCHEDULED',
1382 | 'Schedule': 'cron(0 12 * * ? *)',
1383 | 'Actions': [{'JobName': 'test-job'}],
1384 | 'Description': 'Test trigger',
1385 | }
1386 | mock_glue_client.get_trigger.return_value = {'Trigger': mock_trigger_details}
1387 |
1388 | # Call the manage_aws_glue_triggers method with get-trigger operation
1389 | result = await handler.manage_aws_glue_triggers(
1390 | mock_ctx, operation='get-trigger', trigger_name='test-trigger'
1391 | )
1392 |
1393 | # Verify the result
1394 | assert not result.isError
1395 | assert len(result.content) == 1
1396 | assert result.content[0].type == 'text'
1397 | assert 'Successfully retrieved trigger test-trigger' in result.content[0].text
1398 | assert result.trigger_name == 'test-trigger'
1399 | assert result.trigger_details == mock_trigger_details
1400 |
1401 | # Verify that get_trigger was called with the correct parameters
1402 | mock_glue_client.get_trigger.assert_called_once_with(Name='test-trigger')
1403 |
1404 |
1405 | @pytest.mark.asyncio
1406 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
1407 | async def test_get_triggers_success(mock_create_client):
1408 | # Create a mock Glue client
1409 | mock_glue_client = MagicMock()
1410 | mock_create_client.return_value = mock_glue_client
1411 |
1412 | # Create a mock MCP server
1413 | mock_mcp = MagicMock()
1414 |
1415 | # Initialize the Glue Workflow handler with the mock MCP server
1416 | handler = GlueWorkflowAndTriggerHandler(mock_mcp)
1417 | handler.glue_client = mock_glue_client
1418 |
1419 | # Create a mock context
1420 | mock_ctx = MagicMock(spec=Context)
1421 |
1422 | # Mock the get_triggers response
1423 | mock_glue_client.get_triggers.return_value = {
1424 | 'Triggers': [
1425 | {'Name': 'trigger1', 'Type': 'SCHEDULED'},
1426 | {'Name': 'trigger2', 'Type': 'CONDITIONAL'},
1427 | ],
1428 | 'NextToken': 'next-token',
1429 | }
1430 |
1431 | # Call the manage_aws_glue_triggers method with get-triggers operation
1432 | result = await handler.manage_aws_glue_triggers(
1433 | mock_ctx, operation='get-triggers', max_results=10, next_token='token'
1434 | )
1435 |
1436 | # Verify the result
1437 | assert not result.isError
1438 | assert len(result.content) == 1
1439 | assert result.content[0].type == 'text'
1440 | assert 'Successfully retrieved triggers' in result.content[0].text
1441 | assert len(result.triggers) == 2
1442 | assert result.triggers[0]['Name'] == 'trigger1'
1443 | assert result.triggers[1]['Name'] == 'trigger2'
1444 | assert result.next_token == 'next-token'
1445 |
1446 | # Verify that get_triggers was called with the correct parameters
1447 | mock_glue_client.get_triggers.assert_called_once_with(MaxResults=10, NextToken='token')
1448 |
1449 |
1450 | @pytest.mark.asyncio
1451 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
1452 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_region')
1453 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_account_id')
1454 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.is_resource_mcp_managed')
1455 | async def test_start_trigger_success(
1456 | mock_is_mcp_managed, mock_get_account_id, mock_get_region, mock_create_client
1457 | ):
1458 | # Create a mock Glue client
1459 | mock_glue_client = MagicMock()
1460 | mock_create_client.return_value = mock_glue_client
1461 |
1462 | # Mock the region and account ID
1463 | mock_get_region.return_value = 'us-east-1'
1464 | mock_get_account_id.return_value = '123456789012'
1465 |
1466 | # Mock the is_resource_mcp_managed to return True
1467 | mock_is_mcp_managed.return_value = True
1468 |
1469 | # Create a mock MCP server
1470 | mock_mcp = MagicMock()
1471 |
1472 | # Initialize the Glue Workflow handler with the mock MCP server
1473 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
1474 | handler.glue_client = mock_glue_client
1475 |
1476 | # Create a mock context
1477 | mock_ctx = MagicMock(spec=Context)
1478 |
1479 | # Mock the get_trigger response
1480 | mock_glue_client.get_trigger.return_value = {
1481 | 'Trigger': {'Name': 'test-trigger', 'Tags': {'ManagedBy': 'MCP'}}
1482 | }
1483 |
1484 | # Call the manage_aws_glue_triggers method with start-trigger operation
1485 | result = await handler.manage_aws_glue_triggers(
1486 | mock_ctx, operation='start-trigger', trigger_name='test-trigger'
1487 | )
1488 |
1489 | # Verify the result
1490 | assert not result.isError
1491 | assert len(result.content) == 1
1492 | assert result.content[0].type == 'text'
1493 | assert 'Successfully started trigger test-trigger' in result.content[0].text
1494 | assert result.trigger_name == 'test-trigger'
1495 |
1496 | # Verify that start_trigger was called with the correct parameters
1497 | mock_glue_client.start_trigger.assert_called_once_with(Name='test-trigger')
1498 |
1499 |
1500 | @pytest.mark.asyncio
1501 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
1502 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_region')
1503 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_account_id')
1504 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.is_resource_mcp_managed')
1505 | async def test_stop_trigger_success(
1506 | mock_is_mcp_managed, mock_get_account_id, mock_get_region, mock_create_client
1507 | ):
1508 | # Create a mock Glue client
1509 | mock_glue_client = MagicMock()
1510 | mock_create_client.return_value = mock_glue_client
1511 |
1512 | # Mock the region and account ID
1513 | mock_get_region.return_value = 'us-east-1'
1514 | mock_get_account_id.return_value = '123456789012'
1515 |
1516 | # Mock the is_resource_mcp_managed to return True
1517 | mock_is_mcp_managed.return_value = True
1518 |
1519 | # Create a mock MCP server
1520 | mock_mcp = MagicMock()
1521 |
1522 | # Initialize the Glue Workflow handler with the mock MCP server
1523 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
1524 | handler.glue_client = mock_glue_client
1525 |
1526 | # Create a mock context
1527 | mock_ctx = MagicMock(spec=Context)
1528 |
1529 | # Mock the get_trigger response
1530 | mock_glue_client.get_trigger.return_value = {
1531 | 'Trigger': {'Name': 'test-trigger', 'Tags': {'ManagedBy': 'MCP'}}
1532 | }
1533 |
1534 | # Call the manage_aws_glue_triggers method with stop-trigger operation
1535 | result = await handler.manage_aws_glue_triggers(
1536 | mock_ctx, operation='stop-trigger', trigger_name='test-trigger'
1537 | )
1538 |
1539 | # Verify the result
1540 | assert not result.isError
1541 | assert len(result.content) == 1
1542 | assert result.content[0].type == 'text'
1543 | assert 'Successfully stopped trigger test-trigger' in result.content[0].text
1544 | assert result.trigger_name == 'test-trigger'
1545 |
1546 | # Verify that stop_trigger was called with the correct parameters
1547 | mock_glue_client.stop_trigger.assert_called_once_with(Name='test-trigger')
1548 |
1549 |
1550 | @pytest.mark.asyncio
1551 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
1552 | async def test_trigger_invalid_operation(mock_create_client):
1553 | # Create a mock Glue client
1554 | mock_glue_client = MagicMock()
1555 | mock_create_client.return_value = mock_glue_client
1556 |
1557 | # Create a mock MCP server
1558 | mock_mcp = MagicMock()
1559 |
1560 | # Initialize the Glue Workflow handler with the mock MCP server with write access
1561 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
1562 | handler.glue_client = mock_glue_client
1563 |
1564 | # Create a mock context
1565 | mock_ctx = MagicMock(spec=Context)
1566 |
1567 | # Call the manage_aws_glue_triggers method with an invalid operation
1568 | result = await handler.manage_aws_glue_triggers(
1569 | mock_ctx, operation='invalid-operation', trigger_name='test-trigger'
1570 | )
1571 |
1572 | # Verify the result indicates an error due to invalid operation
1573 | assert result.isError
1574 | assert len(result.content) == 1
1575 | assert result.content[0].type == 'text'
1576 | assert 'Invalid operation: invalid-operation' in result.content[0].text
1577 | assert result.trigger_name == 'test-trigger'
1578 |
1579 |
1580 | @pytest.mark.asyncio
1581 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
1582 | async def test_trigger_not_found(mock_create_client):
1583 | # Create a mock Glue client
1584 | mock_glue_client = MagicMock()
1585 | mock_create_client.return_value = mock_glue_client
1586 |
1587 | # Create a mock MCP server
1588 | mock_mcp = MagicMock()
1589 |
1590 | # Initialize the Glue Workflow handler with the mock MCP server
1591 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
1592 | handler.glue_client = mock_glue_client
1593 |
1594 | # Create a mock context
1595 | mock_ctx = MagicMock(spec=Context)
1596 |
1597 | # Mock the get_trigger to raise EntityNotFoundException
1598 | mock_glue_client.exceptions.EntityNotFoundException = ClientError(
1599 | {'Error': {'Code': 'EntityNotFoundException', 'Message': 'Trigger not found'}},
1600 | 'get_trigger',
1601 | )
1602 | mock_glue_client.get_trigger.side_effect = mock_glue_client.exceptions.EntityNotFoundException
1603 |
1604 | # Call the manage_aws_glue_triggers method with delete-trigger operation
1605 | result = await handler.manage_aws_glue_triggers(
1606 | mock_ctx, operation='delete-trigger', trigger_name='test-trigger'
1607 | )
1608 |
1609 | # Verify the result indicates an error because the trigger was not found
1610 | assert result.isError
1611 | assert len(result.content) == 1
1612 | assert result.content[0].type == 'text'
1613 | assert 'Trigger test-trigger not found' in result.content[0].text
1614 | assert result.trigger_name == 'test-trigger'
1615 |
1616 | # Verify that delete_trigger was NOT called
1617 | mock_glue_client.delete_trigger.assert_not_called()
1618 |
1619 |
1620 | @pytest.mark.asyncio
1621 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
1622 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.prepare_resource_tags')
1623 | async def test_create_workflow_without_description(mock_prepare_tags, mock_create_client):
1624 | """Test creating workflow without description parameter."""
1625 | mock_glue_client = MagicMock()
1626 | mock_create_client.return_value = mock_glue_client
1627 | mock_prepare_tags.return_value = {'ManagedBy': 'MCP'}
1628 | mock_mcp = MagicMock()
1629 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
1630 | handler.glue_client = mock_glue_client
1631 | mock_ctx = MagicMock(spec=Context)
1632 |
1633 | mock_glue_client.create_workflow.return_value = {'Name': 'test-workflow'}
1634 |
1635 | result = await handler.manage_aws_glue_workflows(
1636 | mock_ctx,
1637 | operation='create-workflow',
1638 | workflow_name='test-workflow',
1639 | workflow_definition={},
1640 | )
1641 |
1642 | assert not result.isError
1643 | args, kwargs = mock_glue_client.create_workflow.call_args
1644 | assert 'Description' not in kwargs
1645 |
1646 |
1647 | @pytest.mark.asyncio
1648 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
1649 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.prepare_resource_tags')
1650 | async def test_create_workflow_without_default_run_properties(
1651 | mock_prepare_tags, mock_create_client
1652 | ):
1653 | """Test creating workflow without DefaultRunProperties parameter."""
1654 | mock_glue_client = MagicMock()
1655 | mock_create_client.return_value = mock_glue_client
1656 | mock_prepare_tags.return_value = {'ManagedBy': 'MCP'}
1657 | mock_mcp = MagicMock()
1658 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
1659 | handler.glue_client = mock_glue_client
1660 | mock_ctx = MagicMock(spec=Context)
1661 |
1662 | mock_glue_client.create_workflow.return_value = {'Name': 'test-workflow'}
1663 |
1664 | result = await handler.manage_aws_glue_workflows(
1665 | mock_ctx,
1666 | operation='create-workflow',
1667 | workflow_name='test-workflow',
1668 | workflow_definition={'Description': 'Test'},
1669 | )
1670 |
1671 | assert not result.isError
1672 | args, kwargs = mock_glue_client.create_workflow.call_args
1673 | assert 'DefaultRunProperties' not in kwargs
1674 |
1675 |
1676 | @pytest.mark.asyncio
1677 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
1678 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.prepare_resource_tags')
1679 | async def test_create_workflow_without_max_concurrent_runs(mock_prepare_tags, mock_create_client):
1680 | """Test creating workflow without MaxConcurrentRuns parameter."""
1681 | mock_glue_client = MagicMock()
1682 | mock_create_client.return_value = mock_glue_client
1683 | mock_prepare_tags.return_value = {'ManagedBy': 'MCP'}
1684 | mock_mcp = MagicMock()
1685 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
1686 | handler.glue_client = mock_glue_client
1687 | mock_ctx = MagicMock(spec=Context)
1688 |
1689 | mock_glue_client.create_workflow.return_value = {'Name': 'test-workflow'}
1690 |
1691 | result = await handler.manage_aws_glue_workflows(
1692 | mock_ctx,
1693 | operation='create-workflow',
1694 | workflow_name='test-workflow',
1695 | workflow_definition={'Description': 'Test'},
1696 | )
1697 |
1698 | assert not result.isError
1699 | args, kwargs = mock_glue_client.create_workflow.call_args
1700 | assert 'MaxConcurrentRuns' not in kwargs
1701 |
1702 |
1703 | @pytest.mark.asyncio
1704 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
1705 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_region')
1706 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_account_id')
1707 | async def test_delete_workflow_client_error(
1708 | mock_get_account_id, mock_get_region, mock_create_client
1709 | ):
1710 | """Test delete workflow with non-EntityNotFoundException ClientError."""
1711 | mock_glue_client = MagicMock()
1712 | mock_create_client.return_value = mock_glue_client
1713 | mock_get_region.return_value = 'us-east-1'
1714 | mock_get_account_id.return_value = '123456789012'
1715 | mock_mcp = MagicMock()
1716 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
1717 | handler.glue_client = mock_glue_client
1718 | mock_ctx = MagicMock(spec=Context)
1719 |
1720 | mock_glue_client.get_workflow.side_effect = ClientError(
1721 | {'Error': {'Code': 'AccessDeniedException', 'Message': 'Access denied'}}, 'get_workflow'
1722 | )
1723 |
1724 | result = await handler.manage_aws_glue_workflows(
1725 | mock_ctx, operation='delete-workflow', workflow_name='test-workflow'
1726 | )
1727 |
1728 | assert result.isError
1729 | assert 'Error in manage_aws_glue_workflows' in result.content[0].text
1730 |
1731 |
1732 | @pytest.mark.asyncio
1733 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
1734 | async def test_get_workflow_without_include_graph(mock_create_client):
1735 | """Test get workflow without include_graph parameter."""
1736 | mock_glue_client = MagicMock()
1737 | mock_create_client.return_value = mock_glue_client
1738 | mock_mcp = MagicMock()
1739 | handler = GlueWorkflowAndTriggerHandler(mock_mcp)
1740 | handler.glue_client = mock_glue_client
1741 | mock_ctx = MagicMock(spec=Context)
1742 |
1743 | mock_glue_client.get_workflow.return_value = {'Workflow': {'Name': 'test-workflow'}}
1744 |
1745 | result = await handler.manage_aws_glue_workflows(
1746 | mock_ctx,
1747 | operation='get-workflow',
1748 | workflow_name='test-workflow',
1749 | workflow_definition={},
1750 | )
1751 |
1752 | assert not result.isError
1753 | args, kwargs = mock_glue_client.get_workflow.call_args
1754 | assert 'IncludeGraph' not in kwargs
1755 |
1756 |
1757 | @pytest.mark.asyncio
1758 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
1759 | async def test_list_workflows_without_pagination(mock_create_client):
1760 | """Test list workflows without pagination parameters."""
1761 | mock_glue_client = MagicMock()
1762 | mock_create_client.return_value = mock_glue_client
1763 | mock_mcp = MagicMock()
1764 | handler = GlueWorkflowAndTriggerHandler(mock_mcp)
1765 | handler.glue_client = mock_glue_client
1766 | mock_ctx = MagicMock(spec=Context)
1767 |
1768 | mock_glue_client.list_workflows.return_value = {'Workflows': ['workflow1']}
1769 |
1770 | result = await handler.manage_aws_glue_workflows(mock_ctx, operation='list-workflows')
1771 |
1772 | assert not result.isError
1773 |
1774 |
1775 | @pytest.mark.asyncio
1776 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
1777 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_region')
1778 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_account_id')
1779 | async def test_start_workflow_run_client_error(
1780 | mock_get_account_id, mock_get_region, mock_create_client
1781 | ):
1782 | """Test start workflow run with non-EntityNotFoundException ClientError."""
1783 | mock_glue_client = MagicMock()
1784 | mock_create_client.return_value = mock_glue_client
1785 | mock_get_region.return_value = 'us-east-1'
1786 | mock_get_account_id.return_value = '123456789012'
1787 | mock_mcp = MagicMock()
1788 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
1789 | handler.glue_client = mock_glue_client
1790 | mock_ctx = MagicMock(spec=Context)
1791 |
1792 | mock_glue_client.get_workflow.side_effect = ClientError(
1793 | {'Error': {'Code': 'AccessDeniedException', 'Message': 'Access denied'}}, 'get_workflow'
1794 | )
1795 |
1796 | result = await handler.manage_aws_glue_workflows(
1797 | mock_ctx, operation='start-workflow-run', workflow_name='test-workflow'
1798 | )
1799 |
1800 | assert result.isError
1801 | assert 'Error in manage_aws_glue_workflows' in result.content[0].text
1802 |
1803 |
1804 | @pytest.mark.asyncio
1805 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
1806 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_region')
1807 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_account_id')
1808 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.is_resource_mcp_managed')
1809 | async def test_start_workflow_run_without_run_properties_mcp_managed(
1810 | mock_is_mcp_managed, mock_get_account_id, mock_get_region, mock_create_client
1811 | ):
1812 | """Test start workflow run without run_properties when workflow is MCP managed."""
1813 | mock_glue_client = MagicMock()
1814 | mock_create_client.return_value = mock_glue_client
1815 | mock_get_region.return_value = 'us-east-1'
1816 | mock_get_account_id.return_value = '123456789012'
1817 | mock_is_mcp_managed.return_value = True
1818 | mock_mcp = MagicMock()
1819 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
1820 | handler.glue_client = mock_glue_client
1821 | mock_ctx = MagicMock(spec=Context)
1822 |
1823 | mock_glue_client.get_workflow.return_value = {
1824 | 'Workflow': {'Name': 'test-workflow', 'Tags': {'ManagedBy': 'MCP'}}
1825 | }
1826 | mock_glue_client.start_workflow_run.return_value = {'RunId': 'run-123'}
1827 |
1828 | result = await handler.manage_aws_glue_workflows(
1829 | mock_ctx,
1830 | operation='start-workflow-run',
1831 | workflow_name='test-workflow',
1832 | workflow_definition={},
1833 | )
1834 |
1835 | assert not result.isError
1836 | args, kwargs = mock_glue_client.start_workflow_run.call_args
1837 | assert 'RunProperties' not in kwargs
1838 |
1839 |
1840 | @pytest.mark.asyncio
1841 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
1842 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.prepare_resource_tags')
1843 | async def test_create_trigger_individual_params(mock_prepare_tags, mock_create_client):
1844 | """Test create trigger with individual optional parameters."""
1845 | mock_glue_client = MagicMock()
1846 | mock_create_client.return_value = mock_glue_client
1847 | mock_prepare_tags.return_value = {'ManagedBy': 'MCP'}
1848 | mock_mcp = MagicMock()
1849 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
1850 | handler.glue_client = mock_glue_client
1851 | mock_ctx = MagicMock(spec=Context)
1852 |
1853 | mock_glue_client.create_trigger.return_value = {'Name': 'test-trigger'}
1854 |
1855 | # Test with WorkflowName
1856 | await handler.manage_aws_glue_triggers(
1857 | mock_ctx,
1858 | operation='create-trigger',
1859 | trigger_name='test-trigger',
1860 | trigger_definition={
1861 | 'Type': 'SCHEDULED',
1862 | 'Actions': [{'JobName': 'test-job'}],
1863 | 'WorkflowName': 'test-workflow',
1864 | },
1865 | )
1866 |
1867 | # Test with Schedule
1868 | await handler.manage_aws_glue_triggers(
1869 | mock_ctx,
1870 | operation='create-trigger',
1871 | trigger_name='test-trigger',
1872 | trigger_definition={
1873 | 'Type': 'SCHEDULED',
1874 | 'Actions': [{'JobName': 'test-job'}],
1875 | 'Schedule': 'cron(0 12 * * ? *)',
1876 | },
1877 | )
1878 |
1879 | # Test with Predicate
1880 | await handler.manage_aws_glue_triggers(
1881 | mock_ctx,
1882 | operation='create-trigger',
1883 | trigger_name='test-trigger',
1884 | trigger_definition={
1885 | 'Type': 'CONDITIONAL',
1886 | 'Actions': [{'JobName': 'test-job'}],
1887 | 'Predicate': {'Conditions': []},
1888 | },
1889 | )
1890 |
1891 | # Test with Description
1892 | await handler.manage_aws_glue_triggers(
1893 | mock_ctx,
1894 | operation='create-trigger',
1895 | trigger_name='test-trigger',
1896 | trigger_definition={
1897 | 'Type': 'SCHEDULED',
1898 | 'Actions': [{'JobName': 'test-job'}],
1899 | 'Description': 'Test trigger',
1900 | },
1901 | )
1902 |
1903 | # Test with StartOnCreation
1904 | await handler.manage_aws_glue_triggers(
1905 | mock_ctx,
1906 | operation='create-trigger',
1907 | trigger_name='test-trigger',
1908 | trigger_definition={
1909 | 'Type': 'SCHEDULED',
1910 | 'Actions': [{'JobName': 'test-job'}],
1911 | 'StartOnCreation': True,
1912 | },
1913 | )
1914 |
1915 | # Test with EventBatchingCondition
1916 | await handler.manage_aws_glue_triggers(
1917 | mock_ctx,
1918 | operation='create-trigger',
1919 | trigger_name='test-trigger',
1920 | trigger_definition={
1921 | 'Type': 'EVENT',
1922 | 'Actions': [{'JobName': 'test-job'}],
1923 | 'EventBatchingCondition': {'BatchSize': 5},
1924 | },
1925 | )
1926 |
1927 | assert mock_glue_client.create_trigger.call_count == 6
1928 |
1929 |
1930 | @pytest.mark.asyncio
1931 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
1932 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.prepare_resource_tags')
1933 | async def test_create_trigger_without_user_tags(mock_prepare_tags, mock_create_client):
1934 | """Test create trigger without user-provided tags."""
1935 | mock_glue_client = MagicMock()
1936 | mock_create_client.return_value = mock_glue_client
1937 | mock_prepare_tags.return_value = {'ManagedBy': 'MCP'}
1938 | mock_mcp = MagicMock()
1939 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
1940 | handler.glue_client = mock_glue_client
1941 | mock_ctx = MagicMock(spec=Context)
1942 |
1943 | mock_glue_client.create_trigger.return_value = {'Name': 'test-trigger'}
1944 |
1945 | result = await handler.manage_aws_glue_triggers(
1946 | mock_ctx,
1947 | operation='create-trigger',
1948 | trigger_name='test-trigger',
1949 | trigger_definition={
1950 | 'Type': 'SCHEDULED',
1951 | 'Actions': [{'JobName': 'test-job'}],
1952 | },
1953 | )
1954 |
1955 | assert not result.isError
1956 | args, kwargs = mock_glue_client.create_trigger.call_args
1957 | assert kwargs['Tags'] == {'ManagedBy': 'MCP'}
1958 |
1959 |
1960 | @pytest.mark.asyncio
1961 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
1962 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_region')
1963 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_account_id')
1964 | async def test_delete_trigger_client_error(
1965 | mock_get_account_id, mock_get_region, mock_create_client
1966 | ):
1967 | """Test delete trigger with non-EntityNotFoundException ClientError."""
1968 | mock_glue_client = MagicMock()
1969 | mock_create_client.return_value = mock_glue_client
1970 | mock_get_region.return_value = 'us-east-1'
1971 | mock_get_account_id.return_value = '123456789012'
1972 | mock_mcp = MagicMock()
1973 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
1974 | handler.glue_client = mock_glue_client
1975 | mock_ctx = MagicMock(spec=Context)
1976 |
1977 | mock_glue_client.get_trigger.side_effect = ClientError(
1978 | {'Error': {'Code': 'AccessDeniedException', 'Message': 'Access denied'}}, 'get_trigger'
1979 | )
1980 |
1981 | result = await handler.manage_aws_glue_triggers(
1982 | mock_ctx, operation='delete-trigger', trigger_name='test-trigger'
1983 | )
1984 |
1985 | assert result.isError
1986 | assert 'Error in manage_aws_glue_triggers' in result.content[0].text
1987 |
1988 |
1989 | @pytest.mark.asyncio
1990 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
1991 | async def test_get_triggers_without_pagination(mock_create_client):
1992 | """Test get triggers without pagination parameters."""
1993 | mock_glue_client = MagicMock()
1994 | mock_create_client.return_value = mock_glue_client
1995 | mock_mcp = MagicMock()
1996 | handler = GlueWorkflowAndTriggerHandler(mock_mcp)
1997 | handler.glue_client = mock_glue_client
1998 | mock_ctx = MagicMock(spec=Context)
1999 |
2000 | mock_glue_client.get_triggers.return_value = {'Triggers': []}
2001 |
2002 | result = await handler.manage_aws_glue_triggers(mock_ctx, operation='get-triggers')
2003 |
2004 | assert not result.isError
2005 |
2006 |
2007 | @pytest.mark.asyncio
2008 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
2009 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_region')
2010 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_account_id')
2011 | async def test_start_trigger_client_error(
2012 | mock_get_account_id, mock_get_region, mock_create_client
2013 | ):
2014 | """Test start trigger with non-EntityNotFoundException ClientError."""
2015 | mock_glue_client = MagicMock()
2016 | mock_create_client.return_value = mock_glue_client
2017 | mock_get_region.return_value = 'us-east-1'
2018 | mock_get_account_id.return_value = '123456789012'
2019 | mock_mcp = MagicMock()
2020 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
2021 | handler.glue_client = mock_glue_client
2022 | mock_ctx = MagicMock(spec=Context)
2023 |
2024 | mock_glue_client.get_trigger.side_effect = ClientError(
2025 | {'Error': {'Code': 'AccessDeniedException', 'Message': 'Access denied'}}, 'get_trigger'
2026 | )
2027 |
2028 | result = await handler.manage_aws_glue_triggers(
2029 | mock_ctx, operation='start-trigger', trigger_name='test-trigger'
2030 | )
2031 |
2032 | assert result.isError
2033 | assert 'Error in manage_aws_glue_triggers' in result.content[0].text
2034 |
2035 |
2036 | @pytest.mark.asyncio
2037 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
2038 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_region')
2039 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_account_id')
2040 | async def test_stop_trigger_client_error(mock_get_account_id, mock_get_region, mock_create_client):
2041 | """Test stop trigger with non-EntityNotFoundException ClientError."""
2042 | mock_glue_client = MagicMock()
2043 | mock_create_client.return_value = mock_glue_client
2044 | mock_get_region.return_value = 'us-east-1'
2045 | mock_get_account_id.return_value = '123456789012'
2046 | mock_mcp = MagicMock()
2047 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
2048 | handler.glue_client = mock_glue_client
2049 | mock_ctx = MagicMock(spec=Context)
2050 |
2051 | mock_glue_client.get_trigger.side_effect = ClientError(
2052 | {'Error': {'Code': 'AccessDeniedException', 'Message': 'Access denied'}}, 'get_trigger'
2053 | )
2054 |
2055 | result = await handler.manage_aws_glue_triggers(
2056 | mock_ctx, operation='stop-trigger', trigger_name='test-trigger'
2057 | )
2058 |
2059 | assert result.isError
2060 | assert 'Error in manage_aws_glue_triggers' in result.content[0].text
2061 |
2062 |
2063 | @pytest.mark.asyncio
2064 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
2065 | async def test_triggers_no_write_access_fallback(mock_create_client):
2066 | """Test triggers no write access fallback response."""
2067 | mock_glue_client = MagicMock()
2068 | mock_create_client.return_value = mock_glue_client
2069 | mock_mcp = MagicMock()
2070 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=False)
2071 | handler.glue_client = mock_glue_client
2072 | mock_ctx = MagicMock(spec=Context)
2073 |
2074 | result = await handler.manage_aws_glue_triggers(
2075 | mock_ctx, operation='unknown-operation', trigger_name='test-trigger'
2076 | )
2077 |
2078 | assert result.isError
2079 | assert (
2080 | 'Operation unknown-operation is not allowed without write access' in result.content[0].text
2081 | )
2082 |
2083 |
2084 | @pytest.mark.asyncio
2085 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
2086 | async def test_triggers_general_exception(mock_create_client):
2087 | """Test general exception handling in triggers."""
2088 | mock_glue_client = MagicMock()
2089 | mock_glue_client.get_trigger.side_effect = Exception('Test exception')
2090 | mock_create_client.return_value = mock_glue_client
2091 | mock_mcp = MagicMock()
2092 | handler = GlueWorkflowAndTriggerHandler(mock_mcp)
2093 | handler.glue_client = mock_glue_client
2094 | mock_ctx = MagicMock(spec=Context)
2095 |
2096 | result = await handler.manage_aws_glue_triggers(
2097 | mock_ctx, operation='get-trigger', trigger_name='test-trigger'
2098 | )
2099 |
2100 | assert result.isError
2101 | assert 'Error in manage_aws_glue_triggers: Test exception' in result.content[0].text
2102 |
2103 |
2104 | @pytest.mark.asyncio
2105 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
2106 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.prepare_resource_tags')
2107 | async def test_create_workflow_with_description_only(mock_prepare_tags, mock_create_client):
2108 | """Test creating workflow with description parameter only."""
2109 | mock_glue_client = MagicMock()
2110 | mock_create_client.return_value = mock_glue_client
2111 | mock_prepare_tags.return_value = {'ManagedBy': 'MCP'}
2112 | mock_mcp = MagicMock()
2113 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
2114 | handler.glue_client = mock_glue_client
2115 | mock_ctx = MagicMock(spec=Context)
2116 |
2117 | mock_glue_client.create_workflow.return_value = {'Name': 'test-workflow'}
2118 |
2119 | result = await handler.manage_aws_glue_workflows(
2120 | mock_ctx,
2121 | operation='create-workflow',
2122 | workflow_name='test-workflow',
2123 | workflow_definition={'Description': 'Test workflow'},
2124 | )
2125 |
2126 | assert not result.isError
2127 | args, kwargs = mock_glue_client.create_workflow.call_args
2128 | assert kwargs['Description'] == 'Test workflow'
2129 | assert 'DefaultRunProperties' not in kwargs
2130 |
2131 |
2132 | @pytest.mark.asyncio
2133 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
2134 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.prepare_resource_tags')
2135 | async def test_create_workflow_with_default_run_properties_only(
2136 | mock_prepare_tags, mock_create_client
2137 | ):
2138 | """Test creating workflow with DefaultRunProperties parameter only."""
2139 | mock_glue_client = MagicMock()
2140 | mock_create_client.return_value = mock_glue_client
2141 | mock_prepare_tags.return_value = {'ManagedBy': 'MCP'}
2142 | mock_mcp = MagicMock()
2143 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
2144 | handler.glue_client = mock_glue_client
2145 | mock_ctx = MagicMock(spec=Context)
2146 |
2147 | mock_glue_client.create_workflow.return_value = {'Name': 'test-workflow'}
2148 |
2149 | result = await handler.manage_aws_glue_workflows(
2150 | mock_ctx,
2151 | operation='create-workflow',
2152 | workflow_name='test-workflow',
2153 | workflow_definition={'DefaultRunProperties': {'ENV': 'test'}},
2154 | )
2155 |
2156 | assert not result.isError
2157 | args, kwargs = mock_glue_client.create_workflow.call_args
2158 | assert kwargs['DefaultRunProperties'] == {'ENV': 'test'}
2159 | assert 'Description' not in kwargs
2160 |
2161 |
2162 | @pytest.mark.asyncio
2163 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
2164 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.prepare_resource_tags')
2165 | async def test_create_workflow_with_max_concurrent_runs_only(
2166 | mock_prepare_tags, mock_create_client
2167 | ):
2168 | """Test creating workflow with MaxConcurrentRuns parameter only."""
2169 | mock_glue_client = MagicMock()
2170 | mock_create_client.return_value = mock_glue_client
2171 | mock_prepare_tags.return_value = {'ManagedBy': 'MCP'}
2172 | mock_mcp = MagicMock()
2173 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
2174 | handler.glue_client = mock_glue_client
2175 | mock_ctx = MagicMock(spec=Context)
2176 |
2177 | mock_glue_client.create_workflow.return_value = {'Name': 'test-workflow'}
2178 |
2179 | result = await handler.manage_aws_glue_workflows(
2180 | mock_ctx,
2181 | operation='create-workflow',
2182 | workflow_name='test-workflow',
2183 | workflow_definition={'MaxConcurrentRuns': 2},
2184 | )
2185 |
2186 | assert not result.isError
2187 | args, kwargs = mock_glue_client.create_workflow.call_args
2188 | assert kwargs['MaxConcurrentRuns'] == 2
2189 |
2190 |
2191 | @pytest.mark.asyncio
2192 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
2193 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_region')
2194 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_account_id')
2195 | async def test_delete_workflow_entity_not_found(
2196 | mock_get_account_id, mock_get_region, mock_create_client
2197 | ):
2198 | """Test delete workflow when workflow is not found."""
2199 | mock_glue_client = MagicMock()
2200 | mock_create_client.return_value = mock_glue_client
2201 | mock_get_region.return_value = 'us-east-1'
2202 | mock_get_account_id.return_value = '123456789012'
2203 | mock_mcp = MagicMock()
2204 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
2205 | handler.glue_client = mock_glue_client
2206 | mock_ctx = MagicMock(spec=Context)
2207 |
2208 | mock_glue_client.get_workflow.side_effect = ClientError(
2209 | {'Error': {'Code': 'EntityNotFoundException', 'Message': 'Workflow not found'}},
2210 | 'get_workflow',
2211 | )
2212 |
2213 | result = await handler.manage_aws_glue_workflows(
2214 | mock_ctx, operation='delete-workflow', workflow_name='test-workflow'
2215 | )
2216 |
2217 | assert result.isError
2218 | assert 'Workflow test-workflow not found' in result.content[0].text
2219 |
2220 |
2221 | @pytest.mark.asyncio
2222 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
2223 | async def test_get_workflow_with_include_graph_true(mock_create_client):
2224 | """Test get workflow with include_graph parameter set to True."""
2225 | mock_glue_client = MagicMock()
2226 | mock_create_client.return_value = mock_glue_client
2227 | mock_mcp = MagicMock()
2228 | handler = GlueWorkflowAndTriggerHandler(mock_mcp)
2229 | handler.glue_client = mock_glue_client
2230 | mock_ctx = MagicMock(spec=Context)
2231 |
2232 | mock_glue_client.get_workflow.return_value = {'Workflow': {'Name': 'test-workflow'}}
2233 |
2234 | result = await handler.manage_aws_glue_workflows(
2235 | mock_ctx,
2236 | operation='get-workflow',
2237 | workflow_name='test-workflow',
2238 | workflow_definition={'include_graph': True},
2239 | )
2240 |
2241 | assert not result.isError
2242 | args, kwargs = mock_glue_client.get_workflow.call_args
2243 | assert kwargs['IncludeGraph']
2244 |
2245 |
2246 | @pytest.mark.asyncio
2247 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
2248 | async def test_list_workflows_with_max_results(mock_create_client):
2249 | """Test list workflows with max_results parameter."""
2250 | mock_glue_client = MagicMock()
2251 | mock_create_client.return_value = mock_glue_client
2252 | mock_mcp = MagicMock()
2253 | handler = GlueWorkflowAndTriggerHandler(mock_mcp)
2254 | handler.glue_client = mock_glue_client
2255 | mock_ctx = MagicMock(spec=Context)
2256 |
2257 | mock_glue_client.list_workflows.return_value = {'Workflows': ['workflow1']}
2258 |
2259 | result = await handler.manage_aws_glue_workflows(
2260 | mock_ctx, operation='list-workflows', max_results=10
2261 | )
2262 |
2263 | assert not result.isError
2264 | args, kwargs = mock_glue_client.list_workflows.call_args
2265 | assert kwargs['MaxResults'] == 10
2266 |
2267 |
2268 | @pytest.mark.asyncio
2269 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
2270 | async def test_list_workflows_with_next_token(mock_create_client):
2271 | """Test list workflows with next_token parameter."""
2272 | mock_glue_client = MagicMock()
2273 | mock_create_client.return_value = mock_glue_client
2274 | mock_mcp = MagicMock()
2275 | handler = GlueWorkflowAndTriggerHandler(mock_mcp)
2276 | handler.glue_client = mock_glue_client
2277 | mock_ctx = MagicMock(spec=Context)
2278 |
2279 | mock_glue_client.list_workflows.return_value = {'Workflows': ['workflow1']}
2280 |
2281 | result = await handler.manage_aws_glue_workflows(
2282 | mock_ctx, operation='list-workflows', next_token='token123'
2283 | )
2284 |
2285 | assert not result.isError
2286 | args, kwargs = mock_glue_client.list_workflows.call_args
2287 | assert kwargs['NextToken'] == 'token123'
2288 |
2289 |
2290 | @pytest.mark.asyncio
2291 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
2292 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_region')
2293 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_account_id')
2294 | async def test_start_workflow_run_entity_not_found(
2295 | mock_get_account_id, mock_get_region, mock_create_client
2296 | ):
2297 | """Test start workflow run when workflow is not found."""
2298 | mock_glue_client = MagicMock()
2299 | mock_create_client.return_value = mock_glue_client
2300 | mock_get_region.return_value = 'us-east-1'
2301 | mock_get_account_id.return_value = '123456789012'
2302 | mock_mcp = MagicMock()
2303 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
2304 | handler.glue_client = mock_glue_client
2305 | mock_ctx = MagicMock(spec=Context)
2306 |
2307 | mock_glue_client.get_workflow.side_effect = ClientError(
2308 | {'Error': {'Code': 'EntityNotFoundException', 'Message': 'Workflow not found'}},
2309 | 'get_workflow',
2310 | )
2311 |
2312 | result = await handler.manage_aws_glue_workflows(
2313 | mock_ctx, operation='start-workflow-run', workflow_name='test-workflow'
2314 | )
2315 |
2316 | assert result.isError
2317 | assert 'Workflow test-workflow not found' in result.content[0].text
2318 |
2319 |
2320 | @pytest.mark.asyncio
2321 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
2322 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_region')
2323 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_account_id')
2324 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.is_resource_mcp_managed')
2325 | async def test_start_workflow_run_with_run_properties(
2326 | mock_is_mcp_managed, mock_get_account_id, mock_get_region, mock_create_client
2327 | ):
2328 | """Test start workflow run with run_properties."""
2329 | mock_glue_client = MagicMock()
2330 | mock_create_client.return_value = mock_glue_client
2331 | mock_get_region.return_value = 'us-east-1'
2332 | mock_get_account_id.return_value = '123456789012'
2333 | mock_is_mcp_managed.return_value = True
2334 | mock_mcp = MagicMock()
2335 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
2336 | handler.glue_client = mock_glue_client
2337 | mock_ctx = MagicMock(spec=Context)
2338 |
2339 | mock_glue_client.get_workflow.return_value = {
2340 | 'Workflow': {'Name': 'test-workflow', 'Tags': {'ManagedBy': 'MCP'}}
2341 | }
2342 | mock_glue_client.start_workflow_run.return_value = {'RunId': 'run-123'}
2343 |
2344 | result = await handler.manage_aws_glue_workflows(
2345 | mock_ctx,
2346 | operation='start-workflow-run',
2347 | workflow_name='test-workflow',
2348 | workflow_definition={'run_properties': {'ENV': 'test'}},
2349 | )
2350 |
2351 | assert not result.isError
2352 | args, kwargs = mock_glue_client.start_workflow_run.call_args
2353 | assert kwargs['RunProperties'] == {'ENV': 'test'}
2354 |
2355 |
2356 | @pytest.mark.asyncio
2357 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
2358 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_region')
2359 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_account_id')
2360 | async def test_delete_trigger_entity_not_found(
2361 | mock_get_account_id, mock_get_region, mock_create_client
2362 | ):
2363 | """Test delete trigger when trigger is not found."""
2364 | mock_glue_client = MagicMock()
2365 | mock_create_client.return_value = mock_glue_client
2366 | mock_get_region.return_value = 'us-east-1'
2367 | mock_get_account_id.return_value = '123456789012'
2368 | mock_mcp = MagicMock()
2369 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
2370 | handler.glue_client = mock_glue_client
2371 | mock_ctx = MagicMock(spec=Context)
2372 |
2373 | mock_glue_client.get_trigger.side_effect = ClientError(
2374 | {'Error': {'Code': 'EntityNotFoundException', 'Message': 'Trigger not found'}},
2375 | 'get_trigger',
2376 | )
2377 |
2378 | result = await handler.manage_aws_glue_triggers(
2379 | mock_ctx, operation='delete-trigger', trigger_name='test-trigger'
2380 | )
2381 |
2382 | assert result.isError
2383 | assert 'Trigger test-trigger not found' in result.content[0].text
2384 |
2385 |
2386 | @pytest.mark.asyncio
2387 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
2388 | async def test_get_triggers_with_max_results(mock_create_client):
2389 | """Test get triggers with max_results parameter."""
2390 | mock_glue_client = MagicMock()
2391 | mock_create_client.return_value = mock_glue_client
2392 | mock_mcp = MagicMock()
2393 | handler = GlueWorkflowAndTriggerHandler(mock_mcp)
2394 | handler.glue_client = mock_glue_client
2395 | mock_ctx = MagicMock(spec=Context)
2396 |
2397 | mock_glue_client.get_triggers.return_value = {'Triggers': []}
2398 |
2399 | result = await handler.manage_aws_glue_triggers(
2400 | mock_ctx, operation='get-triggers', max_results=10
2401 | )
2402 |
2403 | assert not result.isError
2404 | args, kwargs = mock_glue_client.get_triggers.call_args
2405 | assert kwargs['MaxResults'] == 10
2406 |
2407 |
2408 | @pytest.mark.asyncio
2409 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
2410 | async def test_get_triggers_with_next_token(mock_create_client):
2411 | """Test get triggers with next_token parameter."""
2412 | mock_glue_client = MagicMock()
2413 | mock_create_client.return_value = mock_glue_client
2414 | mock_mcp = MagicMock()
2415 | handler = GlueWorkflowAndTriggerHandler(mock_mcp)
2416 | handler.glue_client = mock_glue_client
2417 | mock_ctx = MagicMock(spec=Context)
2418 |
2419 | mock_glue_client.get_triggers.return_value = {'Triggers': []}
2420 |
2421 | result = await handler.manage_aws_glue_triggers(
2422 | mock_ctx, operation='get-triggers', next_token='token123'
2423 | )
2424 |
2425 | assert not result.isError
2426 | args, kwargs = mock_glue_client.get_triggers.call_args
2427 | assert kwargs['NextToken'] == 'token123'
2428 |
2429 |
2430 | @pytest.mark.asyncio
2431 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
2432 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_region')
2433 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_account_id')
2434 | async def test_start_trigger_entity_not_found(
2435 | mock_get_account_id, mock_get_region, mock_create_client
2436 | ):
2437 | """Test start trigger when trigger is not found."""
2438 | mock_glue_client = MagicMock()
2439 | mock_create_client.return_value = mock_glue_client
2440 | mock_get_region.return_value = 'us-east-1'
2441 | mock_get_account_id.return_value = '123456789012'
2442 | mock_mcp = MagicMock()
2443 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
2444 | handler.glue_client = mock_glue_client
2445 | mock_ctx = MagicMock(spec=Context)
2446 |
2447 | mock_glue_client.get_trigger.side_effect = ClientError(
2448 | {'Error': {'Code': 'EntityNotFoundException', 'Message': 'Trigger not found'}},
2449 | 'get_trigger',
2450 | )
2451 |
2452 | result = await handler.manage_aws_glue_triggers(
2453 | mock_ctx, operation='start-trigger', trigger_name='test-trigger'
2454 | )
2455 |
2456 | assert result.isError
2457 | assert 'Trigger test-trigger not found' in result.content[0].text
2458 |
2459 |
2460 | @pytest.mark.asyncio
2461 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
2462 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_region')
2463 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.get_aws_account_id')
2464 | async def test_stop_trigger_entity_not_found(
2465 | mock_get_account_id, mock_get_region, mock_create_client
2466 | ):
2467 | """Test stop trigger when trigger is not found."""
2468 | mock_glue_client = MagicMock()
2469 | mock_create_client.return_value = mock_glue_client
2470 | mock_get_region.return_value = 'us-east-1'
2471 | mock_get_account_id.return_value = '123456789012'
2472 | mock_mcp = MagicMock()
2473 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
2474 | handler.glue_client = mock_glue_client
2475 | mock_ctx = MagicMock(spec=Context)
2476 |
2477 | mock_glue_client.get_trigger.side_effect = ClientError(
2478 | {'Error': {'Code': 'EntityNotFoundException', 'Message': 'Trigger not found'}},
2479 | 'get_trigger',
2480 | )
2481 |
2482 | result = await handler.manage_aws_glue_triggers(
2483 | mock_ctx, operation='stop-trigger', trigger_name='test-trigger'
2484 | )
2485 |
2486 | assert result.isError
2487 | assert 'Trigger test-trigger not found' in result.content[0].text
2488 |
2489 |
2490 | @pytest.mark.asyncio
2491 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
2492 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.prepare_resource_tags')
2493 | async def test_create_workflow_empty_definition(mock_prepare_tags, mock_create_client):
2494 | """Test creating workflow with empty definition."""
2495 | mock_glue_client = MagicMock()
2496 | mock_create_client.return_value = mock_glue_client
2497 | mock_prepare_tags.return_value = {'ManagedBy': 'MCP'}
2498 | mock_mcp = MagicMock()
2499 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
2500 | handler.glue_client = mock_glue_client
2501 | mock_ctx = MagicMock(spec=Context)
2502 |
2503 | mock_glue_client.create_workflow.return_value = {'Name': 'test-workflow'}
2504 |
2505 | result = await handler.manage_aws_glue_workflows(
2506 | mock_ctx,
2507 | operation='create-workflow',
2508 | workflow_name='test-workflow',
2509 | workflow_definition={},
2510 | )
2511 |
2512 | assert result.isError is False
2513 | args, kwargs = mock_glue_client.create_workflow.call_args
2514 | assert 'Description' not in kwargs
2515 | assert 'DefaultRunProperties' not in kwargs
2516 | assert 'MaxConcurrentRuns' not in kwargs
2517 | assert kwargs['Tags'] == {'ManagedBy': 'MCP'}
2518 |
2519 |
2520 | @pytest.mark.asyncio
2521 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
2522 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.prepare_resource_tags')
2523 | async def test_create_trigger_minimal_params(mock_prepare_tags, mock_create_client):
2524 | """Test create trigger with minimal parameters."""
2525 | mock_glue_client = MagicMock()
2526 | mock_create_client.return_value = mock_glue_client
2527 | mock_prepare_tags.return_value = {'ManagedBy': 'MCP'}
2528 | mock_mcp = MagicMock()
2529 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
2530 | handler.glue_client = mock_glue_client
2531 | mock_ctx = MagicMock(spec=Context)
2532 |
2533 | mock_glue_client.create_trigger.return_value = {'Name': 'test-trigger'}
2534 |
2535 | result = await handler.manage_aws_glue_triggers(
2536 | mock_ctx,
2537 | operation='create-trigger',
2538 | trigger_name='test-trigger',
2539 | trigger_definition={
2540 | 'Type': 'SCHEDULED',
2541 | 'Actions': [{'JobName': 'test-job'}],
2542 | },
2543 | )
2544 |
2545 | assert result.isError is False
2546 | args, kwargs = mock_glue_client.create_trigger.call_args
2547 | assert kwargs['Type'] == 'SCHEDULED'
2548 | assert kwargs['Actions'] == [{'JobName': 'test-job'}]
2549 | assert 'WorkflowName' not in kwargs
2550 | assert 'Schedule' not in kwargs
2551 | assert 'Predicate' not in kwargs
2552 | assert 'Description' not in kwargs
2553 | assert 'StartOnCreation' not in kwargs
2554 | assert 'EventBatchingCondition' not in kwargs
2555 |
2556 |
2557 | @pytest.mark.asyncio
2558 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
2559 | async def test_workflow_parameter_validation_errors(mock_create_client):
2560 | """Test workflow parameter validation errors."""
2561 | mock_glue_client = MagicMock()
2562 | mock_create_client.return_value = mock_glue_client
2563 | mock_mcp = MagicMock()
2564 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
2565 | handler.glue_client = mock_glue_client
2566 | mock_ctx = MagicMock(spec=Context)
2567 |
2568 | # Test missing workflow_name for get-workflow
2569 | with pytest.raises(ValueError) as excinfo:
2570 | await handler.manage_aws_glue_workflows(
2571 | mock_ctx, operation='get-workflow', workflow_name=None
2572 | )
2573 | assert 'workflow_name is required' in str(excinfo.value)
2574 |
2575 | # Test missing workflow_name for delete-workflow
2576 | with pytest.raises(ValueError) as excinfo:
2577 | await handler.manage_aws_glue_workflows(
2578 | mock_ctx, operation='delete-workflow', workflow_name=None
2579 | )
2580 | assert 'workflow_name is required' in str(excinfo.value)
2581 |
2582 | # Test missing workflow_name for start-workflow-run
2583 | with pytest.raises(ValueError) as excinfo:
2584 | await handler.manage_aws_glue_workflows(
2585 | mock_ctx, operation='start-workflow-run', workflow_name=None
2586 | )
2587 | assert 'workflow_name is required' in str(excinfo.value)
2588 |
2589 |
2590 | @pytest.mark.asyncio
2591 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
2592 | async def test_trigger_parameter_validation_errors(mock_create_client):
2593 | """Test trigger parameter validation errors."""
2594 | mock_glue_client = MagicMock()
2595 | mock_create_client.return_value = mock_glue_client
2596 | mock_mcp = MagicMock()
2597 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=True)
2598 | handler.glue_client = mock_glue_client
2599 | mock_ctx = MagicMock(spec=Context)
2600 |
2601 | # Test missing trigger_name for get-trigger
2602 | with pytest.raises(ValueError) as excinfo:
2603 | await handler.manage_aws_glue_triggers(
2604 | mock_ctx, operation='get-trigger', trigger_name=None
2605 | )
2606 | assert 'trigger_name is required' in str(excinfo.value)
2607 |
2608 | # Test missing trigger_name for delete-trigger
2609 | with pytest.raises(ValueError) as excinfo:
2610 | await handler.manage_aws_glue_triggers(
2611 | mock_ctx, operation='delete-trigger', trigger_name=None
2612 | )
2613 | assert 'trigger_name is required' in str(excinfo.value)
2614 |
2615 | # Test missing trigger_name for start-trigger
2616 | with pytest.raises(ValueError) as excinfo:
2617 | await handler.manage_aws_glue_triggers(
2618 | mock_ctx, operation='start-trigger', trigger_name=None
2619 | )
2620 | assert 'trigger_name is required' in str(excinfo.value)
2621 |
2622 | # Test missing trigger_name for stop-trigger
2623 | with pytest.raises(ValueError) as excinfo:
2624 | await handler.manage_aws_glue_triggers(
2625 | mock_ctx, operation='stop-trigger', trigger_name=None
2626 | )
2627 | assert 'trigger_name is required' in str(excinfo.value)
2628 |
2629 |
2630 | @pytest.mark.asyncio
2631 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
2632 | async def test_workflow_general_exception(mock_create_client):
2633 | """Test general exception handling in workflows."""
2634 | mock_glue_client = MagicMock()
2635 | mock_glue_client.get_workflow.side_effect = Exception('Test exception')
2636 | mock_create_client.return_value = mock_glue_client
2637 | mock_mcp = MagicMock()
2638 | handler = GlueWorkflowAndTriggerHandler(mock_mcp)
2639 | handler.glue_client = mock_glue_client
2640 | mock_ctx = MagicMock(spec=Context)
2641 |
2642 | result = await handler.manage_aws_glue_workflows(
2643 | mock_ctx, operation='get-workflow', workflow_name='test-workflow'
2644 | )
2645 |
2646 | assert result.isError is True
2647 | assert 'Error in manage_aws_glue_workflows: Test exception' in result.content[0].text
2648 |
2649 |
2650 | @pytest.mark.asyncio
2651 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
2652 | async def test_trigger_general_exception(mock_create_client):
2653 | """Test general exception handling in triggers."""
2654 | mock_glue_client = MagicMock()
2655 | mock_glue_client.get_trigger.side_effect = Exception('Test exception')
2656 | mock_create_client.return_value = mock_glue_client
2657 | mock_mcp = MagicMock()
2658 | handler = GlueWorkflowAndTriggerHandler(mock_mcp)
2659 | handler.glue_client = mock_glue_client
2660 | mock_ctx = MagicMock(spec=Context)
2661 |
2662 | result = await handler.manage_aws_glue_triggers(
2663 | mock_ctx, operation='get-trigger', trigger_name='test-trigger'
2664 | )
2665 |
2666 | assert result.isError is True
2667 | assert 'Error in manage_aws_glue_triggers: Test exception' in result.content[0].text
2668 |
2669 |
2670 | @pytest.mark.asyncio
2671 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
2672 | async def test_workflow_no_write_access_fallback(mock_create_client):
2673 | """Test workflow no write access fallback response."""
2674 | mock_glue_client = MagicMock()
2675 | mock_create_client.return_value = mock_glue_client
2676 | mock_mcp = MagicMock()
2677 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=False)
2678 | handler.glue_client = mock_glue_client
2679 | mock_ctx = MagicMock(spec=Context)
2680 |
2681 | result = await handler.manage_aws_glue_workflows(
2682 | mock_ctx, operation='unknown-operation', workflow_name='test-workflow'
2683 | )
2684 |
2685 | assert result.isError is True
2686 | assert 'Invalid operation: unknown-operation' in result.content[0].text
2687 |
2688 |
2689 | @pytest.mark.asyncio
2690 | @patch('awslabs.aws_dataprocessing_mcp_server.utils.aws_helper.AwsHelper.create_boto3_client')
2691 | async def test_trigger_no_write_access_fallback(mock_create_client):
2692 | """Test trigger no write access fallback response."""
2693 | mock_glue_client = MagicMock()
2694 | mock_create_client.return_value = mock_glue_client
2695 | mock_mcp = MagicMock()
2696 | handler = GlueWorkflowAndTriggerHandler(mock_mcp, allow_write=False)
2697 | handler.glue_client = mock_glue_client
2698 | mock_ctx = MagicMock(spec=Context)
2699 |
2700 | result = await handler.manage_aws_glue_triggers(
2701 | mock_ctx, operation='create-trigger', trigger_name='test-trigger'
2702 | )
2703 |
2704 | assert result.isError is True
2705 | assert 'Operation create-trigger is not allowed without write access' in result.content[0].text
2706 |
```