awslabs.valkey-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.
@@ -0,0 +1,376 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
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
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
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.
11
+
12
+ """List operations for Valkey MCP Server."""
13
+
14
+ from awslabs.valkey_mcp_server.common.connection import ValkeyConnectionManager
15
+ from awslabs.valkey_mcp_server.common.server import mcp
16
+ from typing import Any, Optional
17
+ from typing import List as PyList
18
+ from valkey.exceptions import ValkeyError
19
+
20
+
21
+ @mcp.tool()
22
+ async def list_append(key: str, value: Any) -> str:
23
+ """Append value to list.
24
+
25
+ Args:
26
+ key: The name of the key
27
+ value: The value to append
28
+
29
+ Returns:
30
+ Success message or error message
31
+ """
32
+ try:
33
+ r = ValkeyConnectionManager.get_connection()
34
+ result = r.rpush(key, value)
35
+ return f"Successfully appended value to list '{key}', new length: {result}"
36
+ except ValkeyError as e:
37
+ return f"Error appending to list '{key}': {str(e)}"
38
+
39
+
40
+ @mcp.tool()
41
+ async def list_prepend(key: str, value: Any) -> str:
42
+ """Prepend value to list.
43
+
44
+ Args:
45
+ key: The name of the key
46
+ value: The value to prepend
47
+
48
+ Returns:
49
+ Success message or error message
50
+ """
51
+ try:
52
+ r = ValkeyConnectionManager.get_connection()
53
+ result = r.lpush(key, value)
54
+ return f"Successfully prepended value to list '{key}', new length: {result}"
55
+ except ValkeyError as e:
56
+ return f"Error prepending to list '{key}': {str(e)}"
57
+
58
+
59
+ @mcp.tool()
60
+ async def list_append_multiple(key: str, values: PyList[Any]) -> str:
61
+ """Append multiple values to list.
62
+
63
+ Args:
64
+ key: The name of the key
65
+ values: List of values to append
66
+
67
+ Returns:
68
+ Success message or error message
69
+ """
70
+ try:
71
+ r = ValkeyConnectionManager.get_connection()
72
+ result = r.rpush(key, *values)
73
+ return f"Successfully appended {len(values)} values to list '{key}', new length: {result}"
74
+ except ValkeyError as e:
75
+ return f"Error appending multiple values to list '{key}': {str(e)}"
76
+
77
+
78
+ @mcp.tool()
79
+ async def list_prepend_multiple(key: str, values: PyList[Any]) -> str:
80
+ """Prepend multiple values to list.
81
+
82
+ Args:
83
+ key: The name of the key
84
+ values: List of values to prepend
85
+
86
+ Returns:
87
+ Success message or error message
88
+ """
89
+ try:
90
+ r = ValkeyConnectionManager.get_connection()
91
+ result = r.lpush(key, *values)
92
+ return f"Successfully prepended {len(values)} values to list '{key}', new length: {result}"
93
+ except ValkeyError as e:
94
+ return f"Error prepending multiple values to list '{key}': {str(e)}"
95
+
96
+
97
+ @mcp.tool()
98
+ async def list_get(key: str, index: int) -> str:
99
+ """Get value at index from list.
100
+
101
+ Args:
102
+ key: The name of the key
103
+ index: The index (0-based, negative indices supported)
104
+
105
+ Returns:
106
+ Value or error message
107
+ """
108
+ try:
109
+ r = ValkeyConnectionManager.get_connection()
110
+ result = r.lindex(key, index)
111
+ if result is None:
112
+ return f"No value found at index {index} in list '{key}'"
113
+ return str(result)
114
+ except ValkeyError as e:
115
+ return f"Error getting value from list '{key}': {str(e)}"
116
+
117
+
118
+ @mcp.tool()
119
+ async def list_set(key: str, index: int, value: Any) -> str:
120
+ """Set value at index in list.
121
+
122
+ Args:
123
+ key: The name of the key
124
+ index: The index (0-based, negative indices supported)
125
+ value: The value to set
126
+
127
+ Returns:
128
+ Success message or error message
129
+ """
130
+ try:
131
+ r = ValkeyConnectionManager.get_connection()
132
+ r.lset(key, index, value)
133
+ return f"Successfully set value at index {index} in list '{key}'"
134
+ except ValkeyError as e:
135
+ return f"Error setting value in list '{key}': {str(e)}"
136
+
137
+
138
+ @mcp.tool()
139
+ async def list_range(key: str, start: int = 0, stop: int = -1) -> str:
140
+ """Get range of values from list.
141
+
142
+ Args:
143
+ key: The name of the key
144
+ start: Start index (inclusive, default 0)
145
+ stop: Stop index (inclusive, default -1 for end)
146
+
147
+ Returns:
148
+ List of values or error message
149
+ """
150
+ try:
151
+ r = ValkeyConnectionManager.get_connection()
152
+ result = r.lrange(key, start, stop)
153
+ if not result:
154
+ return f"No values found in range [{start}, {stop}] in list '{key}'"
155
+ return str(result)
156
+ except ValkeyError as e:
157
+ return f"Error getting range from list '{key}': {str(e)}"
158
+
159
+
160
+ @mcp.tool()
161
+ async def list_trim(key: str, start: int, stop: int) -> str:
162
+ """Trim list to specified range.
163
+
164
+ Args:
165
+ key: The name of the key
166
+ start: Start index (inclusive)
167
+ stop: Stop index (inclusive)
168
+
169
+ Returns:
170
+ Success message or error message
171
+ """
172
+ try:
173
+ r = ValkeyConnectionManager.get_connection()
174
+ r.ltrim(key, start, stop)
175
+ return f"Successfully trimmed list '{key}' to range [{start}, {stop}]"
176
+ except ValkeyError as e:
177
+ return f"Error trimming list '{key}': {str(e)}"
178
+
179
+
180
+ @mcp.tool()
181
+ async def list_length(key: str) -> str:
182
+ """Get length of list.
183
+
184
+ Args:
185
+ key: The name of the key
186
+
187
+ Returns:
188
+ Length or error message
189
+ """
190
+ try:
191
+ r = ValkeyConnectionManager.get_connection()
192
+ result = r.llen(key)
193
+ return str(result)
194
+ except ValkeyError as e:
195
+ return f"Error getting list length for '{key}': {str(e)}"
196
+
197
+
198
+ @mcp.tool()
199
+ async def list_pop_left(key: str, count: Optional[int] = None) -> str:
200
+ """Pop value(s) from left of list.
201
+
202
+ Args:
203
+ key: The name of the key
204
+ count: Number of values to pop (optional)
205
+
206
+ Returns:
207
+ Value(s) or error message
208
+ """
209
+ try:
210
+ r = ValkeyConnectionManager.get_connection()
211
+ if count:
212
+ result = r.lpop(key, count)
213
+ else:
214
+ result = r.lpop(key)
215
+ if result is None:
216
+ return f"List '{key}' is empty"
217
+ return str(result)
218
+ except ValkeyError as e:
219
+ return f"Error popping from left of list '{key}': {str(e)}"
220
+
221
+
222
+ @mcp.tool()
223
+ async def list_pop_right(key: str, count: Optional[int] = None) -> str:
224
+ """Pop value(s) from right of list.
225
+
226
+ Args:
227
+ key: The name of the key
228
+ count: Number of values to pop (optional)
229
+
230
+ Returns:
231
+ Value(s) or error message
232
+ """
233
+ try:
234
+ r = ValkeyConnectionManager.get_connection()
235
+ if count:
236
+ result = r.rpop(key, count)
237
+ else:
238
+ result = r.rpop(key)
239
+ if result is None:
240
+ return f"List '{key}' is empty"
241
+ return str(result)
242
+ except ValkeyError as e:
243
+ return f"Error popping from right of list '{key}': {str(e)}"
244
+
245
+
246
+ @mcp.tool()
247
+ async def list_position(
248
+ key: str,
249
+ value: Any,
250
+ rank: Optional[int] = None,
251
+ count: Optional[int] = None,
252
+ maxlen: Optional[int] = None,
253
+ ) -> str:
254
+ """Find position(s) of value in list.
255
+
256
+ Args:
257
+ key: The name of the key
258
+ value: Value to search for
259
+ rank: Match the Nth occurrence (optional)
260
+ count: Return this many matches (optional)
261
+ maxlen: Limit search to first N elements (optional)
262
+
263
+ Returns:
264
+ Position(s) or error message
265
+ """
266
+ try:
267
+ r = ValkeyConnectionManager.get_connection()
268
+ options = {}
269
+ if rank is not None:
270
+ options['rank'] = rank
271
+ if count is not None:
272
+ options['count'] = count
273
+ if maxlen is not None:
274
+ options['maxlen'] = maxlen
275
+
276
+ result = r.lpos(key, value, **options)
277
+ if result is None:
278
+ return f"Value not found in list '{key}'"
279
+ return str(result)
280
+ except ValkeyError as e:
281
+ return f"Error finding position in list '{key}': {str(e)}"
282
+
283
+
284
+ @mcp.tool()
285
+ async def list_move(
286
+ source: str, destination: str, wherefrom: str = 'LEFT', whereto: str = 'RIGHT'
287
+ ) -> str:
288
+ """Move element from one list to another.
289
+
290
+ Args:
291
+ source: Source list key
292
+ destination: Destination list key
293
+ wherefrom: Where to pop from ("LEFT" or "RIGHT")
294
+ whereto: Where to push to ("LEFT" or "RIGHT")
295
+
296
+ Returns:
297
+ Moved value or error message
298
+ """
299
+ try:
300
+ r = ValkeyConnectionManager.get_connection()
301
+ wherefrom = wherefrom.upper()
302
+ whereto = whereto.upper()
303
+
304
+ if wherefrom not in ['LEFT', 'RIGHT'] or whereto not in ['LEFT', 'RIGHT']:
305
+ return "Error: wherefrom and whereto must be either 'LEFT' or 'RIGHT'"
306
+
307
+ result = r.lmove(source, destination, wherefrom, whereto)
308
+ if result is None:
309
+ return f"Source list '{source}' is empty"
310
+ return f"Successfully moved value '{result}' from {wherefrom} of '{source}' to {whereto} of '{destination}'"
311
+ except ValkeyError as e:
312
+ return f'Error moving value between lists: {str(e)}'
313
+
314
+
315
+ @mcp.tool()
316
+ async def list_insert_before(key: str, pivot: Any, value: Any) -> str:
317
+ """Insert value before pivot in list.
318
+
319
+ Args:
320
+ key: The name of the key
321
+ pivot: The pivot value
322
+ value: The value to insert
323
+
324
+ Returns:
325
+ Success message or error message
326
+ """
327
+ try:
328
+ r = ValkeyConnectionManager.get_connection()
329
+ result = r.linsert(key, 'BEFORE', pivot, value)
330
+ if result == -1:
331
+ return f"Pivot value not found in list '{key}'"
332
+ return f"Successfully inserted value before pivot in list '{key}', new length: {result}"
333
+ except ValkeyError as e:
334
+ return f"Error inserting before pivot in list '{key}': {str(e)}"
335
+
336
+
337
+ @mcp.tool()
338
+ async def list_insert_after(key: str, pivot: Any, value: Any) -> str:
339
+ """Insert value after pivot in list.
340
+
341
+ Args:
342
+ key: The name of the key
343
+ pivot: The pivot value
344
+ value: The value to insert
345
+
346
+ Returns:
347
+ Success message or error message
348
+ """
349
+ try:
350
+ r = ValkeyConnectionManager.get_connection()
351
+ result = r.linsert(key, 'AFTER', pivot, value)
352
+ if result == -1:
353
+ return f"Pivot value not found in list '{key}'"
354
+ return f"Successfully inserted value after pivot in list '{key}', new length: {result}"
355
+ except ValkeyError as e:
356
+ return f"Error inserting after pivot in list '{key}': {str(e)}"
357
+
358
+
359
+ @mcp.tool()
360
+ async def list_remove(key: str, value: Any, count: int = 0) -> str:
361
+ """Remove occurrences of value from list.
362
+
363
+ Args:
364
+ key: The name of the key
365
+ value: Value to remove
366
+ count: Number of occurrences to remove (0 for all, positive for left-to-right, negative for right-to-left)
367
+
368
+ Returns:
369
+ Success message or error message
370
+ """
371
+ try:
372
+ r = ValkeyConnectionManager.get_connection()
373
+ result = r.lrem(key, count, value)
374
+ return f"Successfully removed {result} occurrence(s) of value from list '{key}'"
375
+ except ValkeyError as e:
376
+ return f"Error removing value from list '{key}': {str(e)}"
@@ -0,0 +1,104 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
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
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
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.
11
+
12
+ from awslabs.valkey_mcp_server.common.connection import ValkeyConnectionManager
13
+ from awslabs.valkey_mcp_server.common.server import mcp
14
+ from typing import Any, Dict
15
+ from valkey.exceptions import ValkeyError as RedisError
16
+
17
+
18
+ @mcp.tool()
19
+ async def delete(key: str) -> str:
20
+ """Delete a Valkey key.
21
+
22
+ Args:
23
+ key (str): The key to delete.
24
+
25
+ Returns:
26
+ str: Confirmation message or an error message.
27
+ """
28
+ try:
29
+ r = ValkeyConnectionManager.get_connection()
30
+ result = r.delete(key)
31
+ return f'Successfully deleted {key}' if result else f'Key {key} not found'
32
+ except RedisError as e:
33
+ return f'Error deleting key {key}: {str(e)}'
34
+
35
+
36
+ @mcp.tool()
37
+ async def type(key: str) -> Dict[str, Any]:
38
+ """Returns the string representation of the type of the value stored at key.
39
+
40
+ Args:
41
+ key (str): The key to check.
42
+
43
+ Returns:
44
+ str: The type of key, or none when key doesn't exist
45
+ """
46
+ try:
47
+ r = ValkeyConnectionManager.get_connection()
48
+ key_type = r.type(key)
49
+ info = {'key': key, 'type': key_type, 'ttl': r.ttl(key)}
50
+
51
+ return info
52
+ except RedisError as e:
53
+ return {'error': str(e)}
54
+
55
+
56
+ @mcp.tool()
57
+ async def expire(name: str, expire_seconds: int) -> str:
58
+ """Set an expiration time for a Redis key.
59
+
60
+ Args:
61
+ name: The Redis key.
62
+ expire_seconds: Time in seconds after which the key should expire.
63
+
64
+ Returns:
65
+ A success message or an error message.
66
+ """
67
+ try:
68
+ r = ValkeyConnectionManager.get_connection()
69
+ success = r.expire(name, expire_seconds)
70
+ return (
71
+ f"Expiration set to {expire_seconds} seconds for '{name}'."
72
+ if success
73
+ else f"Key '{name}' does not exist."
74
+ )
75
+ except RedisError as e:
76
+ return f"Error setting expiration for key '{name}': {str(e)}"
77
+
78
+
79
+ @mcp.tool()
80
+ async def rename(old_key: str, new_key: str) -> Dict[str, Any]:
81
+ """Renames a Redis key from old_key to new_key.
82
+
83
+ Args:
84
+ old_key (str): The current name of the Redis key to rename.
85
+ new_key (str): The new name to assign to the key.
86
+
87
+ Returns:
88
+ Dict[str, Any]: A dictionary containing the result of the operation.
89
+ On success: {"status": "success", "message": "..."}
90
+ On error: {"error": "..."}
91
+ """
92
+ try:
93
+ r = ValkeyConnectionManager.get_connection()
94
+
95
+ # Check if the old key exists
96
+ if not r.exists(old_key):
97
+ return {'error': f"Key '{old_key}' does not exist."}
98
+
99
+ # Rename the key
100
+ r.rename(old_key, new_key)
101
+ return {'status': 'success', 'message': f"Renamed key '{old_key}' to '{new_key}'"}
102
+
103
+ except RedisError as e:
104
+ return {'error': str(e)}
@@ -0,0 +1,54 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
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
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
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.
11
+
12
+ from awslabs.valkey_mcp_server.common.connection import ValkeyConnectionManager
13
+ from awslabs.valkey_mcp_server.common.server import mcp
14
+ from valkey.exceptions import ValkeyError
15
+
16
+
17
+ @mcp.tool()
18
+ async def dbsize() -> str:
19
+ """Get the number of keys stored in the Valkey database."""
20
+ try:
21
+ r = ValkeyConnectionManager.get_connection()
22
+ result = r.dbsize()
23
+ return str(result)
24
+ except ValkeyError as e:
25
+ raise RuntimeError(f'Error getting database size: {str(e)}')
26
+
27
+
28
+ @mcp.tool()
29
+ async def info(section: str = 'default') -> str:
30
+ """Get Valkey server information and statistics.
31
+
32
+ Args:
33
+ section: The section of the info command (default, memory, cpu, etc.).
34
+
35
+ Returns:
36
+ A dictionary of server information or an error message.
37
+ """
38
+ try:
39
+ r = ValkeyConnectionManager.get_connection()
40
+ info = r.info(section)
41
+ return str(info)
42
+ except ValkeyError as e:
43
+ raise RuntimeError(f'Error retrieving Redis info: {str(e)}')
44
+
45
+
46
+ @mcp.tool()
47
+ async def client_list() -> str:
48
+ """Get a list of connected clients to the Valkey server."""
49
+ try:
50
+ r = ValkeyConnectionManager.get_connection()
51
+ clients = r.client_list()
52
+ return str(clients)
53
+ except ValkeyError as e:
54
+ raise RuntimeError(f'Error retrieving client list: {str(e)}')