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,422 @@
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
+ """JSON 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, Union
17
+ from valkey.exceptions import ValkeyError
18
+
19
+
20
+ @mcp.tool()
21
+ async def json_set(key: str, path: str, value: Any, nx: bool = False, xx: bool = False) -> str:
22
+ """Set the JSON value at path.
23
+
24
+ Args:
25
+ key: The name of the key
26
+ path: The path in the JSON document (e.g., "$.name" or "." for root)
27
+ value: The value to set
28
+ nx: Only set if path doesn't exist
29
+ xx: Only set if path exists
30
+
31
+ Returns:
32
+ Success message or error message
33
+ """
34
+ try:
35
+ r = ValkeyConnectionManager.get_connection()
36
+ options = {}
37
+ if nx:
38
+ options['nx'] = True
39
+ if xx:
40
+ options['xx'] = True
41
+
42
+ result = r.json().set(key, path, value, **options)
43
+ if result:
44
+ return f"Successfully set value at path '{path}' in '{key}'"
45
+ return f"Failed to set value at path '{path}' in '{key}' (path condition not met)"
46
+ except ValkeyError as e:
47
+ return f"Error setting JSON value in '{key}': {str(e)}"
48
+
49
+
50
+ @mcp.tool()
51
+ async def json_get(
52
+ key: str,
53
+ path: Optional[str] = None,
54
+ indent: Optional[int] = None,
55
+ newline: Optional[bool] = None,
56
+ space: Optional[bool] = None,
57
+ ) -> str:
58
+ """Get the JSON value at path.
59
+
60
+ Args:
61
+ key: The name of the key
62
+ path: The path in the JSON document (optional, defaults to root)
63
+ indent: Number of spaces for indentation (optional)
64
+ newline: Add newlines in formatted output (optional)
65
+ space: Add spaces in formatted output (optional)
66
+
67
+ Returns:
68
+ JSON value or error message
69
+ """
70
+ try:
71
+ r = ValkeyConnectionManager.get_connection()
72
+ options = {}
73
+ if indent is not None:
74
+ options['indent'] = indent
75
+ if newline is not None:
76
+ options['newline'] = newline
77
+ if space is not None:
78
+ options['space'] = space
79
+
80
+ result = r.json().get(key, path, **options) if path else r.json().get(key)
81
+ if result is None:
82
+ return f"No value found at path '{path or '.'}' in '{key}'"
83
+ return str(result)
84
+ except ValkeyError as e:
85
+ return f"Error getting JSON value from '{key}': {str(e)}"
86
+
87
+
88
+ @mcp.tool()
89
+ async def json_type(key: str, path: Optional[str] = None) -> str:
90
+ """Get the type of JSON value at path.
91
+
92
+ Args:
93
+ key: The name of the key
94
+ path: The path in the JSON document (optional, defaults to root)
95
+
96
+ Returns:
97
+ JSON type or error message
98
+ """
99
+ try:
100
+ r = ValkeyConnectionManager.get_connection()
101
+ result = r.json().type(key, path) if path else r.json().type(key)
102
+ if result is None:
103
+ return f"No value found at path '{path or '.'}' in '{key}'"
104
+ return f"Type at path '{path or '.'}' in '{key}': {result}"
105
+ except ValkeyError as e:
106
+ return f"Error getting JSON type from '{key}': {str(e)}"
107
+
108
+
109
+ @mcp.tool()
110
+ async def json_numincrby(key: str, path: str, value: Union[int, float]) -> str:
111
+ """Increment the number at path by value.
112
+
113
+ Args:
114
+ key: The name of the key
115
+ path: The path in the JSON document
116
+ value: The increment value (integer or float)
117
+
118
+ Returns:
119
+ New value or error message
120
+ """
121
+ try:
122
+ r = ValkeyConnectionManager.get_connection()
123
+ # Convert float to int by rounding if needed
124
+ int_value = round(value) if isinstance(value, float) else value
125
+ result = r.json().numincrby(key, path, int_value)
126
+ return f"Value at path '{path}' in '{key}' incremented to {result}"
127
+ except ValkeyError as e:
128
+ return f"Error incrementing JSON value in '{key}': {str(e)}"
129
+
130
+
131
+ @mcp.tool()
132
+ async def json_nummultby(key: str, path: str, value: Union[int, float]) -> str:
133
+ """Multiply the number at path by value.
134
+
135
+ Args:
136
+ key: The name of the key
137
+ path: The path in the JSON document
138
+ value: The multiplier value (integer or float)
139
+
140
+ Returns:
141
+ New value or error message
142
+ """
143
+ try:
144
+ r = ValkeyConnectionManager.get_connection()
145
+ # Convert float to int by rounding if needed
146
+ int_value = round(value) if isinstance(value, float) else value
147
+ result = r.json().nummultby(key, path, int_value)
148
+ return f"Value at path '{path}' in '{key}' multiplied to {result}"
149
+ except ValkeyError as e:
150
+ return f"Error multiplying JSON value in '{key}': {str(e)}"
151
+
152
+
153
+ @mcp.tool()
154
+ async def json_strappend(key: str, path: str, value: str) -> str:
155
+ """Append a string to the string at path.
156
+
157
+ Args:
158
+ key: The name of the key
159
+ path: The path in the JSON document
160
+ value: The string to append
161
+
162
+ Returns:
163
+ New string length or error message
164
+ """
165
+ try:
166
+ r = ValkeyConnectionManager.get_connection()
167
+ result = r.json().strappend(key, path, value)
168
+ return f"String at path '{path}' in '{key}' appended, new length: {result}"
169
+ except ValkeyError as e:
170
+ return f"Error appending to JSON string in '{key}': {str(e)}"
171
+
172
+
173
+ @mcp.tool()
174
+ async def json_strlen(key: str, path: str) -> str:
175
+ """Get the length of string at path.
176
+
177
+ Args:
178
+ key: The name of the key
179
+ path: The path in the JSON document
180
+
181
+ Returns:
182
+ String length or error message
183
+ """
184
+ try:
185
+ r = ValkeyConnectionManager.get_connection()
186
+ result = r.json().strlen(key, path)
187
+ if result is None:
188
+ return f"No string found at path '{path}' in '{key}'"
189
+ return f"Length of string at path '{path}' in '{key}': {result}"
190
+ except ValkeyError as e:
191
+ return f"Error getting JSON string length from '{key}': {str(e)}"
192
+
193
+
194
+ @mcp.tool()
195
+ async def json_arrappend(key: str, path: str, *values: Any) -> str:
196
+ """Append values to the array at path.
197
+
198
+ Args:
199
+ key: The name of the key
200
+ path: The path in the JSON document
201
+ *values: One or more values to append
202
+
203
+ Returns:
204
+ New array length or error message
205
+ """
206
+ try:
207
+ if not values:
208
+ return 'Error: at least one value is required'
209
+
210
+ r = ValkeyConnectionManager.get_connection()
211
+ result = r.json().arrappend(key, path, *values)
212
+ return f"Array at path '{path}' in '{key}' appended, new length: {result}"
213
+ except ValkeyError as e:
214
+ return f"Error appending to JSON array in '{key}': {str(e)}"
215
+
216
+
217
+ @mcp.tool()
218
+ async def json_arrindex(
219
+ key: str, path: str, value: Any, start: Optional[int] = None, stop: Optional[int] = None
220
+ ) -> str:
221
+ """Get the index of value in array at path.
222
+
223
+ Args:
224
+ key: The name of the key
225
+ path: The path in the JSON document
226
+ value: The value to search for
227
+ start: Start offset (optional)
228
+ stop: Stop offset (optional)
229
+
230
+ Returns:
231
+ Index or error message
232
+ """
233
+ try:
234
+ r = ValkeyConnectionManager.get_connection()
235
+ args = [value]
236
+ if start is not None:
237
+ args.append(start)
238
+ if stop is not None:
239
+ args.append(stop)
240
+
241
+ result = r.json().arrindex(key, path, *args)
242
+ if result == -1:
243
+ range_str = ''
244
+ if start is not None or stop is not None:
245
+ range_str = f' in range [{start or 0}, {stop or "∞"}]'
246
+ return f"Value not found in array at path '{path}' in '{key}'{range_str}"
247
+ return f"Value found at index {result} in array at path '{path}' in '{key}'"
248
+ except ValkeyError as e:
249
+ return f"Error searching JSON array in '{key}': {str(e)}"
250
+
251
+
252
+ @mcp.tool()
253
+ async def json_arrlen(key: str, path: str) -> str:
254
+ """Get the length of array at path.
255
+
256
+ Args:
257
+ key: The name of the key
258
+ path: The path in the JSON document
259
+
260
+ Returns:
261
+ Array length or error message
262
+ """
263
+ try:
264
+ r = ValkeyConnectionManager.get_connection()
265
+ result = r.json().arrlen(key, path)
266
+ if result is None:
267
+ return f"No array found at path '{path}' in '{key}'"
268
+ return f"Length of array at path '{path}' in '{key}': {result}"
269
+ except ValkeyError as e:
270
+ return f"Error getting JSON array length from '{key}': {str(e)}"
271
+
272
+
273
+ @mcp.tool()
274
+ async def json_arrpop(key: str, path: str, index: int = -1) -> str:
275
+ """Pop a value from the array at path and index.
276
+
277
+ Args:
278
+ key: The name of the key
279
+ path: The path in the JSON document
280
+ index: The index to pop from (-1 for last element)
281
+
282
+ Returns:
283
+ Popped value or error message
284
+ """
285
+ try:
286
+ r = ValkeyConnectionManager.get_connection()
287
+ result = r.json().arrpop(key, path, index)
288
+ if result is None:
289
+ return f"No value found at index {index} in array at path '{path}' in '{key}'"
290
+ return f"Popped value from index {index} in array at path '{path}' in '{key}': {result}"
291
+ except ValkeyError as e:
292
+ return f"Error popping from JSON array in '{key}': {str(e)}"
293
+
294
+
295
+ @mcp.tool()
296
+ async def json_arrtrim(key: str, path: str, start: int, stop: int) -> str:
297
+ """Trim array at path to include only elements within range.
298
+
299
+ Args:
300
+ key: The name of the key
301
+ path: The path in the JSON document
302
+ start: Start index (inclusive)
303
+ stop: Stop index (inclusive)
304
+
305
+ Returns:
306
+ New array length or error message
307
+ """
308
+ try:
309
+ r = ValkeyConnectionManager.get_connection()
310
+ result = r.json().arrtrim(key, path, start, stop)
311
+ return f"Array at path '{path}' in '{key}' trimmed to range [{start}, {stop}], new length: {result}"
312
+ except ValkeyError as e:
313
+ return f"Error trimming JSON array in '{key}': {str(e)}"
314
+
315
+
316
+ @mcp.tool()
317
+ async def json_objkeys(key: str, path: str) -> str:
318
+ """Get the keys in the object at path.
319
+
320
+ Args:
321
+ key: The name of the key
322
+ path: The path in the JSON document
323
+
324
+ Returns:
325
+ List of keys or error message
326
+ """
327
+ try:
328
+ r = ValkeyConnectionManager.get_connection()
329
+ result = r.json().objkeys(key, path)
330
+ if result is None:
331
+ return f"No object found at path '{path}' in '{key}'"
332
+ if not result:
333
+ return f"Object at path '{path}' in '{key}' has no keys"
334
+ # Filter out None values and ensure all elements are strings
335
+ valid_keys = [str(key) for key in result if key is not None]
336
+ return f"Keys in object at path '{path}' in '{key}': {', '.join(valid_keys)}"
337
+ except ValkeyError as e:
338
+ return f"Error getting JSON object keys from '{key}': {str(e)}"
339
+
340
+
341
+ @mcp.tool()
342
+ async def json_objlen(key: str, path: str) -> str:
343
+ """Get the number of keys in the object at path.
344
+
345
+ Args:
346
+ key: The name of the key
347
+ path: The path in the JSON document
348
+
349
+ Returns:
350
+ Number of keys or error message
351
+ """
352
+ try:
353
+ r = ValkeyConnectionManager.get_connection()
354
+ result = r.json().objlen(key, path)
355
+ if result is None:
356
+ return f"No object found at path '{path}' in '{key}'"
357
+ return f"Number of keys in object at path '{path}' in '{key}': {result}"
358
+ except ValkeyError as e:
359
+ return f"Error getting JSON object length from '{key}': {str(e)}"
360
+
361
+
362
+ @mcp.tool()
363
+ async def json_toggle(key: str, path: str) -> str:
364
+ """Toggle boolean value at path.
365
+
366
+ Args:
367
+ key: The name of the key
368
+ path: The path in the JSON document
369
+
370
+ Returns:
371
+ New boolean value or error message
372
+ """
373
+ try:
374
+ r = ValkeyConnectionManager.get_connection()
375
+ result = r.json().toggle(key, path)
376
+ if result is None:
377
+ return f"No boolean value found at path '{path}' in '{key}'"
378
+ return f"Boolean value at path '{path}' in '{key}' toggled to: {str(result).lower()}"
379
+ except ValkeyError as e:
380
+ return f"Error toggling JSON boolean in '{key}': {str(e)}"
381
+
382
+
383
+ @mcp.tool()
384
+ async def json_clear(key: str, path: str) -> str:
385
+ """Clear container at path (array or object).
386
+
387
+ Args:
388
+ key: The name of the key
389
+ path: The path in the JSON document
390
+
391
+ Returns:
392
+ Success message or error message
393
+ """
394
+ try:
395
+ r = ValkeyConnectionManager.get_connection()
396
+ result = r.json().clear(key, path)
397
+ if result == 1:
398
+ return f"Successfully cleared container at path '{path}' in '{key}'"
399
+ return f"No container found at path '{path}' in '{key}'"
400
+ except ValkeyError as e:
401
+ return f"Error clearing JSON container in '{key}': {str(e)}"
402
+
403
+
404
+ @mcp.tool()
405
+ async def json_del(key: str, path: str) -> str:
406
+ """Delete value at path.
407
+
408
+ Args:
409
+ key: The name of the key
410
+ path: The path in the JSON document
411
+
412
+ Returns:
413
+ Success message or error message
414
+ """
415
+ try:
416
+ r = ValkeyConnectionManager.get_connection()
417
+ result = r.json().delete(key, path)
418
+ if result == 1:
419
+ return f"Successfully deleted value at path '{path}' in '{key}'"
420
+ return f"No value found at path '{path}' in '{key}'"
421
+ except ValkeyError as e:
422
+ return f"Error deleting JSON value in '{key}': {str(e)}"