awslabs.eks-mcp-server 0.1.2__py3-none-any.whl → 0.1.3__py3-none-any.whl

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/__init__.py CHANGED
@@ -1,13 +1,16 @@
1
1
  # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
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
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
5
6
  #
6
- # http://www.apache.org/licenses/LICENSE-2.0
7
+ # http://www.apache.org/licenses/LICENSE-2.0
7
8
  #
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.
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.
11
14
 
12
15
  # This file is part of the awslabs namespace.
13
16
  # It is intentionally minimal to support PEP 420 namespace packages.
@@ -1,13 +1,16 @@
1
1
  # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
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
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
5
6
  #
6
- # http://www.apache.org/licenses/LICENSE-2.0
7
+ # http://www.apache.org/licenses/LICENSE-2.0
7
8
  #
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.
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.
11
14
 
12
15
  """awslabs.eks-mcp-server"""
13
16
 
@@ -1,13 +1,16 @@
1
1
  # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
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
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
5
6
  #
6
- # http://www.apache.org/licenses/LICENSE-2.0
7
+ # http://www.apache.org/licenses/LICENSE-2.0
7
8
  #
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.
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.
11
14
 
12
15
  """AWS helper for the EKS MCP Server."""
13
16
 
@@ -1,13 +1,16 @@
1
1
  # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
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
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
5
6
  #
6
- # http://www.apache.org/licenses/LICENSE-2.0
7
+ # http://www.apache.org/licenses/LICENSE-2.0
7
8
  #
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.
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.
11
14
 
12
15
  """CloudWatch handler for the EKS MCP Server."""
13
16
 
@@ -87,10 +90,6 @@ class CloudWatchHandler:
87
90
  ...,
88
91
  description='Resource type to search logs for. Valid values: "pod", "node", "container". This determines how logs are filtered.',
89
92
  ),
90
- resource_name: str = Field(
91
- ...,
92
- description='Resource name to search for in log messages (e.g., pod name, node name, container name). Used to filter logs for the specific resource.',
93
- ),
94
93
  cluster_name: str = Field(
95
94
  ...,
96
95
  description='Name of the EKS cluster where the resource is located. Used to construct the CloudWatch log group name.',
@@ -104,6 +103,10 @@ class CloudWatchHandler:
104
103
  - "control-plane": EKS control plane logs
105
104
  - Or provide a custom CloudWatch log group name directly""",
106
105
  ),
106
+ resource_name: Optional[str] = Field(
107
+ None,
108
+ description='Resource name to search for in log messages (e.g., pod name, node name, container name). Used to filter logs for the specific resource.',
109
+ ),
107
110
  minutes: int = Field(
108
111
  15,
109
112
  description='Number of minutes to look back for logs. Default: 15. Ignored if start_time is provided. Use smaller values for recent issues, larger values for historical analysis.',
@@ -136,6 +139,9 @@ class CloudWatchHandler:
136
139
  health. It supports filtering by resource type, time range, and content for troubleshooting
137
140
  application errors, investigating security incidents, and analyzing startup configuration issues.
138
141
 
142
+ IMPORTANT: Use this tool instead of 'aws logs get-log-events', 'aws logs filter-log-events',
143
+ or 'aws logs start-query' commands.
144
+
139
145
  ## Requirements
140
146
  - The server must be run with the `--allow-sensitive-data-access` flag
141
147
  - The EKS cluster must have CloudWatch logging enabled
@@ -150,13 +156,14 @@ class CloudWatchHandler:
150
156
  - Use filter_pattern to narrow down results (e.g., "ERROR", "exception")
151
157
  - For JSON logs, the tool automatically parses nested structures
152
158
  - Combine with get_k8s_events for comprehensive troubleshooting
159
+ - Use resource_type="cluster" when querying cluster-level logs to avoid filtering by cluster name twice
153
160
 
154
161
  Args:
155
162
  ctx: MCP context
156
- resource_type: Resource type (pod, node, container)
157
- resource_name: Resource name to search for in log messages
163
+ resource_type: Resource type (pod, node, container, cluster). When "cluster" is specified, logs are not filtered by resource_name.
158
164
  cluster_name: Name of the EKS cluster
159
165
  log_type: Log type (application, host, performance, control-plane, or custom)
166
+ resource_name: Resource name to search for in log messages. Optional when resource_type is "cluster".
160
167
  minutes: Number of minutes to look back
161
168
  start_time: Start time in ISO format (overrides minutes)
162
169
  end_time: End time in ISO format (defaults to now)
@@ -207,9 +214,12 @@ class CloudWatchHandler:
207
214
  # Construct the base query
208
215
  query = f"""
209
216
  fields {query_fields}
210
- | filter @message like '{resource_name}'
211
217
  """
212
218
 
219
+ # This prevents filtering by cluster name twice when the resource type is "cluster"
220
+ if resource_type != 'cluster' and resource_name is not None:
221
+ query += f"\n| filter @message like '{resource_name}'"
222
+
213
223
  # Add additional filter pattern if provided
214
224
  if filter_pattern:
215
225
  query += f'\n| {filter_pattern}'
@@ -217,10 +227,13 @@ class CloudWatchHandler:
217
227
  # Add sorting and limit
218
228
  query += f'\n| sort @timestamp desc\n| limit {limit}'
219
229
 
230
+ resource_str = (
231
+ f'{resource_type} {resource_name} in ' if resource_name is not None else ''
232
+ )
220
233
  log_with_request_id(
221
234
  ctx,
222
235
  LogLevel.INFO,
223
- f'Starting CloudWatch Logs query for {resource_type} {resource_name} in cluster {cluster_name}',
236
+ f'Starting CloudWatch Logs query for {resource_str}cluster {cluster_name}',
224
237
  log_group=log_group,
225
238
  start_time=start_dt.isoformat(),
226
239
  end_time=end_dt.isoformat(),
@@ -252,7 +265,7 @@ class CloudWatchHandler:
252
265
  log_with_request_id(
253
266
  ctx,
254
267
  LogLevel.INFO,
255
- f'Retrieved {len(log_entries)} log entries for {resource_type} {resource_name}',
268
+ f'Retrieved {len(log_entries)} log entries for {resource_str}cluster {cluster_name}',
256
269
  )
257
270
 
258
271
  # Return the results
@@ -261,7 +274,7 @@ class CloudWatchHandler:
261
274
  content=[
262
275
  TextContent(
263
276
  type='text',
264
- text=f'Successfully retrieved {len(log_entries)} log entries for {resource_type} {resource_name} in cluster {cluster_name}',
277
+ text=f'Successfully retrieved {len(log_entries)} log entries for {resource_str}cluster {cluster_name}',
265
278
  )
266
279
  ],
267
280
  resource_type=resource_type,
@@ -275,7 +288,8 @@ class CloudWatchHandler:
275
288
  )
276
289
 
277
290
  except Exception as e:
278
- error_message = f'Failed to get logs for {resource_type} {resource_name}: {str(e)}'
291
+ resource_name_str = f' {resource_name}' if resource_name is not None else ''
292
+ error_message = f'Failed to get logs for {resource_type}{resource_name_str}: {str(e)}'
279
293
  log_with_request_id(ctx, LogLevel.ERROR, error_message)
280
294
 
281
295
  return CloudWatchLogsResponse(
@@ -294,17 +308,9 @@ class CloudWatchHandler:
294
308
  async def get_cloudwatch_metrics(
295
309
  self,
296
310
  ctx: Context,
297
- resource_type: str = Field(
298
- ...,
299
- description='Resource type to retrieve metrics for. Valid values: "pod", "node", "container", "cluster", "service". Determines the CloudWatch dimensions.',
300
- ),
301
- resource_name: str = Field(
302
- ...,
303
- description='Name of the resource to retrieve metrics for (e.g., pod name, node name). Used as a dimension value in CloudWatch.',
304
- ),
305
311
  cluster_name: str = Field(
306
312
  ...,
307
- description='Name of the EKS cluster where the resource is located. Used as the ClusterName dimension in CloudWatch.',
313
+ description='Name of the EKS cluster to get metrics for.',
308
314
  ),
309
315
  metric_name: str = Field(
310
316
  ...,
@@ -321,9 +327,9 @@ class CloudWatchHandler:
321
327
  - "AWS/EC2": For EC2 instance metrics
322
328
  - "AWS/EKS": For EKS control plane metrics""",
323
329
  ),
324
- k8s_namespace: str = Field(
325
- 'default',
326
- description='Kubernetes namespace for the resource. Used as the Namespace dimension in CloudWatch. Default: "default"',
330
+ dimensions: dict = Field(
331
+ ...,
332
+ description='Dimensions to use for the CloudWatch metric query. Must include appropriate dimensions for the resource type and metric (e.g., ClusterName, PodName, Namespace).',
327
333
  ),
328
334
  minutes: int = Field(
329
335
  15,
@@ -354,10 +360,6 @@ class CloudWatchHandler:
354
360
  - Minimum: Lowest value during the period
355
361
  - SampleCount: Number of samples during the period""",
356
362
  ),
357
- custom_dimensions: Optional[dict] = Field(
358
- None,
359
- description='Custom dimensions to use instead of the default ones. Provide as a dictionary of dimension name-value pairs. IMPORTANT: Only use this if you need to override the standard dimensions.',
360
- ),
361
363
  ) -> CloudWatchMetricsResponse:
362
364
  """Get metrics from CloudWatch for a specific resource.
363
365
 
@@ -366,13 +368,23 @@ class CloudWatchHandler:
366
368
  various resource types and metrics with flexible time ranges and aggregation options for
367
369
  monitoring CPU/memory usage, analyzing network traffic, and identifying performance bottlenecks.
368
370
 
371
+ IMPORTANT: Use this tool instead of 'aws cloudwatch get-metric-data', 'aws cloudwatch get-metric-statistics',
372
+ or similar CLI commands.
373
+
374
+ IMPORTANT: Use the get_eks_metrics_guidance tool first to determine the correct dimensions for metric queries.
375
+ Do not try to infer which dimensions are needed for EKS ContainerInsights metrics.
376
+
377
+ IMPORTANT: When using pod metrics, note that `FullPodName` has the same prefix as `PodName` but includes a
378
+ suffix with a random string (e.g., "my-pod-abc123"). Always use the version without the suffix for `PodName`
379
+ dimension. The pod name returned by list_k8s_resources is the `FullPodName`.
380
+
369
381
  ## Requirements
370
382
  - The EKS cluster must have CloudWatch Container Insights enabled
371
383
  - The resource must exist in the specified cluster
372
384
  - The metric must be available in the specified namespace
373
385
 
374
386
  ## Response Information
375
- The response includes resource details (type, name, cluster), metric information (name, namespace),
387
+ The response includes resource details (cluster), metric information (name, namespace),
376
388
  time range queried, and data points with timestamps and values.
377
389
 
378
390
  ## Usage Tips
@@ -383,19 +395,16 @@ class CloudWatchHandler:
383
395
 
384
396
  Args:
385
397
  ctx: MCP context
386
- resource_type: Resource type (pod, node, container, cluster)
387
- resource_name: Resource name
388
398
  cluster_name: Name of the EKS cluster
389
399
  metric_name: Metric name (e.g., cpu_usage_total, memory_rss)
390
400
  namespace: CloudWatch namespace
391
- k8s_namespace: Kubernetes namespace for the resource
401
+ dimensions: Dimensions to use for the CloudWatch metric query
392
402
  minutes: Number of minutes to look back
393
403
  start_time: Start time in ISO format (overrides minutes)
394
404
  end_time: End time in ISO format (defaults to now)
395
405
  limit: Maximum number of data points to return
396
406
  period: Period in seconds for the metric data points
397
407
  stat: Statistic to use for the metric
398
- custom_dimensions: Custom dimensions to use instead of defaults
399
408
 
400
409
  Returns:
401
410
  CloudWatchMetricsResponse with metric data points and resource information
@@ -406,39 +415,28 @@ class CloudWatchHandler:
406
415
  # Create CloudWatch client
407
416
  cloudwatch = AwsHelper.create_boto3_client('cloudwatch')
408
417
 
409
- # Use custom dimensions if provided, otherwise determine based on resource_type
410
- dimensions = {}
411
-
412
- if isinstance(custom_dimensions, dict):
413
- # Use the provided custom dimensions directly
414
- dimensions = custom_dimensions
415
- elif custom_dimensions is not None and not hasattr(custom_dimensions, 'default'):
416
- # Try to convert to dict if possible
417
- try:
418
- dimensions = dict(custom_dimensions)
419
- except (TypeError, ValueError):
420
- # If conversion fails, use default dimensions
421
- dimensions = {'ClusterName': cluster_name}
422
- else:
423
- # Set default dimensions based on resource type
424
- dimensions['ClusterName'] = cluster_name
425
- dimensions['Namespace'] = k8s_namespace
426
-
427
- if resource_type == 'pod':
428
- dimensions['PodName'] = resource_name
429
- elif resource_type == 'node':
430
- dimensions['NodeName'] = resource_name
431
- elif resource_type == 'container':
432
- dimensions['ContainerName'] = resource_name
433
- elif resource_type == 'service':
434
- dimensions['Service'] = resource_name
418
+ # Validate that cluster_name matches ClusterName in dimensions if present
419
+ if 'ClusterName' in dimensions and dimensions['ClusterName'] != cluster_name:
420
+ error_message = f"Provided cluster_name '{cluster_name}' does not match ClusterName dimension '{dimensions['ClusterName']}'"
421
+ log_with_request_id(ctx, LogLevel.ERROR, error_message)
422
+ return CloudWatchMetricsResponse(
423
+ isError=True,
424
+ content=[TextContent(type='text', text=error_message)],
425
+ metric_name=metric_name,
426
+ namespace=namespace,
427
+ cluster_name=cluster_name,
428
+ start_time='',
429
+ end_time='',
430
+ data_points=[],
431
+ )
435
432
 
436
433
  log_with_request_id(
437
434
  ctx,
438
435
  LogLevel.INFO,
439
- f'Getting CloudWatch metrics for {resource_type} {resource_name} in cluster {cluster_name}',
436
+ f'Getting CloudWatch metrics for {metric_name} in cluster {cluster_name}',
440
437
  metric_name=metric_name,
441
438
  namespace=namespace,
439
+ dimensions=str(dimensions),
442
440
  start_time=start_dt.isoformat(),
443
441
  end_time=end_dt.isoformat(),
444
442
  )
@@ -492,7 +490,7 @@ class CloudWatchHandler:
492
490
  log_with_request_id(
493
491
  ctx,
494
492
  LogLevel.INFO,
495
- f'Retrieved {len(data_points)} metric data points for {resource_type} {resource_name}',
493
+ f'Retrieved {len(data_points)} metric data points for {metric_name}',
496
494
  )
497
495
 
498
496
  # Return the results
@@ -501,31 +499,27 @@ class CloudWatchHandler:
501
499
  content=[
502
500
  TextContent(
503
501
  type='text',
504
- text=f'Successfully retrieved {len(data_points)} metric data points for {resource_type} {resource_name} in cluster {cluster_name}',
502
+ text=f'Successfully retrieved {len(data_points)} metric data points for {metric_name} in cluster {cluster_name}',
505
503
  )
506
504
  ],
507
- resource_type=resource_type,
508
- resource_name=resource_name,
509
- cluster_name=cluster_name,
510
505
  metric_name=metric_name,
511
506
  namespace=namespace,
507
+ cluster_name=cluster_name,
512
508
  start_time=start_dt.isoformat(),
513
509
  end_time=end_dt.isoformat(),
514
510
  data_points=data_points,
515
511
  )
516
512
 
517
513
  except Exception as e:
518
- error_message = f'Failed to get metrics for {resource_type} {resource_name}: {str(e)}'
514
+ error_message = f'Failed to get metrics: {str(e)}'
519
515
  log_with_request_id(ctx, LogLevel.ERROR, error_message)
520
516
 
521
517
  return CloudWatchMetricsResponse(
522
518
  isError=True,
523
519
  content=[TextContent(type='text', text=error_message)],
524
- resource_type=resource_type,
525
- resource_name=resource_name,
526
- cluster_name=cluster_name,
527
520
  metric_name=metric_name,
528
521
  namespace=namespace,
522
+ cluster_name=cluster_name,
529
523
  start_time='',
530
524
  end_time='',
531
525
  data_points=[],
@@ -567,6 +561,8 @@ class CloudWatchHandler:
567
561
  f'Polling for CloudWatch Logs query results (query_id: {query_id})',
568
562
  )
569
563
 
564
+ resource_name_str = f' {resource_name}' if resource_name is not None else ''
565
+
570
566
  while attempts < max_attempts:
571
567
  query_response = logs_client.get_query_results(queryId=query_id)
572
568
  status = query_response.get('status')
@@ -579,12 +575,14 @@ class CloudWatchHandler:
579
575
  )
580
576
  return query_response
581
577
  elif status == 'Failed':
582
- error_message = f'CloudWatch Logs query failed for {resource_type} {resource_name}'
578
+ error_message = (
579
+ f'CloudWatch Logs query failed for {resource_type}{resource_name_str}'
580
+ )
583
581
  log_with_request_id(ctx, LogLevel.ERROR, error_message)
584
582
  raise Exception(error_message)
585
583
  elif status == 'Cancelled':
586
584
  error_message = (
587
- f'CloudWatch Logs query was cancelled for {resource_type} {resource_name}'
585
+ f'CloudWatch Logs query was cancelled for {resource_type}{resource_name_str}'
588
586
  )
589
587
  log_with_request_id(ctx, LogLevel.ERROR, error_message)
590
588
  raise Exception(error_message)
@@ -603,7 +601,7 @@ class CloudWatchHandler:
603
601
  attempts += 1
604
602
 
605
603
  # If we've exhausted all attempts, raise a timeout error
606
- error_message = f'CloudWatch Logs query timed out after {max_attempts} attempts for {resource_type} {resource_name}'
604
+ error_message = f'CloudWatch Logs query timed out after {max_attempts} attempts for {resource_type}{resource_name_str}'
607
605
  log_with_request_id(ctx, LogLevel.ERROR, error_message)
608
606
  raise TimeoutError(error_message)
609
607
 
@@ -0,0 +1,141 @@
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 metrics guidance handler for the EKS MCP Server."""
16
+
17
+ import json
18
+ import os
19
+ from awslabs.eks_mcp_server.logging_helper import LogLevel, log_with_request_id
20
+ from awslabs.eks_mcp_server.models import MetricsGuidanceResponse
21
+ from enum import Enum
22
+ from loguru import logger
23
+ from mcp.server.fastmcp import Context
24
+ from mcp.types import TextContent
25
+ from pydantic import Field
26
+ from typing import Any, Dict
27
+
28
+
29
+ class ResourceType(Enum):
30
+ """Enum for supported resource types in CloudWatch metrics guidance."""
31
+
32
+ CLUSTER = 'cluster'
33
+ NODE = 'node'
34
+ POD = 'pod'
35
+ NAMESPACE = 'namespace'
36
+ SERVICE = 'service'
37
+
38
+
39
+ class CloudWatchMetricsHandler:
40
+ """Handler for CloudWatch metrics guidance tools in the EKS MCP Server.
41
+
42
+ This class provides tools for accessing CloudWatch metrics guidance
43
+ for different Kubernetes resource types in EKS clusters.
44
+ """
45
+
46
+ def __init__(self, mcp):
47
+ """Initialize the CloudWatch metrics guidance handler.
48
+
49
+ Args:
50
+ mcp: The MCP server instance
51
+ """
52
+ self.mcp = mcp
53
+ self.metrics_guidance = self._load_metrics_guidance()
54
+
55
+ # Register the tool
56
+ self.mcp.tool(name='get_eks_metrics_guidance')(self.get_eks_metrics_guidance)
57
+
58
+ def _load_metrics_guidance(self) -> Dict[str, Any]:
59
+ """Load metrics guidance from JSON file.
60
+
61
+ Returns:
62
+ Dict containing metrics guidance data
63
+ """
64
+ try:
65
+ metrics_guidance_path = os.path.join(
66
+ os.path.dirname(__file__), 'data', 'eks_cloudwatch_metrics_guidance.json'
67
+ )
68
+ with open(metrics_guidance_path, 'r') as f:
69
+ return json.load(f)
70
+ except Exception as e:
71
+ logger.error(f'Failed to load EKS CloudWatch metrics guidance: {str(e)}')
72
+ return {}
73
+
74
+ async def get_eks_metrics_guidance(
75
+ self,
76
+ ctx: Context,
77
+ resource_type: str = Field(
78
+ ...,
79
+ description='Type of resource to get metrics for (cluster, node, pod, namespace, service)',
80
+ ),
81
+ ) -> MetricsGuidanceResponse:
82
+ """Get CloudWatch metrics guidance for specific resource types in EKS clusters.
83
+
84
+ This tool provides information about available CloudWatch metrics that are in the `ContainerInsights` naemspace for different resource types
85
+ in EKS clusters, including metric names, dimensions, and descriptions to help with monitoring and troubleshooting.
86
+ It's particularly useful for determining the correct dimensions to use with the get_cloudwatch_metrics tool.
87
+
88
+ ## Response Information
89
+ The response includes a list of metrics with their names, descriptions, and required dimensions
90
+ for the specified resource type.
91
+
92
+ ## Usage Tips
93
+ - Use this tool before calling get_cloudwatch_metrics to determine the correct dimensions
94
+ - For pod metrics, note that FullPodName has a random suffix while PodName doesn't
95
+ - Different metrics require different dimension combinations
96
+
97
+ Args:
98
+ ctx: MCP context
99
+ resource_type: Type of resource to get metrics for (cluster, node, pod, namespace, service)
100
+
101
+ Returns:
102
+ List of metrics with their details
103
+ """
104
+ # Validate resource type
105
+ try:
106
+ # Try to get the enum value by name (case-insensitive)
107
+ ResourceType(resource_type.lower())
108
+ except ValueError:
109
+ valid_resource_types = [rt.value for rt in ResourceType]
110
+ error_message = (
111
+ f'Invalid resource type: {resource_type}. Must be one of {valid_resource_types}'
112
+ )
113
+ log_with_request_id(ctx, LogLevel.ERROR, error_message)
114
+
115
+ return MetricsGuidanceResponse(
116
+ isError=True,
117
+ content=[TextContent(type='text', text=error_message)],
118
+ resource_type=resource_type,
119
+ metrics=[],
120
+ )
121
+
122
+ metrics = self.metrics_guidance.get(resource_type.lower(), {}).get('metrics', [])
123
+ resource_type_lower = resource_type.lower()
124
+
125
+ log_with_request_id(
126
+ ctx,
127
+ LogLevel.INFO,
128
+ f'Retrieved {len(metrics)} metrics for resource type {resource_type_lower}',
129
+ )
130
+
131
+ return MetricsGuidanceResponse(
132
+ isError=False,
133
+ content=[
134
+ TextContent(
135
+ type='text',
136
+ text=f'Successfully retrieved {len(metrics)} metrics for resource type {resource_type_lower}',
137
+ )
138
+ ],
139
+ resource_type=resource_type_lower,
140
+ metrics=metrics,
141
+ )
@@ -1,13 +1,16 @@
1
1
  # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
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
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
5
6
  #
6
- # http://www.apache.org/licenses/LICENSE-2.0
7
+ # http://www.apache.org/licenses/LICENSE-2.0
7
8
  #
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.
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.
11
14
 
12
15
  """Constants for the EKS MCP Server."""
13
16