awslabs.elasticache-mcp-server 0.1.1__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 +16 -0
- awslabs/elasticache_mcp_server/__init__.py +17 -0
- awslabs/elasticache_mcp_server/common/__init__.py +15 -0
- awslabs/elasticache_mcp_server/common/connection.py +117 -0
- awslabs/elasticache_mcp_server/common/decorators.py +41 -0
- awslabs/elasticache_mcp_server/common/server.py +30 -0
- awslabs/elasticache_mcp_server/context.py +39 -0
- awslabs/elasticache_mcp_server/main.py +52 -0
- awslabs/elasticache_mcp_server/tools/__init__.py +15 -0
- awslabs/elasticache_mcp_server/tools/cc/__init__.py +31 -0
- awslabs/elasticache_mcp_server/tools/cc/connect.py +444 -0
- awslabs/elasticache_mcp_server/tools/cc/create.py +212 -0
- awslabs/elasticache_mcp_server/tools/cc/delete.py +65 -0
- awslabs/elasticache_mcp_server/tools/cc/describe.py +80 -0
- awslabs/elasticache_mcp_server/tools/cc/modify.py +159 -0
- awslabs/elasticache_mcp_server/tools/cc/parsers.py +78 -0
- awslabs/elasticache_mcp_server/tools/cc/processors.py +74 -0
- awslabs/elasticache_mcp_server/tools/ce/__init__.py +19 -0
- awslabs/elasticache_mcp_server/tools/ce/get_cost_and_usage.py +76 -0
- awslabs/elasticache_mcp_server/tools/cw/__init__.py +19 -0
- awslabs/elasticache_mcp_server/tools/cw/get_metric_statistics.py +85 -0
- awslabs/elasticache_mcp_server/tools/cwlogs/__init__.py +29 -0
- awslabs/elasticache_mcp_server/tools/cwlogs/create_log_group.py +68 -0
- awslabs/elasticache_mcp_server/tools/cwlogs/describe_log_groups.py +123 -0
- awslabs/elasticache_mcp_server/tools/cwlogs/describe_log_streams.py +120 -0
- awslabs/elasticache_mcp_server/tools/cwlogs/filter_log_events.py +122 -0
- awslabs/elasticache_mcp_server/tools/cwlogs/get_log_events.py +99 -0
- awslabs/elasticache_mcp_server/tools/firehose/__init__.py +19 -0
- awslabs/elasticache_mcp_server/tools/firehose/list_delivery_streams.py +63 -0
- awslabs/elasticache_mcp_server/tools/misc/__init__.py +31 -0
- awslabs/elasticache_mcp_server/tools/misc/batch_apply_update_action.py +62 -0
- awslabs/elasticache_mcp_server/tools/misc/batch_stop_update_action.py +62 -0
- awslabs/elasticache_mcp_server/tools/misc/describe_cache_engine_versions.py +79 -0
- awslabs/elasticache_mcp_server/tools/misc/describe_engine_default_parameters.py +64 -0
- awslabs/elasticache_mcp_server/tools/misc/describe_events.py +86 -0
- awslabs/elasticache_mcp_server/tools/misc/describe_service_updates.py +71 -0
- awslabs/elasticache_mcp_server/tools/rg/__init__.py +54 -0
- awslabs/elasticache_mcp_server/tools/rg/complete_migration.py +94 -0
- awslabs/elasticache_mcp_server/tools/rg/connect.py +537 -0
- awslabs/elasticache_mcp_server/tools/rg/create.py +318 -0
- awslabs/elasticache_mcp_server/tools/rg/delete.py +68 -0
- awslabs/elasticache_mcp_server/tools/rg/describe.py +68 -0
- awslabs/elasticache_mcp_server/tools/rg/modify.py +236 -0
- awslabs/elasticache_mcp_server/tools/rg/parsers.py +268 -0
- awslabs/elasticache_mcp_server/tools/rg/processors.py +227 -0
- awslabs/elasticache_mcp_server/tools/rg/start_migration.py +151 -0
- awslabs/elasticache_mcp_server/tools/rg/test_migration.py +139 -0
- awslabs/elasticache_mcp_server/tools/serverless/__init__.py +37 -0
- awslabs/elasticache_mcp_server/tools/serverless/connect.py +451 -0
- awslabs/elasticache_mcp_server/tools/serverless/create.py +174 -0
- awslabs/elasticache_mcp_server/tools/serverless/delete.py +49 -0
- awslabs/elasticache_mcp_server/tools/serverless/describe.py +69 -0
- awslabs/elasticache_mcp_server/tools/serverless/models.py +160 -0
- awslabs/elasticache_mcp_server/tools/serverless/modify.py +95 -0
- awslabs_elasticache_mcp_server-0.1.1.dist-info/METADATA +257 -0
- awslabs_elasticache_mcp_server-0.1.1.dist-info/RECORD +60 -0
- awslabs_elasticache_mcp_server-0.1.1.dist-info/WHEEL +4 -0
- awslabs_elasticache_mcp_server-0.1.1.dist-info/entry_points.txt +2 -0
- awslabs_elasticache_mcp_server-0.1.1.dist-info/licenses/LICENSE +175 -0
- awslabs_elasticache_mcp_server-0.1.1.dist-info/licenses/NOTICE +2 -0
|
@@ -0,0 +1,65 @@
|
|
|
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
|
+
"""Delete cache cluster tool for ElastiCache MCP server."""
|
|
16
|
+
|
|
17
|
+
from ...common.connection import ElastiCacheConnectionManager
|
|
18
|
+
from ...common.decorators import handle_exceptions
|
|
19
|
+
from ...common.server import mcp
|
|
20
|
+
from ...context import Context
|
|
21
|
+
from typing import Optional
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@mcp.tool(name='delete-cache-cluster')
|
|
25
|
+
@handle_exceptions
|
|
26
|
+
async def delete_cache_cluster(
|
|
27
|
+
cache_cluster_id: str,
|
|
28
|
+
final_snapshot_identifier: Optional[str] = None,
|
|
29
|
+
) -> dict:
|
|
30
|
+
"""Delete an Amazon ElastiCache cache cluster.
|
|
31
|
+
|
|
32
|
+
This tool deletes an existing cache cluster. Optionally, it can create a final
|
|
33
|
+
snapshot of the cluster before deletion.
|
|
34
|
+
|
|
35
|
+
Parameters:
|
|
36
|
+
cache_cluster_id (str): The ID of the cache cluster to delete.
|
|
37
|
+
final_snapshot_identifier (Optional[str]): The user-supplied name of a final
|
|
38
|
+
cache cluster snapshot. This is the unique name that identifies the
|
|
39
|
+
snapshot. ElastiCache creates the snapshot, and then deletes the cache
|
|
40
|
+
cluster immediately afterward.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
Dict containing information about the deleted cache cluster.
|
|
44
|
+
"""
|
|
45
|
+
# Check if readonly mode is enabled
|
|
46
|
+
if Context.readonly_mode():
|
|
47
|
+
raise ValueError(
|
|
48
|
+
'You have configured this tool in readonly mode. To make this change you will have to update your configuration.'
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# Get ElastiCache client
|
|
52
|
+
elasticache_client = ElastiCacheConnectionManager.get_connection()
|
|
53
|
+
|
|
54
|
+
# Build delete request
|
|
55
|
+
delete_request = {
|
|
56
|
+
'CacheClusterId': cache_cluster_id,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# Add optional final snapshot if provided
|
|
60
|
+
if final_snapshot_identifier:
|
|
61
|
+
delete_request['FinalSnapshotIdentifier'] = final_snapshot_identifier
|
|
62
|
+
|
|
63
|
+
# Delete the cache cluster
|
|
64
|
+
response = elasticache_client.delete_cache_cluster(**delete_request)
|
|
65
|
+
return response
|
|
@@ -0,0 +1,80 @@
|
|
|
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
|
+
"""Describe cache clusters tool for ElastiCache MCP server."""
|
|
16
|
+
|
|
17
|
+
from ...common.connection import ElastiCacheConnectionManager
|
|
18
|
+
from ...common.decorators import handle_exceptions
|
|
19
|
+
from ...common.server import mcp
|
|
20
|
+
from typing import Dict, Optional
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@mcp.tool(name='describe-cache-clusters')
|
|
24
|
+
@handle_exceptions
|
|
25
|
+
async def describe_cache_clusters(
|
|
26
|
+
cache_cluster_id: Optional[str] = None,
|
|
27
|
+
max_records: Optional[int] = None,
|
|
28
|
+
marker: Optional[str] = None,
|
|
29
|
+
show_cache_node_info: Optional[bool] = None,
|
|
30
|
+
show_cache_clusters_not_in_replication_groups: Optional[bool] = None,
|
|
31
|
+
) -> Dict:
|
|
32
|
+
"""Describe one or more ElastiCache cache clusters.
|
|
33
|
+
|
|
34
|
+
This tool returns information about provisioned cache clusters. If a cache cluster ID
|
|
35
|
+
is specified, information about only that cache cluster is returned. Otherwise, information
|
|
36
|
+
about up to MaxRecords cache clusters is returned.
|
|
37
|
+
|
|
38
|
+
Parameters:
|
|
39
|
+
cache_cluster_id (Optional[str]): The identifier for the cache cluster to describe.
|
|
40
|
+
If not provided, information about all cache clusters is returned.
|
|
41
|
+
max_records (Optional[int]): The maximum number of records to include in the response.
|
|
42
|
+
If more records exist than the specified MaxRecords value, a marker is included
|
|
43
|
+
in the response so that the remaining results can be retrieved.
|
|
44
|
+
marker (Optional[str]): An optional marker returned from a previous request. Use this marker
|
|
45
|
+
for pagination of results from this operation. If this parameter is specified,
|
|
46
|
+
the response includes only records beyond the marker, up to the value specified
|
|
47
|
+
by MaxRecords.
|
|
48
|
+
show_cache_node_info (Optional[bool]): Whether to include detailed information about
|
|
49
|
+
cache nodes in the response.
|
|
50
|
+
show_cache_clusters_not_in_replication_groups (Optional[bool]): Whether to show only
|
|
51
|
+
cache clusters that are not members of a replication group.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
Dict containing information about the cache cluster(s), including:
|
|
55
|
+
- CacheClusters: List of cache clusters
|
|
56
|
+
- Marker: Pagination marker for next set of results
|
|
57
|
+
"""
|
|
58
|
+
# Get ElastiCache client
|
|
59
|
+
elasticache_client = ElastiCacheConnectionManager.get_connection()
|
|
60
|
+
|
|
61
|
+
# Build describe request
|
|
62
|
+
describe_request = {}
|
|
63
|
+
|
|
64
|
+
# Add optional parameters if provided
|
|
65
|
+
if cache_cluster_id:
|
|
66
|
+
describe_request['CacheClusterId'] = cache_cluster_id
|
|
67
|
+
if max_records:
|
|
68
|
+
describe_request['MaxRecords'] = max_records
|
|
69
|
+
if marker:
|
|
70
|
+
describe_request['Marker'] = marker
|
|
71
|
+
if show_cache_node_info is not None:
|
|
72
|
+
describe_request['ShowCacheNodeInfo'] = show_cache_node_info
|
|
73
|
+
if show_cache_clusters_not_in_replication_groups is not None:
|
|
74
|
+
describe_request['ShowCacheClustersNotInReplicationGroups'] = (
|
|
75
|
+
show_cache_clusters_not_in_replication_groups
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Describe the cache cluster(s)
|
|
79
|
+
response = elasticache_client.describe_cache_clusters(**describe_request)
|
|
80
|
+
return response
|
|
@@ -0,0 +1,159 @@
|
|
|
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
|
+
"""Modify cache cluster tool for ElastiCache MCP server."""
|
|
16
|
+
|
|
17
|
+
from ...common.connection import ElastiCacheConnectionManager
|
|
18
|
+
from ...common.decorators import handle_exceptions
|
|
19
|
+
from ...common.server import mcp
|
|
20
|
+
from ...context import Context
|
|
21
|
+
from ..rg.processors import process_log_delivery_configurations
|
|
22
|
+
from .processors import process_scale_config
|
|
23
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
24
|
+
from typing import Any, Dict, List, Optional, Union
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ModifyCacheClusterRequest(BaseModel):
|
|
28
|
+
"""Request model for modifying an ElastiCache cache cluster."""
|
|
29
|
+
|
|
30
|
+
model_config = ConfigDict(validate_by_name=True, arbitrary_types_allowed=True)
|
|
31
|
+
|
|
32
|
+
cache_cluster_id: str = Field(..., description='The cache cluster to modify')
|
|
33
|
+
num_cache_nodes: Optional[int] = Field(None, description='The new number of cache nodes')
|
|
34
|
+
cache_node_ids_to_remove: Optional[List[str]] = Field(
|
|
35
|
+
None, description='Cache node IDs to remove when scaling down'
|
|
36
|
+
)
|
|
37
|
+
az_mode: Optional[str] = Field(
|
|
38
|
+
None,
|
|
39
|
+
description='Specifies whether nodes in this Memcached cluster are created in a single AZ or multiple AZs',
|
|
40
|
+
)
|
|
41
|
+
new_availability_zones: Optional[List[str]] = Field(
|
|
42
|
+
None, description='List of Availability Zones to use when modifying AZ mode'
|
|
43
|
+
)
|
|
44
|
+
cache_security_group_names: Optional[List[str]] = Field(
|
|
45
|
+
None, description='List of cache security group names to associate'
|
|
46
|
+
)
|
|
47
|
+
security_group_ids: Optional[List[str]] = Field(
|
|
48
|
+
None, description='List of Amazon VPC security group IDs'
|
|
49
|
+
)
|
|
50
|
+
preferred_maintenance_window: Optional[str] = Field(
|
|
51
|
+
None, description='The weekly time range for maintenance'
|
|
52
|
+
)
|
|
53
|
+
notification_topic_arn: Optional[str] = Field(
|
|
54
|
+
None, description='The ARN of an SNS topic for notifications'
|
|
55
|
+
)
|
|
56
|
+
cache_parameter_group_name: Optional[str] = Field(
|
|
57
|
+
None, description='The name of the cache parameter group to apply'
|
|
58
|
+
)
|
|
59
|
+
notification_topic_status: Optional[str] = Field(
|
|
60
|
+
None, description='The status of the SNS notification topic'
|
|
61
|
+
)
|
|
62
|
+
apply_immediately: Optional[bool] = Field(
|
|
63
|
+
None, description='Whether to apply changes immediately or during maintenance window'
|
|
64
|
+
)
|
|
65
|
+
engine_version: Optional[str] = Field(
|
|
66
|
+
None, description='The upgraded version of the cache engine'
|
|
67
|
+
)
|
|
68
|
+
auto_minor_version_upgrade: Optional[bool] = Field(
|
|
69
|
+
None, description='Enable/disable automatic minor version upgrades'
|
|
70
|
+
)
|
|
71
|
+
snapshot_retention_limit: Optional[int] = Field(
|
|
72
|
+
None, description='The number of days to retain backups'
|
|
73
|
+
)
|
|
74
|
+
snapshot_window: Optional[str] = Field(None, description='The daily time range for backups')
|
|
75
|
+
cache_node_type: Optional[str] = Field(
|
|
76
|
+
None, description='The new compute and memory capacity of the nodes'
|
|
77
|
+
)
|
|
78
|
+
auth_token: Optional[str] = Field(
|
|
79
|
+
None, description='The password used to access a password protected server'
|
|
80
|
+
)
|
|
81
|
+
auth_token_update_strategy: Optional[str] = Field(
|
|
82
|
+
None, description="Strategy to use when updating auth token ('SET', 'ROTATE', 'DELETE')"
|
|
83
|
+
)
|
|
84
|
+
log_delivery_configurations: Optional[Union[str, List[Dict]]] = Field(
|
|
85
|
+
None, description='Log delivery configurations'
|
|
86
|
+
)
|
|
87
|
+
scale_config: Optional[Union[str, Dict]] = Field(None, description='Scale configuration')
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@mcp.tool(name='modify-cache-cluster')
|
|
91
|
+
@handle_exceptions
|
|
92
|
+
async def modify_cache_cluster(request: ModifyCacheClusterRequest) -> Dict:
|
|
93
|
+
"""Modify an existing Amazon ElastiCache cache cluster."""
|
|
94
|
+
# Check if readonly mode is enabled
|
|
95
|
+
if Context.readonly_mode():
|
|
96
|
+
raise ValueError(
|
|
97
|
+
'You have configured this tool in readonly mode. To make this change you will have to update your configuration.'
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# Get ElastiCache client
|
|
101
|
+
elasticache_client = ElastiCacheConnectionManager.get_connection()
|
|
102
|
+
|
|
103
|
+
# Convert request model to dictionary, only including non-None values
|
|
104
|
+
modify_request: Dict[str, Any] = {'CacheClusterId': request.cache_cluster_id}
|
|
105
|
+
|
|
106
|
+
if request.num_cache_nodes is not None:
|
|
107
|
+
modify_request['NumCacheNodes'] = request.num_cache_nodes
|
|
108
|
+
if request.cache_node_ids_to_remove:
|
|
109
|
+
modify_request['CacheNodeIdsToRemove'] = request.cache_node_ids_to_remove
|
|
110
|
+
if request.az_mode:
|
|
111
|
+
modify_request['AZMode'] = request.az_mode
|
|
112
|
+
if request.new_availability_zones:
|
|
113
|
+
modify_request['NewAvailabilityZones'] = request.new_availability_zones
|
|
114
|
+
if request.cache_security_group_names:
|
|
115
|
+
modify_request['CacheSecurityGroupNames'] = request.cache_security_group_names
|
|
116
|
+
if request.security_group_ids:
|
|
117
|
+
modify_request['SecurityGroupIds'] = request.security_group_ids
|
|
118
|
+
if request.preferred_maintenance_window:
|
|
119
|
+
modify_request['PreferredMaintenanceWindow'] = request.preferred_maintenance_window
|
|
120
|
+
if request.notification_topic_arn:
|
|
121
|
+
modify_request['NotificationTopicArn'] = request.notification_topic_arn
|
|
122
|
+
if request.cache_parameter_group_name:
|
|
123
|
+
modify_request['CacheParameterGroupName'] = request.cache_parameter_group_name
|
|
124
|
+
if request.notification_topic_status:
|
|
125
|
+
modify_request['NotificationTopicStatus'] = request.notification_topic_status
|
|
126
|
+
if request.apply_immediately is not None:
|
|
127
|
+
modify_request['ApplyImmediately'] = request.apply_immediately
|
|
128
|
+
if request.engine_version:
|
|
129
|
+
modify_request['EngineVersion'] = request.engine_version
|
|
130
|
+
if request.auto_minor_version_upgrade is not None:
|
|
131
|
+
modify_request['AutoMinorVersionUpgrade'] = request.auto_minor_version_upgrade
|
|
132
|
+
if request.snapshot_retention_limit is not None:
|
|
133
|
+
modify_request['SnapshotRetentionLimit'] = request.snapshot_retention_limit
|
|
134
|
+
if request.snapshot_window:
|
|
135
|
+
modify_request['SnapshotWindow'] = request.snapshot_window
|
|
136
|
+
if request.cache_node_type:
|
|
137
|
+
modify_request['CacheNodeType'] = request.cache_node_type
|
|
138
|
+
if request.auth_token:
|
|
139
|
+
modify_request['AuthToken'] = request.auth_token
|
|
140
|
+
if request.auth_token_update_strategy:
|
|
141
|
+
modify_request['AuthTokenUpdateStrategy'] = request.auth_token_update_strategy
|
|
142
|
+
if request.log_delivery_configurations:
|
|
143
|
+
try:
|
|
144
|
+
processed_configs = process_log_delivery_configurations(
|
|
145
|
+
request.log_delivery_configurations
|
|
146
|
+
)
|
|
147
|
+
modify_request['LogDeliveryConfigurations'] = processed_configs
|
|
148
|
+
except ValueError as e:
|
|
149
|
+
return {'error': str(e)}
|
|
150
|
+
if request.scale_config:
|
|
151
|
+
try:
|
|
152
|
+
processed_scale_config = process_scale_config(request.scale_config)
|
|
153
|
+
modify_request['ScaleConfig'] = processed_scale_config
|
|
154
|
+
except ValueError as e:
|
|
155
|
+
return {'error': str(e)}
|
|
156
|
+
|
|
157
|
+
# Modify the cache cluster
|
|
158
|
+
response = elasticache_client.modify_cache_cluster(**modify_request)
|
|
159
|
+
return response
|
|
@@ -0,0 +1,78 @@
|
|
|
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
|
+
"""Parser functions for ElastiCache cache cluster tools."""
|
|
16
|
+
|
|
17
|
+
from typing import Any, Dict
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def parse_shorthand_scale_config(config: str) -> Dict[str, Any]:
|
|
21
|
+
"""Parse a scale configuration from shorthand syntax.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
config: Shorthand syntax string for scale configuration
|
|
25
|
+
Format: ReplicasPerNodeGroup=int,AutomaticFailoverEnabled=bool,ScaleOutEnabled=bool,
|
|
26
|
+
ScaleInEnabled=bool,TargetCapacity=int,MinCapacity=int,MaxCapacity=int
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
Dictionary containing the parsed scale configuration
|
|
30
|
+
|
|
31
|
+
Raises:
|
|
32
|
+
ValueError: If the syntax is invalid
|
|
33
|
+
"""
|
|
34
|
+
if not config:
|
|
35
|
+
raise ValueError('Empty scale configuration')
|
|
36
|
+
|
|
37
|
+
result = {}
|
|
38
|
+
pairs = config.split(',')
|
|
39
|
+
|
|
40
|
+
# Define valid keys and their processors
|
|
41
|
+
key_processors = {
|
|
42
|
+
'ReplicasPerNodeGroup': int,
|
|
43
|
+
'AutomaticFailoverEnabled': lambda x: x.lower() == 'true',
|
|
44
|
+
'ScaleOutEnabled': lambda x: x.lower() == 'true',
|
|
45
|
+
'ScaleInEnabled': lambda x: x.lower() == 'true',
|
|
46
|
+
'TargetCapacity': int,
|
|
47
|
+
'MinCapacity': int,
|
|
48
|
+
'MaxCapacity': int,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
for pair in pairs:
|
|
52
|
+
if '=' not in pair:
|
|
53
|
+
raise ValueError(f'Invalid format. Each parameter must be in key=value format: {pair}')
|
|
54
|
+
|
|
55
|
+
key, value = pair.split('=', 1)
|
|
56
|
+
if not key or not value:
|
|
57
|
+
raise ValueError(f'Empty key or value: {pair}')
|
|
58
|
+
|
|
59
|
+
if key not in key_processors:
|
|
60
|
+
raise ValueError(f'Invalid parameter: {key}')
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
result[key] = key_processors[key](value)
|
|
64
|
+
except ValueError as e:
|
|
65
|
+
raise ValueError(f'Invalid value for {key}: {value}') from e
|
|
66
|
+
|
|
67
|
+
# Validate capacity values if present
|
|
68
|
+
if 'MinCapacity' in result and 'MaxCapacity' in result:
|
|
69
|
+
if result['MinCapacity'] > result['MaxCapacity']:
|
|
70
|
+
raise ValueError('MinCapacity cannot be greater than MaxCapacity')
|
|
71
|
+
if 'TargetCapacity' in result:
|
|
72
|
+
if (
|
|
73
|
+
result['TargetCapacity'] < result['MinCapacity']
|
|
74
|
+
or result['TargetCapacity'] > result['MaxCapacity']
|
|
75
|
+
):
|
|
76
|
+
raise ValueError('TargetCapacity must be between MinCapacity and MaxCapacity')
|
|
77
|
+
|
|
78
|
+
return result
|
|
@@ -0,0 +1,74 @@
|
|
|
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
|
+
"""Processor functions for ElastiCache cache cluster tools."""
|
|
16
|
+
|
|
17
|
+
from .parsers import parse_shorthand_scale_config
|
|
18
|
+
from typing import Dict, Union
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def process_scale_config(scale_config: Union[str, Dict]) -> Dict:
|
|
22
|
+
"""Process scale configuration in either shorthand or JSON format.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
scale_config: Scale configuration in either format
|
|
26
|
+
Shorthand format: "ReplicasPerNodeGroup=int,AutomaticFailoverEnabled=bool,..."
|
|
27
|
+
JSON format: Dictionary with scale configuration parameters
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
Dictionary containing the processed scale configuration
|
|
31
|
+
|
|
32
|
+
Raises:
|
|
33
|
+
ValueError: If the configuration is invalid
|
|
34
|
+
"""
|
|
35
|
+
if isinstance(scale_config, str):
|
|
36
|
+
# Parse shorthand syntax
|
|
37
|
+
try:
|
|
38
|
+
return parse_shorthand_scale_config(scale_config)
|
|
39
|
+
except ValueError as e:
|
|
40
|
+
raise ValueError(f'Invalid scale config shorthand syntax: {str(e)}')
|
|
41
|
+
else:
|
|
42
|
+
# Handle JSON format
|
|
43
|
+
if not isinstance(scale_config, dict):
|
|
44
|
+
raise ValueError('Scale configuration must be a dictionary or a shorthand string')
|
|
45
|
+
|
|
46
|
+
# Validate required fields and types
|
|
47
|
+
field_types = {
|
|
48
|
+
'ReplicasPerNodeGroup': int,
|
|
49
|
+
'AutomaticFailoverEnabled': bool,
|
|
50
|
+
'ScaleOutEnabled': bool,
|
|
51
|
+
'ScaleInEnabled': bool,
|
|
52
|
+
'TargetCapacity': int,
|
|
53
|
+
'MinCapacity': int,
|
|
54
|
+
'MaxCapacity': int,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
processed_config = {}
|
|
58
|
+
for field, field_type in field_types.items():
|
|
59
|
+
if field in scale_config:
|
|
60
|
+
if not isinstance(scale_config[field], field_type):
|
|
61
|
+
raise ValueError(f'{field} must be of type {field_type.__name__}')
|
|
62
|
+
processed_config[field] = scale_config[field]
|
|
63
|
+
|
|
64
|
+
# Validate capacity values if present
|
|
65
|
+
if 'MinCapacity' in processed_config and 'MaxCapacity' in processed_config:
|
|
66
|
+
if processed_config['MinCapacity'] > processed_config['MaxCapacity']:
|
|
67
|
+
raise ValueError('MinCapacity cannot be greater than MaxCapacity')
|
|
68
|
+
if 'TargetCapacity' in processed_config and (
|
|
69
|
+
processed_config['TargetCapacity'] < processed_config['MinCapacity']
|
|
70
|
+
or processed_config['TargetCapacity'] > processed_config['MaxCapacity']
|
|
71
|
+
):
|
|
72
|
+
raise ValueError('TargetCapacity must be between MinCapacity and MaxCapacity')
|
|
73
|
+
|
|
74
|
+
return processed_config
|
|
@@ -0,0 +1,19 @@
|
|
|
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
|
+
"""Cost Explorer tools for ElastiCache MCP server."""
|
|
16
|
+
|
|
17
|
+
from .get_cost_and_usage import get_cost_and_usage, GetCostAndUsageRequest
|
|
18
|
+
|
|
19
|
+
__all__ = ['get_cost_and_usage', 'GetCostAndUsageRequest']
|
|
@@ -0,0 +1,76 @@
|
|
|
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
|
+
"""Get cost and usage data for ElastiCache resources."""
|
|
16
|
+
|
|
17
|
+
from ...common.connection import CostExplorerConnectionManager
|
|
18
|
+
from ...common.decorators import handle_exceptions
|
|
19
|
+
from ...common.server import mcp
|
|
20
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
21
|
+
from typing import Any, Dict
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class GetCostAndUsageRequest(BaseModel):
|
|
25
|
+
"""Request model for getting cost and usage data."""
|
|
26
|
+
|
|
27
|
+
model_config = ConfigDict(validate_by_name=True, arbitrary_types_allowed=True)
|
|
28
|
+
|
|
29
|
+
time_period: str = Field(
|
|
30
|
+
..., description='Time period for the cost and usage data. Format: YYYY-MM-DD/YYYY-MM-DD'
|
|
31
|
+
)
|
|
32
|
+
granularity: str = Field(
|
|
33
|
+
..., description='The granularity of the cost and usage data (DAILY, MONTHLY, or HOURLY)'
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@mcp.tool(name='get-cost-and-usage')
|
|
38
|
+
@handle_exceptions
|
|
39
|
+
async def get_cost_and_usage(request: GetCostAndUsageRequest) -> Dict[str, Any]:
|
|
40
|
+
"""Get cost and usage data for ElastiCache resources.
|
|
41
|
+
|
|
42
|
+
This tool retrieves cost and usage data for ElastiCache resources with customizable
|
|
43
|
+
time periods and granularity. It uses default configurations for:
|
|
44
|
+
- Metrics: BlendedCost, UnblendedCost, UsageQuantity
|
|
45
|
+
- Group By: SERVICE dimension and Environment tag
|
|
46
|
+
- Filter: Filtered to Amazon ElastiCache service
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
request: The GetCostAndUsageRequest object containing:
|
|
50
|
+
- time_period: Time period in YYYY-MM-DD/YYYY-MM-DD format
|
|
51
|
+
- granularity: Data granularity (DAILY, MONTHLY, or HOURLY)
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
Dict containing the cost and usage data.
|
|
55
|
+
"""
|
|
56
|
+
# Get Cost Explorer client
|
|
57
|
+
ce_client = CostExplorerConnectionManager.get_connection()
|
|
58
|
+
|
|
59
|
+
# Split time period into start and end dates
|
|
60
|
+
start_date, end_date = request.time_period.split('/')
|
|
61
|
+
|
|
62
|
+
# Prepare request parameters
|
|
63
|
+
params = {
|
|
64
|
+
'TimePeriod': {'Start': start_date, 'End': end_date},
|
|
65
|
+
'Granularity': request.granularity,
|
|
66
|
+
'Metrics': ['BlendedCost', 'UnblendedCost', 'UsageQuantity'],
|
|
67
|
+
'GroupBy': [
|
|
68
|
+
{'Type': 'DIMENSION', 'Key': 'SERVICE'},
|
|
69
|
+
{'Type': 'TAG', 'Key': 'Environment'},
|
|
70
|
+
],
|
|
71
|
+
'Filter': {'Dimensions': {'Key': 'SERVICE', 'Values': ['Amazon ElastiCache']}},
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
# Get cost and usage data
|
|
75
|
+
response = ce_client.get_cost_and_usage(**params)
|
|
76
|
+
return response
|
|
@@ -0,0 +1,19 @@
|
|
|
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 tools."""
|
|
16
|
+
|
|
17
|
+
from .get_metric_statistics import get_metric_statistics
|
|
18
|
+
|
|
19
|
+
__all__ = ['get_metric_statistics']
|
|
@@ -0,0 +1,85 @@
|
|
|
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
|
+
"""Tool for getting CloudWatch metric statistics."""
|
|
16
|
+
|
|
17
|
+
from ...common.connection import CloudWatchConnectionManager
|
|
18
|
+
from ...common.decorators import handle_exceptions
|
|
19
|
+
from ...common.server import mcp
|
|
20
|
+
from datetime import datetime
|
|
21
|
+
from typing import Any, Dict, List, Optional
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@mcp.tool(name='get-metric-statistics')
|
|
25
|
+
@handle_exceptions
|
|
26
|
+
async def get_metric_statistics(
|
|
27
|
+
metric_name: str,
|
|
28
|
+
start_time: str,
|
|
29
|
+
end_time: str,
|
|
30
|
+
period: int,
|
|
31
|
+
dimensions: Optional[List[Dict[str, str]]] = None,
|
|
32
|
+
statistics: Optional[List[str]] = None,
|
|
33
|
+
extended_statistics: Optional[List[str]] = None,
|
|
34
|
+
unit: Optional[str] = None,
|
|
35
|
+
) -> Dict[str, Any]:
|
|
36
|
+
"""Get CloudWatch metric statistics.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
metric_name: The name of the metric
|
|
40
|
+
start_time: The start time in ISO 8601 format
|
|
41
|
+
end_time: The end time in ISO 8601 format
|
|
42
|
+
period: The granularity, in seconds, of the returned data points
|
|
43
|
+
dimensions: The dimensions to filter by
|
|
44
|
+
statistics: The metric statistics to return
|
|
45
|
+
extended_statistics: The percentile statistics to return
|
|
46
|
+
unit: The unit for the metric
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Dict containing metric statistics
|
|
50
|
+
"""
|
|
51
|
+
client = CloudWatchConnectionManager.get_connection()
|
|
52
|
+
|
|
53
|
+
# Convert ISO 8601 strings to datetime objects
|
|
54
|
+
start = datetime.fromisoformat(start_time.replace('Z', '+00:00'))
|
|
55
|
+
end = datetime.fromisoformat(end_time.replace('Z', '+00:00'))
|
|
56
|
+
|
|
57
|
+
# Build request parameters
|
|
58
|
+
params: Dict[str, Any] = {
|
|
59
|
+
'Namespace': 'AWS/ElastiCache',
|
|
60
|
+
'MetricName': metric_name,
|
|
61
|
+
'StartTime': start,
|
|
62
|
+
'EndTime': end,
|
|
63
|
+
'Period': period,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
# Add optional parameters
|
|
67
|
+
if dimensions:
|
|
68
|
+
params['Dimensions'] = [{'Name': k, 'Value': v} for d in dimensions for k, v in d.items()]
|
|
69
|
+
if statistics:
|
|
70
|
+
params['Statistics'] = statistics
|
|
71
|
+
if extended_statistics:
|
|
72
|
+
params['ExtendedStatistics'] = extended_statistics
|
|
73
|
+
if unit:
|
|
74
|
+
params['Unit'] = unit
|
|
75
|
+
|
|
76
|
+
# Make API call
|
|
77
|
+
response = client.get_metric_statistics(**params)
|
|
78
|
+
|
|
79
|
+
# Extract relevant information
|
|
80
|
+
datapoints = response.get('Datapoints', [])
|
|
81
|
+
label = response.get('Label')
|
|
82
|
+
|
|
83
|
+
result = {'Label': label, 'Datapoints': datapoints}
|
|
84
|
+
|
|
85
|
+
return result
|