@@ -94,4 +94,103 @@ async def rename(old_key: str, new_key: str) -> Dict[str, Any]:
94
94
}
95
95
96
96
except RedisError as e :
97
- return {"error" : str (e )}
97
+ return {"error" : str (e )}
98
+
99
+
100
+ @mcp .tool ()
101
+ async def get_all_keys (pattern : str = "*" ) -> list :
102
+ """
103
+ Retrieve all keys matching a pattern from the Redis database using the KEYS command.
104
+
105
+ Note: The KEYS command is blocking and can impact performance on large databases.
106
+ For production use with large datasets, consider using SCAN instead.
107
+
108
+ Args:
109
+ pattern: Pattern to match keys against (default is "*" for all keys).
110
+ Common patterns: "user:*", "cache:*", "*:123", etc.
111
+
112
+ Returns:
113
+ A list of keys matching the pattern or an error message.
114
+ """
115
+ try :
116
+ r = RedisConnectionManager .get_connection ()
117
+ keys = r .keys (pattern )
118
+ # Convert bytes to strings if needed
119
+ return [key .decode ('utf-8' ) if isinstance (key , bytes ) else key for key in keys ]
120
+ except RedisError as e :
121
+ return f"Error retrieving keys with pattern '{ pattern } ': { str (e )} "
122
+
123
+
124
+ @mcp .tool ()
125
+ async def scan_keys (pattern : str = "*" , count : int = 100 , cursor : int = 0 ) -> dict :
126
+ """
127
+ Scan keys in the Redis database using the SCAN command (non-blocking, production-safe).
128
+
129
+ The SCAN command iterates through the keyspace in small chunks, making it safe to use
130
+ on large databases without blocking other operations.
131
+
132
+ Args:
133
+ pattern: Pattern to match keys against (default is "*" for all keys).
134
+ Common patterns: "user:*", "cache:*", "*:123", etc.
135
+ count: Hint for the number of keys to return per iteration (default 100).
136
+ Redis may return more or fewer keys than this hint.
137
+ cursor: The cursor position to start scanning from (0 to start from beginning).
138
+
139
+ Returns:
140
+ A dictionary containing:
141
+ - 'cursor': Next cursor position (0 means scan is complete)
142
+ - 'keys': List of keys found in this iteration
143
+ - 'total_scanned': Number of keys returned in this batch
144
+ Or an error message if something goes wrong.
145
+ """
146
+ try :
147
+ r = RedisConnectionManager .get_connection ()
148
+ cursor , keys = r .scan (cursor = cursor , match = pattern , count = count )
149
+
150
+ # Convert bytes to strings if needed
151
+ decoded_keys = [key .decode ('utf-8' ) if isinstance (key , bytes ) else key for key in keys ]
152
+
153
+ return {
154
+ 'cursor' : cursor ,
155
+ 'keys' : decoded_keys ,
156
+ 'total_scanned' : len (decoded_keys ),
157
+ 'scan_complete' : cursor == 0
158
+ }
159
+ except RedisError as e :
160
+ return f"Error scanning keys with pattern '{ pattern } ': { str (e )} "
161
+
162
+
163
+ @mcp .tool ()
164
+ async def scan_all_keys (pattern : str = "*" , batch_size : int = 100 ) -> list :
165
+ """
166
+ Scan and return ALL keys matching a pattern using multiple SCAN iterations.
167
+
168
+ This function automatically handles the SCAN cursor iteration to collect all matching keys.
169
+ It's safer than KEYS * for large databases but will still collect all results in memory.
170
+
171
+ Args:
172
+ pattern: Pattern to match keys against (default is "*" for all keys).
173
+ batch_size: Number of keys to scan per iteration (default 100).
174
+
175
+ Returns:
176
+ A list of all keys matching the pattern or an error message.
177
+ """
178
+ try :
179
+ r = RedisConnectionManager .get_connection ()
180
+ all_keys = []
181
+ cursor = 0
182
+
183
+ while True :
184
+ cursor , keys = r .scan (cursor = cursor , match = pattern , count = batch_size )
185
+
186
+ # Convert bytes to strings if needed and add to results
187
+ decoded_keys = [key .decode ('utf-8' ) if isinstance (key , bytes ) else key for key in keys ]
188
+ all_keys .extend (decoded_keys )
189
+
190
+ # Break when scan is complete (cursor returns to 0)
191
+ if cursor == 0 :
192
+ break
193
+
194
+ return all_keys
195
+ except RedisError as e :
196
+ return f"Error scanning all keys with pattern '{ pattern } ': { str (e )} "
0 commit comments