awslabs.cloudwatch-mcp-server 0.0.5__tar.gz → 0.0.7__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.
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/Dockerfile +33 -22
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/PKG-INFO +7 -4
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/README.md +5 -2
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/awslabs/cloudwatch_mcp_server/cloudwatch_logs/tools.py +2 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/docker-healthcheck.sh +7 -8
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/pyproject.toml +2 -2
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/tests/cloudwatch_logs/test_logs_error_handling.py +148 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/tests/cloudwatch_logs/test_logs_server.py +60 -0
- awslabs_cloudwatch_mcp_server-0.0.7/uv-requirements.txt +26 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/uv.lock +755 -555
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/.gitignore +0 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/.python-version +0 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/CHANGELOG.md +0 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/LICENSE +0 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/NOTICE +0 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/awslabs/__init__.py +0 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/awslabs/cloudwatch_mcp_server/__init__.py +0 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/awslabs/cloudwatch_mcp_server/cloudwatch_alarms/models.py +0 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/awslabs/cloudwatch_mcp_server/cloudwatch_alarms/tools.py +0 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/awslabs/cloudwatch_mcp_server/cloudwatch_logs/models.py +0 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/awslabs/cloudwatch_mcp_server/cloudwatch_metrics/data/metric_metadata.json +0 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/awslabs/cloudwatch_mcp_server/cloudwatch_metrics/models.py +0 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/awslabs/cloudwatch_mcp_server/cloudwatch_metrics/tools.py +0 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/awslabs/cloudwatch_mcp_server/common.py +0 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/awslabs/cloudwatch_mcp_server/server.py +0 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/tests/cloudwatch_alarms/test_active_alarms.py +0 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/tests/cloudwatch_alarms/test_alarm_history.py +0 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/tests/cloudwatch_alarms/test_alarm_history_integration.py +0 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/tests/cloudwatch_alarms/test_alarms_error_handling.py +0 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/tests/cloudwatch_logs/test_logs_models.py +0 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/tests/cloudwatch_metrics/test_metrics_error_handling.py +0 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/tests/cloudwatch_metrics/test_metrics_models.py +0 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/tests/cloudwatch_metrics/test_metrics_server.py +0 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/tests/cloudwatch_metrics/test_validation_error.py +0 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/tests/test_common_and_server.py +0 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/tests/test_init.py +0 -0
- {awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/tests/test_main.py +0 -0
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
# dependabot should continue to update this to the latest hash.
|
|
16
|
+
FROM public.ecr.aws/docker/library/python:3.13.5-alpine3.21@sha256:c9a09c45a4bcc618c7f7128585b8dd0d41d0c31a8a107db4c8255ffe0b69375d AS uv
|
|
16
17
|
|
|
17
18
|
# Install the project into `/app`
|
|
18
19
|
WORKDIR /app
|
|
@@ -30,40 +31,50 @@ ENV UV_PYTHON_PREFERENCE=only-system
|
|
|
30
31
|
ENV UV_FROZEN=true
|
|
31
32
|
|
|
32
33
|
# Copy the required files first
|
|
33
|
-
COPY pyproject.toml uv.lock ./
|
|
34
|
+
COPY pyproject.toml uv.lock uv-requirements.txt ./
|
|
35
|
+
|
|
36
|
+
# Python optimization and uv configuration
|
|
37
|
+
ENV PIP_NO_CACHE_DIR=1 \
|
|
38
|
+
PIP_DISABLE_PIP_VERSION_CHECK=1
|
|
39
|
+
|
|
40
|
+
# Install system dependencies and Python package manager
|
|
41
|
+
RUN apk update && \
|
|
42
|
+
apk add --no-cache --virtual .build-deps \
|
|
43
|
+
build-base \
|
|
44
|
+
gcc \
|
|
45
|
+
musl-dev \
|
|
46
|
+
libffi-dev \
|
|
47
|
+
openssl-dev \
|
|
48
|
+
cargo
|
|
34
49
|
|
|
35
50
|
# Install the project's dependencies using the lockfile and settings
|
|
36
51
|
RUN --mount=type=cache,target=/root/.cache/uv \
|
|
37
|
-
pip install uv
|
|
38
|
-
uv sync --frozen --no-install-project --no-dev --no-editable
|
|
52
|
+
pip install --require-hashes --requirement uv-requirements.txt --no-cache-dir && \
|
|
53
|
+
uv sync --python 3.13 --frozen --no-install-project --no-dev --no-editable
|
|
39
54
|
|
|
40
55
|
# Then, add the rest of the project source code and install it
|
|
41
56
|
# Installing separately from its dependencies allows optimal layer caching
|
|
42
57
|
COPY . /app
|
|
43
58
|
RUN --mount=type=cache,target=/root/.cache/uv \
|
|
44
|
-
uv sync --frozen --no-dev --no-editable
|
|
59
|
+
uv sync --python 3.13 --frozen --no-dev --no-editable
|
|
45
60
|
|
|
46
61
|
# Make the directory just in case it doesn't exist
|
|
47
62
|
RUN mkdir -p /root/.local
|
|
48
63
|
|
|
49
|
-
FROM public.ecr.aws/
|
|
64
|
+
FROM public.ecr.aws/docker/library/python:3.13.5-alpine3.21@sha256:c9a09c45a4bcc618c7f7128585b8dd0d41d0c31a8a107db4c8255ffe0b69375d
|
|
50
65
|
|
|
51
66
|
# Place executables in the environment at the front of the path and include other binaries
|
|
52
|
-
ENV PATH="/app/.venv/bin:$PATH
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
# Install
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
chmod o+x /root
|
|
64
|
-
|
|
65
|
-
# Get the project from the uv layer
|
|
66
|
-
COPY --from=uv --chown=app:app /root/.local /root/.local
|
|
67
|
+
ENV PATH="/app/.venv/bin:$PATH" \
|
|
68
|
+
PYTHONUNBUFFERED=1
|
|
69
|
+
|
|
70
|
+
# Install runtime dependencies and create application user
|
|
71
|
+
RUN apk update && \
|
|
72
|
+
apk add --no-cache ca-certificates && \
|
|
73
|
+
update-ca-certificates && \
|
|
74
|
+
addgroup -S app && \
|
|
75
|
+
adduser -S app -G app -h /app
|
|
76
|
+
|
|
77
|
+
# Copy application artifacts from build stage
|
|
67
78
|
COPY --from=uv --chown=app:app /app/.venv /app/.venv
|
|
68
79
|
|
|
69
80
|
# Get healthcheck script
|
|
@@ -73,5 +84,5 @@ COPY ./docker-healthcheck.sh /usr/local/bin/docker-healthcheck.sh
|
|
|
73
84
|
USER app
|
|
74
85
|
|
|
75
86
|
# When running the container, add --db-path and a bind mount to the host's db file
|
|
76
|
-
HEALTHCHECK --interval=
|
|
87
|
+
HEALTHCHECK --interval=60s --timeout=10s --start-period=10s --retries=3 CMD ["docker-healthcheck.sh"]
|
|
77
88
|
ENTRYPOINT ["awslabs.cloudwatch-mcp-server"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: awslabs.cloudwatch-mcp-server
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.7
|
|
4
4
|
Summary: An AWS Labs Model Context Protocol (MCP) server for cloudwatch
|
|
5
5
|
Project-URL: homepage, https://awslabs.github.io/mcp/
|
|
6
6
|
Project-URL: docs, https://awslabs.github.io/mcp/servers/cloudwatch-mcp-server/
|
|
@@ -23,7 +23,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
23
23
|
Requires-Python: >=3.10
|
|
24
24
|
Requires-Dist: boto3>=1.38.22
|
|
25
25
|
Requires-Dist: loguru>=0.7.0
|
|
26
|
-
Requires-Dist: mcp[cli]>=1.
|
|
26
|
+
Requires-Dist: mcp[cli]>=1.11.0
|
|
27
27
|
Requires-Dist: pydantic>=2.10.6
|
|
28
28
|
Description-Content-Type: text/markdown
|
|
29
29
|
|
|
@@ -91,8 +91,11 @@ Alarm Recommendations - Suggests recommended alarm configurations for CloudWatch
|
|
|
91
91
|
1. Install `uv` from [Astral](https://docs.astral.sh/uv/getting-started/installation/) or the [GitHub README](https://github.com/astral-sh/uv#installation)
|
|
92
92
|
2. Install Python using `uv python install 3.10`
|
|
93
93
|
|
|
94
|
-
#### One Click
|
|
95
|
-
|
|
94
|
+
#### One Click Install
|
|
95
|
+
|
|
96
|
+
| Cursor | VS Code |
|
|
97
|
+
|:------:|:-------:|
|
|
98
|
+
| [](https://cursor.com/install-mcp?name=awslabs.cloudwatch-mcp-server&config=ewogICAgImF1dG9BcHByb3ZlIjogW10sCiAgICAiZGlzYWJsZWQiOiBmYWxzZSwKICAgICJjb21tYW5kIjogInV2eCBhd3NsYWJzLmNsb3Vkd2F0Y2gtbWNwLXNlcnZlckBsYXRlc3QiLAogICAgImVudiI6IHsKICAgICAgIkFXU19QUk9GSUxFIjogIltUaGUgQVdTIFByb2ZpbGUgTmFtZSB0byB1c2UgZm9yIEFXUyBhY2Nlc3NdIiwKICAgICAgIkZBU1RNQ1BfTE9HX0xFVkVMIjogIkVSUk9SIgogICAgfSwKICAgICJ0cmFuc3BvcnRUeXBlIjogInN0ZGlvIgp9) | [](https://insiders.vscode.dev/redirect/mcp/install?name=CloudWatch%20MCP%20Server&config=%7B%22autoApprove%22%3A%5B%5D%2C%22disabled%22%3Afalse%2C%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22awslabs.cloudwatch-mcp-server%40latest%22%5D%2C%22env%22%3A%7B%22AWS_PROFILE%22%3A%22%5BThe%20AWS%20Profile%20Name%20to%20use%20for%20AWS%20access%5D%22%2C%22FASTMCP_LOG_LEVEL%22%3A%22ERROR%22%7D%2C%22transportType%22%3A%22stdio%22%7D) |
|
|
96
99
|
|
|
97
100
|
#### MCP Config (Q CLI, Cline)
|
|
98
101
|
* For Q CLI, update MCP Config Amazon Q Developer CLI (~/.aws/amazonq/mcp.json)
|
|
@@ -62,8 +62,11 @@ Alarm Recommendations - Suggests recommended alarm configurations for CloudWatch
|
|
|
62
62
|
1. Install `uv` from [Astral](https://docs.astral.sh/uv/getting-started/installation/) or the [GitHub README](https://github.com/astral-sh/uv#installation)
|
|
63
63
|
2. Install Python using `uv python install 3.10`
|
|
64
64
|
|
|
65
|
-
#### One Click
|
|
66
|
-
|
|
65
|
+
#### One Click Install
|
|
66
|
+
|
|
67
|
+
| Cursor | VS Code |
|
|
68
|
+
|:------:|:-------:|
|
|
69
|
+
| [](https://cursor.com/install-mcp?name=awslabs.cloudwatch-mcp-server&config=ewogICAgImF1dG9BcHByb3ZlIjogW10sCiAgICAiZGlzYWJsZWQiOiBmYWxzZSwKICAgICJjb21tYW5kIjogInV2eCBhd3NsYWJzLmNsb3Vkd2F0Y2gtbWNwLXNlcnZlckBsYXRlc3QiLAogICAgImVudiI6IHsKICAgICAgIkFXU19QUk9GSUxFIjogIltUaGUgQVdTIFByb2ZpbGUgTmFtZSB0byB1c2UgZm9yIEFXUyBhY2Nlc3NdIiwKICAgICAgIkZBU1RNQ1BfTE9HX0xFVkVMIjogIkVSUk9SIgogICAgfSwKICAgICJ0cmFuc3BvcnRUeXBlIjogInN0ZGlvIgp9) | [](https://insiders.vscode.dev/redirect/mcp/install?name=CloudWatch%20MCP%20Server&config=%7B%22autoApprove%22%3A%5B%5D%2C%22disabled%22%3Afalse%2C%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22awslabs.cloudwatch-mcp-server%40latest%22%5D%2C%22env%22%3A%7B%22AWS_PROFILE%22%3A%22%5BThe%20AWS%20Profile%20Name%20to%20use%20for%20AWS%20access%5D%22%2C%22FASTMCP_LOG_LEVEL%22%3A%22ERROR%22%7D%2C%22transportType%22%3A%22stdio%22%7D) |
|
|
67
70
|
|
|
68
71
|
#### MCP Config (Q CLI, Cline)
|
|
69
72
|
* For Q CLI, update MCP Config Amazon Q Developer CLI (~/.aws/amazonq/mcp.json)
|
|
@@ -451,6 +451,7 @@ class CloudWatchLogsTools:
|
|
|
451
451
|
query_string='pattern @message | sort @sampleCount desc | limit 5',
|
|
452
452
|
limit=5,
|
|
453
453
|
max_timeout=30,
|
|
454
|
+
region=region,
|
|
454
455
|
),
|
|
455
456
|
self.execute_log_insights_query(
|
|
456
457
|
ctx,
|
|
@@ -461,6 +462,7 @@ class CloudWatchLogsTools:
|
|
|
461
462
|
query_string='fields @timestamp, @message | filter @message like /(?i)(error|exception|fail|timeout|fatal)/ | pattern @message | limit 5',
|
|
462
463
|
limit=5,
|
|
463
464
|
max_timeout=30,
|
|
465
|
+
region=region,
|
|
464
466
|
),
|
|
465
467
|
)
|
|
466
468
|
|
{awslabs_cloudwatch_mcp_server-0.0.5 → awslabs_cloudwatch_mcp_server-0.0.7}/docker-healthcheck.sh
RENAMED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
|
-
|
|
3
2
|
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
4
3
|
#
|
|
5
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -14,13 +13,13 @@
|
|
|
14
13
|
# See the License for the specific language governing permissions and
|
|
15
14
|
# limitations under the License.
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
SERVER="cloudwatch-mcp-server"
|
|
17
|
+
|
|
18
|
+
# Check if the server process is running
|
|
19
|
+
if pgrep -P 0 -a -l -x -f "/app/.venv/bin/python3 /app/.venv/bin/awslabs.$SERVER" > /dev/null; then
|
|
20
|
+
echo -n "$SERVER is running";
|
|
19
21
|
exit 0;
|
|
20
|
-
else
|
|
21
|
-
echo -n "Zero awslabs.* streams found";
|
|
22
|
-
exit 1;
|
|
23
22
|
fi;
|
|
24
23
|
|
|
25
|
-
|
|
26
|
-
exit
|
|
24
|
+
# Unhealthy
|
|
25
|
+
exit 1;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "awslabs.cloudwatch-mcp-server"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.7"
|
|
4
4
|
description = "An AWS Labs Model Context Protocol (MCP) server for cloudwatch"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
7
7
|
dependencies = [
|
|
8
8
|
"boto3>=1.38.22",
|
|
9
9
|
"loguru>=0.7.0",
|
|
10
|
-
"mcp[cli]>=1.
|
|
10
|
+
"mcp[cli]>=1.11.0",
|
|
11
11
|
"pydantic>=2.10.6",
|
|
12
12
|
]
|
|
13
13
|
license = {text = "Apache-2.0"}
|
|
@@ -386,3 +386,151 @@ class TestEdgeCases:
|
|
|
386
386
|
assert params['logGroupNames'] is None
|
|
387
387
|
assert params['logGroupIdentifiers'] == ['arn:test']
|
|
388
388
|
assert params['limit'] is None
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
class TestRegionHandling:
|
|
392
|
+
"""Test region parameter handling across tools."""
|
|
393
|
+
|
|
394
|
+
def test_get_logs_client_region_parameter(self):
|
|
395
|
+
"""Test that _get_logs_client creates client with correct region."""
|
|
396
|
+
with patch(
|
|
397
|
+
'awslabs.cloudwatch_mcp_server.cloudwatch_logs.tools.boto3.Session'
|
|
398
|
+
) as mock_session:
|
|
399
|
+
mock_client = Mock()
|
|
400
|
+
mock_session.return_value.client.return_value = mock_client
|
|
401
|
+
|
|
402
|
+
tools = CloudWatchLogsTools()
|
|
403
|
+
result = tools._get_logs_client('ap-northeast-1')
|
|
404
|
+
|
|
405
|
+
# Verify session was created with correct region
|
|
406
|
+
mock_session.assert_called_with(region_name='ap-northeast-1')
|
|
407
|
+
mock_session.return_value.client.assert_called_with(
|
|
408
|
+
'logs', config=mock_session.return_value.client.call_args[1]['config']
|
|
409
|
+
)
|
|
410
|
+
assert result == mock_client
|
|
411
|
+
|
|
412
|
+
def test_get_logs_client_with_aws_profile(self):
|
|
413
|
+
"""Test _get_logs_client with AWS_PROFILE environment variable."""
|
|
414
|
+
with patch.dict('os.environ', {'AWS_PROFILE': 'test-profile'}):
|
|
415
|
+
with patch(
|
|
416
|
+
'awslabs.cloudwatch_mcp_server.cloudwatch_logs.tools.boto3.Session'
|
|
417
|
+
) as mock_session:
|
|
418
|
+
mock_client = Mock()
|
|
419
|
+
mock_session.return_value.client.return_value = mock_client
|
|
420
|
+
|
|
421
|
+
tools = CloudWatchLogsTools()
|
|
422
|
+
result = tools._get_logs_client('ca-central-1')
|
|
423
|
+
|
|
424
|
+
# Verify session was created with profile and region
|
|
425
|
+
mock_session.assert_called_with(
|
|
426
|
+
profile_name='test-profile', region_name='ca-central-1'
|
|
427
|
+
)
|
|
428
|
+
assert result == mock_client
|
|
429
|
+
|
|
430
|
+
@pytest.mark.asyncio
|
|
431
|
+
async def test_execute_log_insights_query_region_parameter(self, mock_context):
|
|
432
|
+
"""Test that execute_log_insights_query uses correct region for client creation."""
|
|
433
|
+
with patch(
|
|
434
|
+
'awslabs.cloudwatch_mcp_server.cloudwatch_logs.tools.boto3.Session'
|
|
435
|
+
) as mock_session:
|
|
436
|
+
mock_client = Mock()
|
|
437
|
+
mock_client.start_query.return_value = {'queryId': 'test-query-id'}
|
|
438
|
+
mock_client.get_query_results.return_value = {
|
|
439
|
+
'status': 'Complete',
|
|
440
|
+
'results': [],
|
|
441
|
+
'statistics': {},
|
|
442
|
+
}
|
|
443
|
+
mock_session.return_value.client.return_value = mock_client
|
|
444
|
+
|
|
445
|
+
tools = CloudWatchLogsTools()
|
|
446
|
+
|
|
447
|
+
# Mock _get_logs_client to capture the region parameter
|
|
448
|
+
with patch.object(
|
|
449
|
+
tools, '_get_logs_client', return_value=mock_client
|
|
450
|
+
) as mock_get_client:
|
|
451
|
+
await tools.execute_log_insights_query(
|
|
452
|
+
mock_context,
|
|
453
|
+
log_group_names=['test-group'],
|
|
454
|
+
log_group_identifiers=None,
|
|
455
|
+
start_time='2023-01-01T00:00:00+00:00',
|
|
456
|
+
end_time='2023-01-01T01:00:00+00:00',
|
|
457
|
+
query_string='fields @message',
|
|
458
|
+
region='ap-southeast-2',
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
# Verify _get_logs_client was called with correct region
|
|
462
|
+
mock_get_client.assert_called_once_with('ap-southeast-2')
|
|
463
|
+
|
|
464
|
+
@pytest.mark.asyncio
|
|
465
|
+
async def test_get_logs_insight_query_results_region_parameter(self, mock_context):
|
|
466
|
+
"""Test that get_logs_insight_query_results uses correct region for client creation."""
|
|
467
|
+
with patch(
|
|
468
|
+
'awslabs.cloudwatch_mcp_server.cloudwatch_logs.tools.boto3.Session'
|
|
469
|
+
) as mock_session:
|
|
470
|
+
mock_client = Mock()
|
|
471
|
+
mock_client.get_query_results.return_value = {
|
|
472
|
+
'status': 'Complete',
|
|
473
|
+
'results': [],
|
|
474
|
+
'statistics': {},
|
|
475
|
+
}
|
|
476
|
+
mock_session.return_value.client.return_value = mock_client
|
|
477
|
+
|
|
478
|
+
tools = CloudWatchLogsTools()
|
|
479
|
+
|
|
480
|
+
# Mock _get_logs_client to capture the region parameter
|
|
481
|
+
with patch.object(
|
|
482
|
+
tools, '_get_logs_client', return_value=mock_client
|
|
483
|
+
) as mock_get_client:
|
|
484
|
+
await tools.get_logs_insight_query_results(
|
|
485
|
+
mock_context, query_id='test-query-id', region='eu-central-1'
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
# Verify _get_logs_client was called with correct region
|
|
489
|
+
mock_get_client.assert_called_once_with('eu-central-1')
|
|
490
|
+
|
|
491
|
+
@pytest.mark.asyncio
|
|
492
|
+
async def test_cancel_logs_insight_query_region_parameter(self, mock_context):
|
|
493
|
+
"""Test that cancel_logs_insight_query uses correct region for client creation."""
|
|
494
|
+
with patch(
|
|
495
|
+
'awslabs.cloudwatch_mcp_server.cloudwatch_logs.tools.boto3.Session'
|
|
496
|
+
) as mock_session:
|
|
497
|
+
mock_client = Mock()
|
|
498
|
+
mock_client.stop_query.return_value = {'success': True}
|
|
499
|
+
mock_session.return_value.client.return_value = mock_client
|
|
500
|
+
|
|
501
|
+
tools = CloudWatchLogsTools()
|
|
502
|
+
|
|
503
|
+
# Mock _get_logs_client to capture the region parameter
|
|
504
|
+
with patch.object(
|
|
505
|
+
tools, '_get_logs_client', return_value=mock_client
|
|
506
|
+
) as mock_get_client:
|
|
507
|
+
await tools.cancel_logs_insight_query(
|
|
508
|
+
mock_context, query_id='test-query-id', region='sa-east-1'
|
|
509
|
+
)
|
|
510
|
+
|
|
511
|
+
# Verify _get_logs_client was called with correct region
|
|
512
|
+
mock_get_client.assert_called_once_with('sa-east-1')
|
|
513
|
+
|
|
514
|
+
@pytest.mark.asyncio
|
|
515
|
+
async def test_describe_log_groups_region_parameter(self, mock_context):
|
|
516
|
+
"""Test that describe_log_groups uses correct region for client creation."""
|
|
517
|
+
with patch(
|
|
518
|
+
'awslabs.cloudwatch_mcp_server.cloudwatch_logs.tools.boto3.Session'
|
|
519
|
+
) as mock_session:
|
|
520
|
+
mock_client = Mock()
|
|
521
|
+
mock_paginator = Mock()
|
|
522
|
+
mock_paginator.paginate.return_value = [{'logGroups': []}]
|
|
523
|
+
mock_client.get_paginator.return_value = mock_paginator
|
|
524
|
+
mock_client.describe_query_definitions.return_value = {'queryDefinitions': []}
|
|
525
|
+
mock_session.return_value.client.return_value = mock_client
|
|
526
|
+
|
|
527
|
+
tools = CloudWatchLogsTools()
|
|
528
|
+
|
|
529
|
+
# Mock _get_logs_client to capture the region parameter
|
|
530
|
+
with patch.object(
|
|
531
|
+
tools, '_get_logs_client', return_value=mock_client
|
|
532
|
+
) as mock_get_client:
|
|
533
|
+
await tools.describe_log_groups(mock_context, region='us-west-1')
|
|
534
|
+
|
|
535
|
+
# Verify _get_logs_client was called with correct region
|
|
536
|
+
mock_get_client.assert_called_once_with('us-west-1')
|
|
@@ -392,3 +392,63 @@ class TestAnalyzeLogGroup:
|
|
|
392
392
|
assert result.log_anomaly_results.anomaly_detectors[0].detectorName == 'test-detector'
|
|
393
393
|
assert 'results' in result.top_patterns
|
|
394
394
|
assert 'results' in result.top_patterns_containing_errors
|
|
395
|
+
|
|
396
|
+
async def test_analyze_log_group_region_parameter(self, ctx, cloudwatch_tools):
|
|
397
|
+
"""Test that analyze_log_group passes region parameter to execute_log_insights_query calls."""
|
|
398
|
+
log_group_arn = 'arn:aws:logs:eu-west-1:123456789012:log-group:/aws/test/group1'
|
|
399
|
+
|
|
400
|
+
# Mock anomaly detection
|
|
401
|
+
cloudwatch_tools.logs_client.get_paginator = MagicMock()
|
|
402
|
+
|
|
403
|
+
# Mock list_log_anomaly_detectors paginator
|
|
404
|
+
anomaly_paginator = MagicMock()
|
|
405
|
+
anomaly_paginator.paginate.return_value = [{'anomalyDetectors': []}]
|
|
406
|
+
|
|
407
|
+
# Mock list_anomalies paginator
|
|
408
|
+
anomalies_paginator = MagicMock()
|
|
409
|
+
anomalies_paginator.paginate.return_value = [{'anomalies': []}]
|
|
410
|
+
|
|
411
|
+
def get_paginator_side_effect(operation_name):
|
|
412
|
+
if operation_name == 'list_log_anomaly_detectors':
|
|
413
|
+
return anomaly_paginator
|
|
414
|
+
elif operation_name == 'list_anomalies':
|
|
415
|
+
return anomalies_paginator
|
|
416
|
+
else:
|
|
417
|
+
return MagicMock()
|
|
418
|
+
|
|
419
|
+
cloudwatch_tools.logs_client.get_paginator.side_effect = get_paginator_side_effect
|
|
420
|
+
|
|
421
|
+
# Mock execute_log_insights_query to capture the region parameter
|
|
422
|
+
executed_queries = []
|
|
423
|
+
|
|
424
|
+
async def mock_execute_query(*args, **kwargs):
|
|
425
|
+
executed_queries.append(kwargs)
|
|
426
|
+
return {
|
|
427
|
+
'queryId': 'test-query-id',
|
|
428
|
+
'status': 'Complete',
|
|
429
|
+
'results': [{'@message': 'Test pattern', '@sampleCount': '10'}],
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
# Patch the execute_log_insights_query method
|
|
433
|
+
with patch.object(
|
|
434
|
+
cloudwatch_tools, 'execute_log_insights_query', side_effect=mock_execute_query
|
|
435
|
+
):
|
|
436
|
+
# Call analyze_log_group with specific region
|
|
437
|
+
await cloudwatch_tools.analyze_log_group(
|
|
438
|
+
ctx,
|
|
439
|
+
log_group_arn=log_group_arn,
|
|
440
|
+
start_time='2023-01-01T00:00:00+00:00',
|
|
441
|
+
end_time='2023-01-01T01:00:00+00:00',
|
|
442
|
+
region='eu-west-1',
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
# Verify that both execute_log_insights_query calls received the correct region
|
|
446
|
+
assert len(executed_queries) == 2
|
|
447
|
+
assert executed_queries[0]['region'] == 'eu-west-1'
|
|
448
|
+
assert executed_queries[1]['region'] == 'eu-west-1'
|
|
449
|
+
|
|
450
|
+
# Verify other parameters are passed correctly
|
|
451
|
+
for query in executed_queries:
|
|
452
|
+
assert query['log_group_identifiers'] == [log_group_arn]
|
|
453
|
+
assert query['start_time'] == '2023-01-01T00:00:00+00:00'
|
|
454
|
+
assert query['end_time'] == '2023-01-01T01:00:00+00:00'
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#
|
|
2
|
+
# This file is autogenerated by pip-compile with Python 3.10
|
|
3
|
+
# by the following command:
|
|
4
|
+
#
|
|
5
|
+
# pip-compile --generate-hashes --output-file=uv-requirements.txt --strip-extras uv-requirements-0.7.13.in
|
|
6
|
+
#
|
|
7
|
+
uv==0.7.13 \
|
|
8
|
+
--hash=sha256:05f3c03c4ea55d294f3da725b6c2c2ff544754c18552da7594def4ec3889dcfb \
|
|
9
|
+
--hash=sha256:1afdbfcabc3425b383141ba42d413841c0a48b9ee0f4da65459313275e3cea84 \
|
|
10
|
+
--hash=sha256:33837aca7bdf02d47554d5d44f9e71756ee17c97073b07b4afead25309855bc7 \
|
|
11
|
+
--hash=sha256:4efa555b217e15767f0691a51d435f7bb2b0bf473fdfd59f173aeda8a93b8d17 \
|
|
12
|
+
--hash=sha256:4f828174e15a557d3bc0f809de76135c3b66bcbf524657f8ced9d22fc978b89c \
|
|
13
|
+
--hash=sha256:527a12d0c2f4d15f72b275b6f4561ae92af76dd59b4624796fddd45867f13c33 \
|
|
14
|
+
--hash=sha256:5786a29e286f2cc3cbda13a357fd9a4dd5bf1d7448a9d3d842b26b4f784a3a86 \
|
|
15
|
+
--hash=sha256:59915aec9fd2b845708a76ddc6c0639cfc99b6e2811854ea2425ee7552aff0e9 \
|
|
16
|
+
--hash=sha256:721b058064150fc1c6d88e277af093d1b4f8bb7a59546fe9969d9ff7dbe3f6fd \
|
|
17
|
+
--hash=sha256:866cad0d04a7de1aaa3c5cbef203f9d3feef9655972dcccc3283d60122db743b \
|
|
18
|
+
--hash=sha256:88fcf2bfbb53309531a850af50d2ea75874099b19d4159625d0b4f88c53494b9 \
|
|
19
|
+
--hash=sha256:8c0c29a2089ff9011d6c3abccd272f3ee6d0e166dae9e5232099fd83d26104d9 \
|
|
20
|
+
--hash=sha256:9c457a84cfbe2019ba301e14edd3e1c950472abd0b87fc77622ab3fc475ba012 \
|
|
21
|
+
--hash=sha256:9d2952a1e74c7027347c74cee1cb2be09121a5290db38498b8b17ff585f73748 \
|
|
22
|
+
--hash=sha256:a51006c7574e819308d92a3452b22d5bd45ef8593a4983b5856aa7cb8220885f \
|
|
23
|
+
--hash=sha256:b1af81e57d098b21b28f42ec756f0e26dce2341d59ba4e4f11759bc3ca2c0a99 \
|
|
24
|
+
--hash=sha256:e077dcac19e564cae8b4223b7807c2f617a59938f8142ca77fc6348ae9c6d0aa \
|
|
25
|
+
--hash=sha256:f28e70baadfebe71dcc2d9505059b988d75e903fc62258b102eb87dc4b6994a3
|
|
26
|
+
# via -r uv-requirements-0.7.13.in (contents of `uv==0.7.13`)
|