awslabs.cloudwatch-appsignals-mcp-server 0.1.11__tar.gz → 0.1.12__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/PKG-INFO +12 -2
  2. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/README.md +11 -1
  3. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/awslabs/cloudwatch_appsignals_mcp_server/__init__.py +1 -1
  4. awslabs_cloudwatch_appsignals_mcp_server-0.1.12/awslabs/cloudwatch_appsignals_mcp_server/enablement_guides/templates/ec2/ec2-python-enablement.md +1 -0
  5. awslabs_cloudwatch_appsignals_mcp_server-0.1.12/awslabs/cloudwatch_appsignals_mcp_server/enablement_tools.py +147 -0
  6. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/pyproject.toml +1 -1
  7. awslabs_cloudwatch_appsignals_mcp_server-0.1.12/tests/test_enablement_tools.py +218 -0
  8. awslabs_cloudwatch_appsignals_mcp_server-0.1.12/uv-requirements.txt +27 -0
  9. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/uv.lock +1 -1
  10. awslabs_cloudwatch_appsignals_mcp_server-0.1.11/uv-requirements.txt +0 -24
  11. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/.gitignore +0 -0
  12. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/.python-version +0 -0
  13. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/CHANGELOG.md +0 -0
  14. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/Dockerfile +0 -0
  15. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/LICENSE +0 -0
  16. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/NOTICE +0 -0
  17. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/awslabs/__init__.py +0 -0
  18. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/awslabs/cloudwatch_appsignals_mcp_server/audit_presentation_utils.py +0 -0
  19. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/awslabs/cloudwatch_appsignals_mcp_server/audit_utils.py +0 -0
  20. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/awslabs/cloudwatch_appsignals_mcp_server/aws_clients.py +0 -0
  21. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/awslabs/cloudwatch_appsignals_mcp_server/canary_utils.py +0 -0
  22. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/awslabs/cloudwatch_appsignals_mcp_server/server.py +0 -0
  23. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/awslabs/cloudwatch_appsignals_mcp_server/service_audit_utils.py +0 -0
  24. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/awslabs/cloudwatch_appsignals_mcp_server/service_tools.py +0 -0
  25. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/awslabs/cloudwatch_appsignals_mcp_server/sli_report_client.py +0 -0
  26. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/awslabs/cloudwatch_appsignals_mcp_server/slo_tools.py +0 -0
  27. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/awslabs/cloudwatch_appsignals_mcp_server/trace_tools.py +0 -0
  28. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/awslabs/cloudwatch_appsignals_mcp_server/utils.py +0 -0
  29. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/docker-healthcheck.sh +0 -0
  30. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/tests/conftest.py +0 -0
  31. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/tests/test_audit_presentation_utils.py +0 -0
  32. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/tests/test_audit_utils.py +0 -0
  33. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/tests/test_aws_profile.py +0 -0
  34. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/tests/test_canary_utils.py +0 -0
  35. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/tests/test_initialization.py +0 -0
  36. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/tests/test_server.py +0 -0
  37. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/tests/test_server_audit_functions.py +0 -0
  38. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/tests/test_server_audit_tools.py +0 -0
  39. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/tests/test_service_audit_utils.py +0 -0
  40. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/tests/test_service_tools_operations.py +0 -0
  41. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/tests/test_sli_report_client.py +0 -0
  42. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/tests/test_slo_tools.py +0 -0
  43. {awslabs_cloudwatch_appsignals_mcp_server-0.1.11 → awslabs_cloudwatch_appsignals_mcp_server-0.1.12}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: awslabs.cloudwatch-appsignals-mcp-server
3
- Version: 0.1.11
3
+ Version: 0.1.12
4
4
  Summary: An AWS Labs Model Context Protocol (MCP) server for AWS Application Signals
5
5
  Project-URL: Homepage, https://awslabs.github.io/mcp/
6
6
  Project-URL: Documentation, https://awslabs.github.io/mcp/servers/cloudwatch-appsignals-mcp-server/
@@ -658,14 +658,24 @@ The server requires the following AWS IAM permissions:
658
658
  "application-signals:ListServiceLevelObjectives",
659
659
  "application-signals:GetServiceLevelObjective",
660
660
  "application-signals:BatchGetServiceLevelObjectiveBudgetReport",
661
+ "application-signals:ListAuditFindings",
661
662
  "cloudwatch:GetMetricData",
662
663
  "cloudwatch:GetMetricStatistics",
663
664
  "logs:GetQueryResults",
664
665
  "logs:StartQuery",
665
666
  "logs:StopQuery",
667
+ "logs:FilterLogEvents",
666
668
  "xray:GetTraceSummaries",
667
669
  "xray:BatchGetTraces",
668
- "xray:GetTraceSegmentDestination"
670
+ "xray:GetTraceSegmentDestination",
671
+ "synthetics:GetCanary",
672
+ "synthetics:GetCanaryRuns",
673
+ "s3:GetObject",
674
+ "s3:ListBucket",
675
+ "iam:GetRole",
676
+ "iam:ListAttachedRolePolicies",
677
+ "iam:GetPolicy",
678
+ "iam:GetPolicyVersion"
669
679
  ],
670
680
  "Resource": "*"
671
681
  }
@@ -628,14 +628,24 @@ The server requires the following AWS IAM permissions:
628
628
  "application-signals:ListServiceLevelObjectives",
629
629
  "application-signals:GetServiceLevelObjective",
630
630
  "application-signals:BatchGetServiceLevelObjectiveBudgetReport",
631
+ "application-signals:ListAuditFindings",
631
632
  "cloudwatch:GetMetricData",
632
633
  "cloudwatch:GetMetricStatistics",
633
634
  "logs:GetQueryResults",
634
635
  "logs:StartQuery",
635
636
  "logs:StopQuery",
637
+ "logs:FilterLogEvents",
636
638
  "xray:GetTraceSummaries",
637
639
  "xray:BatchGetTraces",
638
- "xray:GetTraceSegmentDestination"
640
+ "xray:GetTraceSegmentDestination",
641
+ "synthetics:GetCanary",
642
+ "synthetics:GetCanaryRuns",
643
+ "s3:GetObject",
644
+ "s3:ListBucket",
645
+ "iam:GetRole",
646
+ "iam:ListAttachedRolePolicies",
647
+ "iam:GetPolicy",
648
+ "iam:GetPolicyVersion"
639
649
  ],
640
650
  "Resource": "*"
641
651
  }
@@ -14,4 +14,4 @@
14
14
 
15
15
  """AWS Application Signals MCP Server."""
16
16
 
17
- __version__ = '0.1.11'
17
+ __version__ = '0.1.12'
@@ -0,0 +1,147 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """CloudWatch Application Signals MCP Server - Enablement Tools."""
16
+
17
+ from loguru import logger
18
+ from pathlib import Path
19
+
20
+
21
+ async def get_enablement_guide(
22
+ service_platform: str,
23
+ service_language: str,
24
+ iac_directory: str,
25
+ app_directory: str,
26
+ ) -> str:
27
+ """Get enablement guide for AWS Application Signals.
28
+
29
+ Use this tool when the user wants to:
30
+ - Enable Application Signals for their AWS service
31
+ - Set up automatic instrumentation for their application on AWS
32
+ - Instrument their service running on EC2, ECS, Lambda, or EKS
33
+
34
+ This tool returns step-by-step enablement instructions that guide you through
35
+ modifying your infrastructure and application code to enable Application Signals,
36
+ which is the preferred way to enable automatic instrumentation for services on AWS.
37
+
38
+ Before calling this tool:
39
+ 1. Ensure you know where the application code is located and that you have read/write permissions
40
+ 2. Ensure you know where the IaC code is located and that you have read/write permissions
41
+ 3. If the user provides relative paths or descriptions (e.g., "./infrastructure", "in the root"):
42
+ - Use the Bash tool to run 'pwd' to get the current working directory
43
+ - Use file exploration tools to locate the directories
44
+ - Convert relative paths to absolute paths before calling this tool
45
+ 4. This tool REQUIRES absolute paths for both iac_directory and app_directory parameters
46
+
47
+ After calling this tool, you should:
48
+ 1. Review the enablement guide and create a visible, trackable checklist of required changes
49
+ - Use your system's task tracking mechanism (todo lists, markdown checklists, etc.)
50
+ - Each item should be granular enough to complete in one step
51
+ - Mark items as complete as you finish them to track progress
52
+ - This allows you to resume work if the context window fills up
53
+ 2. Work through the checklist systematically, one item at a time:
54
+ - Identify the specific file(s) that need modification for this step
55
+ - Read only the relevant file(s) (DO NOT load all IaC and app files at once)
56
+ - Apply the changes as specified in the guide
57
+ 3. Keep context focused: Only load files needed for the current checklist item
58
+
59
+ Important guidelines:
60
+ - Use ABSOLUTE PATHS when reading and writing files
61
+ - Do NOT modify actual application logic files (.py, .js, .java source code), only
62
+ modify IaC code, Dockerfiles, and dependency files (requirements.txt, pyproject.toml,
63
+ package.json, pom.xml, build.gradle, *.csproj, etc.) as instructed by the guide.
64
+ - Read application files if needed to understand the setup, but avoid modifying them
65
+
66
+ Args:
67
+ service_platform: The AWS platform where the service runs.
68
+ MUST be one of: 'ec2', 'ecs', 'lambda', 'eks' (lowercase, exact match).
69
+ To help user determine: check their IaC for ECS services, Lambda functions, EKS deployments, or EC2 instances.
70
+ service_language: The service's programming language.
71
+ MUST be one of: 'python', 'nodejs', 'java', 'dotnet' (lowercase, exact match).
72
+ IMPORTANT: Use 'nodejs' (not 'js', 'node', or 'javascript'), 'dotnet' (not 'csharp' or 'c#').
73
+ To help user determine: check for package.json (nodejs), requirements.txt (python), pom.xml (java), or .csproj (dotnet).
74
+ iac_directory: ABSOLUTE path to the Infrastructure as Code (IaC) directory (e.g., /home/user/project/infrastructure)
75
+ app_directory: ABSOLUTE path to the application code directory (e.g., /home/user/project/app)
76
+
77
+ Returns:
78
+ Markdown-formatted enablement guide with step-by-step instructions
79
+ """
80
+ logger.debug(
81
+ f'get_enablement_guide called: service_platform={service_platform}, service_language={service_language}, '
82
+ f'iac_directory={iac_directory}, app_directory={app_directory}'
83
+ )
84
+
85
+ # Normalize to lowercase
86
+ platform_str = service_platform.lower().strip()
87
+ language_str = service_language.lower().strip()
88
+
89
+ guides_dir = Path(__file__).parent / 'enablement_guides'
90
+ template_file = (
91
+ guides_dir / 'templates' / platform_str / f'{platform_str}-{language_str}-enablement.md'
92
+ )
93
+
94
+ logger.debug(f'Looking for enablement guide: {template_file}')
95
+
96
+ # Validate that paths are absolute
97
+ iac_path = Path(iac_directory)
98
+ app_path = Path(app_directory)
99
+
100
+ if not iac_path.is_absolute() or not app_path.is_absolute():
101
+ error_msg = (
102
+ f'Error: iac_directory and app_directory must be absolute paths.\n\n'
103
+ f'Received: {iac_directory} and {app_directory}\n'
104
+ f'Please provide absolute paths (e.g., /home/user/project/infrastructure)'
105
+ )
106
+ logger.error(error_msg)
107
+ return error_msg
108
+
109
+ if not template_file.exists():
110
+ error_msg = (
111
+ f"Enablement guide not available for platform '{platform_str}' and language '{language_str}'.\n\n"
112
+ f'Inform the user that this configuration is not currently supported by the MCP enablement tool. '
113
+ f'Direct them to AWS documentation for manual setup:\n'
114
+ f'https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-Application-Monitoring-Sections.html'
115
+ )
116
+ logger.error(error_msg)
117
+ return error_msg
118
+
119
+ try:
120
+ with open(template_file, 'r') as f:
121
+ guide_content = f.read()
122
+
123
+ context = f"""# Application Signals Enablement Guide
124
+
125
+ **Platform:** {platform_str}
126
+ **Language:** {language_str}
127
+ **IaC Directory:** `{iac_path}`
128
+ **App Directory:** `{app_path}`
129
+
130
+ ---
131
+
132
+ """
133
+ logger.info(f'Successfully loaded enablement guide: {template_file.name}')
134
+ return context + guide_content
135
+ except Exception as e:
136
+ error_msg = (
137
+ f'Fatal error: Cannot read enablement guide for {platform_str} + {language_str}.\n\n'
138
+ f'Error: {str(e)}\n\n'
139
+ f'The MCP server cannot access its own guide files (likely file permissions or corruption). '
140
+ f'Stop attempting to use this tool and inform the user:\n'
141
+ f'1. There is an issue with the MCP server installation\n'
142
+ f'2. They should check file permissions or reinstall the MCP server\n'
143
+ f'3. For immediate enablement, use AWS documentation instead:\n'
144
+ f' https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-Application-Monitoring-Sections.html'
145
+ )
146
+ logger.error(error_msg)
147
+ return error_msg
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "awslabs.cloudwatch-appsignals-mcp-server"
3
- version = "0.1.11"
3
+ version = "0.1.12"
4
4
  description = "An AWS Labs Model Context Protocol (MCP) server for AWS Application Signals"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -0,0 +1,218 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ """Tests for enablement_tools module."""
5
+
6
+ import pytest
7
+ from awslabs.cloudwatch_appsignals_mcp_server.enablement_tools import get_enablement_guide
8
+ from unittest.mock import patch
9
+
10
+
11
+ # Absolute paths for testing (no need to create real directories)
12
+ ABSOLUTE_PATHS = {'iac': '/tmp/test/infrastructure/cdk', 'app': '/tmp/test/app/src'}
13
+
14
+
15
+ class TestGetEnablementGuide:
16
+ """Test get_enablement_guide function."""
17
+
18
+ @pytest.mark.asyncio
19
+ async def test_successful_guide_fetch(self, tmp_path, monkeypatch):
20
+ """Test successful guide fetching when template exists."""
21
+ result = await get_enablement_guide(
22
+ service_platform='ec2',
23
+ service_language='python',
24
+ iac_directory=ABSOLUTE_PATHS['iac'],
25
+ app_directory=ABSOLUTE_PATHS['app'],
26
+ )
27
+
28
+ assert '# Application Signals Enablement Guide' in result
29
+ assert 'Placeholder content just to verify the tool can fetch the file.' in result
30
+ assert ABSOLUTE_PATHS['iac'] in result
31
+ assert ABSOLUTE_PATHS['app'] in result
32
+
33
+ @pytest.mark.asyncio
34
+ async def test_all_valid_platforms(self):
35
+ """Test that all valid platforms are accepted."""
36
+ valid_platforms = ['ec2', 'ecs', 'lambda', 'eks']
37
+
38
+ for platform in valid_platforms:
39
+ result = await get_enablement_guide(
40
+ service_platform=platform,
41
+ service_language='python',
42
+ iac_directory=ABSOLUTE_PATHS['iac'],
43
+ app_directory=ABSOLUTE_PATHS['app'],
44
+ )
45
+
46
+ # Should either succeed or say template not found with friendly message
47
+ assert (
48
+ 'Enablement guide not available' in result
49
+ or '# Application Signals Enablement Guide' in result
50
+ )
51
+
52
+ @pytest.mark.asyncio
53
+ async def test_all_valid_languages(self):
54
+ """Test that all valid languages are accepted."""
55
+ valid_languages = ['python', 'nodejs', 'java', 'dotnet']
56
+
57
+ for language in valid_languages:
58
+ result = await get_enablement_guide(
59
+ service_platform='ec2',
60
+ service_language=language,
61
+ iac_directory=ABSOLUTE_PATHS['iac'],
62
+ app_directory=ABSOLUTE_PATHS['app'],
63
+ )
64
+
65
+ # Should either succeed or say template not found with friendly message
66
+ assert (
67
+ 'Enablement guide not available' in result
68
+ or '# Application Signals Enablement Guide' in result
69
+ )
70
+
71
+ @pytest.mark.asyncio
72
+ async def test_relative_path_rejected(self):
73
+ """Test that relative paths are rejected with clear error message."""
74
+ result = await get_enablement_guide(
75
+ service_platform='ec2',
76
+ service_language='python',
77
+ iac_directory='infrastructure/cdk',
78
+ app_directory=ABSOLUTE_PATHS['app'],
79
+ )
80
+
81
+ assert 'Error: iac_directory and app_directory must be absolute paths' in result
82
+ assert 'infrastructure/cdk' in result
83
+
84
+ @pytest.mark.asyncio
85
+ async def test_relative_app_directory_rejected(self):
86
+ """Test that relative app directory is rejected with clear error message."""
87
+ result = await get_enablement_guide(
88
+ service_platform='ec2',
89
+ service_language='python',
90
+ iac_directory=ABSOLUTE_PATHS['iac'],
91
+ app_directory='app/src',
92
+ )
93
+
94
+ assert 'Error: iac_directory and app_directory must be absolute paths' in result
95
+ assert 'app/src' in result
96
+
97
+ @pytest.mark.asyncio
98
+ async def test_absolute_path_handling(self):
99
+ """Test that absolute paths are handled correctly."""
100
+ result = await get_enablement_guide(
101
+ service_platform='ec2',
102
+ service_language='python',
103
+ iac_directory=ABSOLUTE_PATHS['iac'],
104
+ app_directory=ABSOLUTE_PATHS['app'],
105
+ )
106
+
107
+ assert '# Application Signals Enablement Guide' in result
108
+ assert ABSOLUTE_PATHS['iac'] in result
109
+ assert ABSOLUTE_PATHS['app'] in result
110
+
111
+ @pytest.mark.asyncio
112
+ async def test_unsupported_language_ruby(self):
113
+ """Test that unsupported language (ruby) returns friendly error message."""
114
+ result = await get_enablement_guide(
115
+ service_platform='ec2',
116
+ service_language='ruby',
117
+ iac_directory=ABSOLUTE_PATHS['iac'],
118
+ app_directory=ABSOLUTE_PATHS['app'],
119
+ )
120
+
121
+ assert 'Enablement guide not available' in result
122
+ assert 'ruby' in result.lower()
123
+ assert 'not currently supported' in result
124
+
125
+ @pytest.mark.asyncio
126
+ async def test_unsupported_platform_k8s(self):
127
+ """Test that unsupported platform (k8s) returns friendly error message."""
128
+ result = await get_enablement_guide(
129
+ service_platform='k8s',
130
+ service_language='python',
131
+ iac_directory=ABSOLUTE_PATHS['iac'],
132
+ app_directory=ABSOLUTE_PATHS['app'],
133
+ )
134
+
135
+ assert 'Enablement guide not available' in result
136
+ assert 'k8s' in result.lower()
137
+ assert 'not currently supported' in result
138
+
139
+ @pytest.mark.asyncio
140
+ async def test_case_insensitive_platform(self):
141
+ """Test that uppercase platform names are normalized to lowercase."""
142
+ result = await get_enablement_guide(
143
+ service_platform='EC2',
144
+ service_language='python',
145
+ iac_directory=ABSOLUTE_PATHS['iac'],
146
+ app_directory=ABSOLUTE_PATHS['app'],
147
+ )
148
+
149
+ # Should work the same as lowercase
150
+ assert 'Error: iac_directory and app_directory must be absolute paths' not in result
151
+ assert (
152
+ '# Application Signals Enablement Guide' in result
153
+ or 'Enablement guide not available' in result
154
+ )
155
+
156
+ @pytest.mark.asyncio
157
+ async def test_case_insensitive_language(self):
158
+ """Test that uppercase language names are normalized to lowercase."""
159
+ result = await get_enablement_guide(
160
+ service_platform='ec2',
161
+ service_language='PYTHON',
162
+ iac_directory=ABSOLUTE_PATHS['iac'],
163
+ app_directory=ABSOLUTE_PATHS['app'],
164
+ )
165
+
166
+ # Should work the same as lowercase
167
+ assert 'Error: iac_directory and app_directory must be absolute paths' not in result
168
+ assert (
169
+ '# Application Signals Enablement Guide' in result
170
+ or 'Enablement guide not available' in result
171
+ )
172
+
173
+ @pytest.mark.asyncio
174
+ async def test_whitespace_trimming(self):
175
+ """Test that leading/trailing whitespace is trimmed from inputs."""
176
+ result = await get_enablement_guide(
177
+ service_platform=' ec2 ',
178
+ service_language=' python ',
179
+ iac_directory=ABSOLUTE_PATHS['iac'],
180
+ app_directory=ABSOLUTE_PATHS['app'],
181
+ )
182
+
183
+ # Should work the same as trimmed input
184
+ assert 'Error: iac_directory and app_directory must be absolute paths' not in result
185
+ assert (
186
+ '# Application Signals Enablement Guide' in result
187
+ or 'Enablement guide not available' in result
188
+ )
189
+
190
+ @pytest.mark.asyncio
191
+ async def test_both_paths_relative(self):
192
+ """Test that error message shows both paths when both are relative."""
193
+ result = await get_enablement_guide(
194
+ service_platform='ec2',
195
+ service_language='python',
196
+ iac_directory='infrastructure/cdk',
197
+ app_directory='app/src',
198
+ )
199
+
200
+ assert 'Error: iac_directory and app_directory must be absolute paths' in result
201
+ assert 'infrastructure/cdk' in result
202
+ assert 'app/src' in result
203
+
204
+ @pytest.mark.asyncio
205
+ async def test_file_read_error(self):
206
+ """Test that file read errors are handled gracefully with helpful message."""
207
+ with patch('builtins.open', side_effect=PermissionError('Permission denied')):
208
+ result = await get_enablement_guide(
209
+ service_platform='ec2',
210
+ service_language='python',
211
+ iac_directory=ABSOLUTE_PATHS['iac'],
212
+ app_directory=ABSOLUTE_PATHS['app'],
213
+ )
214
+
215
+ assert 'Fatal error: Cannot read enablement guide' in result
216
+ assert 'Permission denied' in result
217
+ assert 'file permissions or reinstall' in result
218
+ assert 'issue with the MCP server installation' in result
@@ -0,0 +1,27 @@
1
+ #
2
+ # This file is autogenerated by pip-compile with Python 3.12
3
+ # by the following command:
4
+ #
5
+ # pip-compile --generate-hashes --output-file=uv-requirements.txt --strip-extras uv-requirements.in
6
+ #
7
+ uv==0.9.6 \
8
+ --hash=sha256:0169a85d3ba5ef1c37089d64ff26de573439ca84ecf549276a2eee42d7f833f2 \
9
+ --hash=sha256:0fde18c22376c8b02954c7db3847bc75ac42619932c44b43f49d056e5cfb05f9 \
10
+ --hash=sha256:166175ba952d2ad727e1dbd57d7cfc1782dfe7b8d79972174a46a7aa33ddceec \
11
+ --hash=sha256:3c2c2b2b093330e603d838fec26941ab6f62e8d62a012f9fa0d5ed88da39d907 \
12
+ --hash=sha256:538716ec97f8d899baa7e1c427f4411525459c0ef72ea9b3625ce9610c9976e6 \
13
+ --hash=sha256:547fd27ab5da7cd1a833288a36858852451d416a056825f162ecf2af5be6f8b8 \
14
+ --hash=sha256:62e3f057a9ae5e5003a7cd56b617e940f519f6dabcbb22d36cdd0149df25d409 \
15
+ --hash=sha256:6403176b55388cf94fb8737e73b26ee2a7b1805a9139da5afa951210986d4fcd \
16
+ --hash=sha256:7e89c964f614fa3f0481060cac709d6da50feac553e1e11227d6c4c81c87af7c \
17
+ --hash=sha256:86e05782f9b75d39ab1c0af98bf11e87e646a36a61d425021d5b284073e56315 \
18
+ --hash=sha256:90122a76e6441b8c580fc9faf06bd8c4dbe276cb1c185ad91eceb2afa78e492a \
19
+ --hash=sha256:95a62c1f668272555ad0c446bf44a9924dee06054b831d04c162e0bad736dc28 \
20
+ --hash=sha256:a7c6067919d87208c4a6092033c3bc9799cb8be1c8bc6ef419a1f6d42a755329 \
21
+ --hash=sha256:b2f934737c93f88c906b6a47bcc083170210fe5d66565e80a7c139599e5cbf2f \
22
+ --hash=sha256:b31377ebf2d0499afc5abe3fe1abded5ca843f3a1161b432fe26eb0ce15bab8e \
23
+ --hash=sha256:d1072db92cc9525febdf9d113c23916dfc20ca03e21218cc7beefe7185a90631 \
24
+ --hash=sha256:e700b2098f9d365061c572d0729b4e8bc71c6468d83dfaae2537cd66e3cb1b98 \
25
+ --hash=sha256:ea67369918af24ea7e01991dfc8b8988d1b0b7c49cb39d9e5bc0c409930a0a3f \
26
+ --hash=sha256:f0ba311b3ca49d246f36d444d3ee81571619ef95e5f509eb694a81defcbed262
27
+ # via -r uv-requirements.in (contents of `uv==0.9.6`)
@@ -46,7 +46,7 @@ wheels = [
46
46
 
47
47
  [[package]]
48
48
  name = "awslabs-cloudwatch-appsignals-mcp-server"
49
- version = "0.1.11"
49
+ version = "0.1.12"
50
50
  source = { editable = "." }
51
51
  dependencies = [
52
52
  { name = "boto3" },
@@ -1,24 +0,0 @@
1
- # This file was autogenerated by uv via the following command:
2
- # echo "uv==0.8.10" > uv-requirements.in
3
- # uv pip compile --generate-hashes --output-file=uv-requirements.txt --strip-extras --python=3.10 uv-requirements.in
4
- uv==0.8.10 \
5
- --hash=sha256:31e4fc37ee94b94c032384a0957ad32ba7dce4ce6c04b4880fd3e31e25e51a82 \
6
- --hash=sha256:36a5ce708d52388c37043e7335f9eb3fea5a19a56166a2cc6adb365179a1cd77 \
7
- --hash=sha256:38286d230daad82388469c8dc7a1d2f5dc279c11178319c886d1a88d7938e513 \
8
- --hash=sha256:3e190cee3bb2b4f574a419eef87ae8e33f713e9cd6f856b83277ece70ad9ca9b \
9
- --hash=sha256:3fdf89fc40af9902141c39ed943bcfca15664623363335eb032a44f22001e2b4 \
10
- --hash=sha256:4cc190d403a89e46d13cec83b6f8e8d7d07aaf1e5a996eac9a3f0c2a8cd92537 \
11
- --hash=sha256:57b71dc79eff25a5419d3fe4a563d3b9397f55d789f685ef27f43f033b31f482 \
12
- --hash=sha256:86fe044c2be43977566a0d184a487edd7aace2febb757fd95927684b629ef50b \
13
- --hash=sha256:88df34c32555064fae459cce665757619fd1af7deb2dc393352b15d909d2d131 \
14
- --hash=sha256:9ad21eeaa4156a1bf5ed85903f80db06e2c02badd3a587ba98d3171517960555 \
15
- --hash=sha256:a5495b5a6e3111c03cf5e4dbdd598bc8fd1da887e3920d58cd5a2d4c8bc9a473 \
16
- --hash=sha256:ab072cd3bf2f9dc264659a1ff48ad91a910ac4830bcfe965e2d3f89c86646f46 \
17
- --hash=sha256:af8a5526b0e331775a264fa0dbccfd53c183cb974f269a208af136d7561f9eb2 \
18
- --hash=sha256:b00637c63d5dfc9f879281c5c91db2bb909ab1f9ab275dab015e7fb6cac6be5b \
19
- --hash=sha256:b3ff3c451fcd23ea78356d8c18e802d0e423cbe655273601e3ec039a51b33286 \
20
- --hash=sha256:c4a493cd4b15b3aef11523531aff96a77a586666a63e842fa437966b7b7ee62d \
21
- --hash=sha256:defc50bb319be2d58be74a680710cd4b7697e88d5f79974eacd354df95f0b6b0 \
22
- --hash=sha256:e0a02bcec766eb0862b7082ab746b204add7d9fcaa62322502d159b5a7ccc54a \
23
- --hash=sha256:eb79a46d8099f563ef58237bf4e9009f876a40145e757ea883a92b24b724d01e
24
- # via -r uv-requirements.in