mcp-sqlite-memory-bank 1.4.3__py3-none-any.whl → 1.5.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.
- mcp_sqlite_memory_bank/__init__.py +20 -3
- mcp_sqlite_memory_bank/__main__.py +59 -0
- mcp_sqlite_memory_bank/database.py +60 -12
- mcp_sqlite_memory_bank/resources.py +256 -0
- mcp_sqlite_memory_bank/semantic.py +14 -4
- mcp_sqlite_memory_bank/server.py +336 -491
- mcp_sqlite_memory_bank/tools/__init__.py +79 -0
- mcp_sqlite_memory_bank/tools/analytics.py +419 -0
- mcp_sqlite_memory_bank/tools/basic.py +112 -0
- mcp_sqlite_memory_bank/tools/discovery.py +1176 -0
- mcp_sqlite_memory_bank/tools/search.py +380 -0
- mcp_sqlite_memory_bank/utils.py +189 -0
- {mcp_sqlite_memory_bank-1.4.3.dist-info → mcp_sqlite_memory_bank-1.5.1.dist-info}/METADATA +1 -1
- mcp_sqlite_memory_bank-1.5.1.dist-info/RECORD +21 -0
- mcp_sqlite_memory_bank-1.4.3.dist-info/RECORD +0 -15
- {mcp_sqlite_memory_bank-1.4.3.dist-info → mcp_sqlite_memory_bank-1.5.1.dist-info}/WHEEL +0 -0
- {mcp_sqlite_memory_bank-1.4.3.dist-info → mcp_sqlite_memory_bank-1.5.1.dist-info}/entry_points.txt +0 -0
- {mcp_sqlite_memory_bank-1.4.3.dist-info → mcp_sqlite_memory_bank-1.5.1.dist-info}/licenses/LICENSE +0 -0
- {mcp_sqlite_memory_bank-1.4.3.dist-info → mcp_sqlite_memory_bank-1.5.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,380 @@
|
|
1
|
+
"""
|
2
|
+
Search tools module for SQLite Memory Bank.
|
3
|
+
|
4
|
+
This module contains all search-related MCP tools including content search,
|
5
|
+
semantic search, embedding management, and intelligent search capabilities.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import logging
|
9
|
+
import traceback
|
10
|
+
from typing import Any, Dict, List, Optional, cast
|
11
|
+
|
12
|
+
from ..database import get_database
|
13
|
+
from ..types import MemoryBankError, DatabaseError, ToolResponse
|
14
|
+
from ..utils import catch_errors
|
15
|
+
from ..semantic import is_semantic_search_available
|
16
|
+
|
17
|
+
|
18
|
+
@catch_errors
|
19
|
+
def search_content(
|
20
|
+
query: str,
|
21
|
+
tables: Optional[List[str]] = None,
|
22
|
+
limit: int = 50,
|
23
|
+
) -> ToolResponse:
|
24
|
+
"""Perform full-text search across table content using natural language queries."""
|
25
|
+
from .. import server
|
26
|
+
return cast(ToolResponse, get_database(server.DB_PATH).search_content(query, tables, limit))
|
27
|
+
|
28
|
+
|
29
|
+
@catch_errors
|
30
|
+
def explore_tables(
|
31
|
+
pattern: Optional[str] = None,
|
32
|
+
include_row_counts: bool = True,
|
33
|
+
) -> ToolResponse:
|
34
|
+
"""Explore and discover table structures and content for better searchability."""
|
35
|
+
from .. import server
|
36
|
+
return cast(ToolResponse, get_database(server.DB_PATH).explore_tables(pattern, include_row_counts))
|
37
|
+
|
38
|
+
|
39
|
+
@catch_errors
|
40
|
+
def add_embeddings(
|
41
|
+
table_name: str,
|
42
|
+
text_columns: List[str],
|
43
|
+
embedding_column: str = "embedding",
|
44
|
+
model_name: str = "all-MiniLM-L6-v2",
|
45
|
+
) -> ToolResponse:
|
46
|
+
"""Generate and store vector embeddings for semantic search on table content."""
|
47
|
+
from .. import server
|
48
|
+
return cast(ToolResponse, get_database(server.DB_PATH).generate_embeddings(
|
49
|
+
table_name, text_columns, embedding_column, model_name
|
50
|
+
))
|
51
|
+
|
52
|
+
|
53
|
+
@catch_errors
|
54
|
+
def semantic_search(
|
55
|
+
query: str,
|
56
|
+
tables: Optional[List[str]] = None,
|
57
|
+
similarity_threshold: float = 0.5,
|
58
|
+
limit: int = 10,
|
59
|
+
model_name: str = "all-MiniLM-L6-v2",
|
60
|
+
) -> ToolResponse:
|
61
|
+
"""Find content using natural language semantic similarity rather than exact keyword matching."""
|
62
|
+
from .. import server
|
63
|
+
return cast(ToolResponse, get_database(server.DB_PATH).semantic_search(
|
64
|
+
query, tables, "embedding", None, similarity_threshold, limit, model_name
|
65
|
+
))
|
66
|
+
|
67
|
+
|
68
|
+
@catch_errors
|
69
|
+
def find_related(
|
70
|
+
table_name: str,
|
71
|
+
row_id: int,
|
72
|
+
similarity_threshold: float = 0.5,
|
73
|
+
limit: int = 5,
|
74
|
+
model_name: str = "all-MiniLM-L6-v2",
|
75
|
+
) -> ToolResponse:
|
76
|
+
"""Find content related to a specific row by semantic similarity."""
|
77
|
+
from .. import server
|
78
|
+
return cast(ToolResponse, get_database(server.DB_PATH).find_related_content(
|
79
|
+
table_name, row_id, "embedding", similarity_threshold, limit, model_name
|
80
|
+
))
|
81
|
+
|
82
|
+
|
83
|
+
@catch_errors
|
84
|
+
def smart_search(
|
85
|
+
query: str,
|
86
|
+
tables: Optional[List[str]] = None,
|
87
|
+
semantic_weight: float = 0.7,
|
88
|
+
text_weight: float = 0.3,
|
89
|
+
limit: int = 10,
|
90
|
+
model_name: str = "all-MiniLM-L6-v2",
|
91
|
+
) -> ToolResponse:
|
92
|
+
"""Intelligent hybrid search combining semantic understanding with keyword matching."""
|
93
|
+
from .. import server
|
94
|
+
return cast(ToolResponse, get_database(server.DB_PATH).hybrid_search(
|
95
|
+
query, tables, None, "embedding", semantic_weight, text_weight, limit, model_name
|
96
|
+
))
|
97
|
+
|
98
|
+
|
99
|
+
@catch_errors
|
100
|
+
def embedding_stats(
|
101
|
+
table_name: str,
|
102
|
+
embedding_column: str = "embedding",
|
103
|
+
) -> ToolResponse:
|
104
|
+
"""Get statistics about semantic search readiness for a table."""
|
105
|
+
from .. import server
|
106
|
+
return cast(ToolResponse, get_database(server.DB_PATH).get_embedding_stats(table_name, embedding_column))
|
107
|
+
|
108
|
+
|
109
|
+
@catch_errors
|
110
|
+
def auto_semantic_search(
|
111
|
+
query: str,
|
112
|
+
tables: Optional[List[str]] = None,
|
113
|
+
similarity_threshold: float = 0.5,
|
114
|
+
limit: int = 10,
|
115
|
+
model_name: str = "all-MiniLM-L6-v2",
|
116
|
+
) -> ToolResponse:
|
117
|
+
"""
|
118
|
+
🚀 **ZERO-SETUP SEMANTIC SEARCH** - Just search, embeddings are handled automatically!
|
119
|
+
|
120
|
+
Find content using natural language semantic similarity. If embeddings don't exist,
|
121
|
+
they will be automatically generated for text columns. This is the easiest way to
|
122
|
+
do semantic search - no manual setup required!
|
123
|
+
|
124
|
+
Args:
|
125
|
+
query (str): Natural language search query
|
126
|
+
tables (Optional[List[str]]): Specific tables to search (default: all tables)
|
127
|
+
similarity_threshold (float): Minimum similarity score (0.0-1.0, default: 0.5)
|
128
|
+
limit (int): Maximum number of results to return (default: 10)
|
129
|
+
model_name (str): Model to use for embeddings (default: "all-MiniLM-L6-v2")
|
130
|
+
|
131
|
+
Returns:
|
132
|
+
ToolResponse: On success: {"success": True, "results": List[...], "auto_embedded_tables": List[str]}
|
133
|
+
On error: {"success": False, "error": str, "category": str, "details": dict}
|
134
|
+
|
135
|
+
Examples:
|
136
|
+
>>> auto_semantic_search("API design patterns")
|
137
|
+
{"success": True, "results": [
|
138
|
+
{"table_name": "technical_decisions", "similarity_score": 0.87, "decision_name": "REST API Structure", ...}
|
139
|
+
], "auto_embedded_tables": ["technical_decisions"]}
|
140
|
+
|
141
|
+
>>> auto_semantic_search("machine learning concepts")
|
142
|
+
# Finds content about "ML", "AI", "neural networks", etc.
|
143
|
+
# Automatically creates embeddings if they don't exist!
|
144
|
+
|
145
|
+
FastMCP Tool Info:
|
146
|
+
- **COMPLETELY AUTOMATIC**: No manual embedding setup required
|
147
|
+
- Auto-detects text columns and creates embeddings as needed
|
148
|
+
- Works across multiple tables simultaneously
|
149
|
+
- Finds conceptually similar content regardless of exact wording
|
150
|
+
- Returns relevance scores for ranking results
|
151
|
+
- Supports fuzzy matching and concept discovery
|
152
|
+
- Perfect for agents - just search and it works!
|
153
|
+
"""
|
154
|
+
try:
|
155
|
+
from .. import server
|
156
|
+
db = get_database(server.DB_PATH)
|
157
|
+
auto_embedded_tables: List[str] = []
|
158
|
+
|
159
|
+
# Get tables to search
|
160
|
+
search_tables: List[str]
|
161
|
+
if tables:
|
162
|
+
search_tables = tables
|
163
|
+
else:
|
164
|
+
tables_result = db.list_tables()
|
165
|
+
if not tables_result.get("success"):
|
166
|
+
return cast(ToolResponse, tables_result)
|
167
|
+
all_tables = tables_result.get("tables", [])
|
168
|
+
if isinstance(all_tables, list):
|
169
|
+
search_tables = all_tables
|
170
|
+
else:
|
171
|
+
search_tables = []
|
172
|
+
|
173
|
+
# Auto-embed text columns in tables that don't have embeddings
|
174
|
+
for table_name in search_tables:
|
175
|
+
try:
|
176
|
+
# Check if table has embeddings
|
177
|
+
stats_result = db.get_embedding_stats(table_name, "embedding")
|
178
|
+
coverage_percent = stats_result.get("coverage_percent", 0)
|
179
|
+
if stats_result.get("success") and isinstance(coverage_percent, (int, float)) and coverage_percent > 0:
|
180
|
+
continue # Table already has embeddings
|
181
|
+
|
182
|
+
# Get table schema to find text columns
|
183
|
+
schema_result = db.describe_table(table_name)
|
184
|
+
if not schema_result.get("success"):
|
185
|
+
continue
|
186
|
+
|
187
|
+
# Find text columns
|
188
|
+
text_columns = []
|
189
|
+
columns = schema_result.get("columns", [])
|
190
|
+
if isinstance(columns, list):
|
191
|
+
for col in columns:
|
192
|
+
if isinstance(col, dict) and "TEXT" in col.get("type", "").upper():
|
193
|
+
text_columns.append(col["name"])
|
194
|
+
|
195
|
+
# Auto-embed text columns
|
196
|
+
if text_columns:
|
197
|
+
embed_result = db.generate_embeddings(table_name, text_columns, "embedding", model_name)
|
198
|
+
if embed_result.get("success"):
|
199
|
+
auto_embedded_tables.append(table_name)
|
200
|
+
|
201
|
+
except Exception:
|
202
|
+
# If auto-embedding fails, continue without it
|
203
|
+
continue
|
204
|
+
|
205
|
+
# Perform semantic search
|
206
|
+
search_result = db.semantic_search(
|
207
|
+
query, search_tables, "embedding", None, similarity_threshold, limit, model_name
|
208
|
+
)
|
209
|
+
|
210
|
+
# Add auto-embedding info to result
|
211
|
+
if isinstance(search_result, dict):
|
212
|
+
search_result["auto_embedded_tables"] = auto_embedded_tables
|
213
|
+
if auto_embedded_tables:
|
214
|
+
search_result["auto_embedding_note"] = f"Automatically generated embeddings for {len(auto_embedded_tables)} table(s)"
|
215
|
+
|
216
|
+
return cast(ToolResponse, search_result)
|
217
|
+
|
218
|
+
except Exception as e:
|
219
|
+
return cast(ToolResponse, {
|
220
|
+
"success": False,
|
221
|
+
"error": f"Auto semantic search failed: {str(e)}",
|
222
|
+
"category": "SEMANTIC_SEARCH_ERROR",
|
223
|
+
"details": {"query": query, "tables": tables}
|
224
|
+
})
|
225
|
+
|
226
|
+
|
227
|
+
@catch_errors
|
228
|
+
def auto_smart_search(
|
229
|
+
query: str,
|
230
|
+
tables: Optional[List[str]] = None,
|
231
|
+
semantic_weight: float = 0.7,
|
232
|
+
text_weight: float = 0.3,
|
233
|
+
limit: int = 10,
|
234
|
+
model_name: str = "all-MiniLM-L6-v2",
|
235
|
+
) -> ToolResponse:
|
236
|
+
"""
|
237
|
+
🚀 **ZERO-SETUP HYBRID SEARCH** - Best of both worlds with automatic embedding!
|
238
|
+
|
239
|
+
Intelligent hybrid search combining semantic understanding with keyword matching.
|
240
|
+
Automatically generates embeddings for text columns when needed. This is the
|
241
|
+
ultimate search tool - no manual setup required!
|
242
|
+
|
243
|
+
Args:
|
244
|
+
query (str): Search query (natural language or keywords)
|
245
|
+
tables (Optional[List[str]]): Tables to search (default: all)
|
246
|
+
semantic_weight (float): Weight for semantic similarity (0.0-1.0, default: 0.7)
|
247
|
+
text_weight (float): Weight for keyword matching (0.0-1.0, default: 0.3)
|
248
|
+
limit (int): Maximum results (default: 10)
|
249
|
+
model_name (str): Semantic model to use (default: "all-MiniLM-L6-v2")
|
250
|
+
|
251
|
+
Returns:
|
252
|
+
ToolResponse: On success: {"success": True, "results": List[...], "search_type": "auto_hybrid"}
|
253
|
+
On error: {"success": False, "error": str, "category": str, "details": dict}
|
254
|
+
|
255
|
+
Examples:
|
256
|
+
>>> auto_smart_search("user authentication security")
|
257
|
+
{"success": True, "results": [
|
258
|
+
{"combined_score": 0.89, "semantic_score": 0.92, "text_score": 0.82, ...}
|
259
|
+
], "search_type": "auto_hybrid", "auto_embedded_tables": ["user_data"]}
|
260
|
+
|
261
|
+
FastMCP Tool Info:
|
262
|
+
- **COMPLETELY AUTOMATIC**: No manual embedding setup required
|
263
|
+
- Automatically balances semantic and keyword search
|
264
|
+
- Auto-detects text columns and creates embeddings as needed
|
265
|
+
- Provides separate scores for transparency
|
266
|
+
- Falls back gracefully if semantic search unavailable
|
267
|
+
- Optimal for both exploratory and precise searches
|
268
|
+
- Perfect for agents - ultimate search tool that just works!
|
269
|
+
"""
|
270
|
+
try:
|
271
|
+
from .. import server
|
272
|
+
db = get_database(server.DB_PATH)
|
273
|
+
auto_embedded_tables: List[str] = []
|
274
|
+
|
275
|
+
# Get tables to search
|
276
|
+
search_tables: List[str]
|
277
|
+
if tables:
|
278
|
+
search_tables = tables
|
279
|
+
else:
|
280
|
+
tables_result = db.list_tables()
|
281
|
+
if not tables_result.get("success"):
|
282
|
+
return cast(ToolResponse, tables_result)
|
283
|
+
all_tables = tables_result.get("tables", [])
|
284
|
+
if isinstance(all_tables, list):
|
285
|
+
search_tables = all_tables
|
286
|
+
else:
|
287
|
+
search_tables = []
|
288
|
+
|
289
|
+
# Auto-embed text columns in tables that don't have embeddings
|
290
|
+
for table_name in search_tables:
|
291
|
+
try:
|
292
|
+
# Check if table has embeddings
|
293
|
+
stats_result = db.get_embedding_stats(table_name, "embedding")
|
294
|
+
coverage_percent = stats_result.get("coverage_percent", 0)
|
295
|
+
if stats_result.get("success") and isinstance(coverage_percent, (int, float)) and coverage_percent > 0:
|
296
|
+
continue # Table already has embeddings
|
297
|
+
|
298
|
+
# Get table schema to find text columns
|
299
|
+
schema_result = db.describe_table(table_name)
|
300
|
+
if not schema_result.get("success"):
|
301
|
+
continue
|
302
|
+
|
303
|
+
# Find text columns
|
304
|
+
text_columns = []
|
305
|
+
columns = schema_result.get("columns", [])
|
306
|
+
if isinstance(columns, list):
|
307
|
+
for col in columns:
|
308
|
+
if isinstance(col, dict) and "TEXT" in col.get("type", "").upper():
|
309
|
+
text_columns.append(col["name"])
|
310
|
+
|
311
|
+
# Auto-embed text columns
|
312
|
+
if text_columns:
|
313
|
+
embed_result = db.generate_embeddings(table_name, text_columns, "embedding", model_name)
|
314
|
+
if embed_result.get("success"):
|
315
|
+
auto_embedded_tables.append(table_name)
|
316
|
+
|
317
|
+
except Exception:
|
318
|
+
# If auto-embedding fails, continue without it
|
319
|
+
continue
|
320
|
+
|
321
|
+
# Now perform hybrid search using the same pattern as smart_search
|
322
|
+
try:
|
323
|
+
hybrid_result = get_database(server.DB_PATH).hybrid_search(
|
324
|
+
query, search_tables, None, "embedding", semantic_weight, text_weight, limit, model_name
|
325
|
+
)
|
326
|
+
except Exception as search_error:
|
327
|
+
# If hybrid search fails, fall back to regular content search
|
328
|
+
logging.warning(f"Hybrid search failed, falling back to content search: {search_error}")
|
329
|
+
try:
|
330
|
+
fallback_result = get_database(server.DB_PATH).search_content(query, search_tables, limit)
|
331
|
+
if fallback_result.get("success"):
|
332
|
+
# Create a new dictionary to avoid type issues
|
333
|
+
enhanced_fallback = dict(fallback_result)
|
334
|
+
enhanced_fallback["search_type"] = "text_fallback"
|
335
|
+
enhanced_fallback["auto_embedded_tables"] = auto_embedded_tables
|
336
|
+
enhanced_fallback["fallback_reason"] = str(search_error)
|
337
|
+
return cast(ToolResponse, enhanced_fallback)
|
338
|
+
except Exception as fallback_error:
|
339
|
+
return cast(ToolResponse, {
|
340
|
+
"success": False,
|
341
|
+
"error": f"Both hybrid and fallback search failed. Hybrid: {search_error}, Fallback: {fallback_error}",
|
342
|
+
"category": "HYBRID_SEARCH_ERROR",
|
343
|
+
"details": {"query": query, "tables": tables}
|
344
|
+
})
|
345
|
+
|
346
|
+
# If we get here, both searches failed
|
347
|
+
return cast(ToolResponse, {
|
348
|
+
"success": False,
|
349
|
+
"error": f"Hybrid search failed: {search_error}",
|
350
|
+
"category": "HYBRID_SEARCH_ERROR",
|
351
|
+
"details": {"query": query, "tables": tables}
|
352
|
+
})
|
353
|
+
|
354
|
+
# Add auto-embedding info to result
|
355
|
+
if isinstance(hybrid_result, dict) and hybrid_result.get("success"):
|
356
|
+
# Convert to mutable dict to add extra fields
|
357
|
+
final_result = dict(hybrid_result)
|
358
|
+
final_result["search_type"] = "auto_hybrid"
|
359
|
+
final_result["auto_embedded_tables"] = auto_embedded_tables
|
360
|
+
if auto_embedded_tables:
|
361
|
+
final_result["auto_embedding_note"] = f"Automatically generated embeddings for {len(auto_embedded_tables)} table(s)"
|
362
|
+
return cast(ToolResponse, final_result)
|
363
|
+
else:
|
364
|
+
return cast(ToolResponse, hybrid_result)
|
365
|
+
|
366
|
+
except Exception as e:
|
367
|
+
# Add detailed error information for debugging
|
368
|
+
error_details = {
|
369
|
+
"query": query,
|
370
|
+
"tables": tables,
|
371
|
+
"error_type": type(e).__name__,
|
372
|
+
"error_str": str(e),
|
373
|
+
"traceback": traceback.format_exc()
|
374
|
+
}
|
375
|
+
return cast(ToolResponse, {
|
376
|
+
"success": False,
|
377
|
+
"error": f"Auto smart search failed: {str(e)}",
|
378
|
+
"category": "HYBRID_SEARCH_ERROR",
|
379
|
+
"details": error_details
|
380
|
+
})
|
mcp_sqlite_memory_bank/utils.py
CHANGED
@@ -9,6 +9,9 @@ import re
|
|
9
9
|
import os
|
10
10
|
import sqlite3
|
11
11
|
import logging
|
12
|
+
import sys
|
13
|
+
import traceback
|
14
|
+
from datetime import datetime
|
12
15
|
from functools import wraps
|
13
16
|
from typing import Any, Callable, Dict, List, TypeVar, cast, Union, Tuple
|
14
17
|
from .types import ValidationError, DatabaseError, SchemaError, MemoryBankError, ToolResponse
|
@@ -174,3 +177,189 @@ def build_where_clause(where: Dict[str, Any], valid_columns: List[str]) -> Union
|
|
174
177
|
except Exception as e:
|
175
178
|
logging.error(f"Error in build_where_clause: {e}")
|
176
179
|
return {"success": False, "error": f"Error building WHERE clause: {e}"}
|
180
|
+
|
181
|
+
|
182
|
+
def suggest_recovery(error: Exception, function_name: str) -> Dict[str, Any]:
|
183
|
+
"""
|
184
|
+
Suggest recovery actions based on the error type and context.
|
185
|
+
|
186
|
+
Args:
|
187
|
+
error: The exception that occurred
|
188
|
+
function_name: Name of the function where error occurred
|
189
|
+
|
190
|
+
Returns:
|
191
|
+
Dictionary with recovery suggestions
|
192
|
+
"""
|
193
|
+
suggestions = {
|
194
|
+
"auto_recovery_available": False,
|
195
|
+
"manual_steps": [],
|
196
|
+
"documentation_links": [],
|
197
|
+
"similar_errors": []
|
198
|
+
}
|
199
|
+
|
200
|
+
error_str = str(error).lower()
|
201
|
+
error_type = type(error).__name__
|
202
|
+
|
203
|
+
# Dependency-related errors
|
204
|
+
if "sentence-transformers" in error_str or "transformers" in error_str:
|
205
|
+
suggestions.update({
|
206
|
+
"auto_recovery_available": True,
|
207
|
+
"install_command": "pip install sentence-transformers",
|
208
|
+
"manual_steps": [
|
209
|
+
"Install sentence-transformers: pip install sentence-transformers",
|
210
|
+
"Restart the MCP server",
|
211
|
+
"Try the semantic search operation again"
|
212
|
+
],
|
213
|
+
"explanation": "Semantic search requires the sentence-transformers library",
|
214
|
+
"fallback_available": "Keyword search is available as fallback"
|
215
|
+
})
|
216
|
+
|
217
|
+
# Database errors
|
218
|
+
elif "database" in error_str or "sqlite" in error_str:
|
219
|
+
suggestions.update({
|
220
|
+
"manual_steps": [
|
221
|
+
"Check if database file exists and is writable",
|
222
|
+
"Verify disk space is available",
|
223
|
+
"Check if another process is using the database",
|
224
|
+
"Try creating a new database file"
|
225
|
+
],
|
226
|
+
"auto_recovery_available": False,
|
227
|
+
"diagnostics": {
|
228
|
+
"check_db_path": "Verify DB_PATH environment variable",
|
229
|
+
"check_permissions": "Ensure write permissions to database directory"
|
230
|
+
}
|
231
|
+
})
|
232
|
+
|
233
|
+
# Table/schema errors
|
234
|
+
elif "table" in error_str and ("not exist" in error_str or "missing" in error_str):
|
235
|
+
suggestions.update({
|
236
|
+
"auto_recovery_available": True,
|
237
|
+
"manual_steps": [
|
238
|
+
"List available tables with list_tables()",
|
239
|
+
"Check table name spelling",
|
240
|
+
"Create the table if it doesn't exist",
|
241
|
+
"Refresh your table list"
|
242
|
+
],
|
243
|
+
"next_actions": ["call list_tables() to see available tables"]
|
244
|
+
})
|
245
|
+
|
246
|
+
# Column errors
|
247
|
+
elif "column" in error_str and ("not exist" in error_str or "invalid" in error_str):
|
248
|
+
suggestions.update({
|
249
|
+
"auto_recovery_available": True,
|
250
|
+
"manual_steps": [
|
251
|
+
"Use describe_table() to see available columns",
|
252
|
+
"Check column name spelling and case",
|
253
|
+
"Verify the column exists in the table schema"
|
254
|
+
],
|
255
|
+
"next_actions": ["call describe_table() to see column schema"]
|
256
|
+
})
|
257
|
+
|
258
|
+
# Import/module errors
|
259
|
+
elif "import" in error_str or "module" in error_str:
|
260
|
+
suggestions.update({
|
261
|
+
"manual_steps": [
|
262
|
+
"Check if required packages are installed",
|
263
|
+
"Verify Python environment is correct",
|
264
|
+
"Try reinstalling the package",
|
265
|
+
"Check for version compatibility issues"
|
266
|
+
],
|
267
|
+
"diagnostics": {
|
268
|
+
"python_version": sys.version,
|
269
|
+
"check_packages": "pip list | grep -E '(torch|transformers|sentence)'"
|
270
|
+
}
|
271
|
+
})
|
272
|
+
|
273
|
+
# Function/method errors (like our recent 'FunctionTool' issue)
|
274
|
+
elif "not callable" in error_str or "has no attribute" in error_str:
|
275
|
+
suggestions.update({
|
276
|
+
"manual_steps": [
|
277
|
+
"Check if you're using the correct function/method name",
|
278
|
+
"Verify the object type is what you expect",
|
279
|
+
"Check for import issues or namespace conflicts",
|
280
|
+
"Try restarting the MCP server"
|
281
|
+
],
|
282
|
+
"diagnostics": {
|
283
|
+
"object_type": "Check the actual type of the object being called",
|
284
|
+
"namespace_check": "Verify imports and module loading"
|
285
|
+
},
|
286
|
+
"likely_causes": [
|
287
|
+
"Using PyPI version instead of local development code",
|
288
|
+
"Import conflicts between different module versions",
|
289
|
+
"Object not properly initialized"
|
290
|
+
]
|
291
|
+
})
|
292
|
+
|
293
|
+
# Add context-specific suggestions
|
294
|
+
if function_name.startswith("semantic") or function_name.startswith("embedding"):
|
295
|
+
suggestions["context_help"] = {
|
296
|
+
"semantic_search_help": "Semantic search requires sentence-transformers and embeddings to be generated",
|
297
|
+
"embedding_help": "Use add_embeddings() and generate_embeddings() before semantic search",
|
298
|
+
"fallback_option": "Consider using search_content() for keyword-based search"
|
299
|
+
}
|
300
|
+
|
301
|
+
return suggestions
|
302
|
+
|
303
|
+
|
304
|
+
def enhanced_catch_errors(include_traceback: bool = False, auto_recovery: bool = True):
|
305
|
+
"""
|
306
|
+
Enhanced error decorator with debugging context and auto-recovery suggestions.
|
307
|
+
|
308
|
+
Args:
|
309
|
+
include_traceback: Whether to include full traceback in error details
|
310
|
+
auto_recovery: Whether to include auto-recovery suggestions
|
311
|
+
"""
|
312
|
+
def decorator(f: T) -> T:
|
313
|
+
@wraps(f)
|
314
|
+
def wrapper(*args: Any, **kwargs: Any) -> ToolResponse:
|
315
|
+
try:
|
316
|
+
return f(*args, **kwargs)
|
317
|
+
except Exception as e:
|
318
|
+
# Enhanced error context
|
319
|
+
error_context = {
|
320
|
+
"function": f.__name__,
|
321
|
+
"timestamp": datetime.now().isoformat(),
|
322
|
+
"args_preview": str(args)[:200], # Truncate for safety
|
323
|
+
"kwargs_preview": {k: str(v)[:100] for k, v in kwargs.items()},
|
324
|
+
"python_version": sys.version,
|
325
|
+
"error_type": type(e).__name__
|
326
|
+
}
|
327
|
+
|
328
|
+
# Add traceback if requested
|
329
|
+
if include_traceback:
|
330
|
+
error_context["traceback"] = traceback.format_exc()
|
331
|
+
|
332
|
+
# Auto-recovery suggestions
|
333
|
+
recovery_suggestions = {}
|
334
|
+
if auto_recovery:
|
335
|
+
recovery_suggestions = suggest_recovery(e, f.__name__)
|
336
|
+
|
337
|
+
# Determine error category
|
338
|
+
if isinstance(e, MemoryBankError):
|
339
|
+
category = e.category.name if hasattr(e, 'category') else "MEMORY_BANK"
|
340
|
+
return cast(ToolResponse, {
|
341
|
+
"success": False,
|
342
|
+
"error": str(e),
|
343
|
+
"category": category,
|
344
|
+
"details": error_context,
|
345
|
+
"recovery": recovery_suggestions
|
346
|
+
})
|
347
|
+
elif isinstance(e, sqlite3.Error):
|
348
|
+
return cast(ToolResponse, {
|
349
|
+
"success": False,
|
350
|
+
"error": f"Database error: {str(e)}",
|
351
|
+
"category": "DATABASE",
|
352
|
+
"details": error_context,
|
353
|
+
"recovery": recovery_suggestions
|
354
|
+
})
|
355
|
+
else:
|
356
|
+
return cast(ToolResponse, {
|
357
|
+
"success": False,
|
358
|
+
"error": f"Unexpected error: {str(e)}",
|
359
|
+
"category": "SYSTEM",
|
360
|
+
"details": error_context,
|
361
|
+
"recovery": recovery_suggestions
|
362
|
+
})
|
363
|
+
|
364
|
+
return cast(T, wrapper)
|
365
|
+
return decorator
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: mcp_sqlite_memory_bank
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.5.1
|
4
4
|
Summary: A dynamic, agent/LLM-friendly SQLite memory bank for MCP servers with semantic search capabilities.
|
5
5
|
Author-email: Robert Meisner <robert@catchit.pl>
|
6
6
|
License-Expression: MIT
|
@@ -0,0 +1,21 @@
|
|
1
|
+
mcp_sqlite_memory_bank/__init__.py,sha256=XHBjR2IbhTdlXtGCCUSM-4M1y-SIVTMIVx3iQXWSd14,2796
|
2
|
+
mcp_sqlite_memory_bank/__main__.py,sha256=MkEV9A5JNAi4Pt5zbKM0qguPN5v8a0GXSS0OqXeDzVM,2040
|
3
|
+
mcp_sqlite_memory_bank/database.py,sha256=E-GZ150XWgimgAi3LbATz2WlrzhOd1OcMhkuQip3BkI,46489
|
4
|
+
mcp_sqlite_memory_bank/prompts.py,sha256=nLY6rf08wU5TeSLoSxjTlwcU_OIiJeOIkJYDQM_PFpo,11762
|
5
|
+
mcp_sqlite_memory_bank/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
|
+
mcp_sqlite_memory_bank/resources.py,sha256=ozk0GYTwDxITWrUiOE735hn1v17kf8lJwIe9V1o2fb8,21680
|
7
|
+
mcp_sqlite_memory_bank/semantic.py,sha256=LTZWBnENx6G1QGqppMLanikAC_bXGjEMB79ojU6rjDg,15349
|
8
|
+
mcp_sqlite_memory_bank/server.py,sha256=YC4n_0yXrToLquCZE6iyLp_AdQUzCCNixYZm384fQno,43710
|
9
|
+
mcp_sqlite_memory_bank/types.py,sha256=Lr6RVUUrdDdHUod6IvxLFaWg3H2uezL9luL8pYKxahM,6692
|
10
|
+
mcp_sqlite_memory_bank/utils.py,sha256=1sNMlqpauqezDMd4uvjl_31qANm4K9C6HP4pGWo1Pxg,13963
|
11
|
+
mcp_sqlite_memory_bank/tools/__init__.py,sha256=hLOaeSndOAmeXGPYZmdwpnPEwE8nCqKKAaDWFENDsyE,1807
|
12
|
+
mcp_sqlite_memory_bank/tools/analytics.py,sha256=iTWZ5CVUiw3itdwvY8XEjnw8uwEYInO11LnVQU1UBas,19470
|
13
|
+
mcp_sqlite_memory_bank/tools/basic.py,sha256=6wB6r_n67WLWYGTHlpgUfTT7uFZPeAFJOX3Ly0flgyg,3604
|
14
|
+
mcp_sqlite_memory_bank/tools/discovery.py,sha256=pHwM0P2ffef3_MHFIehnWHRWzWz0vNGI8wZDN9IgVwg,53327
|
15
|
+
mcp_sqlite_memory_bank/tools/search.py,sha256=SZhxtP0t_dUr3ttIEl6CDUFcdKESVcPQSBeCpBDD250,16176
|
16
|
+
mcp_sqlite_memory_bank-1.5.1.dist-info/licenses/LICENSE,sha256=KPr7eFgCJqQIjeSAcwRafbjcgm-10zkrJ7MFoTOGJQg,1092
|
17
|
+
mcp_sqlite_memory_bank-1.5.1.dist-info/METADATA,sha256=rrGClHtmpAGrRD2fXJOa917MjGGx4dlRvPWsc27DiF4,33094
|
18
|
+
mcp_sqlite_memory_bank-1.5.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
19
|
+
mcp_sqlite_memory_bank-1.5.1.dist-info/entry_points.txt,sha256=S9yGWiCe8f_rgcGCgbwEAX2FfJ9jXWxcc4K4Jenbcn8,150
|
20
|
+
mcp_sqlite_memory_bank-1.5.1.dist-info/top_level.txt,sha256=xQ8MTGECpWMR-9DV4H8mMqaSoZqE-C8EvpOg9E2U1wM,23
|
21
|
+
mcp_sqlite_memory_bank-1.5.1.dist-info/RECORD,,
|
@@ -1,15 +0,0 @@
|
|
1
|
-
mcp_sqlite_memory_bank/__init__.py,sha256=6Y9_iSiQIWOPJYMcMkbrqmaWiM-ymRHdNR6W-8jHj-k,2403
|
2
|
-
mcp_sqlite_memory_bank/database.py,sha256=cL60QMCVUO0fNqeMk0_PvKCQXfNTcl04-1jQX0TIK90,43000
|
3
|
-
mcp_sqlite_memory_bank/prompts.py,sha256=nLY6rf08wU5TeSLoSxjTlwcU_OIiJeOIkJYDQM_PFpo,11762
|
4
|
-
mcp_sqlite_memory_bank/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
-
mcp_sqlite_memory_bank/resources.py,sha256=A55G44MIQdNiYlOa9fJqGVC7OL_Cx7sbE8oCF4JlnX8,7464
|
6
|
-
mcp_sqlite_memory_bank/semantic.py,sha256=wvabBqlThNN76feyEa8BNRachBZYxkZQaPPIweHgxV8,14855
|
7
|
-
mcp_sqlite_memory_bank/server.py,sha256=MM0Cgjx8sLpKrpGj0nBUHZRy-I_g9TN6cGYDNuzaPkI,51775
|
8
|
-
mcp_sqlite_memory_bank/types.py,sha256=Lr6RVUUrdDdHUod6IvxLFaWg3H2uezL9luL8pYKxahM,6692
|
9
|
-
mcp_sqlite_memory_bank/utils.py,sha256=wHbR0cUlV-AWBk8ToI5ZgCrfrMp380ofyEc_GLB0l4g,6185
|
10
|
-
mcp_sqlite_memory_bank-1.4.3.dist-info/licenses/LICENSE,sha256=KPr7eFgCJqQIjeSAcwRafbjcgm-10zkrJ7MFoTOGJQg,1092
|
11
|
-
mcp_sqlite_memory_bank-1.4.3.dist-info/METADATA,sha256=j_nVJdXKzIRkMWg5VMm7YfD6DI_1Fok-UYEkowxIy4o,33094
|
12
|
-
mcp_sqlite_memory_bank-1.4.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
13
|
-
mcp_sqlite_memory_bank-1.4.3.dist-info/entry_points.txt,sha256=S9yGWiCe8f_rgcGCgbwEAX2FfJ9jXWxcc4K4Jenbcn8,150
|
14
|
-
mcp_sqlite_memory_bank-1.4.3.dist-info/top_level.txt,sha256=xQ8MTGECpWMR-9DV4H8mMqaSoZqE-C8EvpOg9E2U1wM,23
|
15
|
-
mcp_sqlite_memory_bank-1.4.3.dist-info/RECORD,,
|
File without changes
|
{mcp_sqlite_memory_bank-1.4.3.dist-info → mcp_sqlite_memory_bank-1.5.1.dist-info}/entry_points.txt
RENAMED
File without changes
|
{mcp_sqlite_memory_bank-1.4.3.dist-info → mcp_sqlite_memory_bank-1.5.1.dist-info}/licenses/LICENSE
RENAMED
File without changes
|
{mcp_sqlite_memory_bank-1.4.3.dist-info → mcp_sqlite_memory_bank-1.5.1.dist-info}/top_level.txt
RENAMED
File without changes
|