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,318 @@
|
|
|
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
|
+
"""Create replication group 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 .processors import process_log_delivery_configurations, process_nodegroup_configuration
|
|
22
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
23
|
+
from typing import Any, Dict, List, Optional, Union
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Tag(BaseModel):
|
|
27
|
+
"""Tag model for ElastiCache resources."""
|
|
28
|
+
|
|
29
|
+
Key: str = Field(..., description='The key for the tag')
|
|
30
|
+
Value: Optional[str] = Field(None, description="The tag's value")
|
|
31
|
+
model_config = ConfigDict(validate_by_name=True, arbitrary_types_allowed=True)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class NodeGroupConfiguration(BaseModel):
|
|
35
|
+
"""Node group configuration model."""
|
|
36
|
+
|
|
37
|
+
NodeGroupId: Optional[str] = Field(None, description='The identifier for the node group')
|
|
38
|
+
ReplicaCount: Optional[int] = Field(None, description='The number of replica nodes')
|
|
39
|
+
Slots: Optional[str] = Field(None, description='The keyspace for the node group')
|
|
40
|
+
PrimaryAvailabilityZone: Optional[str] = Field(
|
|
41
|
+
None, description='The Availability Zone where the primary node will be launched'
|
|
42
|
+
)
|
|
43
|
+
ReplicaAvailabilityZones: Optional[List[str]] = Field(
|
|
44
|
+
None, description='A list of Availability Zones where the replica nodes will be launched'
|
|
45
|
+
)
|
|
46
|
+
model_config = ConfigDict(validate_by_name=True, arbitrary_types_allowed=True)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class LogDeliveryDestinationDetails(BaseModel):
|
|
50
|
+
"""Log delivery destination details model."""
|
|
51
|
+
|
|
52
|
+
CloudWatchLogsDetails: Optional[Dict] = Field(
|
|
53
|
+
None, description='The configuration details of CloudWatch Logs destination'
|
|
54
|
+
)
|
|
55
|
+
KinesisFirehoseDetails: Optional[Dict] = Field(
|
|
56
|
+
None, description='The configuration details of Kinesis Data Firehose destination'
|
|
57
|
+
)
|
|
58
|
+
model_config = ConfigDict(validate_by_name=True, arbitrary_types_allowed=True)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class LogDeliveryConfiguration(BaseModel):
|
|
62
|
+
"""Log delivery configuration model."""
|
|
63
|
+
|
|
64
|
+
LogType: str = Field(..., description='The type of log to deliver')
|
|
65
|
+
DestinationType: str = Field(..., description='The type of destination to deliver to')
|
|
66
|
+
DestinationDetails: LogDeliveryDestinationDetails = Field(
|
|
67
|
+
..., description='The configuration details of the destination'
|
|
68
|
+
)
|
|
69
|
+
LogFormat: str = Field(..., description='The format of the logs')
|
|
70
|
+
Enabled: bool = Field(..., description='Whether log delivery is enabled')
|
|
71
|
+
model_config = ConfigDict(validate_by_name=True, arbitrary_types_allowed=True)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class CreateReplicationGroupRequest(BaseModel):
|
|
75
|
+
"""Request model for creating an ElastiCache replication group."""
|
|
76
|
+
|
|
77
|
+
model_config = ConfigDict(validate_by_name=True, arbitrary_types_allowed=True)
|
|
78
|
+
|
|
79
|
+
replication_group_id: str = Field(..., description='The identifier of the replication group')
|
|
80
|
+
replication_group_description: str = Field(
|
|
81
|
+
..., description='The description of the replication group'
|
|
82
|
+
)
|
|
83
|
+
cache_node_type: Optional[str] = Field(
|
|
84
|
+
None, description='The compute and memory capacity of nodes'
|
|
85
|
+
)
|
|
86
|
+
engine: Optional[str] = Field(None, description='The name of the cache engine')
|
|
87
|
+
engine_version: Optional[str] = Field(
|
|
88
|
+
None, description='The version number of the cache engine'
|
|
89
|
+
)
|
|
90
|
+
num_cache_clusters: Optional[int] = Field(None, description='The number of cache clusters')
|
|
91
|
+
preferred_cache_cluster_azs: Optional[List[str]] = Field(
|
|
92
|
+
None, description='List of Availability Zones'
|
|
93
|
+
)
|
|
94
|
+
num_node_groups: Optional[int] = Field(None, description='The number of node groups')
|
|
95
|
+
replicas_per_node_group: Optional[int] = Field(
|
|
96
|
+
None, description='The number of replica nodes in each node group'
|
|
97
|
+
)
|
|
98
|
+
node_group_configuration: Optional[Union[str, List[NodeGroupConfiguration]]] = Field(
|
|
99
|
+
None, description='Configuration for each node group'
|
|
100
|
+
)
|
|
101
|
+
cache_parameter_group_name: Optional[str] = Field(
|
|
102
|
+
None, description='The name of the parameter group to associate'
|
|
103
|
+
)
|
|
104
|
+
cache_subnet_group_name: Optional[str] = Field(
|
|
105
|
+
None, description='The name of the cache subnet group to use'
|
|
106
|
+
)
|
|
107
|
+
cache_security_group_names: Optional[List[str]] = Field(
|
|
108
|
+
None, description='List of cache security group names'
|
|
109
|
+
)
|
|
110
|
+
security_group_ids: Optional[List[str]] = Field(
|
|
111
|
+
None, description='List of Amazon VPC security group IDs'
|
|
112
|
+
)
|
|
113
|
+
tags: Optional[Union[str, List[Tag], Dict[str, Optional[str]]]] = Field(
|
|
114
|
+
None, description='Tags to apply to the replication group'
|
|
115
|
+
)
|
|
116
|
+
snapshot_arns: Optional[List[str]] = Field(
|
|
117
|
+
None, description='List of ARNs of snapshots to restore from'
|
|
118
|
+
)
|
|
119
|
+
snapshot_name: Optional[str] = Field(
|
|
120
|
+
None, description='The name of a snapshot to restore from'
|
|
121
|
+
)
|
|
122
|
+
preferred_maintenance_window: Optional[str] = Field(
|
|
123
|
+
None, description='The weekly time range for maintenance'
|
|
124
|
+
)
|
|
125
|
+
port: Optional[int] = Field(
|
|
126
|
+
None, description='The port number on which the cache accepts connections'
|
|
127
|
+
)
|
|
128
|
+
notification_topic_arn: Optional[str] = Field(
|
|
129
|
+
None, description='The ARN of an SNS topic for notifications'
|
|
130
|
+
)
|
|
131
|
+
auto_minor_version_upgrade: Optional[bool] = Field(
|
|
132
|
+
None, description='Enable/disable automatic minor version upgrades'
|
|
133
|
+
)
|
|
134
|
+
snapshot_retention_limit: Optional[int] = Field(
|
|
135
|
+
None, description='The number of days to retain backups'
|
|
136
|
+
)
|
|
137
|
+
snapshot_window: Optional[str] = Field(None, description='The daily time range for backups')
|
|
138
|
+
auth_token: Optional[str] = Field(
|
|
139
|
+
None, description='Password used to access a password protected server'
|
|
140
|
+
)
|
|
141
|
+
transit_encryption_enabled: Optional[bool] = Field(
|
|
142
|
+
None, description='Enable/disable encryption in transit'
|
|
143
|
+
)
|
|
144
|
+
at_rest_encryption_enabled: Optional[bool] = Field(
|
|
145
|
+
None, description='Enable/disable encryption at rest'
|
|
146
|
+
)
|
|
147
|
+
kms_key_id: Optional[str] = Field(
|
|
148
|
+
None, description='The ID of the KMS key used to encrypt the disk'
|
|
149
|
+
)
|
|
150
|
+
user_group_ids: Optional[List[str]] = Field(
|
|
151
|
+
None, description='List of user group IDs to associate'
|
|
152
|
+
)
|
|
153
|
+
log_delivery_configurations: Optional[Union[str, List[LogDeliveryConfiguration]]] = Field(
|
|
154
|
+
None, description='Log delivery configurations'
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def prepare_request_dict(request: CreateReplicationGroupRequest) -> Dict[str, Any]:
|
|
159
|
+
"""Prepare the request dictionary for the AWS API.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
request: The CreateReplicationGroupRequest object
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
Dict containing the properly formatted request parameters
|
|
166
|
+
"""
|
|
167
|
+
# Start with required parameters
|
|
168
|
+
create_request: Dict[str, Any] = {
|
|
169
|
+
'ReplicationGroupId': request.replication_group_id,
|
|
170
|
+
'ReplicationGroupDescription': request.replication_group_description,
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
# Optional string parameters
|
|
174
|
+
for param_name, value in [
|
|
175
|
+
('CacheNodeType', request.cache_node_type),
|
|
176
|
+
('Engine', request.engine),
|
|
177
|
+
('EngineVersion', request.engine_version),
|
|
178
|
+
('CacheParameterGroupName', request.cache_parameter_group_name),
|
|
179
|
+
('CacheSubnetGroupName', request.cache_subnet_group_name),
|
|
180
|
+
('SnapshotName', request.snapshot_name),
|
|
181
|
+
('PreferredMaintenanceWindow', request.preferred_maintenance_window),
|
|
182
|
+
('NotificationTopicArn', request.notification_topic_arn),
|
|
183
|
+
('SnapshotWindow', request.snapshot_window),
|
|
184
|
+
('AuthToken', request.auth_token),
|
|
185
|
+
('KmsKeyId', request.kms_key_id),
|
|
186
|
+
]:
|
|
187
|
+
if value:
|
|
188
|
+
create_request[param_name] = str(value)
|
|
189
|
+
|
|
190
|
+
# Optional numeric parameters
|
|
191
|
+
for param_name, value in [
|
|
192
|
+
('NumCacheClusters', request.num_cache_clusters),
|
|
193
|
+
('NumNodeGroups', request.num_node_groups),
|
|
194
|
+
('ReplicasPerNodeGroup', request.replicas_per_node_group),
|
|
195
|
+
('Port', request.port),
|
|
196
|
+
('SnapshotRetentionLimit', request.snapshot_retention_limit),
|
|
197
|
+
]:
|
|
198
|
+
if value is not None:
|
|
199
|
+
create_request[param_name] = value
|
|
200
|
+
|
|
201
|
+
# Optional boolean parameters
|
|
202
|
+
for param_name, value in [
|
|
203
|
+
('AutoMinorVersionUpgrade', request.auto_minor_version_upgrade),
|
|
204
|
+
('TransitEncryptionEnabled', request.transit_encryption_enabled),
|
|
205
|
+
('AtRestEncryptionEnabled', request.at_rest_encryption_enabled),
|
|
206
|
+
]:
|
|
207
|
+
if value is not None:
|
|
208
|
+
create_request[param_name] = value
|
|
209
|
+
|
|
210
|
+
# Optional list parameters
|
|
211
|
+
for param_name, value in [
|
|
212
|
+
('PreferredCacheClusterAZs', request.preferred_cache_cluster_azs),
|
|
213
|
+
('CacheSecurityGroupNames', request.cache_security_group_names),
|
|
214
|
+
('SecurityGroupIds', request.security_group_ids),
|
|
215
|
+
('SnapshotArns', request.snapshot_arns),
|
|
216
|
+
('UserGroupIds', request.user_group_ids),
|
|
217
|
+
]:
|
|
218
|
+
if value:
|
|
219
|
+
create_request[param_name] = list(value)
|
|
220
|
+
|
|
221
|
+
# Handle node group configuration
|
|
222
|
+
if request.node_group_configuration:
|
|
223
|
+
if isinstance(request.node_group_configuration, list):
|
|
224
|
+
configs = [
|
|
225
|
+
config.model_dump(exclude_none=True) for config in request.node_group_configuration
|
|
226
|
+
]
|
|
227
|
+
create_request['NodeGroupConfiguration'] = configs
|
|
228
|
+
else:
|
|
229
|
+
processed_config = process_nodegroup_configuration(request.node_group_configuration)
|
|
230
|
+
if processed_config:
|
|
231
|
+
create_request['NodeGroupConfiguration'] = processed_config
|
|
232
|
+
|
|
233
|
+
# Handle tags
|
|
234
|
+
if request.tags:
|
|
235
|
+
if isinstance(request.tags, str):
|
|
236
|
+
# Parse shorthand syntax: Key=string,Value=string
|
|
237
|
+
tag_list = []
|
|
238
|
+
try:
|
|
239
|
+
pairs = [p.strip() for p in request.tags.split(',') if p.strip()]
|
|
240
|
+
for pair in pairs:
|
|
241
|
+
if '=' not in pair:
|
|
242
|
+
raise ValueError(
|
|
243
|
+
'Invalid tag format. Each tag must be in Key=Value format'
|
|
244
|
+
)
|
|
245
|
+
key, value = pair.split('=', 1)
|
|
246
|
+
key = key.strip()
|
|
247
|
+
value = value.strip() if value.strip() else None
|
|
248
|
+
if not key:
|
|
249
|
+
raise ValueError('Tag key cannot be empty')
|
|
250
|
+
tag_list.append({'Key': key, 'Value': value})
|
|
251
|
+
create_request['Tags'] = tag_list
|
|
252
|
+
except Exception as e:
|
|
253
|
+
raise ValueError(
|
|
254
|
+
f'Invalid tag shorthand syntax. Expected format: Key=string,Value=string. Error: {str(e)}'
|
|
255
|
+
)
|
|
256
|
+
elif isinstance(request.tags, dict):
|
|
257
|
+
# Handle dictionary format
|
|
258
|
+
tag_list = []
|
|
259
|
+
for k, v in request.tags.items():
|
|
260
|
+
if not k:
|
|
261
|
+
raise ValueError('Tag key cannot be empty')
|
|
262
|
+
tag_list.append({'Key': k, 'Value': v})
|
|
263
|
+
create_request['Tags'] = tag_list
|
|
264
|
+
elif isinstance(request.tags, list):
|
|
265
|
+
create_request['Tags'] = [tag.model_dump(exclude_none=True) for tag in request.tags]
|
|
266
|
+
|
|
267
|
+
# Handle log delivery configurations
|
|
268
|
+
if request.log_delivery_configurations:
|
|
269
|
+
if isinstance(request.log_delivery_configurations, list):
|
|
270
|
+
configs = [
|
|
271
|
+
config.model_dump(exclude_none=True)
|
|
272
|
+
for config in request.log_delivery_configurations
|
|
273
|
+
]
|
|
274
|
+
create_request['LogDeliveryConfigurations'] = configs
|
|
275
|
+
else:
|
|
276
|
+
processed_configs = process_log_delivery_configurations(
|
|
277
|
+
request.log_delivery_configurations
|
|
278
|
+
)
|
|
279
|
+
if processed_configs:
|
|
280
|
+
create_request['LogDeliveryConfigurations'] = processed_configs
|
|
281
|
+
|
|
282
|
+
return create_request
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
@mcp.tool(name='create-replication-group')
|
|
286
|
+
@handle_exceptions
|
|
287
|
+
async def create_replication_group(request: CreateReplicationGroupRequest) -> Dict:
|
|
288
|
+
"""Create an Amazon ElastiCache replication group.
|
|
289
|
+
|
|
290
|
+
This tool creates a new replication group with specified configuration including:
|
|
291
|
+
- Basic replication group settings
|
|
292
|
+
- Cache node configuration
|
|
293
|
+
- Network and security settings
|
|
294
|
+
- Encryption settings
|
|
295
|
+
- Backup and maintenance settings
|
|
296
|
+
- Monitoring and logging settings
|
|
297
|
+
|
|
298
|
+
Args:
|
|
299
|
+
request: The CreateReplicationGroupRequest object containing all parameters
|
|
300
|
+
|
|
301
|
+
Returns:
|
|
302
|
+
Dict containing information about the created replication group.
|
|
303
|
+
"""
|
|
304
|
+
# Check if readonly mode is enabled
|
|
305
|
+
if Context.readonly_mode():
|
|
306
|
+
raise ValueError(
|
|
307
|
+
'You have configured this tool in readonly mode. To make this change you will have to update your configuration.'
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
# Get ElastiCache client
|
|
311
|
+
elasticache_client = ElastiCacheConnectionManager.get_connection()
|
|
312
|
+
|
|
313
|
+
# Prepare request dictionary
|
|
314
|
+
create_request = prepare_request_dict(request)
|
|
315
|
+
|
|
316
|
+
# Create the replication group
|
|
317
|
+
response = elasticache_client.create_replication_group(**create_request)
|
|
318
|
+
return response
|
|
@@ -0,0 +1,68 @@
|
|
|
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 replication group 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 Dict, Optional
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@mcp.tool(name='delete-replication-group')
|
|
25
|
+
@handle_exceptions
|
|
26
|
+
async def delete_replication_group(
|
|
27
|
+
replication_group_id: str,
|
|
28
|
+
retain_primary_cluster: Optional[bool] = None,
|
|
29
|
+
final_snapshot_name: Optional[str] = None,
|
|
30
|
+
) -> Dict:
|
|
31
|
+
"""Delete an Amazon ElastiCache replication group.
|
|
32
|
+
|
|
33
|
+
This tool deletes an existing replication group. You can optionally retain the primary cluster
|
|
34
|
+
as a standalone cache cluster or create a final snapshot before deletion.
|
|
35
|
+
|
|
36
|
+
Parameters:
|
|
37
|
+
replication_group_id (str): The identifier of the replication group to delete.
|
|
38
|
+
retain_primary_cluster (Optional[bool]): If True, retains the primary cluster as a standalone
|
|
39
|
+
cache cluster. If False, deletes all clusters in the replication group.
|
|
40
|
+
final_snapshot_name (Optional[str]): The name of a final cache cluster snapshot to create
|
|
41
|
+
before deleting the replication group.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
Dict containing information about the deleted replication group.
|
|
45
|
+
"""
|
|
46
|
+
# Check if readonly mode is enabled
|
|
47
|
+
if Context.readonly_mode():
|
|
48
|
+
raise ValueError(
|
|
49
|
+
'You have configured this tool in readonly mode. To make this change you will have to update your configuration.'
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Get ElastiCache client
|
|
53
|
+
elasticache_client = ElastiCacheConnectionManager.get_connection()
|
|
54
|
+
|
|
55
|
+
# Build delete request
|
|
56
|
+
delete_request = {
|
|
57
|
+
'ReplicationGroupId': replication_group_id,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# Add optional parameters if provided
|
|
61
|
+
if retain_primary_cluster is not None:
|
|
62
|
+
delete_request['RetainPrimaryCluster'] = str(retain_primary_cluster).lower()
|
|
63
|
+
if final_snapshot_name:
|
|
64
|
+
delete_request['FinalSnapshotIdentifier'] = final_snapshot_name
|
|
65
|
+
|
|
66
|
+
# Delete the replication group
|
|
67
|
+
response = elasticache_client.delete_replication_group(**delete_request)
|
|
68
|
+
return response
|
|
@@ -0,0 +1,68 @@
|
|
|
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 replication groups 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-replication-groups')
|
|
24
|
+
@handle_exceptions
|
|
25
|
+
async def describe_replication_groups(
|
|
26
|
+
replication_group_id: Optional[str] = None,
|
|
27
|
+
max_records: Optional[int] = None,
|
|
28
|
+
marker: Optional[str] = None,
|
|
29
|
+
) -> Dict:
|
|
30
|
+
"""Describe one or more ElastiCache replication groups.
|
|
31
|
+
|
|
32
|
+
This tool returns information about provisioned replication groups. If a replication group ID
|
|
33
|
+
is specified, information about only that replication group is returned. Otherwise, information
|
|
34
|
+
about up to MaxRecords replication groups is returned.
|
|
35
|
+
|
|
36
|
+
Parameters:
|
|
37
|
+
replication_group_id (Optional[str]): The identifier for the replication group to describe.
|
|
38
|
+
If not provided, information about all replication groups is returned.
|
|
39
|
+
max_records (Optional[int]): The maximum number of records to include in the response.
|
|
40
|
+
If more records exist than the specified MaxRecords value, a marker is included
|
|
41
|
+
in the response so that the remaining results can be retrieved.
|
|
42
|
+
marker (Optional[str]): An optional marker returned from a previous request. Use this marker
|
|
43
|
+
for pagination of results from this operation. If this parameter is specified,
|
|
44
|
+
the response includes only records beyond the marker, up to the value specified
|
|
45
|
+
by MaxRecords.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
Dict containing information about the replication group(s), including:
|
|
49
|
+
- ReplicationGroups: List of replication groups
|
|
50
|
+
- Marker: Pagination marker for next set of results
|
|
51
|
+
"""
|
|
52
|
+
# Get ElastiCache client
|
|
53
|
+
elasticache_client = ElastiCacheConnectionManager.get_connection()
|
|
54
|
+
|
|
55
|
+
# Build describe request
|
|
56
|
+
describe_request = {}
|
|
57
|
+
|
|
58
|
+
# Add optional parameters if provided
|
|
59
|
+
if replication_group_id:
|
|
60
|
+
describe_request['ReplicationGroupId'] = replication_group_id
|
|
61
|
+
if max_records:
|
|
62
|
+
describe_request['MaxRecords'] = max_records
|
|
63
|
+
if marker:
|
|
64
|
+
describe_request['Marker'] = marker
|
|
65
|
+
|
|
66
|
+
# Describe the replication group(s)
|
|
67
|
+
response = elasticache_client.describe_replication_groups(**describe_request)
|
|
68
|
+
return response
|
|
@@ -0,0 +1,236 @@
|
|
|
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 replication group tool for ElastiCache MCP server."""
|
|
16
|
+
|
|
17
|
+
import json
|
|
18
|
+
from ...common.connection import ElastiCacheConnectionManager
|
|
19
|
+
from ...common.decorators import handle_exceptions
|
|
20
|
+
from ...common.server import mcp
|
|
21
|
+
from ...context import Context
|
|
22
|
+
from .processors import process_log_delivery_configurations, process_resharding_configuration
|
|
23
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
24
|
+
from typing import Any, Dict, List, Optional, Union
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@mcp.tool(name='modify-replication-group-shard-configuration')
|
|
28
|
+
@handle_exceptions
|
|
29
|
+
async def modify_replication_group_shard_configuration(
|
|
30
|
+
replication_group_id: str,
|
|
31
|
+
node_group_count: int,
|
|
32
|
+
apply_immediately: Optional[bool] = None,
|
|
33
|
+
resharding_configuration: Optional[Union[str, List[Dict]]] = None,
|
|
34
|
+
) -> Dict:
|
|
35
|
+
"""Modify the shard configuration of an existing Amazon ElastiCache replication group.
|
|
36
|
+
|
|
37
|
+
This tool modifies the shard configuration of an existing replication group by:
|
|
38
|
+
- Modifying the number of replicas in a shard
|
|
39
|
+
- Specifying preferred availability zones for replicas
|
|
40
|
+
|
|
41
|
+
Parameters:
|
|
42
|
+
replication_group_id (str): The identifier of the replication group to modify.
|
|
43
|
+
node_group_count (int): The number of node groups (shards) in the replication group.
|
|
44
|
+
apply_immediately (Optional[bool]): Whether to apply changes immediately or during maintenance window.
|
|
45
|
+
resharding_configuration (Optional[Union[str, List[Dict]]]): Resharding configuration in either shorthand string format or list of dictionaries format.
|
|
46
|
+
Shorthand format: "NodeGroupId=string,NewShardConfiguration={NewReplicaCount=integer,PreferredAvailabilityZones=string1,string2}"
|
|
47
|
+
Multiple configurations can be separated by spaces.
|
|
48
|
+
JSON format: List of dictionaries with required fields:
|
|
49
|
+
- NodeGroupId: string
|
|
50
|
+
- NewShardConfiguration:
|
|
51
|
+
- NewReplicaCount: integer
|
|
52
|
+
- PreferredAvailabilityZones: list of strings (optional)
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Dict containing information about the modified replication group.
|
|
56
|
+
"""
|
|
57
|
+
# Check if readonly mode is enabled
|
|
58
|
+
if Context.readonly_mode():
|
|
59
|
+
raise ValueError(
|
|
60
|
+
'You have configured this tool in readonly mode. To make this change you will have to update your configuration.'
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# Get ElastiCache client
|
|
64
|
+
elasticache_client = ElastiCacheConnectionManager.get_connection()
|
|
65
|
+
|
|
66
|
+
# Build modify request
|
|
67
|
+
modify_request = {
|
|
68
|
+
'ReplicationGroupId': replication_group_id,
|
|
69
|
+
'NodeGroupCount': node_group_count,
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# Add optional parameters if provided
|
|
73
|
+
if apply_immediately is not None:
|
|
74
|
+
modify_request['ApplyImmediately'] = str(apply_immediately).lower()
|
|
75
|
+
|
|
76
|
+
if resharding_configuration:
|
|
77
|
+
try:
|
|
78
|
+
processed_configs = process_resharding_configuration(resharding_configuration)
|
|
79
|
+
modify_request['ReshardingConfiguration'] = json.dumps(processed_configs)
|
|
80
|
+
except ValueError as e:
|
|
81
|
+
return {'error': str(e)}
|
|
82
|
+
|
|
83
|
+
# Modify the replication group shard configuration
|
|
84
|
+
response = elasticache_client.modify_replication_group_shard_configuration(**modify_request)
|
|
85
|
+
return response
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class ModifyReplicationGroupRequest(BaseModel):
|
|
89
|
+
"""Request model for modifying an ElastiCache replication group."""
|
|
90
|
+
|
|
91
|
+
model_config = ConfigDict(extra='allow') # Allow extra fields to support future API additions
|
|
92
|
+
|
|
93
|
+
replication_group_id: str = Field(..., description='The identifier of the replication group')
|
|
94
|
+
apply_immediately: Optional[bool] = None
|
|
95
|
+
auto_minor_version_upgrade: Optional[bool] = None
|
|
96
|
+
automatic_failover_enabled: Optional[bool] = None
|
|
97
|
+
cache_node_type: Optional[str] = None
|
|
98
|
+
cache_parameter_group_name: Optional[str] = None
|
|
99
|
+
cache_security_group_names: Optional[List[str]] = None
|
|
100
|
+
engine_version: Optional[str] = None
|
|
101
|
+
log_delivery_configurations: Optional[Union[str, List[Dict]]] = None
|
|
102
|
+
maintenance_window: Optional[str] = None
|
|
103
|
+
multi_az_enabled: Optional[bool] = None
|
|
104
|
+
notification_topic_arn: Optional[str] = None
|
|
105
|
+
notification_topic_status: Optional[str] = None
|
|
106
|
+
num_node_groups: Optional[int] = None
|
|
107
|
+
preferred_node_groups_to_remove: Optional[List[int]] = None
|
|
108
|
+
primary_cluster_id: Optional[str] = None
|
|
109
|
+
replicas_per_node_group: Optional[int] = None
|
|
110
|
+
replication_group_description: Optional[str] = None
|
|
111
|
+
security_group_ids: Optional[List[str]] = None
|
|
112
|
+
snapshot_retention_limit: Optional[int] = None
|
|
113
|
+
snapshot_window: Optional[str] = None
|
|
114
|
+
user_group_ids_to_add: Optional[List[str]] = None
|
|
115
|
+
user_group_ids_to_remove: Optional[List[str]] = None
|
|
116
|
+
node_group_id: Optional[str] = None
|
|
117
|
+
remove_user_groups: Optional[bool] = None
|
|
118
|
+
auth_token: Optional[str] = None
|
|
119
|
+
auth_token_update_strategy: Optional[str] = None
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def prepare_modify_request_dict(request: ModifyReplicationGroupRequest) -> Dict[str, Any]:
|
|
123
|
+
"""Prepare the request dictionary for the AWS API.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
request: The ModifyReplicationGroupRequest object
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
Dict containing the properly formatted request parameters
|
|
130
|
+
"""
|
|
131
|
+
# Start with required parameters
|
|
132
|
+
modify_request: Dict[str, Any] = {
|
|
133
|
+
'ReplicationGroupId': request.replication_group_id,
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
# Optional string parameters
|
|
137
|
+
for param_name, value in [
|
|
138
|
+
('CacheNodeType', request.cache_node_type),
|
|
139
|
+
('CacheParameterGroupName', request.cache_parameter_group_name),
|
|
140
|
+
('EngineVersion', request.engine_version),
|
|
141
|
+
('PreferredMaintenanceWindow', request.maintenance_window),
|
|
142
|
+
('NotificationTopicArn', request.notification_topic_arn),
|
|
143
|
+
('NotificationTopicStatus', request.notification_topic_status),
|
|
144
|
+
('PrimaryClusterId', request.primary_cluster_id),
|
|
145
|
+
('ReplicationGroupDescription', request.replication_group_description),
|
|
146
|
+
('SnapshotWindow', request.snapshot_window),
|
|
147
|
+
('NodeGroupId', request.node_group_id),
|
|
148
|
+
('AuthToken', request.auth_token),
|
|
149
|
+
('AuthTokenUpdateStrategy', request.auth_token_update_strategy),
|
|
150
|
+
]:
|
|
151
|
+
if value:
|
|
152
|
+
modify_request[param_name] = str(value)
|
|
153
|
+
|
|
154
|
+
# Optional numeric parameters
|
|
155
|
+
for param_name, value in [
|
|
156
|
+
('NodeGroupCount', request.num_node_groups),
|
|
157
|
+
('ReplicasPerNodeGroup', request.replicas_per_node_group),
|
|
158
|
+
('SnapshotRetentionLimit', request.snapshot_retention_limit),
|
|
159
|
+
]:
|
|
160
|
+
if value is not None:
|
|
161
|
+
modify_request[param_name] = value
|
|
162
|
+
|
|
163
|
+
# Optional boolean parameters
|
|
164
|
+
for param_name, value in [
|
|
165
|
+
('ApplyImmediately', request.apply_immediately),
|
|
166
|
+
('AutoMinorVersionUpgrade', request.auto_minor_version_upgrade),
|
|
167
|
+
('AutomaticFailoverEnabled', request.automatic_failover_enabled),
|
|
168
|
+
('MultiAZEnabled', request.multi_az_enabled),
|
|
169
|
+
('RemoveUserGroups', request.remove_user_groups),
|
|
170
|
+
]:
|
|
171
|
+
if value is not None:
|
|
172
|
+
modify_request[param_name] = value
|
|
173
|
+
|
|
174
|
+
# Optional list parameters
|
|
175
|
+
for param_name, value in [
|
|
176
|
+
('CacheSecurityGroupNames', request.cache_security_group_names),
|
|
177
|
+
('SecurityGroupIds', request.security_group_ids),
|
|
178
|
+
('UserGroupIdsToAdd', request.user_group_ids_to_add),
|
|
179
|
+
('UserGroupIdsToRemove', request.user_group_ids_to_remove),
|
|
180
|
+
]:
|
|
181
|
+
if value:
|
|
182
|
+
modify_request[param_name] = list(value)
|
|
183
|
+
|
|
184
|
+
# Handle node groups to remove
|
|
185
|
+
if request.preferred_node_groups_to_remove:
|
|
186
|
+
modify_request['NodeGroupsToRemove'] = request.preferred_node_groups_to_remove
|
|
187
|
+
|
|
188
|
+
# Handle log delivery configurations
|
|
189
|
+
if request.log_delivery_configurations:
|
|
190
|
+
try:
|
|
191
|
+
processed_configs = process_log_delivery_configurations(
|
|
192
|
+
request.log_delivery_configurations
|
|
193
|
+
)
|
|
194
|
+
if processed_configs:
|
|
195
|
+
modify_request['LogDeliveryConfigurations'] = processed_configs
|
|
196
|
+
except ValueError as e:
|
|
197
|
+
return {'error': str(e)}
|
|
198
|
+
|
|
199
|
+
return modify_request
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
@mcp.tool(name='modify-replication-group')
|
|
203
|
+
@handle_exceptions
|
|
204
|
+
async def modify_replication_group(request: ModifyReplicationGroupRequest) -> Dict:
|
|
205
|
+
"""Modify an existing Amazon ElastiCache replication group.
|
|
206
|
+
|
|
207
|
+
This tool modifies the settings of an existing replication group including:
|
|
208
|
+
- Node configuration
|
|
209
|
+
- Security settings
|
|
210
|
+
- Maintenance settings
|
|
211
|
+
- Backup settings
|
|
212
|
+
- Engine settings
|
|
213
|
+
- Monitoring settings
|
|
214
|
+
- User group settings
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
request: The ModifyReplicationGroupRequest object containing all parameters
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
Dict containing information about the modified replication group.
|
|
221
|
+
"""
|
|
222
|
+
# Check if readonly mode is enabled
|
|
223
|
+
if Context.readonly_mode():
|
|
224
|
+
raise ValueError(
|
|
225
|
+
'You have configured this tool in readonly mode. To make this change you will have to update your configuration.'
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# Get ElastiCache client
|
|
229
|
+
elasticache_client = ElastiCacheConnectionManager.get_connection()
|
|
230
|
+
|
|
231
|
+
# Prepare request dictionary
|
|
232
|
+
modify_request = prepare_modify_request_dict(request)
|
|
233
|
+
|
|
234
|
+
# Modify the replication group
|
|
235
|
+
response = elasticache_client.modify_replication_group(**modify_request)
|
|
236
|
+
return response
|