mcp-sqlite-memory-bank 1.5.1__py3-none-any.whl → 1.6.2__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 +3 -3
- mcp_sqlite_memory_bank/__main__.py +8 -7
- mcp_sqlite_memory_bank/database.py +166 -48
- mcp_sqlite_memory_bank/prompts.py +64 -48
- mcp_sqlite_memory_bank/resources.py +218 -144
- mcp_sqlite_memory_bank/semantic.py +25 -13
- mcp_sqlite_memory_bank/server.py +174 -32
- mcp_sqlite_memory_bank/tools/__init__.py +26 -29
- mcp_sqlite_memory_bank/tools/analytics.py +179 -130
- mcp_sqlite_memory_bank/tools/basic.py +417 -4
- mcp_sqlite_memory_bank/tools/discovery.py +549 -360
- mcp_sqlite_memory_bank/tools/search.py +147 -71
- mcp_sqlite_memory_bank/types.py +6 -1
- mcp_sqlite_memory_bank/utils.py +154 -105
- {mcp_sqlite_memory_bank-1.5.1.dist-info → mcp_sqlite_memory_bank-1.6.2.dist-info}/METADATA +54 -6
- mcp_sqlite_memory_bank-1.6.2.dist-info/RECORD +21 -0
- mcp_sqlite_memory_bank-1.5.1.dist-info/RECORD +0 -21
- {mcp_sqlite_memory_bank-1.5.1.dist-info → mcp_sqlite_memory_bank-1.6.2.dist-info}/WHEEL +0 -0
- {mcp_sqlite_memory_bank-1.5.1.dist-info → mcp_sqlite_memory_bank-1.6.2.dist-info}/entry_points.txt +0 -0
- {mcp_sqlite_memory_bank-1.5.1.dist-info → mcp_sqlite_memory_bank-1.6.2.dist-info}/licenses/LICENSE +0 -0
- {mcp_sqlite_memory_bank-1.5.1.dist-info → mcp_sqlite_memory_bank-1.6.2.dist-info}/top_level.txt +0 -0
@@ -20,63 +20,73 @@ import json
|
|
20
20
|
|
21
21
|
class MemoryBankResources:
|
22
22
|
"""Manages MCP Resources for the SQLite Memory Bank."""
|
23
|
-
|
23
|
+
|
24
24
|
def __init__(self, mcp_app: FastMCP, db_path: str):
|
25
25
|
self.mcp = mcp_app
|
26
26
|
self.db_path = db_path
|
27
27
|
self._register_resources()
|
28
|
-
|
28
|
+
|
29
29
|
def _register_resources(self):
|
30
30
|
"""Register MCP resources with the FastMCP app."""
|
31
|
-
|
31
|
+
|
32
32
|
@self.mcp.resource("memory://tables/list")
|
33
33
|
async def get_tables_list() -> str:
|
34
34
|
"""Provide a list of all available tables as an MCP resource."""
|
35
35
|
db = get_database(self.db_path)
|
36
36
|
result = cast(Dict[str, Any], db.list_tables())
|
37
|
-
|
37
|
+
|
38
38
|
if not result.get("success"):
|
39
39
|
return json.dumps({"error": "Failed to fetch tables", "details": result})
|
40
|
-
|
40
|
+
|
41
41
|
resource_content = {
|
42
42
|
"resource_type": "table_list",
|
43
43
|
"description": "List of all available tables in the memory bank",
|
44
44
|
"tables": result.get("tables", []),
|
45
45
|
"total_count": len(result.get("tables", [])),
|
46
|
-
"last_updated": "dynamic"
|
46
|
+
"last_updated": "dynamic",
|
47
47
|
}
|
48
|
-
|
48
|
+
|
49
49
|
return json.dumps(resource_content, indent=2)
|
50
|
-
|
50
|
+
|
51
51
|
@self.mcp.resource("memory://tables/{table_name}/schema")
|
52
52
|
async def get_table_schema(table_name: str) -> str:
|
53
53
|
"""Provide table schema information as an MCP resource."""
|
54
54
|
db = get_database(self.db_path)
|
55
55
|
result = cast(Dict[str, Any], db.describe_table(table_name))
|
56
|
-
|
56
|
+
|
57
57
|
if not result.get("success"):
|
58
|
-
return json.dumps(
|
59
|
-
|
58
|
+
return json.dumps(
|
59
|
+
{
|
60
|
+
"error": f"Failed to fetch schema for table '{table_name}'",
|
61
|
+
"details": result,
|
62
|
+
}
|
63
|
+
)
|
64
|
+
|
60
65
|
resource_content = {
|
61
66
|
"resource_type": "table_schema",
|
62
67
|
"table_name": table_name,
|
63
68
|
"description": f"Schema definition for table '{table_name}'",
|
64
69
|
"columns": result.get("columns", []),
|
65
70
|
"column_count": len(result.get("columns", [])),
|
66
|
-
"last_updated": "dynamic"
|
71
|
+
"last_updated": "dynamic",
|
67
72
|
}
|
68
|
-
|
73
|
+
|
69
74
|
return json.dumps(resource_content, indent=2)
|
70
|
-
|
75
|
+
|
71
76
|
@self.mcp.resource("memory://tables/{table_name}/data")
|
72
77
|
async def get_table_data(table_name: str) -> str:
|
73
78
|
"""Provide table data as an MCP resource."""
|
74
79
|
db = get_database(self.db_path)
|
75
80
|
result = cast(Dict[str, Any], db.read_rows(table_name, {}))
|
76
|
-
|
81
|
+
|
77
82
|
if not result.get("success"):
|
78
|
-
return json.dumps(
|
79
|
-
|
83
|
+
return json.dumps(
|
84
|
+
{
|
85
|
+
"error": f"Failed to fetch data for table '{table_name}'",
|
86
|
+
"details": result,
|
87
|
+
}
|
88
|
+
)
|
89
|
+
|
80
90
|
rows = result.get("rows", [])
|
81
91
|
resource_content = {
|
82
92
|
"resource_type": "table_data",
|
@@ -84,20 +94,22 @@ class MemoryBankResources:
|
|
84
94
|
"description": f"All data from table '{table_name}'",
|
85
95
|
"rows": rows,
|
86
96
|
"row_count": len(rows),
|
87
|
-
"last_updated": "dynamic"
|
97
|
+
"last_updated": "dynamic",
|
88
98
|
}
|
89
|
-
|
99
|
+
|
90
100
|
return json.dumps(resource_content, indent=2)
|
91
|
-
|
101
|
+
|
92
102
|
@self.mcp.resource("memory://search/{query}")
|
93
103
|
async def search_memory_content(query: str) -> str:
|
94
104
|
"""Provide search results as an MCP resource."""
|
95
105
|
db = get_database(self.db_path)
|
96
|
-
result = cast(
|
97
|
-
|
106
|
+
result = cast(
|
107
|
+
Dict[str, Any], db.search_content(query, None, 50)
|
108
|
+
) # Search all tables, limit to 50 results
|
109
|
+
|
98
110
|
if not result.get("success"):
|
99
111
|
return json.dumps({"error": f"Failed to search for '{query}'", "details": result})
|
100
|
-
|
112
|
+
|
101
113
|
search_results = result.get("results", [])
|
102
114
|
resource_content = {
|
103
115
|
"resource_type": "search_results",
|
@@ -105,25 +117,30 @@ class MemoryBankResources:
|
|
105
117
|
"description": f"Search results for query: '{query}'",
|
106
118
|
"results": search_results,
|
107
119
|
"result_count": len(search_results),
|
108
|
-
"last_updated": "dynamic"
|
120
|
+
"last_updated": "dynamic",
|
109
121
|
}
|
110
|
-
|
122
|
+
|
111
123
|
return json.dumps(resource_content, indent=2)
|
112
|
-
|
124
|
+
|
113
125
|
@self.mcp.resource("memory://analytics/overview")
|
114
126
|
async def get_memory_overview() -> str:
|
115
127
|
"""Provide memory bank overview analytics as an MCP resource."""
|
116
128
|
db = get_database(self.db_path)
|
117
|
-
|
129
|
+
|
118
130
|
# Get table list
|
119
131
|
tables_result = cast(Dict[str, Any], db.list_tables())
|
120
132
|
if not tables_result.get("success"):
|
121
|
-
return json.dumps(
|
122
|
-
|
133
|
+
return json.dumps(
|
134
|
+
{
|
135
|
+
"error": "Failed to fetch memory overview",
|
136
|
+
"details": tables_result,
|
137
|
+
}
|
138
|
+
)
|
139
|
+
|
123
140
|
tables = tables_result.get("tables", [])
|
124
141
|
total_rows = 0
|
125
142
|
table_stats = {}
|
126
|
-
|
143
|
+
|
127
144
|
# Get row counts for each table
|
128
145
|
for table in tables:
|
129
146
|
try:
|
@@ -132,20 +149,14 @@ class MemoryBankResources:
|
|
132
149
|
row_count = len(rows_result.get("rows", []))
|
133
150
|
table_stats[table] = {
|
134
151
|
"row_count": row_count,
|
135
|
-
"status": "accessible"
|
152
|
+
"status": "accessible",
|
136
153
|
}
|
137
154
|
total_rows += row_count
|
138
155
|
else:
|
139
|
-
table_stats[table] = {
|
140
|
-
"row_count": 0,
|
141
|
-
"status": "error"
|
142
|
-
}
|
156
|
+
table_stats[table] = {"row_count": 0, "status": "error"}
|
143
157
|
except Exception as e:
|
144
|
-
table_stats[table] = {
|
145
|
-
|
146
|
-
"status": f"error: {str(e)}"
|
147
|
-
}
|
148
|
-
|
158
|
+
table_stats[table] = {"row_count": 0, "status": f"error: {str(e)}"}
|
159
|
+
|
149
160
|
# Find largest table
|
150
161
|
largest_table = None
|
151
162
|
if table_stats:
|
@@ -163,37 +174,39 @@ class MemoryBankResources:
|
|
163
174
|
"summary": {
|
164
175
|
"total_tables": len(tables),
|
165
176
|
"total_rows": total_rows,
|
166
|
-
"largest_table": largest_table
|
177
|
+
"largest_table": largest_table,
|
167
178
|
},
|
168
179
|
"table_statistics": table_stats,
|
169
|
-
"last_updated": "dynamic"
|
180
|
+
"last_updated": "dynamic",
|
170
181
|
}
|
171
|
-
|
182
|
+
|
172
183
|
return json.dumps(resource_content, indent=2)
|
173
|
-
|
184
|
+
|
174
185
|
@self.mcp.resource("memory://live/recent-activity")
|
175
186
|
async def get_recent_activity() -> str:
|
176
187
|
"""Real-time feed of recent memory bank changes and activity."""
|
177
188
|
db = get_database(self.db_path)
|
178
|
-
|
189
|
+
|
179
190
|
# Get tables with timestamp columns for activity tracking
|
180
191
|
tables_result = cast(Dict[str, Any], db.list_tables())
|
181
192
|
if not tables_result.get("success"):
|
182
193
|
return json.dumps({"error": "Failed to get tables", "details": tables_result})
|
183
|
-
|
194
|
+
|
184
195
|
recent_activity = []
|
185
196
|
tables = tables_result.get("tables", [])
|
186
|
-
|
197
|
+
|
187
198
|
for table_name in tables:
|
188
199
|
try:
|
189
200
|
# Check if table has timestamp column
|
190
201
|
schema_result = cast(Dict[str, Any], db.describe_table(table_name))
|
191
202
|
if not schema_result.get("success"):
|
192
203
|
continue
|
193
|
-
|
204
|
+
|
194
205
|
columns = schema_result.get("columns", [])
|
195
|
-
timestamp_cols = [
|
196
|
-
|
206
|
+
timestamp_cols = [
|
207
|
+
col for col in columns if "timestamp" in col.get("name", "").lower()
|
208
|
+
]
|
209
|
+
|
197
210
|
if timestamp_cols:
|
198
211
|
# Get recent entries (last 10)
|
199
212
|
recent_result = cast(Dict[str, Any], db.read_rows(table_name, None, 10))
|
@@ -204,112 +217,142 @@ class MemoryBankResources:
|
|
204
217
|
"table": table_name,
|
205
218
|
"action": "content_added",
|
206
219
|
"timestamp": row.get(timestamp_cols[0]["name"]),
|
207
|
-
"content_preview":
|
208
|
-
|
220
|
+
"content_preview": (
|
221
|
+
str(row).replace('"', "'")[:100] + "..."
|
222
|
+
if len(str(row)) > 100
|
223
|
+
else str(row)
|
224
|
+
),
|
225
|
+
"row_id": row.get("id"),
|
209
226
|
}
|
210
227
|
recent_activity.append(activity_entry)
|
211
|
-
|
212
|
-
except Exception
|
228
|
+
|
229
|
+
except Exception:
|
213
230
|
continue
|
214
|
-
|
231
|
+
|
215
232
|
# Sort by timestamp (most recent first)
|
216
233
|
recent_activity.sort(key=lambda x: x.get("timestamp", ""), reverse=True)
|
217
234
|
recent_activity = recent_activity[:20] # Limit to 20 most recent
|
218
|
-
|
235
|
+
|
219
236
|
resource_content = {
|
220
237
|
"resource_type": "recent_activity",
|
221
238
|
"description": "Recent changes and additions to the memory bank",
|
222
239
|
"activities": recent_activity,
|
223
240
|
"activity_count": len(recent_activity),
|
224
241
|
"last_updated": "real-time",
|
225
|
-
"refresh_rate": "dynamic"
|
242
|
+
"refresh_rate": "dynamic",
|
226
243
|
}
|
227
|
-
|
244
|
+
|
228
245
|
return json.dumps(resource_content, indent=2)
|
229
|
-
|
246
|
+
|
230
247
|
@self.mcp.resource("memory://live/content-suggestions")
|
231
248
|
async def get_content_suggestions() -> str:
|
232
249
|
"""AI-powered suggestions for content improvements and organization."""
|
233
250
|
db = get_database(self.db_path)
|
234
|
-
|
235
|
-
suggestions = {
|
251
|
+
|
252
|
+
suggestions: dict[str, list[dict[str, Any]]] = {
|
236
253
|
"organization_suggestions": [],
|
237
254
|
"content_gaps": [],
|
238
255
|
"semantic_opportunities": [],
|
239
|
-
"quality_improvements": []
|
256
|
+
"quality_improvements": [],
|
240
257
|
}
|
241
|
-
|
258
|
+
|
242
259
|
try:
|
243
260
|
# Get basic analysis
|
244
261
|
tables_result = cast(Dict[str, Any], db.list_tables())
|
245
262
|
if not tables_result.get("success"):
|
246
|
-
return json.dumps(
|
247
|
-
|
263
|
+
return json.dumps(
|
264
|
+
{"error": "Failed to analyze content", "details": tables_result}
|
265
|
+
)
|
266
|
+
|
248
267
|
tables = tables_result.get("tables", [])
|
249
|
-
|
268
|
+
|
250
269
|
for table_name in tables:
|
251
270
|
try:
|
252
271
|
# Analyze table content
|
253
272
|
rows_result = cast(Dict[str, Any], db.read_rows(table_name))
|
254
273
|
if not rows_result.get("success"):
|
255
274
|
continue
|
256
|
-
|
275
|
+
|
257
276
|
rows = rows_result.get("rows", [])
|
258
|
-
|
277
|
+
|
259
278
|
# Check for organization opportunities
|
260
279
|
if len(rows) > 50:
|
261
|
-
suggestions["organization_suggestions"].append(
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
280
|
+
suggestions["organization_suggestions"].append(
|
281
|
+
{
|
282
|
+
"table": table_name,
|
283
|
+
"suggestion": "Consider adding categories or tags for better organization",
|
284
|
+
"reason": f"Large table with {len(rows)} rows could benefit from categorization",
|
285
|
+
}
|
286
|
+
)
|
287
|
+
|
267
288
|
# Check for semantic search opportunities
|
268
289
|
if is_semantic_search_available():
|
269
|
-
embedding_stats = cast(
|
270
|
-
|
290
|
+
embedding_stats = cast(
|
291
|
+
Dict[str, Any], db.get_embedding_stats(table_name)
|
292
|
+
)
|
293
|
+
if (
|
294
|
+
embedding_stats.get("success")
|
295
|
+
and embedding_stats.get("coverage_percent", 0) == 0
|
296
|
+
):
|
271
297
|
schema_result = cast(Dict[str, Any], db.describe_table(table_name))
|
272
298
|
if schema_result.get("success"):
|
273
|
-
text_cols = [
|
274
|
-
|
299
|
+
text_cols = [
|
300
|
+
col
|
301
|
+
for col in schema_result.get("columns", [])
|
302
|
+
if "TEXT" in col.get("type", "").upper()
|
303
|
+
]
|
275
304
|
if text_cols and len(rows) > 5:
|
276
|
-
suggestions["semantic_opportunities"].append(
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
305
|
+
suggestions["semantic_opportunities"].append(
|
306
|
+
{
|
307
|
+
"table": table_name,
|
308
|
+
"suggestion": "Set up semantic search for better content discovery",
|
309
|
+
"reason": f"Table has {len(text_cols)} text columns and {len(rows)} rows",
|
310
|
+
"action": f"Use add_embeddings('{table_name}', {[col['name'] for col in text_cols[:3]]})",
|
311
|
+
}
|
312
|
+
)
|
313
|
+
|
283
314
|
# Check for content gaps (sparse tables)
|
284
315
|
if 1 <= len(rows) <= 5:
|
285
|
-
suggestions["content_gaps"].append(
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
316
|
+
suggestions["content_gaps"].append(
|
317
|
+
{
|
318
|
+
"table": table_name,
|
319
|
+
"suggestion": "Consider adding more content or consolidating with other tables",
|
320
|
+
"reason": f"Table has only {len(rows)} rows - might be underutilized",
|
321
|
+
}
|
322
|
+
)
|
323
|
+
|
291
324
|
# Sample content for quality analysis
|
292
325
|
if rows:
|
293
326
|
sample_row = rows[0]
|
294
|
-
short_values = [
|
295
|
-
|
327
|
+
short_values = [
|
328
|
+
k
|
329
|
+
for k, v in sample_row.items()
|
330
|
+
if isinstance(v, str) and 0 < len(v) < 10
|
331
|
+
]
|
296
332
|
if len(short_values) > 2:
|
297
|
-
suggestions["quality_improvements"].append(
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
333
|
+
suggestions["quality_improvements"].append(
|
334
|
+
{
|
335
|
+
"table": table_name,
|
336
|
+
"suggestion": "Consider adding more detailed content",
|
337
|
+
"reason": f"Several columns have very short values: {short_values[:3]}",
|
338
|
+
}
|
339
|
+
)
|
340
|
+
|
341
|
+
except Exception:
|
304
342
|
continue
|
305
|
-
|
343
|
+
|
306
344
|
# Prioritize suggestions
|
307
|
-
priority_order = [
|
345
|
+
priority_order = [
|
346
|
+
"semantic_opportunities",
|
347
|
+
"organization_suggestions",
|
348
|
+
"quality_improvements",
|
349
|
+
"content_gaps",
|
350
|
+
]
|
308
351
|
prioritized = {}
|
309
352
|
for category in priority_order:
|
310
353
|
if suggestions[category]:
|
311
354
|
prioritized[category] = suggestions[category]
|
312
|
-
|
355
|
+
|
313
356
|
resource_content = {
|
314
357
|
"resource_type": "content_suggestions",
|
315
358
|
"description": "AI-powered suggestions for improving your memory bank",
|
@@ -319,46 +362,48 @@ class MemoryBankResources:
|
|
319
362
|
"next_actions": [
|
320
363
|
"Review semantic opportunities for high-value tables",
|
321
364
|
"Consider organization improvements for large tables",
|
322
|
-
"Add more detailed content where suggested"
|
323
|
-
]
|
365
|
+
"Add more detailed content where suggested",
|
366
|
+
],
|
324
367
|
}
|
325
|
-
|
368
|
+
|
326
369
|
return json.dumps(resource_content, indent=2)
|
327
|
-
|
370
|
+
|
328
371
|
except Exception as e:
|
329
|
-
return json.dumps(
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
372
|
+
return json.dumps(
|
373
|
+
{
|
374
|
+
"error": f"Failed to generate content suggestions: {str(e)}",
|
375
|
+
"suggestions": suggestions,
|
376
|
+
}
|
377
|
+
)
|
378
|
+
|
334
379
|
@self.mcp.resource("memory://analytics/insights")
|
335
380
|
async def get_memory_insights() -> str:
|
336
381
|
"""Real-time analytics and insights about memory bank usage and patterns."""
|
337
382
|
db = get_database(self.db_path)
|
338
|
-
|
339
|
-
insights = {
|
383
|
+
|
384
|
+
insights: dict[str, dict[str, Any]] = {
|
340
385
|
"usage_patterns": {},
|
341
386
|
"content_trends": {},
|
342
387
|
"search_recommendations": {},
|
343
|
-
"health_indicators": {}
|
388
|
+
"health_indicators": {},
|
344
389
|
}
|
345
|
-
|
390
|
+
|
346
391
|
try:
|
347
392
|
tables_result = cast(Dict[str, Any], db.list_tables())
|
348
393
|
if not tables_result.get("success"):
|
349
394
|
return json.dumps({"error": "Failed to get insights", "details": tables_result})
|
350
|
-
|
395
|
+
|
351
396
|
tables = tables_result.get("tables", [])
|
352
397
|
total_rows = 0
|
353
398
|
content_quality_scores = []
|
354
|
-
|
399
|
+
|
355
400
|
for table_name in tables:
|
356
401
|
rows_result = cast(Dict[str, Any], db.read_rows(table_name))
|
357
402
|
if rows_result.get("success"):
|
358
403
|
rows = rows_result.get("rows", [])
|
359
404
|
row_count = len(rows)
|
360
405
|
total_rows += row_count
|
361
|
-
|
406
|
+
|
362
407
|
# Calculate content quality score for this table
|
363
408
|
if rows:
|
364
409
|
# Sample content to estimate quality
|
@@ -368,63 +413,92 @@ class MemoryBankResources:
|
|
368
413
|
for value in row.values():
|
369
414
|
if isinstance(value, str):
|
370
415
|
total_content_length += len(value)
|
371
|
-
|
372
|
-
avg_content_length =
|
416
|
+
|
417
|
+
avg_content_length = (
|
418
|
+
total_content_length / sample_size if sample_size > 0 else 0
|
419
|
+
)
|
373
420
|
quality_score = min(10, avg_content_length / 50) # Normalize to 0-10
|
374
421
|
content_quality_scores.append(quality_score)
|
375
|
-
|
422
|
+
|
376
423
|
insights["usage_patterns"][table_name] = {
|
377
424
|
"row_count": row_count,
|
378
425
|
"avg_content_length": round(avg_content_length),
|
379
426
|
"quality_score": round(quality_score, 1),
|
380
|
-
"category":
|
427
|
+
"category": (
|
428
|
+
"high_value"
|
429
|
+
if quality_score > 7
|
430
|
+
else ("medium_value" if quality_score > 3 else "low_value")
|
431
|
+
),
|
381
432
|
}
|
382
|
-
|
433
|
+
|
383
434
|
# Overall health indicators
|
384
|
-
avg_quality =
|
435
|
+
avg_quality = (
|
436
|
+
sum(content_quality_scores) / len(content_quality_scores)
|
437
|
+
if content_quality_scores
|
438
|
+
else 0
|
439
|
+
)
|
385
440
|
insights["health_indicators"] = {
|
386
441
|
"total_tables": len(tables),
|
387
442
|
"total_content_rows": total_rows,
|
388
443
|
"average_content_quality": round(avg_quality, 1),
|
389
|
-
"content_distribution":
|
390
|
-
|
444
|
+
"content_distribution": (
|
445
|
+
"balanced"
|
446
|
+
if len(tables) > 0 and total_rows / len(tables) > 10
|
447
|
+
else "sparse"
|
448
|
+
),
|
449
|
+
"semantic_readiness": (
|
450
|
+
"available" if is_semantic_search_available() else "unavailable"
|
451
|
+
),
|
391
452
|
}
|
392
|
-
|
453
|
+
|
393
454
|
# Search recommendations
|
394
|
-
high_value_tables = [
|
395
|
-
|
396
|
-
|
455
|
+
high_value_tables = [
|
456
|
+
name
|
457
|
+
for name, data in insights["usage_patterns"].items()
|
458
|
+
if data.get("category") == "high_value"
|
459
|
+
]
|
460
|
+
|
397
461
|
if high_value_tables:
|
398
462
|
insights["search_recommendations"]["intelligent_search"] = {
|
399
463
|
"recommended_tables": high_value_tables,
|
400
|
-
"strategy": "Use intelligent_search() for best results across high-value content"
|
464
|
+
"strategy": "Use intelligent_search() for best results across high-value content",
|
401
465
|
}
|
402
|
-
|
466
|
+
|
403
467
|
if is_semantic_search_available():
|
404
468
|
insights["search_recommendations"]["semantic_opportunities"] = {
|
405
469
|
"suggestion": "Consider semantic search for conceptual queries",
|
406
|
-
"best_for": "Finding related concepts, patterns, and thematic content"
|
470
|
+
"best_for": "Finding related concepts, patterns, and thematic content",
|
407
471
|
}
|
408
|
-
|
472
|
+
|
409
473
|
resource_content = {
|
410
474
|
"resource_type": "memory_insights",
|
411
475
|
"description": "Real-time analytics and insights about your memory bank",
|
412
476
|
"insights": insights,
|
413
477
|
"last_updated": "real-time",
|
414
478
|
"recommendations": [
|
415
|
-
|
479
|
+
(
|
480
|
+
f"Focus on high-value tables: {', '.join(high_value_tables[:3])}"
|
481
|
+
if high_value_tables
|
482
|
+
else "Add more detailed content to improve value"
|
483
|
+
),
|
416
484
|
"Use intelligent_search() for optimal search results",
|
417
|
-
|
418
|
-
|
485
|
+
(
|
486
|
+
"Consider semantic search setup for better content discovery"
|
487
|
+
if is_semantic_search_available()
|
488
|
+
else "Install sentence-transformers for semantic search"
|
489
|
+
),
|
490
|
+
],
|
419
491
|
}
|
420
|
-
|
492
|
+
|
421
493
|
return json.dumps(resource_content, indent=2)
|
422
|
-
|
494
|
+
|
423
495
|
except Exception as e:
|
424
|
-
return json.dumps(
|
425
|
-
|
426
|
-
|
427
|
-
|
496
|
+
return json.dumps(
|
497
|
+
{
|
498
|
+
"error": f"Failed to generate insights: {str(e)}",
|
499
|
+
"insights": insights,
|
500
|
+
}
|
501
|
+
)
|
428
502
|
|
429
503
|
|
430
504
|
def setup_mcp_resources(mcp_app: FastMCP, db_path: str) -> MemoryBankResources:
|