mcp-sqlite-memory-bank 1.5.1__py3-none-any.whl → 1.6.0__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.
@@ -19,13 +19,17 @@ def create_table(
19
19
  ) -> ToolResponse:
20
20
  """Create a new table in the SQLite memory bank."""
21
21
  from .. import server
22
- return cast(ToolResponse, get_database(server.DB_PATH).create_table(table_name, columns))
22
+
23
+ return cast(
24
+ ToolResponse, get_database(server.DB_PATH).create_table(table_name, columns)
25
+ )
23
26
 
24
27
 
25
28
  @catch_errors
26
29
  def list_tables() -> ToolResponse:
27
30
  """List all tables in the SQLite memory bank."""
28
31
  from .. import server
32
+
29
33
  return cast(ToolResponse, get_database(server.DB_PATH).list_tables())
30
34
 
31
35
 
@@ -33,6 +37,7 @@ def list_tables() -> ToolResponse:
33
37
  def describe_table(table_name: str) -> ToolResponse:
34
38
  """Get detailed schema information for a table."""
35
39
  from .. import server
40
+
36
41
  return cast(ToolResponse, get_database(server.DB_PATH).describe_table(table_name))
37
42
 
38
43
 
@@ -40,6 +45,7 @@ def describe_table(table_name: str) -> ToolResponse:
40
45
  def drop_table(table_name: str) -> ToolResponse:
41
46
  """Drop (delete) a table from the SQLite memory bank."""
42
47
  from .. import server
48
+
43
49
  return cast(ToolResponse, get_database(server.DB_PATH).drop_table(table_name))
44
50
 
45
51
 
@@ -47,7 +53,10 @@ def drop_table(table_name: str) -> ToolResponse:
47
53
  def rename_table(old_name: str, new_name: str) -> ToolResponse:
48
54
  """Rename a table in the SQLite memory bank."""
49
55
  from .. import server
50
- return cast(ToolResponse, get_database(server.DB_PATH).rename_table(old_name, new_name))
56
+
57
+ return cast(
58
+ ToolResponse, get_database(server.DB_PATH).rename_table(old_name, new_name)
59
+ )
51
60
 
52
61
 
53
62
  @catch_errors
@@ -57,6 +66,7 @@ def create_row(
57
66
  ) -> ToolResponse:
58
67
  """Insert a new row into any table in the SQLite Memory Bank."""
59
68
  from .. import server
69
+
60
70
  return cast(ToolResponse, get_database(server.DB_PATH).insert_row(table_name, data))
61
71
 
62
72
 
@@ -67,6 +77,7 @@ def read_rows(
67
77
  ) -> ToolResponse:
68
78
  """Read rows from any table in the SQLite memory bank, with optional filtering."""
69
79
  from .. import server
80
+
70
81
  return cast(ToolResponse, get_database(server.DB_PATH).read_rows(table_name, where))
71
82
 
72
83
 
@@ -78,7 +89,10 @@ def update_rows(
78
89
  ) -> ToolResponse:
79
90
  """Update rows in any table in the SQLite Memory Bank, matching the WHERE clause."""
80
91
  from .. import server
81
- return cast(ToolResponse, get_database(server.DB_PATH).update_rows(table_name, data, where))
92
+
93
+ return cast(
94
+ ToolResponse, get_database(server.DB_PATH).update_rows(table_name, data, where)
95
+ )
82
96
 
83
97
 
84
98
  @catch_errors
@@ -88,7 +102,10 @@ def delete_rows(
88
102
  ) -> ToolResponse:
89
103
  """Delete rows from any table in the SQLite Memory Bank, matching the WHERE clause."""
90
104
  from .. import server
91
- return cast(ToolResponse, get_database(server.DB_PATH).delete_rows(table_name, where))
105
+
106
+ return cast(
107
+ ToolResponse, get_database(server.DB_PATH).delete_rows(table_name, where)
108
+ )
92
109
 
93
110
 
94
111
  @catch_errors
@@ -100,13 +117,406 @@ def run_select_query(
100
117
  ) -> ToolResponse:
101
118
  """Run a safe SELECT query on a table in the SQLite memory bank."""
102
119
  from .. import server
103
- return cast(ToolResponse, get_database(server.DB_PATH).select_query(
104
- table_name, columns, where, limit
105
- ))
120
+
121
+ return cast(
122
+ ToolResponse,
123
+ get_database(server.DB_PATH).select_query(table_name, columns, where, limit),
124
+ )
106
125
 
107
126
 
108
127
  @catch_errors
109
128
  def list_all_columns() -> ToolResponse:
110
129
  """List all columns for all tables in the SQLite memory bank."""
111
130
  from .. import server
131
+
112
132
  return cast(ToolResponse, get_database(server.DB_PATH).list_all_columns())
133
+
134
+
135
+ @catch_errors
136
+ def upsert_memory(
137
+ table_name: str, data: Dict[str, Any], match_columns: List[str]
138
+ ) -> ToolResponse:
139
+ """
140
+ Smart memory upsert: Update existing records or create new ones based on matching columns.
141
+
142
+ This is the preferred method for memory management as it prevents duplicates
143
+ and maintains data consistency.
144
+
145
+ Args:
146
+ table_name (str): Table to upsert into
147
+ data (Dict[str, Any]): Data to upsert
148
+ match_columns (List[str]): Columns to use for finding existing records
149
+
150
+ Returns:
151
+ ToolResponse: {"success": True, "action": "updated"|"created", "id": rowid}
152
+ """
153
+ import os
154
+
155
+ db_path = os.environ.get("DB_PATH", "./test.db")
156
+ db = get_database(db_path)
157
+
158
+ try:
159
+ # Build WHERE clause for matching
160
+ where_conditions = {col: data[col] for col in match_columns if col in data}
161
+
162
+ if not where_conditions:
163
+ # No match columns provided, just insert
164
+ return cast(ToolResponse, db.insert_row(table_name, data))
165
+
166
+ # Check for existing records
167
+ existing_result = db.read_rows(table_name, where_conditions)
168
+ if not existing_result.get("success"):
169
+ return cast(ToolResponse, existing_result)
170
+
171
+ existing_rows = existing_result.get("rows", [])
172
+
173
+ if existing_rows:
174
+ # Update the first matching record
175
+ row_id = existing_rows[0].get("id")
176
+ if row_id:
177
+ update_result = db.update_rows(table_name, data, {"id": row_id})
178
+ if update_result.get("success"):
179
+ return cast(
180
+ ToolResponse,
181
+ {
182
+ "success": True,
183
+ "action": "updated",
184
+ "id": row_id,
185
+ "rows_affected": update_result.get("rows_affected", 1),
186
+ },
187
+ )
188
+ return cast(ToolResponse, update_result)
189
+
190
+ # No existing record found, create new one
191
+ insert_result = db.insert_row(table_name, data)
192
+ if insert_result.get("success"):
193
+ return cast(
194
+ ToolResponse,
195
+ {"success": True, "action": "created", "id": insert_result.get("id")},
196
+ )
197
+ return cast(ToolResponse, insert_result)
198
+
199
+ except Exception as e:
200
+ return cast(
201
+ ToolResponse,
202
+ {
203
+ "success": False,
204
+ "error": f"Memory upsert failed: {str(e)}",
205
+ "category": "UPSERT_ERROR",
206
+ "details": {"table": table_name, "match_columns": match_columns},
207
+ },
208
+ )
209
+
210
+
211
+ @catch_errors
212
+ def batch_create_memories(
213
+ table_name: str,
214
+ data_list: List[Dict[str, Any]],
215
+ match_columns: Optional[List[str]] = None,
216
+ use_upsert: bool = True,
217
+ ) -> ToolResponse:
218
+ """
219
+ Efficiently create multiple memory records in a single operation.
220
+
221
+ Supports both batch insert (fast) and batch upsert (prevents duplicates).
222
+
223
+ Args:
224
+ table_name (str): Table to insert records into
225
+ data_list (List[Dict[str, Any]]): List of records to create
226
+ match_columns (Optional[List[str]]): Columns to use for duplicate detection (if use_upsert=True)
227
+ use_upsert (bool): Whether to use upsert logic to prevent duplicates (default: True)
228
+
229
+ Returns:
230
+ ToolResponse: {"success": True, "created": int, "updated": int, "failed": int, "results": List}
231
+ """
232
+ if not data_list:
233
+ return cast(
234
+ ToolResponse,
235
+ {
236
+ "success": True,
237
+ "created": 0,
238
+ "updated": 0,
239
+ "failed": 0,
240
+ "results": [],
241
+ "message": "No data provided",
242
+ },
243
+ )
244
+
245
+ import os
246
+
247
+ db_path = os.environ.get("DB_PATH", "./test.db")
248
+ db = get_database(db_path)
249
+
250
+ created_count = 0
251
+ updated_count = 0
252
+ failed_count = 0
253
+ results = []
254
+
255
+ try:
256
+ for i, data in enumerate(data_list):
257
+ try:
258
+ if use_upsert and match_columns:
259
+ # Use upsert logic to prevent duplicates
260
+ result = upsert_memory(table_name, data, match_columns)
261
+ if result.get("success"):
262
+ action = result.get("action", "unknown")
263
+ if action == "created":
264
+ created_count += 1
265
+ elif action == "updated":
266
+ updated_count += 1
267
+ results.append(
268
+ {
269
+ "index": i,
270
+ "action": action,
271
+ "id": result.get("id"),
272
+ "success": True,
273
+ }
274
+ )
275
+ else:
276
+ failed_count += 1
277
+ results.append(
278
+ {
279
+ "index": i,
280
+ "action": "failed",
281
+ "error": result.get("error", "Unknown error"),
282
+ "success": False,
283
+ }
284
+ )
285
+ else:
286
+ # Simple batch insert (faster but no duplicate prevention)
287
+ insert_result = db.insert_row(table_name, data)
288
+ if insert_result.get("success"):
289
+ created_count += 1
290
+ results.append(
291
+ {
292
+ "index": i,
293
+ "action": "created",
294
+ "id": insert_result.get("id"),
295
+ "success": True,
296
+ }
297
+ )
298
+ else:
299
+ failed_count += 1
300
+ results.append(
301
+ {
302
+ "index": i,
303
+ "action": "failed",
304
+ "error": insert_result.get("error", "Unknown error"),
305
+ "success": False,
306
+ }
307
+ )
308
+
309
+ except Exception as e:
310
+ failed_count += 1
311
+ results.append(
312
+ {"index": i, "action": "failed", "error": str(e), "success": False}
313
+ )
314
+
315
+ return cast(
316
+ ToolResponse,
317
+ {
318
+ "success": True,
319
+ "created": created_count,
320
+ "updated": updated_count,
321
+ "failed": failed_count,
322
+ "total_processed": len(data_list),
323
+ "results": results,
324
+ "message": f"Processed {len(data_list)} records: {created_count} created, {updated_count} updated, {failed_count} failed",
325
+ },
326
+ )
327
+
328
+ except Exception as e:
329
+ return cast(
330
+ ToolResponse,
331
+ {
332
+ "success": False,
333
+ "error": f"Batch operation failed: {str(e)}",
334
+ "category": "BATCH_CREATE_ERROR",
335
+ "details": {"table": table_name, "records_count": len(data_list)},
336
+ },
337
+ )
338
+
339
+
340
+ @catch_errors
341
+ def batch_delete_memories(
342
+ table_name: str, where_conditions: List[Dict[str, Any]], match_all: bool = False
343
+ ) -> ToolResponse:
344
+ """
345
+ Efficiently delete multiple memory records in a single operation.
346
+
347
+ Supports both individual record deletion and bulk deletion with shared conditions.
348
+
349
+ Args:
350
+ table_name (str): Table to delete records from
351
+ where_conditions (List[Dict[str, Any]]): List of WHERE conditions for deletion
352
+ match_all (bool): If True, delete records matching ALL conditions; if False, delete records matching ANY condition
353
+
354
+ Returns:
355
+ ToolResponse: {"success": True, "deleted": int, "failed": int, "results": List}
356
+ """
357
+ if not where_conditions:
358
+ return cast(
359
+ ToolResponse,
360
+ {
361
+ "success": True,
362
+ "deleted": 0,
363
+ "failed": 0,
364
+ "results": [],
365
+ "message": "No deletion conditions provided",
366
+ },
367
+ )
368
+
369
+ import os
370
+
371
+ db_path = os.environ.get("DB_PATH", "./test.db")
372
+ db = get_database(db_path)
373
+
374
+ deleted_count = 0
375
+ failed_count = 0
376
+ results = []
377
+
378
+ try:
379
+ if match_all and len(where_conditions) == 1:
380
+ # Single condition - use direct delete
381
+ condition = where_conditions[0]
382
+ try:
383
+ delete_result = db.delete_rows(table_name, condition)
384
+ if delete_result.get("success"):
385
+ rows_affected = delete_result.get("rows_affected", 0)
386
+ deleted_count += rows_affected
387
+ results.append(
388
+ {
389
+ "condition_index": 0,
390
+ "condition": condition,
391
+ "action": "deleted",
392
+ "rows_affected": rows_affected,
393
+ "success": True,
394
+ }
395
+ )
396
+ else:
397
+ failed_count += 1
398
+ results.append(
399
+ {
400
+ "condition_index": 0,
401
+ "condition": condition,
402
+ "action": "failed",
403
+ "error": delete_result.get("error", "Unknown error"),
404
+ "success": False,
405
+ }
406
+ )
407
+ except Exception as e:
408
+ failed_count += 1
409
+ results.append(
410
+ {
411
+ "condition_index": 0,
412
+ "condition": condition,
413
+ "action": "failed",
414
+ "error": str(e),
415
+ "success": False,
416
+ }
417
+ )
418
+
419
+ elif match_all:
420
+ # Multiple conditions with AND logic - combine conditions
421
+ combined_condition = {}
422
+ for condition in where_conditions:
423
+ combined_condition.update(condition)
424
+
425
+ try:
426
+ delete_result = db.delete_rows(table_name, combined_condition)
427
+ if delete_result.get("success"):
428
+ rows_affected = delete_result.get("rows_affected", 0)
429
+ deleted_count += rows_affected
430
+ results.append(
431
+ {
432
+ "combined_conditions": where_conditions,
433
+ "action": "deleted",
434
+ "rows_affected": rows_affected,
435
+ "success": True,
436
+ }
437
+ )
438
+ else:
439
+ failed_count += 1
440
+ results.append(
441
+ {
442
+ "combined_conditions": where_conditions,
443
+ "action": "failed",
444
+ "error": delete_result.get("error", "Unknown error"),
445
+ "success": False,
446
+ }
447
+ )
448
+ except Exception as e:
449
+ failed_count += 1
450
+ results.append(
451
+ {
452
+ "combined_conditions": where_conditions,
453
+ "action": "failed",
454
+ "error": str(e),
455
+ "success": False,
456
+ }
457
+ )
458
+ else:
459
+ # Multiple conditions with OR logic - delete each separately
460
+ for i, condition in enumerate(where_conditions):
461
+ try:
462
+ delete_result = db.delete_rows(table_name, condition)
463
+ if delete_result.get("success"):
464
+ rows_affected = delete_result.get("rows_affected", 0)
465
+ deleted_count += rows_affected
466
+ results.append(
467
+ {
468
+ "condition_index": i,
469
+ "condition": condition,
470
+ "action": "deleted",
471
+ "rows_affected": rows_affected,
472
+ "success": True,
473
+ }
474
+ )
475
+ else:
476
+ failed_count += 1
477
+ results.append(
478
+ {
479
+ "condition_index": i,
480
+ "condition": condition,
481
+ "action": "failed",
482
+ "error": delete_result.get("error", "Unknown error"),
483
+ "success": False,
484
+ }
485
+ )
486
+ except Exception as e:
487
+ failed_count += 1
488
+ results.append(
489
+ {
490
+ "condition_index": i,
491
+ "condition": condition,
492
+ "action": "failed",
493
+ "error": str(e),
494
+ "success": False,
495
+ }
496
+ )
497
+
498
+ return cast(
499
+ ToolResponse,
500
+ {
501
+ "success": True,
502
+ "deleted": deleted_count,
503
+ "failed": failed_count,
504
+ "total_conditions": len(where_conditions),
505
+ "results": results,
506
+ "message": f"Processed {len(where_conditions)} deletion conditions: {deleted_count} records deleted, {failed_count} operations failed",
507
+ },
508
+ )
509
+
510
+ except Exception as e:
511
+ return cast(
512
+ ToolResponse,
513
+ {
514
+ "success": False,
515
+ "error": f"Batch deletion failed: {str(e)}",
516
+ "category": "BATCH_DELETE_ERROR",
517
+ "details": {
518
+ "table": table_name,
519
+ "conditions_count": len(where_conditions),
520
+ },
521
+ },
522
+ )