awslabs.cloudwatch-mcp-server 0.0.16__tar.gz → 0.0.17__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 (51) hide show
  1. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/Dockerfile +19 -22
  2. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/PKG-INFO +1 -1
  3. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/awslabs/cloudwatch_mcp_server/__init__.py +1 -1
  4. awslabs_cloudwatch_mcp_server-0.0.17/awslabs/cloudwatch_mcp_server/aws_common.py +55 -0
  5. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/awslabs/cloudwatch_mcp_server/cloudwatch_alarms/tools.py +29 -29
  6. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/awslabs/cloudwatch_mcp_server/cloudwatch_logs/tools.py +62 -49
  7. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/awslabs/cloudwatch_mcp_server/cloudwatch_metrics/tools.py +39 -39
  8. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/docker-healthcheck.sh +1 -1
  9. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/pyproject.toml +1 -1
  10. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/tests/cloudwatch_alarms/test_active_alarms.py +15 -43
  11. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/tests/cloudwatch_alarms/test_alarm_history.py +13 -50
  12. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/tests/cloudwatch_alarms/test_alarm_history_integration.py +9 -25
  13. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/tests/cloudwatch_alarms/test_alarms_error_handling.py +20 -28
  14. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/tests/cloudwatch_logs/test_logs_error_handling.py +55 -120
  15. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/tests/cloudwatch_logs/test_logs_server.py +30 -38
  16. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/tests/cloudwatch_metrics/test_analyze_metric.py +33 -17
  17. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/tests/cloudwatch_metrics/test_metrics_error_handling.py +31 -36
  18. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/tests/cloudwatch_metrics/test_metrics_server.py +37 -27
  19. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/tests/cloudwatch_metrics/test_validation_error.py +1 -3
  20. awslabs_cloudwatch_mcp_server-0.0.17/tests/test_aws_common.py +185 -0
  21. awslabs_cloudwatch_mcp_server-0.0.17/uv-requirements.txt +23 -0
  22. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/uv.lock +8 -7
  23. awslabs_cloudwatch_mcp_server-0.0.16/uv-requirements.txt +0 -27
  24. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/.gitignore +0 -0
  25. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/.python-version +0 -0
  26. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/CHANGELOG.md +0 -0
  27. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/LICENSE +0 -0
  28. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/NOTICE +0 -0
  29. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/README.md +0 -0
  30. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/awslabs/__init__.py +0 -0
  31. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/awslabs/cloudwatch_mcp_server/cloudwatch_alarms/models.py +0 -0
  32. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/awslabs/cloudwatch_mcp_server/cloudwatch_logs/models.py +0 -0
  33. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/awslabs/cloudwatch_mcp_server/cloudwatch_metrics/cloudformation_template_generator.py +0 -0
  34. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/awslabs/cloudwatch_mcp_server/cloudwatch_metrics/constants.py +0 -0
  35. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/awslabs/cloudwatch_mcp_server/cloudwatch_metrics/data/metric_metadata.json +0 -0
  36. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/awslabs/cloudwatch_mcp_server/cloudwatch_metrics/metric_analyzer.py +0 -0
  37. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/awslabs/cloudwatch_mcp_server/cloudwatch_metrics/metric_data_decomposer.py +0 -0
  38. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/awslabs/cloudwatch_mcp_server/cloudwatch_metrics/models.py +0 -0
  39. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/awslabs/cloudwatch_mcp_server/common.py +0 -0
  40. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/awslabs/cloudwatch_mcp_server/server.py +0 -0
  41. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/tests/cloudwatch_logs/test_logs_models.py +0 -0
  42. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/tests/cloudwatch_metrics/test_cloudformation_template_generator.py +0 -0
  43. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/tests/cloudwatch_metrics/test_decomposer_trend.py +0 -0
  44. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/tests/cloudwatch_metrics/test_metric_analyzer.py +0 -0
  45. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/tests/cloudwatch_metrics/test_metrics_models.py +0 -0
  46. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/tests/cloudwatch_metrics/test_seasonal_detector.py +0 -0
  47. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/tests/cloudwatch_metrics/test_seasonality_enum.py +0 -0
  48. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/tests/cloudwatch_metrics/test_utils.py +0 -0
  49. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/tests/test_common_and_server.py +0 -0
  50. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/tests/test_init.py +0 -0
  51. {awslabs_cloudwatch_mcp_server-0.0.16 → awslabs_cloudwatch_mcp_server-0.0.17}/tests/test_main.py +0 -0
@@ -13,7 +13,11 @@
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-alpine@sha256:e7e041128ffc3e3600509f508e44d34ab08ff432bdb62ec508d01dfc5ca459f7 AS uv
16
+ FROM public.ecr.aws/amazonlinux/amazonlinux@sha256:e27a70c006c68f0d194cc9b9624714d6ed8d979a94f60f7d31392f4c8294155b AS uv
17
+
18
+ # Install build dependencies needed for compiling packages
19
+ RUN dnf install -y shadow-utils python3 python3-devel gcc && \
20
+ dnf clean all
17
21
 
18
22
  # Install the project into `/app`
19
23
  WORKDIR /app
@@ -25,7 +29,7 @@ ENV UV_COMPILE_BYTECODE=1
25
29
  ENV UV_LINK_MODE=copy
26
30
 
27
31
  # Prefer the system python
28
- ENV UV_PYTHON_PREFERENCE=only-system
32
+ ENV UV_PYTHON_PREFERENCE=only-managed
29
33
 
30
34
  # Run without updating the uv.lock file like running with `--frozen`
31
35
  ENV UV_FROZEN=true
@@ -37,19 +41,10 @@ COPY pyproject.toml uv.lock uv-requirements.txt ./
37
41
  ENV PIP_NO_CACHE_DIR=1 \
38
42
  PIP_DISABLE_PIP_VERSION_CHECK=1
39
43
 
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
49
-
50
44
  # Install the project's dependencies using the lockfile and settings
51
45
  RUN --mount=type=cache,target=/root/.cache/uv \
52
- pip install --require-hashes --requirement uv-requirements.txt --no-cache-dir && \
46
+ python3 -m ensurepip && \
47
+ python3 -m pip install --require-hashes --requirement uv-requirements.txt --no-cache-dir && \
53
48
  uv sync --python 3.13 --frozen --no-install-project --no-dev --no-editable
54
49
 
55
50
  # Then, add the rest of the project source code and install it
@@ -61,20 +56,22 @@ RUN --mount=type=cache,target=/root/.cache/uv \
61
56
  # Make the directory just in case it doesn't exist
62
57
  RUN mkdir -p /root/.local
63
58
 
64
- FROM public.ecr.aws/docker/library/python:3.13-alpine@sha256:e7e041128ffc3e3600509f508e44d34ab08ff432bdb62ec508d01dfc5ca459f7
59
+ FROM public.ecr.aws/amazonlinux/amazonlinux@sha256:e27a70c006c68f0d194cc9b9624714d6ed8d979a94f60f7d31392f4c8294155b
65
60
 
66
61
  # Place executables in the environment at the front of the path and include other binaries
67
- ENV PATH="/app/.venv/bin:$PATH" \
62
+ ENV PATH="/app/.venv/bin:$PATH:/usr/sbin" \
68
63
  PYTHONUNBUFFERED=1
69
64
 
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
65
+ # Install other tools as needed for the MCP server
66
+ # Add non-root user and ability to change directory into /root
67
+ RUN dnf install -y shadow-utils procps && \
68
+ dnf clean all && \
69
+ groupadd --force --system app && \
70
+ useradd app -g app -d /app && \
71
+ chmod o+x /root
76
72
 
77
- # Copy application artifacts from build stage
73
+ # Get the project from the uv layer
74
+ COPY --from=uv --chown=app:app /root/.local /root/.local
78
75
  COPY --from=uv --chown=app:app /app/.venv /app/.venv
79
76
 
80
77
  # Get healthcheck script
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: awslabs.cloudwatch-mcp-server
3
- Version: 0.0.16
3
+ Version: 0.0.17
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/
@@ -14,5 +14,5 @@
14
14
 
15
15
  """awslabs.cloudwatch-mcp-server"""
16
16
 
17
- __version__ = '0.0.16'
17
+ __version__ = '0.0.17'
18
18
  MCP_SERVER_VERSION = __version__
@@ -0,0 +1,55 @@
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
+ """AWS client utilities for CloudWatch MCP Server with multi-profile support."""
16
+
17
+ from awslabs.cloudwatch_mcp_server import MCP_SERVER_VERSION
18
+ from boto3 import Session
19
+ from botocore.config import Config
20
+ from os import getenv
21
+
22
+
23
+ def get_aws_client(
24
+ service_name: str,
25
+ region_name: str | None = None,
26
+ profile_name: str | None = None,
27
+ ):
28
+ """AWS Client handler with multi-profile support.
29
+
30
+ Args:
31
+ service_name: AWS service name (e.g., 'logs', 'cloudwatch')
32
+ region_name: AWS region. Defaults to AWS_REGION env var or us-east-1 if not set
33
+ profile_name: AWS CLI profile name. Falls back to AWS_PROFILE env var if not specified,
34
+ or uses default AWS credential chain
35
+
36
+ Returns:
37
+ boto3 client for the specified service
38
+ """
39
+ # Set profile from parameter or environment
40
+ if profile_name is None:
41
+ profile_name = getenv('AWS_PROFILE', None)
42
+
43
+ # Configure user agent
44
+ config = Config(user_agent_extra=f'awslabs/mcp/cloudwatch-mcp-server/{MCP_SERVER_VERSION}')
45
+
46
+ # Create session with or without profile
47
+ if profile_name:
48
+ session = Session(profile_name=profile_name)
49
+ else:
50
+ session = Session()
51
+
52
+ # Use provided region, or session's region, or fallback to us-east-1
53
+ region = region_name or session.region_name or 'us-east-1'
54
+
55
+ return session.client(service_name, region_name=region, config=config)
@@ -14,10 +14,8 @@
14
14
 
15
15
  """CloudWatch Alarms tools for MCP server."""
16
16
 
17
- import boto3
18
17
  import json
19
- import os
20
- from awslabs.cloudwatch_mcp_server import MCP_SERVER_VERSION
18
+ from awslabs.cloudwatch_mcp_server.aws_common import get_aws_client
21
19
  from awslabs.cloudwatch_mcp_server.cloudwatch_alarms.models import (
22
20
  ActiveAlarmsResponse,
23
21
  AlarmDetails,
@@ -28,7 +26,6 @@ from awslabs.cloudwatch_mcp_server.cloudwatch_alarms.models import (
28
26
  MetricAlarmSummary,
29
27
  TimeRangeSuggestion,
30
28
  )
31
- from botocore.config import Config
32
29
  from datetime import datetime, timedelta
33
30
  from loguru import logger
34
31
  from mcp.server.fastmcp import Context
@@ -43,21 +40,6 @@ class CloudWatchAlarmsTools:
43
40
  """Initialize the CloudWatch Alarms tools."""
44
41
  pass
45
42
 
46
- def _get_cloudwatch_client(self, region: str):
47
- """Create a CloudWatch client for the specified region."""
48
- config = Config(user_agent_extra=f'awslabs/mcp/cloudwatch-mcp-server/{MCP_SERVER_VERSION}')
49
-
50
- try:
51
- if aws_profile := os.environ.get('AWS_PROFILE'):
52
- return boto3.Session(profile_name=aws_profile, region_name=region).client(
53
- 'cloudwatch', config=config
54
- )
55
- else:
56
- return boto3.Session(region_name=region).client('cloudwatch', config=config)
57
- except Exception as e:
58
- logger.error(f'Error creating cloudwatch client for region {region}: {str(e)}')
59
- raise
60
-
61
43
  def register(self, mcp):
62
44
  """Register all CloudWatch Alarms tools with the MCP server."""
63
45
  # Register get_active_alarms tool
@@ -76,9 +58,17 @@ class CloudWatchAlarmsTools:
76
58
  ),
77
59
  ] = 50,
78
60
  region: Annotated[
79
- str,
80
- Field(description='AWS region to query. Defaults to us-east-1.'),
81
- ] = 'us-east-1',
61
+ str | None,
62
+ Field(
63
+ description='AWS region to query. Defaults to AWS_REGION environment variable or us-east-1 if not set.'
64
+ ),
65
+ ] = None,
66
+ profile_name: Annotated[
67
+ str | None,
68
+ Field(
69
+ description='AWS CLI Profile Name to use for AWS access. Falls back to AWS_PROFILE environment variable if not specified, or uses default AWS credential chain.'
70
+ ),
71
+ ] = None,
82
72
  ) -> ActiveAlarmsResponse:
83
73
  """Gets all CloudWatch Alarms currently in ALARM state.
84
74
 
@@ -92,7 +82,8 @@ class CloudWatchAlarmsTools:
92
82
  Args:
93
83
  ctx: The MCP context object for error handling and logging.
94
84
  max_items: Maximum number of alarms to return (default: 50).
95
- region: AWS region to query. Defaults to 'us-east-1'.
85
+ region: AWS region to query. Defaults to AWS_REGION environment variable or us-east-1 if not set.
86
+ profile_name: AWS CLI Profile Name to use for AWS access. Falls back to AWS_PROFILE environment variable if not specified, or uses default AWS credential chain.
96
87
 
97
88
  Returns:
98
89
  ActiveAlarmsResponse: Response containing active alarms.
@@ -118,7 +109,7 @@ class CloudWatchAlarmsTools:
118
109
  raise ValueError('max_items must be at least 1')
119
110
 
120
111
  # Create CloudWatch client for the specified region
121
- cloudwatch_client = self._get_cloudwatch_client(region)
112
+ cloudwatch_client = get_aws_client('cloudwatch', region, profile_name)
122
113
 
123
114
  # Fetch active alarms using paginator
124
115
  logger.info(f'Fetching up to {max_items} active alarms')
@@ -220,9 +211,17 @@ class CloudWatchAlarmsTools:
220
211
  ),
221
212
  ] = False,
222
213
  region: Annotated[
223
- str,
224
- Field(description='AWS region to query. Defaults to us-east-1.'),
225
- ] = 'us-east-1',
214
+ str | None,
215
+ Field(
216
+ description='AWS region to query. Defaults to AWS_REGION environment variable or us-east-1 if not set.'
217
+ ),
218
+ ] = None,
219
+ profile_name: Annotated[
220
+ str | None,
221
+ Field(
222
+ description='AWS CLI Profile Name to use for AWS access. Falls back to AWS_PROFILE environment variable if not specified, or uses default AWS credential chain.'
223
+ ),
224
+ ] = None,
226
225
  ) -> Union[AlarmHistoryResponse, CompositeAlarmComponentResponse]:
227
226
  """Gets the history for a CloudWatch alarm with time range suggestions for investigation.
228
227
 
@@ -237,13 +236,14 @@ class CloudWatchAlarmsTools:
237
236
 
238
237
  Args:
239
238
  ctx: The MCP context object for error handling and logging.
240
- region: AWS region to query. Defaults to 'us-east-1'.
239
+ region: AWS region to query. Defaults to AWS_REGION environment variable or us-east-1 if not set.
241
240
  alarm_name: Name of the alarm to retrieve history for.
242
241
  start_time: Optional start time for the history query. Defaults to 24 hours ago.
243
242
  end_time: Optional end time for the history query. Defaults to current time.
244
243
  history_item_type: Optional type of history items to retrieve. Defaults to 'StateUpdate'.
245
244
  max_items: Maximum number of history items to return. Defaults to 50.
246
245
  include_component_alarms: For composite alarms, whether to include details about component alarms.
246
+ profile_name: AWS CLI Profile Name to use for AWS access. Falls back to AWS_PROFILE environment variable if not specified, or uses default AWS credential chain.
247
247
 
248
248
  Returns:
249
249
  Union[AlarmHistoryResponse, CompositeAlarmComponentResponse]: Either a response containing
@@ -271,7 +271,7 @@ class CloudWatchAlarmsTools:
271
271
  history_item_type = 'StateUpdate'
272
272
 
273
273
  # Create CloudWatch client for the specified region
274
- cloudwatch_client = self._get_cloudwatch_client(region)
274
+ cloudwatch_client = get_aws_client('cloudwatch', region, profile_name)
275
275
 
276
276
  # Set up default time range (last 24 hours)
277
277
  if end_time is None or not isinstance(end_time, str):
@@ -15,10 +15,8 @@
15
15
  """CloudWatch Logs tools for MCP server."""
16
16
 
17
17
  import asyncio
18
- import boto3
19
18
  import datetime
20
- import os
21
- from awslabs.cloudwatch_mcp_server import MCP_SERVER_VERSION
19
+ from awslabs.cloudwatch_mcp_server.aws_common import get_aws_client
22
20
  from awslabs.cloudwatch_mcp_server.cloudwatch_logs.models import (
23
21
  LogAnomaly,
24
22
  LogAnomalyDetector,
@@ -34,7 +32,6 @@ from awslabs.cloudwatch_mcp_server.common import (
34
32
  filter_by_prefixes,
35
33
  remove_null_values,
36
34
  )
37
- from botocore.config import Config
38
35
  from loguru import logger
39
36
  from mcp.server.fastmcp import Context
40
37
  from pydantic import Field
@@ -47,31 +44,7 @@ class CloudWatchLogsTools:
47
44
 
48
45
  def __init__(self):
49
46
  """Initialize the CloudWatch Logs tools."""
50
- self._logs_client = None
51
- self._logs_client_region = None
52
-
53
- @property
54
- def logs_client(self):
55
- """Get the logs client for the default region (us-east-1)."""
56
- if self._logs_client is None or self._logs_client_region != 'us-east-1':
57
- self._logs_client = self._get_logs_client('us-east-1')
58
- self._logs_client_region = 'us-east-1'
59
- return self._logs_client
60
-
61
- def _get_logs_client(self, region: str):
62
- """Create a CloudWatch Logs client for the specified region."""
63
- config = Config(user_agent_extra=f'awslabs/mcp/cloudwatch-mcp-server/{MCP_SERVER_VERSION}')
64
-
65
- try:
66
- if aws_profile := os.environ.get('AWS_PROFILE'):
67
- return boto3.Session(profile_name=aws_profile, region_name=region).client(
68
- 'logs', config=config
69
- )
70
- else:
71
- return boto3.Session(region_name=region).client('logs', config=config)
72
- except Exception as e:
73
- logger.error(f'Error creating cloudwatch logs client for region {region}: {str(e)}')
74
- raise
47
+ pass
75
48
 
76
49
  def _validate_log_group_parameters(
77
50
  self, log_group_names: Optional[List[str]], log_group_identifiers: Optional[List[str]]
@@ -267,9 +240,17 @@ class CloudWatchLogsTools:
267
240
  int | None, Field(description=('The maximum number of log groups to return.'))
268
241
  ] = None,
269
242
  region: Annotated[
270
- str,
271
- Field(description='AWS region to query. Defaults to us-east-1.'),
272
- ] = 'us-east-1',
243
+ str | None,
244
+ Field(
245
+ description='AWS region to query. Defaults to AWS_REGION environment variable or us-east-1 if not set.'
246
+ ),
247
+ ] = None,
248
+ profile_name: Annotated[
249
+ str | None,
250
+ Field(
251
+ description='AWS CLI Profile Name to use for AWS access. Falls back to AWS_PROFILE environment variable if not specified, or uses default AWS credential chain.'
252
+ ),
253
+ ] = None,
273
254
  ) -> LogsMetadata:
274
255
  """Lists AWS CloudWatch log groups and saved queries associated with them, optionally filtering by a name prefix.
275
256
 
@@ -295,7 +276,7 @@ class CloudWatchLogsTools:
295
276
  Any saved queries that are applicable to the returned log groups are also included.
296
277
  """
297
278
  # Create logs client for the specified region
298
- logs_client = self._get_logs_client(region)
279
+ logs_client = get_aws_client('logs', region, profile_name)
299
280
 
300
281
  def describe_log_groups() -> List[LogGroupMetadata]:
301
282
  paginator = logs_client.get_paginator('describe_log_groups')
@@ -379,9 +360,17 @@ class CloudWatchLogsTools:
379
360
  ),
380
361
  ),
381
362
  region: Annotated[
382
- str,
383
- Field(description='AWS region to query. Defaults to us-east-1.'),
384
- ] = 'us-east-1',
363
+ str | None,
364
+ Field(
365
+ description='AWS region to query. Defaults to AWS_REGION environment variable or us-east-1 if not set.'
366
+ ),
367
+ ] = None,
368
+ profile_name: Annotated[
369
+ str | None,
370
+ Field(
371
+ description='AWS CLI Profile Name to use for AWS access. Falls back to AWS_PROFILE environment variable if not specified, or uses default AWS credential chain.'
372
+ ),
373
+ ] = None,
385
374
  ) -> LogsAnalysisResult:
386
375
  """Analyzes a CloudWatch log group for anomalies, message patterns, and error patterns within a specified time window.
387
376
 
@@ -426,7 +415,7 @@ class CloudWatchLogsTools:
426
415
  return log_group_arn in anomaly.logGroupArnList
427
416
 
428
417
  # Create logs client for the specified region
429
- logs_client = self._get_logs_client(region)
418
+ logs_client = get_aws_client('logs', region, profile_name)
430
419
 
431
420
  async def get_applicable_anomalies() -> LogAnomalyResults:
432
421
  detectors: List[LogAnomalyDetector] = []
@@ -552,9 +541,17 @@ class CloudWatchLogsTools:
552
541
  ),
553
542
  ] = 30,
554
543
  region: Annotated[
555
- str,
556
- Field(description='AWS region to query. Defaults to us-east-1.'),
557
- ] = 'us-east-1',
544
+ str | None,
545
+ Field(
546
+ description='AWS region to query. Defaults to AWS_REGION environment variable or us-east-1 if not set.'
547
+ ),
548
+ ] = None,
549
+ profile_name: Annotated[
550
+ str | None,
551
+ Field(
552
+ description='AWS CLI Profile Name to use for AWS access. Falls back to AWS_PROFILE environment variable if not specified, or uses default AWS credential chain.'
553
+ ),
554
+ ] = None,
558
555
  ) -> Dict:
559
556
  """Executes a CloudWatch Logs Insights query and waits for the results to be available.
560
557
 
@@ -590,7 +587,7 @@ class CloudWatchLogsTools:
590
587
  )
591
588
 
592
589
  # Create logs client for the specified region
593
- logs_client = self._get_logs_client(region)
590
+ logs_client = get_aws_client('logs', region, profile_name)
594
591
 
595
592
  # Start the query
596
593
  start_response = logs_client.start_query(**remove_null_values(kwargs))
@@ -621,9 +618,17 @@ class CloudWatchLogsTools:
621
618
  description='The unique ID of the query to retrieve the results for. CRITICAL: This ID is returned by the execute_log_insights_query tool.',
622
619
  ),
623
620
  region: Annotated[
624
- str,
625
- Field(description='AWS region to query. Defaults to us-east-1.'),
626
- ] = 'us-east-1',
621
+ str | None,
622
+ Field(
623
+ description='AWS region to query. Defaults to AWS_REGION environment variable or us-east-1 if not set.'
624
+ ),
625
+ ] = None,
626
+ profile_name: Annotated[
627
+ str | None,
628
+ Field(
629
+ description='AWS CLI Profile Name to use for AWS access. Falls back to AWS_PROFILE environment variable if not specified, or uses default AWS credential chain.'
630
+ ),
631
+ ] = None,
627
632
  ) -> Dict:
628
633
  """Retrieves the results of a previously started CloudWatch Logs Insights query.
629
634
 
@@ -640,7 +645,7 @@ class CloudWatchLogsTools:
640
645
  """
641
646
  try:
642
647
  # Create logs client for the specified region
643
- logs_client = self._get_logs_client(region)
648
+ logs_client = get_aws_client('logs', region, profile_name)
644
649
 
645
650
  response = logs_client.get_query_results(queryId=query_id)
646
651
 
@@ -675,9 +680,17 @@ class CloudWatchLogsTools:
675
680
  description='The unique ID of the ongoing query to cancel. CRITICAL: This ID is returned by the execute_log_insights_query tool.',
676
681
  ),
677
682
  region: Annotated[
678
- str,
679
- Field(description='AWS region to query. Defaults to us-east-1.'),
680
- ] = 'us-east-1',
683
+ str | None,
684
+ Field(
685
+ description='AWS region to query. Defaults to AWS_REGION environment variable or us-east-1 if not set.'
686
+ ),
687
+ ] = None,
688
+ profile_name: Annotated[
689
+ str | None,
690
+ Field(
691
+ description='AWS CLI Profile Name to use for AWS access. Falls back to AWS_PROFILE environment variable if not specified, or uses default AWS credential chain.'
692
+ ),
693
+ ] = None,
681
694
  ) -> LogsQueryCancelResult:
682
695
  """Cancels an ongoing CloudWatch Logs Insights query. If the query has already ended, returns an error that the given query is not running.
683
696
 
@@ -690,7 +703,7 @@ class CloudWatchLogsTools:
690
703
  """
691
704
  try:
692
705
  # Create logs client for the specified region
693
- logs_client = self._get_logs_client(region)
706
+ logs_client = get_aws_client('logs', region, profile_name)
694
707
 
695
708
  response = logs_client.stop_query(queryId=query_id)
696
709
  return LogsQueryCancelResult.model_validate(response)
@@ -14,10 +14,8 @@
14
14
 
15
15
  """CloudWatch Metrics tools for MCP server."""
16
16
 
17
- import boto3
18
17
  import json
19
- import os
20
- from awslabs.cloudwatch_mcp_server import MCP_SERVER_VERSION
18
+ from awslabs.cloudwatch_mcp_server.aws_common import get_aws_client
21
19
  from awslabs.cloudwatch_mcp_server.cloudwatch_metrics.cloudformation_template_generator import (
22
20
  CloudFormationTemplateGenerator,
23
21
  )
@@ -42,7 +40,6 @@ from awslabs.cloudwatch_mcp_server.cloudwatch_metrics.models import (
42
40
  MetricMetadataIndexKey,
43
41
  StaticAlarmThreshold,
44
42
  )
45
- from botocore.config import Config
46
43
  from datetime import datetime, timedelta, timezone
47
44
  from loguru import logger
48
45
  from mcp.server.fastmcp import Context
@@ -64,21 +61,6 @@ class CloudWatchMetricsTools:
64
61
  self.cloudformation_generator = CloudFormationTemplateGenerator()
65
62
  self.metric_analyzer = MetricAnalyzer()
66
63
 
67
- def _get_cloudwatch_client(self, region: str):
68
- """Create a CloudWatch client for the specified region."""
69
- config = Config(user_agent_extra=f'awslabs/mcp/cloudwatch-mcp-server/{MCP_SERVER_VERSION}')
70
-
71
- try:
72
- if aws_profile := os.environ.get('AWS_PROFILE'):
73
- return boto3.Session(profile_name=aws_profile, region_name=region).client(
74
- 'cloudwatch', config=config
75
- )
76
- else:
77
- return boto3.Session(region_name=region).client('cloudwatch', config=config)
78
- except Exception as e:
79
- logger.error(f'Error creating cloudwatch client for region {region}: {str(e)}')
80
- raise
81
-
82
64
  def _load_and_index_metadata(self) -> Dict[MetricMetadataIndexKey, Any]:
83
65
  """Load metric metadata from JSON file and create an indexed structure.
84
66
 
@@ -225,9 +207,17 @@ class CloudWatchMetricsTools:
225
207
  ),
226
208
  ] = None,
227
209
  region: Annotated[
228
- str,
229
- Field(description='AWS region to query. Defaults to us-east-1.'),
230
- ] = 'us-east-1',
210
+ str | None,
211
+ Field(
212
+ description='AWS region to query. Defaults to AWS_REGION environment variable or us-east-1 if not set.'
213
+ ),
214
+ ] = None,
215
+ profile_name: Annotated[
216
+ str | None,
217
+ Field(
218
+ description='AWS CLI Profile Name to use for AWS access. Falls back to AWS_PROFILE environment variable if not specified, or uses default AWS credential chain.'
219
+ ),
220
+ ] = None,
231
221
  ) -> GetMetricDataResponse:
232
222
  """Retrieves CloudWatch metric data for a specific metric.
233
223
 
@@ -371,7 +361,7 @@ class CloudWatchMetricsTools:
371
361
  )
372
362
 
373
363
  # Create CloudWatch client for the specified region
374
- cloudwatch_client = self._get_cloudwatch_client(region)
364
+ cloudwatch_client = get_aws_client('cloudwatch', region, profile_name)
375
365
 
376
366
  # Call the GetMetricData API
377
367
  response = cloudwatch_client.get_metric_data(
@@ -610,18 +600,13 @@ class CloudWatchMetricsTools:
610
600
  metric_name: str = Field(
611
601
  ..., description="The name of the metric (e.g., 'CPUUtilization', 'Duration')"
612
602
  ),
613
- region: Annotated[
614
- str,
615
- Field(
616
- description='AWS region for consistency. Note: This function uses local metadata and does not make AWS API calls. Defaults to us-east-1.'
617
- ),
618
- ] = 'us-east-1',
619
603
  ) -> Optional[MetricMetadata]:
620
604
  """Gets metadata for a CloudWatch metric including description, unit and recommended
621
605
  statistics that can be used for metric data retrieval.
622
606
 
623
607
  This tool retrieves comprehensive metadata about a specific CloudWatch metric
624
- identified by its namespace and metric name.
608
+ identified by its namespace and metric name. Note: This function uses local metadata
609
+ and does not make AWS API calls.
625
610
 
626
611
  Usage: Use this tool to get detailed information about CloudWatch metrics,
627
612
  including their descriptions, units, and recommended statistics to use.
@@ -630,7 +615,6 @@ class CloudWatchMetricsTools:
630
615
  ctx: The MCP context object for error handling and logging.
631
616
  namespace: The metric namespace (e.g., "AWS/EC2", "AWS/Lambda")
632
617
  metric_name: The name of the metric (e.g., "CPUUtilization", "Duration")
633
- region: AWS region to query. Defaults to 'us-east-1'.
634
618
 
635
619
  Returns:
636
620
  Optional[MetricMetadata]: An object containing the metric's description,
@@ -692,11 +676,17 @@ class CloudWatchMetricsTools:
692
676
  description='List of dimensions that identify the metric, each with name and value',
693
677
  ),
694
678
  region: Annotated[
695
- str,
679
+ str | None,
680
+ Field(
681
+ description='AWS region to query. Defaults to AWS_REGION environment variable or us-east-1 if not set.'
682
+ ),
683
+ ] = None,
684
+ profile_name: Annotated[
685
+ str | None,
696
686
  Field(
697
- description='AWS region for consistency. Note: This function uses local metadata and does not make AWS API calls. Defaults to us-east-1.'
687
+ description='AWS CLI Profile Name to use for AWS access. Falls back to AWS_PROFILE environment variable if not specified, or uses default AWS credential chain.'
698
688
  ),
699
- ] = 'us-east-1',
689
+ ] = None,
700
690
  statistic: Annotated[
701
691
  Literal[
702
692
  'AVG',
@@ -727,7 +717,8 @@ class CloudWatchMetricsTools:
727
717
  namespace: The metric namespace (e.g., "AWS/EC2", "AWS/Lambda")
728
718
  metric_name: The name of the metric (e.g., "CPUUtilization", "Duration")
729
719
  dimensions: List of dimensions with name and value pairs
730
- region: AWS region to query. Defaults to 'us-east-1'.
720
+ region: AWS region to query. Defaults to AWS_REGION environment variable or us-east-1 if not set.
721
+ profile_name: AWS CLI Profile Name to use for AWS access. Falls back to AWS_PROFILE environment variable if not specified, or uses default AWS credential chain.
731
722
  statistic: The statistic to use for alarm recommendations. Must match the metric's data type:
732
723
  - Aggregate count metrics (RequestCount, Errors, Faults, Throttles, CacheHits, Connections, EventsProcessed): Use 'Sum'
733
724
  - Event occurrence metrics (Invocations, CacheMisses): Use 'SampleCount'
@@ -1057,9 +1048,17 @@ class CloudWatchMetricsTools:
1057
1048
  description='List of dimensions that identify the metric, each with name and value',
1058
1049
  ),
1059
1050
  region: Annotated[
1060
- str,
1061
- Field(description='AWS region to query. Defaults to us-east-1.'),
1062
- ] = 'us-east-1',
1051
+ str | None,
1052
+ Field(
1053
+ description='AWS region to query. Defaults to AWS_REGION environment variable or us-east-1 if not set.'
1054
+ ),
1055
+ ] = None,
1056
+ profile_name: Annotated[
1057
+ str | None,
1058
+ Field(
1059
+ description='AWS CLI Profile Name to use for AWS access. Falls back to AWS_PROFILE environment variable if not specified, or uses default AWS credential chain.'
1060
+ ),
1061
+ ] = None,
1063
1062
  statistic: Annotated[
1064
1063
  Literal[
1065
1064
  'AVG',
@@ -1091,8 +1090,9 @@ class CloudWatchMetricsTools:
1091
1090
  namespace: The metric namespace (e.g., "AWS/EC2", "AWS/Lambda")
1092
1091
  metric_name: The name of the metric (e.g., "CPUUtilization", "Duration")
1093
1092
  dimensions: List of dimensions with name and value pairs
1093
+ region: AWS region to query. Defaults to AWS_REGION environment variable or us-east-1 if not set.
1094
+ profile_name: AWS CLI Profile Name to use for AWS access. Falls back to AWS_PROFILE environment variable if not specified, or uses default AWS credential chain.
1094
1095
  statistic: The statistic to use for metric analysis. For guidance on choosing the correct statistic, refer to the get_recommended_metric_alarms tool.
1095
- region: AWS region to query. Defaults to 'us-east-1'.
1096
1096
 
1097
1097
  Returns:
1098
1098
  Dict[str, Any]: Analysis results including:
@@ -16,7 +16,7 @@
16
16
  SERVER="cloudwatch-mcp-server"
17
17
 
18
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
19
+ if pgrep -P 0 -a -l -x -f "/app/.venv/bin/python3? /app/.venv/bin/awslabs.$SERVER" > /dev/null; then
20
20
  echo -n "$SERVER is running";
21
21
  exit 0;
22
22
  fi;
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "awslabs.cloudwatch-mcp-server"
3
- version = "0.0.16"
3
+ version = "0.0.17"
4
4
  description = "An AWS Labs Model Context Protocol (MCP) server for cloudwatch"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"