awslabs.eks-mcp-server 0.1.16__py3-none-any.whl → 0.1.17__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/eks_mcp_server/__init__.py +1 -1
- awslabs/eks_mcp_server/cloudwatch_handler.py +42 -54
- awslabs/eks_mcp_server/cloudwatch_metrics_guidance_handler.py +15 -10
- awslabs/eks_mcp_server/eks_kb_handler.py +22 -4
- awslabs/eks_mcp_server/eks_stack_handler.py +97 -122
- awslabs/eks_mcp_server/iam_handler.py +36 -36
- awslabs/eks_mcp_server/insights_handler.py +29 -32
- awslabs/eks_mcp_server/k8s_handler.py +125 -145
- awslabs/eks_mcp_server/models.py +52 -62
- awslabs/eks_mcp_server/vpc_config_handler.py +18 -35
- {awslabs_eks_mcp_server-0.1.16.dist-info → awslabs_eks_mcp_server-0.1.17.dist-info}/METADATA +2 -2
- {awslabs_eks_mcp_server-0.1.16.dist-info → awslabs_eks_mcp_server-0.1.17.dist-info}/RECORD +16 -16
- {awslabs_eks_mcp_server-0.1.16.dist-info → awslabs_eks_mcp_server-0.1.17.dist-info}/WHEEL +1 -1
- {awslabs_eks_mcp_server-0.1.16.dist-info → awslabs_eks_mcp_server-0.1.17.dist-info}/entry_points.txt +0 -0
- {awslabs_eks_mcp_server-0.1.16.dist-info → awslabs_eks_mcp_server-0.1.17.dist-info}/licenses/LICENSE +0 -0
- {awslabs_eks_mcp_server-0.1.16.dist-info → awslabs_eks_mcp_server-0.1.17.dist-info}/licenses/NOTICE +0 -0
|
@@ -19,9 +19,9 @@ import json
|
|
|
19
19
|
import time
|
|
20
20
|
from awslabs.eks_mcp_server.aws_helper import AwsHelper
|
|
21
21
|
from awslabs.eks_mcp_server.logging_helper import LogLevel, log_with_request_id
|
|
22
|
-
from awslabs.eks_mcp_server.models import
|
|
22
|
+
from awslabs.eks_mcp_server.models import CloudWatchLogsData, CloudWatchMetricsData
|
|
23
23
|
from mcp.server.fastmcp import Context
|
|
24
|
-
from mcp.types import TextContent
|
|
24
|
+
from mcp.types import CallToolResult, TextContent
|
|
25
25
|
from pydantic import Field
|
|
26
26
|
from typing import Optional, Union
|
|
27
27
|
|
|
@@ -131,7 +131,7 @@ class CloudWatchHandler:
|
|
|
131
131
|
None,
|
|
132
132
|
description='Custom fields to include in the query results (defaults to "@timestamp, @message"). Use CloudWatch Logs Insights field syntax. IMPORTANT: Only specify if you need fields beyond the default timestamp and message.',
|
|
133
133
|
),
|
|
134
|
-
) ->
|
|
134
|
+
) -> CallToolResult:
|
|
135
135
|
"""Get logs from CloudWatch for a specific resource.
|
|
136
136
|
|
|
137
137
|
This tool retrieves logs from CloudWatch for Kubernetes resources in an EKS cluster,
|
|
@@ -181,17 +181,9 @@ class CloudWatchHandler:
|
|
|
181
181
|
'Access to CloudWatch logs requires --allow-sensitive-data-access flag'
|
|
182
182
|
)
|
|
183
183
|
log_with_request_id(ctx, LogLevel.ERROR, error_message)
|
|
184
|
-
return
|
|
184
|
+
return CallToolResult(
|
|
185
185
|
isError=True,
|
|
186
186
|
content=[TextContent(type='text', text=error_message)],
|
|
187
|
-
resource_type=resource_type,
|
|
188
|
-
resource_name=resource_name,
|
|
189
|
-
cluster_name=cluster_name,
|
|
190
|
-
log_type=log_type,
|
|
191
|
-
log_group='',
|
|
192
|
-
start_time='',
|
|
193
|
-
end_time='',
|
|
194
|
-
log_entries=[],
|
|
195
187
|
)
|
|
196
188
|
|
|
197
189
|
start_dt, end_dt = self.resolve_time_range(start_time, end_time, minutes)
|
|
@@ -268,15 +260,8 @@ class CloudWatchHandler:
|
|
|
268
260
|
f'Retrieved {len(log_entries)} log entries for {resource_str}cluster {cluster_name}',
|
|
269
261
|
)
|
|
270
262
|
|
|
271
|
-
#
|
|
272
|
-
|
|
273
|
-
isError=False,
|
|
274
|
-
content=[
|
|
275
|
-
TextContent(
|
|
276
|
-
type='text',
|
|
277
|
-
text=f'Successfully retrieved {len(log_entries)} log entries for {resource_str}cluster {cluster_name}',
|
|
278
|
-
)
|
|
279
|
-
],
|
|
263
|
+
# Create structured data model
|
|
264
|
+
data = CloudWatchLogsData(
|
|
280
265
|
resource_type=resource_type,
|
|
281
266
|
resource_name=resource_name,
|
|
282
267
|
cluster_name=cluster_name,
|
|
@@ -287,22 +272,29 @@ class CloudWatchHandler:
|
|
|
287
272
|
log_entries=log_entries,
|
|
288
273
|
)
|
|
289
274
|
|
|
275
|
+
# Return CallToolResult with structured content
|
|
276
|
+
return CallToolResult(
|
|
277
|
+
isError=False,
|
|
278
|
+
content=[
|
|
279
|
+
TextContent(
|
|
280
|
+
type='text',
|
|
281
|
+
text=f'Successfully retrieved {len(log_entries)} log entries for {resource_str}cluster {cluster_name}',
|
|
282
|
+
),
|
|
283
|
+
TextContent(
|
|
284
|
+
type='text',
|
|
285
|
+
text=json.dumps(data.model_dump()),
|
|
286
|
+
),
|
|
287
|
+
],
|
|
288
|
+
)
|
|
289
|
+
|
|
290
290
|
except Exception as e:
|
|
291
291
|
resource_name_str = f' {resource_name}' if resource_name is not None else ''
|
|
292
292
|
error_message = f'Failed to get logs for {resource_type}{resource_name_str}: {str(e)}'
|
|
293
293
|
log_with_request_id(ctx, LogLevel.ERROR, error_message)
|
|
294
294
|
|
|
295
|
-
return
|
|
295
|
+
return CallToolResult(
|
|
296
296
|
isError=True,
|
|
297
297
|
content=[TextContent(type='text', text=error_message)],
|
|
298
|
-
resource_type=resource_type,
|
|
299
|
-
resource_name=resource_name,
|
|
300
|
-
cluster_name=cluster_name,
|
|
301
|
-
log_type=log_type,
|
|
302
|
-
log_group='',
|
|
303
|
-
start_time='',
|
|
304
|
-
end_time='',
|
|
305
|
-
log_entries=[],
|
|
306
298
|
)
|
|
307
299
|
|
|
308
300
|
async def get_cloudwatch_metrics(
|
|
@@ -360,7 +352,7 @@ class CloudWatchHandler:
|
|
|
360
352
|
- Minimum: Lowest value during the period
|
|
361
353
|
- SampleCount: Number of samples during the period""",
|
|
362
354
|
),
|
|
363
|
-
) ->
|
|
355
|
+
) -> CallToolResult:
|
|
364
356
|
"""Get metrics from CloudWatch for a specific resource.
|
|
365
357
|
|
|
366
358
|
This tool retrieves metrics from CloudWatch for Kubernetes resources in an EKS cluster,
|
|
@@ -419,15 +411,9 @@ class CloudWatchHandler:
|
|
|
419
411
|
if 'ClusterName' in dimensions and dimensions['ClusterName'] != cluster_name:
|
|
420
412
|
error_message = f"Provided cluster_name '{cluster_name}' does not match ClusterName dimension '{dimensions['ClusterName']}'"
|
|
421
413
|
log_with_request_id(ctx, LogLevel.ERROR, error_message)
|
|
422
|
-
return
|
|
414
|
+
return CallToolResult(
|
|
423
415
|
isError=True,
|
|
424
416
|
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
417
|
)
|
|
432
418
|
|
|
433
419
|
log_with_request_id(
|
|
@@ -493,15 +479,8 @@ class CloudWatchHandler:
|
|
|
493
479
|
f'Retrieved {len(data_points)} metric data points for {metric_name}',
|
|
494
480
|
)
|
|
495
481
|
|
|
496
|
-
#
|
|
497
|
-
|
|
498
|
-
isError=False,
|
|
499
|
-
content=[
|
|
500
|
-
TextContent(
|
|
501
|
-
type='text',
|
|
502
|
-
text=f'Successfully retrieved {len(data_points)} metric data points for {metric_name} in cluster {cluster_name}',
|
|
503
|
-
)
|
|
504
|
-
],
|
|
482
|
+
# Create structured data model
|
|
483
|
+
data = CloudWatchMetricsData(
|
|
505
484
|
metric_name=metric_name,
|
|
506
485
|
namespace=namespace,
|
|
507
486
|
cluster_name=cluster_name,
|
|
@@ -510,19 +489,28 @@ class CloudWatchHandler:
|
|
|
510
489
|
data_points=data_points,
|
|
511
490
|
)
|
|
512
491
|
|
|
492
|
+
# Return CallToolResult with structured content
|
|
493
|
+
return CallToolResult(
|
|
494
|
+
isError=False,
|
|
495
|
+
content=[
|
|
496
|
+
TextContent(
|
|
497
|
+
type='text',
|
|
498
|
+
text=f'Successfully retrieved {len(data_points)} metric data points for {metric_name} in cluster {cluster_name}',
|
|
499
|
+
),
|
|
500
|
+
TextContent(
|
|
501
|
+
type='text',
|
|
502
|
+
text=json.dumps(data.model_dump()),
|
|
503
|
+
),
|
|
504
|
+
],
|
|
505
|
+
)
|
|
506
|
+
|
|
513
507
|
except Exception as e:
|
|
514
508
|
error_message = f'Failed to get metrics: {str(e)}'
|
|
515
509
|
log_with_request_id(ctx, LogLevel.ERROR, error_message)
|
|
516
510
|
|
|
517
|
-
return
|
|
511
|
+
return CallToolResult(
|
|
518
512
|
isError=True,
|
|
519
513
|
content=[TextContent(type='text', text=error_message)],
|
|
520
|
-
metric_name=metric_name,
|
|
521
|
-
namespace=namespace,
|
|
522
|
-
cluster_name=cluster_name,
|
|
523
|
-
start_time='',
|
|
524
|
-
end_time='',
|
|
525
|
-
data_points=[],
|
|
526
514
|
)
|
|
527
515
|
|
|
528
516
|
def _poll_query_results(
|
|
@@ -17,11 +17,11 @@
|
|
|
17
17
|
import json
|
|
18
18
|
import os
|
|
19
19
|
from awslabs.eks_mcp_server.logging_helper import LogLevel, log_with_request_id
|
|
20
|
-
from awslabs.eks_mcp_server.models import
|
|
20
|
+
from awslabs.eks_mcp_server.models import MetricsGuidanceData
|
|
21
21
|
from enum import Enum
|
|
22
22
|
from loguru import logger
|
|
23
23
|
from mcp.server.fastmcp import Context
|
|
24
|
-
from mcp.types import TextContent
|
|
24
|
+
from mcp.types import CallToolResult, TextContent
|
|
25
25
|
from pydantic import Field
|
|
26
26
|
from typing import Any, Dict
|
|
27
27
|
|
|
@@ -78,7 +78,7 @@ class CloudWatchMetricsHandler:
|
|
|
78
78
|
...,
|
|
79
79
|
description='Type of resource to get metrics for (cluster, node, pod, namespace, service)',
|
|
80
80
|
),
|
|
81
|
-
) ->
|
|
81
|
+
) -> CallToolResult:
|
|
82
82
|
"""Get CloudWatch metrics guidance for specific resource types in EKS clusters.
|
|
83
83
|
|
|
84
84
|
This tool provides information about available CloudWatch metrics that are in the `ContainerInsights` naemspace for different resource types
|
|
@@ -112,11 +112,9 @@ class CloudWatchMetricsHandler:
|
|
|
112
112
|
)
|
|
113
113
|
log_with_request_id(ctx, LogLevel.ERROR, error_message)
|
|
114
114
|
|
|
115
|
-
return
|
|
115
|
+
return CallToolResult(
|
|
116
116
|
isError=True,
|
|
117
117
|
content=[TextContent(type='text', text=error_message)],
|
|
118
|
-
resource_type=resource_type,
|
|
119
|
-
metrics=[],
|
|
120
118
|
)
|
|
121
119
|
|
|
122
120
|
metrics = self.metrics_guidance.get(resource_type.lower(), {}).get('metrics', [])
|
|
@@ -128,14 +126,21 @@ class CloudWatchMetricsHandler:
|
|
|
128
126
|
f'Retrieved {len(metrics)} metrics for resource type {resource_type_lower}',
|
|
129
127
|
)
|
|
130
128
|
|
|
131
|
-
|
|
129
|
+
data = MetricsGuidanceData(
|
|
130
|
+
resource_type=resource_type_lower,
|
|
131
|
+
metrics=metrics,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
return CallToolResult(
|
|
132
135
|
isError=False,
|
|
133
136
|
content=[
|
|
134
137
|
TextContent(
|
|
135
138
|
type='text',
|
|
136
139
|
text=f'Successfully retrieved {len(metrics)} metrics for resource type {resource_type_lower}',
|
|
137
|
-
)
|
|
140
|
+
),
|
|
141
|
+
TextContent(
|
|
142
|
+
type='text',
|
|
143
|
+
text=json.dumps(data.model_dump()),
|
|
144
|
+
),
|
|
138
145
|
],
|
|
139
|
-
resource_type=resource_type_lower,
|
|
140
|
-
metrics=metrics,
|
|
141
146
|
)
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import requests
|
|
18
18
|
from loguru import logger
|
|
19
|
+
from mcp.types import CallToolResult, TextContent
|
|
19
20
|
from pydantic import Field
|
|
20
21
|
from requests_auth_aws_sigv4 import AWSSigV4
|
|
21
22
|
|
|
@@ -49,7 +50,7 @@ class EKSKnowledgeBaseHandler:
|
|
|
49
50
|
...,
|
|
50
51
|
description='Your specific question or issue description related to EKS troubleshooting',
|
|
51
52
|
),
|
|
52
|
-
) ->
|
|
53
|
+
) -> CallToolResult:
|
|
53
54
|
"""Search the EKS Troubleshoot Guide for troubleshooting information.
|
|
54
55
|
|
|
55
56
|
This tool provides troubleshooting guidance for Amazon EKS issues by querying
|
|
@@ -74,7 +75,7 @@ class EKSKnowledgeBaseHandler:
|
|
|
74
75
|
contain letters, numbers, commas, periods, question marks, colons, and spaces.
|
|
75
76
|
|
|
76
77
|
Returns:
|
|
77
|
-
|
|
78
|
+
CallToolResult: Detailed troubleshooting guidance for the EKS issue
|
|
78
79
|
"""
|
|
79
80
|
try:
|
|
80
81
|
response = requests.post(
|
|
@@ -83,7 +84,24 @@ class EKSKnowledgeBaseHandler:
|
|
|
83
84
|
auth=AWSSigV4(AWS_SERVICE, region=AWS_REGION),
|
|
84
85
|
)
|
|
85
86
|
response.raise_for_status()
|
|
86
|
-
|
|
87
|
+
|
|
88
|
+
return CallToolResult(
|
|
89
|
+
isError=False,
|
|
90
|
+
content=[
|
|
91
|
+
TextContent(
|
|
92
|
+
type='text',
|
|
93
|
+
text=response.text,
|
|
94
|
+
)
|
|
95
|
+
],
|
|
96
|
+
)
|
|
87
97
|
except Exception as e:
|
|
88
98
|
logger.error(f'Error in search_eks_troubleshoot_guide: {str(e)}')
|
|
89
|
-
return
|
|
99
|
+
return CallToolResult(
|
|
100
|
+
isError=True,
|
|
101
|
+
content=[
|
|
102
|
+
TextContent(
|
|
103
|
+
type='text',
|
|
104
|
+
text=f'Error: {str(e)}',
|
|
105
|
+
)
|
|
106
|
+
],
|
|
107
|
+
)
|