mcp-sqlite-memory-bank 1.4.3__py3-none-any.whl → 1.5.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.
- mcp_sqlite_memory_bank/__init__.py +20 -3
- 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 +193 -504
- mcp_sqlite_memory_bank/tools/__init__.py +68 -0
- mcp_sqlite_memory_bank/tools/analytics.py +419 -0
- mcp_sqlite_memory_bank/tools/basic.py +112 -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.0.dist-info}/METADATA +1 -1
- mcp_sqlite_memory_bank-1.5.0.dist-info/RECORD +19 -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.0.dist-info}/WHEEL +0 -0
- {mcp_sqlite_memory_bank-1.4.3.dist-info → mcp_sqlite_memory_bank-1.5.0.dist-info}/entry_points.txt +0 -0
- {mcp_sqlite_memory_bank-1.4.3.dist-info → mcp_sqlite_memory_bank-1.5.0.dist-info}/licenses/LICENSE +0 -0
- {mcp_sqlite_memory_bank-1.4.3.dist-info → mcp_sqlite_memory_bank-1.5.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,68 @@
|
|
1
|
+
"""
|
2
|
+
Tools module for SQLite Memory Bank MCP server.
|
3
|
+
|
4
|
+
This module organizes the various MCP tools into logical categories:
|
5
|
+
- analytics: Content analysis and health assessment tools
|
6
|
+
- search: Intelligent search and discovery tools
|
7
|
+
- basic: Core CRUD operations and table management
|
8
|
+
"""
|
9
|
+
|
10
|
+
# Import all tools to make them available at the package level
|
11
|
+
from .analytics import (
|
12
|
+
analyze_memory_patterns,
|
13
|
+
get_content_health_score,
|
14
|
+
)
|
15
|
+
from .search import (
|
16
|
+
search_content,
|
17
|
+
explore_tables,
|
18
|
+
add_embeddings,
|
19
|
+
semantic_search,
|
20
|
+
find_related,
|
21
|
+
smart_search,
|
22
|
+
embedding_stats,
|
23
|
+
auto_semantic_search,
|
24
|
+
auto_smart_search,
|
25
|
+
)
|
26
|
+
from .basic import (
|
27
|
+
create_table,
|
28
|
+
list_tables,
|
29
|
+
describe_table,
|
30
|
+
drop_table,
|
31
|
+
rename_table,
|
32
|
+
create_row,
|
33
|
+
read_rows,
|
34
|
+
update_rows,
|
35
|
+
delete_rows,
|
36
|
+
run_select_query,
|
37
|
+
list_all_columns,
|
38
|
+
)
|
39
|
+
|
40
|
+
__all__ = [
|
41
|
+
# Analytics tools
|
42
|
+
'analyze_memory_patterns',
|
43
|
+
'get_content_health_score',
|
44
|
+
|
45
|
+
# Search tools
|
46
|
+
'search_content',
|
47
|
+
'explore_tables',
|
48
|
+
'add_embeddings',
|
49
|
+
'semantic_search',
|
50
|
+
'find_related',
|
51
|
+
'smart_search',
|
52
|
+
'embedding_stats',
|
53
|
+
'auto_semantic_search',
|
54
|
+
'auto_smart_search',
|
55
|
+
|
56
|
+
# Basic tools
|
57
|
+
'create_table',
|
58
|
+
'list_tables',
|
59
|
+
'describe_table',
|
60
|
+
'drop_table',
|
61
|
+
'rename_table',
|
62
|
+
'create_row',
|
63
|
+
'read_rows',
|
64
|
+
'update_rows',
|
65
|
+
'delete_rows',
|
66
|
+
'run_select_query',
|
67
|
+
'list_all_columns',
|
68
|
+
]
|
@@ -0,0 +1,419 @@
|
|
1
|
+
"""
|
2
|
+
Analytics and Content Analysis Tools for SQLite Memory Bank
|
3
|
+
===========================================================
|
4
|
+
|
5
|
+
This module contains tools for analyzing memory bank content, assessing health,
|
6
|
+
and providing insights for better knowledge organization.
|
7
|
+
"""
|
8
|
+
|
9
|
+
import logging
|
10
|
+
from typing import Dict, Optional, List, cast, Any
|
11
|
+
from fastmcp import FastMCP
|
12
|
+
|
13
|
+
from ..database import get_database
|
14
|
+
from ..semantic import is_semantic_search_available
|
15
|
+
from ..types import ToolResponse
|
16
|
+
from ..utils import catch_errors
|
17
|
+
|
18
|
+
|
19
|
+
@catch_errors
|
20
|
+
def analyze_memory_patterns() -> ToolResponse:
|
21
|
+
"""
|
22
|
+
🔍 **MEMORY PATTERN ANALYSIS** - Discover insights in your memory bank!
|
23
|
+
|
24
|
+
Analyzes content patterns, usage statistics, and identifies opportunities
|
25
|
+
for better organization and knowledge discovery.
|
26
|
+
|
27
|
+
Returns:
|
28
|
+
ToolResponse: On success: {"success": True, "analysis": dict}
|
29
|
+
On error: {"success": False, "error": str, "category": str, "details": dict}
|
30
|
+
|
31
|
+
Examples:
|
32
|
+
>>> analyze_memory_patterns()
|
33
|
+
{"success": True, "analysis": {
|
34
|
+
"content_distribution": {"technical_decisions": 15, "project_notes": 8},
|
35
|
+
"text_density": {"high": ["documentation"], "low": ["metadata"]},
|
36
|
+
"semantic_readiness": {"ready": 2, "needs_setup": 1},
|
37
|
+
"recommendations": ["Consider embedding setup for 'notes' table"]
|
38
|
+
}}
|
39
|
+
|
40
|
+
FastMCP Tool Info:
|
41
|
+
- **CONTENT INSIGHTS**: Analyzes distribution and quality of stored content
|
42
|
+
- **SEMANTIC READINESS**: Shows which tables are ready for semantic search
|
43
|
+
- **ORGANIZATION TIPS**: Suggests improvements for better knowledge discovery
|
44
|
+
- **USAGE PATTERNS**: Identifies most and least used content areas
|
45
|
+
"""
|
46
|
+
try:
|
47
|
+
from .. import server
|
48
|
+
db = get_database(server.DB_PATH)
|
49
|
+
|
50
|
+
# Get all tables
|
51
|
+
tables_result = db.list_tables()
|
52
|
+
if not tables_result.get("success"):
|
53
|
+
return cast(ToolResponse, tables_result)
|
54
|
+
|
55
|
+
tables = tables_result.get("tables", [])
|
56
|
+
analysis = {
|
57
|
+
"content_distribution": {},
|
58
|
+
"text_density": {"high": [], "medium": [], "low": []},
|
59
|
+
"semantic_readiness": {"ready": [], "partial": [], "needs_setup": []},
|
60
|
+
"schema_analysis": {},
|
61
|
+
"recommendations": [],
|
62
|
+
"total_tables": len(tables),
|
63
|
+
"total_content_rows": 0
|
64
|
+
}
|
65
|
+
|
66
|
+
for table_name in tables:
|
67
|
+
try:
|
68
|
+
# Get basic table info
|
69
|
+
rows_result = db.read_rows(table_name)
|
70
|
+
if not rows_result.get("success"):
|
71
|
+
continue
|
72
|
+
|
73
|
+
rows = rows_result.get("rows", [])
|
74
|
+
row_count = len(rows)
|
75
|
+
analysis["content_distribution"][table_name] = row_count
|
76
|
+
analysis["total_content_rows"] += row_count
|
77
|
+
|
78
|
+
# Analyze schema
|
79
|
+
schema_result = db.describe_table(table_name)
|
80
|
+
if schema_result.get("success"):
|
81
|
+
columns = schema_result.get("columns", [])
|
82
|
+
text_columns = [col for col in columns if "TEXT" in col.get("type", "").upper()]
|
83
|
+
|
84
|
+
analysis["schema_analysis"][table_name] = {
|
85
|
+
"total_columns": len(columns),
|
86
|
+
"text_columns": len(text_columns),
|
87
|
+
"has_id_column": any(col.get("name") == "id" for col in columns),
|
88
|
+
"has_timestamp": any("timestamp" in col.get("name", "").lower() for col in columns)
|
89
|
+
}
|
90
|
+
|
91
|
+
# Analyze text density
|
92
|
+
if rows and text_columns:
|
93
|
+
text_content_lengths = []
|
94
|
+
for row in rows[:10]: # Sample first 10 rows
|
95
|
+
for col in text_columns:
|
96
|
+
content = row.get(col["name"], "")
|
97
|
+
if content:
|
98
|
+
text_content_lengths.append(len(str(content)))
|
99
|
+
|
100
|
+
if text_content_lengths:
|
101
|
+
avg_length = sum(text_content_lengths) / len(text_content_lengths)
|
102
|
+
if avg_length > 500:
|
103
|
+
analysis["text_density"]["high"].append(table_name)
|
104
|
+
elif avg_length > 100:
|
105
|
+
analysis["text_density"]["medium"].append(table_name)
|
106
|
+
else:
|
107
|
+
analysis["text_density"]["low"].append(table_name)
|
108
|
+
|
109
|
+
# Check semantic readiness
|
110
|
+
if is_semantic_search_available():
|
111
|
+
embedding_stats = db.get_embedding_stats(table_name)
|
112
|
+
if embedding_stats.get("success"):
|
113
|
+
coverage = embedding_stats.get("coverage_percent", 0)
|
114
|
+
if coverage >= 80:
|
115
|
+
analysis["semantic_readiness"]["ready"].append(table_name)
|
116
|
+
elif coverage > 0:
|
117
|
+
analysis["semantic_readiness"]["partial"].append(table_name)
|
118
|
+
else:
|
119
|
+
analysis["semantic_readiness"]["needs_setup"].append(table_name)
|
120
|
+
|
121
|
+
except Exception as e:
|
122
|
+
logging.warning(f"Error analyzing table {table_name}: {e}")
|
123
|
+
continue
|
124
|
+
|
125
|
+
# Generate recommendations
|
126
|
+
recommendations = []
|
127
|
+
|
128
|
+
# Semantic search recommendations
|
129
|
+
if len(analysis["semantic_readiness"]["needs_setup"]) > 0:
|
130
|
+
high_value_tables = [t for t in analysis["semantic_readiness"]["needs_setup"]
|
131
|
+
if t in analysis["text_density"]["high"] + analysis["text_density"]["medium"]]
|
132
|
+
if high_value_tables:
|
133
|
+
recommendations.append(f"Consider setting up semantic search for high-value tables: {', '.join(high_value_tables[:3])}")
|
134
|
+
|
135
|
+
# Content organization recommendations
|
136
|
+
large_tables = [t for t, count in analysis["content_distribution"].items() if count > 50]
|
137
|
+
if large_tables:
|
138
|
+
recommendations.append(f"Large tables detected: {', '.join(large_tables)}. Consider organizing with categories or tags.")
|
139
|
+
|
140
|
+
# Empty or sparse tables
|
141
|
+
sparse_tables = [t for t, count in analysis["content_distribution"].items() if count < 5 and count > 0]
|
142
|
+
if sparse_tables:
|
143
|
+
recommendations.append(f"Sparse tables found: {', '.join(sparse_tables)}. Consider consolidating or adding more content.")
|
144
|
+
|
145
|
+
# Schema improvements
|
146
|
+
tables_without_timestamps = [t for t, schema in analysis["schema_analysis"].items()
|
147
|
+
if not schema.get("has_timestamp")]
|
148
|
+
if len(tables_without_timestamps) > 2:
|
149
|
+
recommendations.append("Consider adding timestamp columns to track when content was created/modified.")
|
150
|
+
|
151
|
+
analysis["recommendations"] = recommendations
|
152
|
+
|
153
|
+
return cast(ToolResponse, {
|
154
|
+
"success": True,
|
155
|
+
"analysis": analysis,
|
156
|
+
"summary": {
|
157
|
+
"tables_analyzed": len(tables),
|
158
|
+
"total_rows": analysis["total_content_rows"],
|
159
|
+
"semantic_ready": len(analysis["semantic_readiness"]["ready"]),
|
160
|
+
"high_value_content": len(analysis["text_density"]["high"]),
|
161
|
+
"recommendations_count": len(recommendations)
|
162
|
+
}
|
163
|
+
})
|
164
|
+
|
165
|
+
except Exception as e:
|
166
|
+
return cast(ToolResponse, {
|
167
|
+
"success": False,
|
168
|
+
"error": f"Memory pattern analysis failed: {str(e)}",
|
169
|
+
"category": "ANALYSIS",
|
170
|
+
"details": {"exception": str(e)}
|
171
|
+
})
|
172
|
+
|
173
|
+
|
174
|
+
@catch_errors
|
175
|
+
def get_content_health_score() -> ToolResponse:
|
176
|
+
"""
|
177
|
+
📊 **CONTENT HEALTH ASSESSMENT** - Rate the quality of your memory bank!
|
178
|
+
|
179
|
+
Provides a comprehensive health score based on content quality, organization,
|
180
|
+
semantic search readiness, and usage patterns.
|
181
|
+
|
182
|
+
Returns:
|
183
|
+
ToolResponse: On success: {"success": True, "health_score": float, "metrics": dict}
|
184
|
+
On error: {"success": False, "error": str, "category": str, "details": dict}
|
185
|
+
|
186
|
+
Examples:
|
187
|
+
>>> get_content_health_score()
|
188
|
+
{"success": True, "health_score": 8.5, "metrics": {
|
189
|
+
"content_quality": 9.0, "organization": 7.5, "semantic_readiness": 8.0,
|
190
|
+
"accessibility": 9.0, "recommendations": [...]
|
191
|
+
}}
|
192
|
+
|
193
|
+
FastMCP Tool Info:
|
194
|
+
- **OVERALL SCORE**: Single metric (0-10) indicating memory bank health
|
195
|
+
- **DETAILED METRICS**: Breakdown by quality, organization, and readiness
|
196
|
+
- **ACTIONABLE INSIGHTS**: Specific recommendations for improvement
|
197
|
+
- **TREND TRACKING**: Compare health over time (if run regularly)
|
198
|
+
"""
|
199
|
+
try:
|
200
|
+
# Get the pattern analysis first - call database methods directly
|
201
|
+
from .. import server
|
202
|
+
db = get_database(server.DB_PATH)
|
203
|
+
|
204
|
+
# Get all tables
|
205
|
+
tables_result = db.list_tables()
|
206
|
+
if not tables_result.get("success"):
|
207
|
+
return cast(ToolResponse, {
|
208
|
+
"success": False,
|
209
|
+
"error": "Failed to get tables for health analysis",
|
210
|
+
"category": "DATABASE",
|
211
|
+
"details": tables_result
|
212
|
+
})
|
213
|
+
|
214
|
+
tables = tables_result.get("tables", [])
|
215
|
+
|
216
|
+
# Build basic analysis for health scoring
|
217
|
+
analysis = {
|
218
|
+
"content_distribution": {},
|
219
|
+
"text_density": {"high": [], "medium": [], "low": []},
|
220
|
+
"semantic_readiness": {"ready": [], "partial": [], "needs_setup": []},
|
221
|
+
"schema_analysis": {},
|
222
|
+
"total_tables": len(tables),
|
223
|
+
"total_content_rows": 0
|
224
|
+
}
|
225
|
+
|
226
|
+
for table_name in tables:
|
227
|
+
try:
|
228
|
+
# Get basic table info
|
229
|
+
rows_result = db.read_rows(table_name)
|
230
|
+
if not rows_result.get("success"):
|
231
|
+
continue
|
232
|
+
|
233
|
+
rows = rows_result.get("rows", [])
|
234
|
+
row_count = len(rows)
|
235
|
+
analysis["content_distribution"][table_name] = row_count
|
236
|
+
analysis["total_content_rows"] += row_count
|
237
|
+
|
238
|
+
# Analyze schema
|
239
|
+
schema_result = db.describe_table(table_name)
|
240
|
+
if schema_result.get("success"):
|
241
|
+
columns = schema_result.get("columns", [])
|
242
|
+
text_columns = [col for col in columns if "TEXT" in col.get("type", "").upper()]
|
243
|
+
|
244
|
+
analysis["schema_analysis"][table_name] = {
|
245
|
+
"total_columns": len(columns),
|
246
|
+
"text_columns": len(text_columns),
|
247
|
+
"has_id_column": any(col.get("name") == "id" for col in columns),
|
248
|
+
"has_timestamp": any("timestamp" in col.get("name", "").lower() for col in columns)
|
249
|
+
}
|
250
|
+
|
251
|
+
# Analyze text density
|
252
|
+
if rows and text_columns:
|
253
|
+
text_content_lengths = []
|
254
|
+
for row in rows[:10]: # Sample first 10 rows
|
255
|
+
for col in text_columns:
|
256
|
+
content = row.get(col["name"], "")
|
257
|
+
if content:
|
258
|
+
text_content_lengths.append(len(str(content)))
|
259
|
+
|
260
|
+
if text_content_lengths:
|
261
|
+
avg_length = sum(text_content_lengths) / len(text_content_lengths)
|
262
|
+
if avg_length > 500:
|
263
|
+
analysis["text_density"]["high"].append(table_name)
|
264
|
+
elif avg_length > 100:
|
265
|
+
analysis["text_density"]["medium"].append(table_name)
|
266
|
+
else:
|
267
|
+
analysis["text_density"]["low"].append(table_name)
|
268
|
+
|
269
|
+
# Check semantic readiness
|
270
|
+
if is_semantic_search_available():
|
271
|
+
embedding_stats = db.get_embedding_stats(table_name)
|
272
|
+
if embedding_stats.get("success"):
|
273
|
+
coverage = embedding_stats.get("coverage_percent", 0)
|
274
|
+
if coverage >= 80:
|
275
|
+
analysis["semantic_readiness"]["ready"].append(table_name)
|
276
|
+
elif coverage > 0:
|
277
|
+
analysis["semantic_readiness"]["partial"].append(table_name)
|
278
|
+
else:
|
279
|
+
analysis["semantic_readiness"]["needs_setup"].append(table_name)
|
280
|
+
|
281
|
+
except Exception as e:
|
282
|
+
logging.warning(f"Error analyzing table {table_name}: {e}")
|
283
|
+
continue
|
284
|
+
|
285
|
+
summary = {
|
286
|
+
"tables_analyzed": len(tables),
|
287
|
+
"total_rows": analysis["total_content_rows"],
|
288
|
+
"semantic_ready": len(analysis["semantic_readiness"]["ready"]),
|
289
|
+
"high_value_content": len(analysis["text_density"]["high"])
|
290
|
+
}
|
291
|
+
|
292
|
+
# Calculate health metrics (0-10 scale)
|
293
|
+
metrics = {}
|
294
|
+
|
295
|
+
# 1. Content Quality Score (based on text density and volume)
|
296
|
+
total_rows = summary.get("total_rows", 0)
|
297
|
+
high_quality_tables = len(analysis.get("text_density", {}).get("high", []))
|
298
|
+
total_tables = summary.get("tables_analyzed", 1)
|
299
|
+
|
300
|
+
if total_rows == 0:
|
301
|
+
metrics["content_volume"] = 0.0
|
302
|
+
elif total_rows < 10:
|
303
|
+
metrics["content_volume"] = 3.0
|
304
|
+
elif total_rows < 50:
|
305
|
+
metrics["content_volume"] = 6.0
|
306
|
+
elif total_rows < 200:
|
307
|
+
metrics["content_volume"] = 8.0
|
308
|
+
else:
|
309
|
+
metrics["content_volume"] = 10.0
|
310
|
+
|
311
|
+
metrics["content_quality"] = min(10.0, (high_quality_tables / total_tables) * 10 + 3)
|
312
|
+
|
313
|
+
# 2. Organization Score (based on schema quality)
|
314
|
+
schema_analysis = analysis.get("schema_analysis", {})
|
315
|
+
organization_factors = []
|
316
|
+
|
317
|
+
for table_name, schema in schema_analysis.items():
|
318
|
+
table_score = 0
|
319
|
+
if schema.get("has_id_column"):
|
320
|
+
table_score += 2
|
321
|
+
if schema.get("has_timestamp"):
|
322
|
+
table_score += 2
|
323
|
+
if schema.get("text_columns", 0) > 0:
|
324
|
+
table_score += 3
|
325
|
+
if 2 <= schema.get("total_columns", 0) <= 10: # Good column count
|
326
|
+
table_score += 3
|
327
|
+
organization_factors.append(table_score)
|
328
|
+
|
329
|
+
metrics["organization"] = (sum(organization_factors) / len(organization_factors)) if organization_factors else 5.0
|
330
|
+
|
331
|
+
# 3. Semantic Readiness Score
|
332
|
+
semantic_ready = len(analysis.get("semantic_readiness", {}).get("ready", []))
|
333
|
+
semantic_partial = len(analysis.get("semantic_readiness", {}).get("partial", []))
|
334
|
+
semantic_needed = len(analysis.get("semantic_readiness", {}).get("needs_setup", []))
|
335
|
+
|
336
|
+
if not is_semantic_search_available():
|
337
|
+
metrics["semantic_readiness"] = 5.0 # Neutral score if not available
|
338
|
+
metrics["semantic_note"] = "Semantic search dependencies not available"
|
339
|
+
else:
|
340
|
+
semantic_score = ((semantic_ready * 2 + semantic_partial) / (total_tables * 2)) * 10
|
341
|
+
metrics["semantic_readiness"] = min(10.0, semantic_score)
|
342
|
+
|
343
|
+
# 4. Accessibility Score (how easy it is to find and use content)
|
344
|
+
medium_density = len(analysis.get("text_density", {}).get("medium", []))
|
345
|
+
low_density = len(analysis.get("text_density", {}).get("low", []))
|
346
|
+
|
347
|
+
# Prefer medium density (not too verbose, not too sparse)
|
348
|
+
if total_tables == 0:
|
349
|
+
metrics["accessibility"] = 5.0
|
350
|
+
else:
|
351
|
+
accessibility_score = ((high_quality_tables + medium_density * 1.5) / total_tables) * 8 + 2
|
352
|
+
metrics["accessibility"] = min(10.0, accessibility_score)
|
353
|
+
|
354
|
+
# 5. Overall Health Score (weighted average)
|
355
|
+
weights = {
|
356
|
+
"content_volume": 0.2,
|
357
|
+
"content_quality": 0.3,
|
358
|
+
"organization": 0.2,
|
359
|
+
"semantic_readiness": 0.15,
|
360
|
+
"accessibility": 0.15
|
361
|
+
}
|
362
|
+
|
363
|
+
health_score = sum(metrics[key] * weights[key] for key in weights.keys())
|
364
|
+
|
365
|
+
# Generate health-specific recommendations
|
366
|
+
health_recommendations = []
|
367
|
+
|
368
|
+
if metrics["content_volume"] < 5:
|
369
|
+
health_recommendations.append("🔴 LOW CONTENT: Add more valuable content to your memory bank")
|
370
|
+
elif metrics["content_volume"] < 7:
|
371
|
+
health_recommendations.append("🟡 MODERATE CONTENT: Consider expanding your knowledge base")
|
372
|
+
|
373
|
+
if metrics["content_quality"] < 6:
|
374
|
+
health_recommendations.append("🔴 CONTENT QUALITY: Focus on adding more detailed, rich content")
|
375
|
+
|
376
|
+
if metrics["organization"] < 6:
|
377
|
+
health_recommendations.append("🔴 ORGANIZATION: Improve table schemas with timestamps and proper columns")
|
378
|
+
|
379
|
+
if metrics["semantic_readiness"] < 5 and is_semantic_search_available():
|
380
|
+
health_recommendations.append("🟡 SEMANTIC SEARCH: Set up embeddings for better content discovery")
|
381
|
+
|
382
|
+
if metrics["accessibility"] < 6:
|
383
|
+
health_recommendations.append("🔴 ACCESSIBILITY: Improve content structure for easier discovery")
|
384
|
+
|
385
|
+
# Health grade
|
386
|
+
if health_score >= 9:
|
387
|
+
grade = "A+ (Excellent)"
|
388
|
+
elif health_score >= 8:
|
389
|
+
grade = "A (Great)"
|
390
|
+
elif health_score >= 7:
|
391
|
+
grade = "B+ (Good)"
|
392
|
+
elif health_score >= 6:
|
393
|
+
grade = "B (Adequate)"
|
394
|
+
elif health_score >= 5:
|
395
|
+
grade = "C (Needs Improvement)"
|
396
|
+
else:
|
397
|
+
grade = "D (Poor - Needs Attention)"
|
398
|
+
|
399
|
+
return cast(ToolResponse, {
|
400
|
+
"success": True,
|
401
|
+
"health_score": round(health_score, 1),
|
402
|
+
"grade": grade,
|
403
|
+
"metrics": {k: round(v, 1) for k, v in metrics.items()},
|
404
|
+
"recommendations": health_recommendations,
|
405
|
+
"detailed_analysis": analysis,
|
406
|
+
"improvement_priority": {
|
407
|
+
"highest": [k for k, v in metrics.items() if v < 5],
|
408
|
+
"medium": [k for k, v in metrics.items() if 5 <= v < 7],
|
409
|
+
"good": [k for k, v in metrics.items() if v >= 7]
|
410
|
+
}
|
411
|
+
})
|
412
|
+
|
413
|
+
except Exception as e:
|
414
|
+
return cast(ToolResponse, {
|
415
|
+
"success": False,
|
416
|
+
"error": f"Content health assessment failed: {str(e)}",
|
417
|
+
"category": "ANALYSIS",
|
418
|
+
"details": {"exception": str(e)}
|
419
|
+
})
|
@@ -0,0 +1,112 @@
|
|
1
|
+
"""
|
2
|
+
Basic tools module for SQLite Memory Bank.
|
3
|
+
|
4
|
+
This module contains all basic CRUD and utility MCP tools including table management,
|
5
|
+
data operations, and core functionality.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from typing import Any, Dict, List, Optional, cast
|
9
|
+
|
10
|
+
from ..database import get_database
|
11
|
+
from ..types import MemoryBankError, DatabaseError, ToolResponse
|
12
|
+
from ..utils import catch_errors
|
13
|
+
|
14
|
+
|
15
|
+
@catch_errors
|
16
|
+
def create_table(
|
17
|
+
table_name: str,
|
18
|
+
columns: List[Dict[str, str]],
|
19
|
+
) -> ToolResponse:
|
20
|
+
"""Create a new table in the SQLite memory bank."""
|
21
|
+
from .. import server
|
22
|
+
return cast(ToolResponse, get_database(server.DB_PATH).create_table(table_name, columns))
|
23
|
+
|
24
|
+
|
25
|
+
@catch_errors
|
26
|
+
def list_tables() -> ToolResponse:
|
27
|
+
"""List all tables in the SQLite memory bank."""
|
28
|
+
from .. import server
|
29
|
+
return cast(ToolResponse, get_database(server.DB_PATH).list_tables())
|
30
|
+
|
31
|
+
|
32
|
+
@catch_errors
|
33
|
+
def describe_table(table_name: str) -> ToolResponse:
|
34
|
+
"""Get detailed schema information for a table."""
|
35
|
+
from .. import server
|
36
|
+
return cast(ToolResponse, get_database(server.DB_PATH).describe_table(table_name))
|
37
|
+
|
38
|
+
|
39
|
+
@catch_errors
|
40
|
+
def drop_table(table_name: str) -> ToolResponse:
|
41
|
+
"""Drop (delete) a table from the SQLite memory bank."""
|
42
|
+
from .. import server
|
43
|
+
return cast(ToolResponse, get_database(server.DB_PATH).drop_table(table_name))
|
44
|
+
|
45
|
+
|
46
|
+
@catch_errors
|
47
|
+
def rename_table(old_name: str, new_name: str) -> ToolResponse:
|
48
|
+
"""Rename a table in the SQLite memory bank."""
|
49
|
+
from .. import server
|
50
|
+
return cast(ToolResponse, get_database(server.DB_PATH).rename_table(old_name, new_name))
|
51
|
+
|
52
|
+
|
53
|
+
@catch_errors
|
54
|
+
def create_row(
|
55
|
+
table_name: str,
|
56
|
+
data: Dict[str, Any],
|
57
|
+
) -> ToolResponse:
|
58
|
+
"""Insert a new row into any table in the SQLite Memory Bank."""
|
59
|
+
from .. import server
|
60
|
+
return cast(ToolResponse, get_database(server.DB_PATH).insert_row(table_name, data))
|
61
|
+
|
62
|
+
|
63
|
+
@catch_errors
|
64
|
+
def read_rows(
|
65
|
+
table_name: str,
|
66
|
+
where: Optional[Dict[str, Any]] = None,
|
67
|
+
) -> ToolResponse:
|
68
|
+
"""Read rows from any table in the SQLite memory bank, with optional filtering."""
|
69
|
+
from .. import server
|
70
|
+
return cast(ToolResponse, get_database(server.DB_PATH).read_rows(table_name, where))
|
71
|
+
|
72
|
+
|
73
|
+
@catch_errors
|
74
|
+
def update_rows(
|
75
|
+
table_name: str,
|
76
|
+
data: Dict[str, Any],
|
77
|
+
where: Optional[Dict[str, Any]] = None,
|
78
|
+
) -> ToolResponse:
|
79
|
+
"""Update rows in any table in the SQLite Memory Bank, matching the WHERE clause."""
|
80
|
+
from .. import server
|
81
|
+
return cast(ToolResponse, get_database(server.DB_PATH).update_rows(table_name, data, where))
|
82
|
+
|
83
|
+
|
84
|
+
@catch_errors
|
85
|
+
def delete_rows(
|
86
|
+
table_name: str,
|
87
|
+
where: Optional[Dict[str, Any]] = None,
|
88
|
+
) -> ToolResponse:
|
89
|
+
"""Delete rows from any table in the SQLite Memory Bank, matching the WHERE clause."""
|
90
|
+
from .. import server
|
91
|
+
return cast(ToolResponse, get_database(server.DB_PATH).delete_rows(table_name, where))
|
92
|
+
|
93
|
+
|
94
|
+
@catch_errors
|
95
|
+
def run_select_query(
|
96
|
+
table_name: str,
|
97
|
+
columns: Optional[List[str]] = None,
|
98
|
+
where: Optional[Dict[str, Any]] = None,
|
99
|
+
limit: int = 100,
|
100
|
+
) -> ToolResponse:
|
101
|
+
"""Run a safe SELECT query on a table in the SQLite memory bank."""
|
102
|
+
from .. import server
|
103
|
+
return cast(ToolResponse, get_database(server.DB_PATH).select_query(
|
104
|
+
table_name, columns, where, limit
|
105
|
+
))
|
106
|
+
|
107
|
+
|
108
|
+
@catch_errors
|
109
|
+
def list_all_columns() -> ToolResponse:
|
110
|
+
"""List all columns for all tables in the SQLite memory bank."""
|
111
|
+
from .. import server
|
112
|
+
return cast(ToolResponse, get_database(server.DB_PATH).list_all_columns())
|