mcp-sqlite-memory-bank 1.3.0__py3-none-any.whl → 1.4.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.
@@ -0,0 +1,252 @@
1
+ """
2
+ MCP Prompts Support for SQLite Memory Bank
3
+ =========================================
4
+
5
+ This module adds MCP Prompts support, providing templated prompts and workflows
6
+ that leverage the memory bank content for enhanced AI interactions.
7
+
8
+ Prompts provide reusable, contextual templates that can dynamically incorporate
9
+ stored memory content into LLM conversations.
10
+
11
+ Author: Robert Meisner
12
+ """
13
+
14
+ from typing import Optional
15
+ from fastmcp import FastMCP
16
+ from .database import get_database
17
+ import json
18
+
19
+
20
+ class MemoryBankPrompts:
21
+ """Manages MCP Prompts for the SQLite Memory Bank."""
22
+
23
+ def __init__(self, mcp_app: FastMCP, db_path: str):
24
+ self.mcp = mcp_app
25
+ self.db_path = db_path
26
+ self._register_prompts()
27
+
28
+ def _register_prompts(self):
29
+ """Register MCP prompts with the FastMCP app."""
30
+
31
+ @self.mcp.prompt("analyze-memory-content")
32
+ async def analyze_memory_content(table_name: Optional[str] = None) -> str:
33
+ """Analyze memory bank content and provide insights."""
34
+ db = get_database(self.db_path)
35
+
36
+ if table_name:
37
+ # Analyze specific table
38
+ result = db.read_rows(table_name, {})
39
+ if not result.get("success"):
40
+ return f"Error: Could not access table '{table_name}'. Please check if it exists."
41
+
42
+ rows = result.get("rows", [])
43
+ prompt = f"""Please analyze the content in the '{table_name}' table from the memory bank.
44
+
45
+ Table: {table_name}
46
+ Row count: {len(rows)}
47
+ Sample data: {json.dumps(rows[:3], indent=2) if rows else "No data"}
48
+
49
+ Please provide:
50
+ 1. A summary of the content patterns
51
+ 2. Key insights or themes
52
+ 3. Suggestions for better organization
53
+ 4. Potential use cases for this data
54
+
55
+ Focus on actionable insights that could help improve how this information is stored and retrieved."""
56
+ else:
57
+ # Analyze all tables
58
+ tables_result = db.list_tables()
59
+ if not tables_result.get("success"):
60
+ return "Error: Could not access memory bank tables."
61
+
62
+ tables = tables_result.get("tables", [])
63
+ overview = {"tables": len(tables), "total_content": []}
64
+
65
+ for table in tables[:5]: # Limit to first 5 tables
66
+ rows_result = db.read_rows(table, {})
67
+ if rows_result.get("success"):
68
+ rows = rows_result.get("rows", [])
69
+ overview["total_content"].append({
70
+ "table": table,
71
+ "rows": len(rows),
72
+ "sample": rows[:2] if rows else []
73
+ })
74
+
75
+ prompt = f"""Please analyze the overall content in this memory bank.
76
+
77
+ Memory Bank Overview:
78
+ {json.dumps(overview, indent=2)}
79
+
80
+ Please provide:
81
+ 1. Assessment of content organization
82
+ 2. Identification of content patterns across tables
83
+ 3. Recommendations for improving memory structure
84
+ 4. Suggestions for leveraging this content more effectively
85
+
86
+ Focus on high-level strategic insights about the memory bank's utility and organization."""
87
+
88
+ return prompt
89
+
90
+ @self.mcp.prompt("search-and-summarize")
91
+ async def search_and_summarize(query: str, max_results: Optional[int] = 10) -> str:
92
+ """Search memory content and create a summary prompt."""
93
+ db = get_database(self.db_path)
94
+
95
+ # Perform search
96
+ result = db.search_content(query, None, max_results or 10)
97
+ if not result.get("success"):
98
+ return f"Error: Could not search for '{query}'. {result.get('error', 'Unknown error')}"
99
+
100
+ search_results = result.get("results", [])
101
+ if not search_results:
102
+ return f"No results found for query: '{query}'. Please try different search terms or check if relevant content exists in the memory bank."
103
+
104
+ # Format results for prompt
105
+ formatted_results = []
106
+ for i, result in enumerate(search_results[:max_results or 10], 1):
107
+ formatted_results.append(f"{i}. Table: {result.get('table', 'unknown')}")
108
+ formatted_results.append(f" Content: {result.get('content', 'No content')[:200]}...")
109
+ formatted_results.append(f" Relevance: {result.get('relevance', 'N/A')}")
110
+ formatted_results.append("")
111
+
112
+ prompt = f"""Based on the search query "{query}", here are the most relevant results from the memory bank:
113
+
114
+ Search Results:
115
+ {chr(10).join(formatted_results)}
116
+
117
+ Please provide:
118
+ 1. A comprehensive summary of the key information found
119
+ 2. Common themes or patterns across the results
120
+ 3. Any gaps or additional information that might be needed
121
+ 4. Actionable insights based on this content
122
+
123
+ Use this information to provide a thorough, well-organized response that synthesizes the search results."""
124
+
125
+ return prompt
126
+
127
+ @self.mcp.prompt("technical-decision-analysis")
128
+ async def technical_decision_analysis(decision_topic: Optional[str] = None) -> str:
129
+ """Analyze technical decisions from the memory bank."""
130
+ db = get_database(self.db_path)
131
+
132
+ # Try to find technical_decisions table
133
+ tables_result = db.list_tables()
134
+ if not tables_result.get("success"):
135
+ return "Error: Could not access memory bank."
136
+
137
+ tables = tables_result.get("tables", [])
138
+ if "technical_decisions" not in tables:
139
+ return """No technical decisions table found in the memory bank.
140
+
141
+ To use this prompt effectively, please:
142
+ 1. Create a 'technical_decisions' table
143
+ 2. Store your technical decisions with context
144
+ 3. Try this prompt again
145
+
146
+ The table should include fields like: decision_name, chosen_approach, rationale, alternatives, timestamp."""
147
+
148
+ # Get technical decisions
149
+ where_clause = {}
150
+ if decision_topic:
151
+ # This is a simplified search - in practice you'd want semantic search
152
+ where_clause = {"decision_name": decision_topic}
153
+
154
+ result = db.read_rows("technical_decisions", where_clause)
155
+ if not result.get("success"):
156
+ return "Error: Could not read technical decisions."
157
+
158
+ decisions = result.get("rows", [])
159
+ if not decisions:
160
+ topic_msg = f" related to '{decision_topic}'" if decision_topic else ""
161
+ return f"No technical decisions found{topic_msg}. Consider adding some decisions to the memory bank first."
162
+
163
+ # Format decisions for analysis
164
+ formatted_decisions = []
165
+ for i, decision in enumerate(decisions, 1):
166
+ formatted_decisions.append(f"{i}. Decision: {decision.get('decision_name', 'Unknown')}")
167
+ formatted_decisions.append(f" Approach: {decision.get('chosen_approach', 'Not specified')}")
168
+ formatted_decisions.append(f" Rationale: {decision.get('rationale', 'Not provided')}")
169
+ if decision.get('alternatives'):
170
+ formatted_decisions.append(f" Alternatives: {decision.get('alternatives')}")
171
+ formatted_decisions.append(f" Date: {decision.get('timestamp', 'Unknown')}")
172
+ formatted_decisions.append("")
173
+
174
+ prompt = f"""Please analyze these technical decisions from the memory bank:
175
+
176
+ Technical Decisions{f" (filtered by: {decision_topic})" if decision_topic else ""}:
177
+ {chr(10).join(formatted_decisions)}
178
+
179
+ Please provide:
180
+ 1. Analysis of decision-making patterns
181
+ 2. Assessment of the rationale quality
182
+ 3. Identification of any decision dependencies or conflicts
183
+ 4. Recommendations for future decisions
184
+ 5. Suggestions for improving decision documentation
185
+
186
+ Focus on actionable insights that can improve technical decision-making processes."""
187
+
188
+ return prompt
189
+
190
+ @self.mcp.prompt("memory-bank-context")
191
+ async def memory_bank_context(context_type: str = "full") -> str:
192
+ """Provide memory bank context for AI conversations."""
193
+ db = get_database(self.db_path)
194
+
195
+ # Get overview
196
+ tables_result = db.list_tables()
197
+ if not tables_result.get("success"):
198
+ return "Error: Could not access memory bank for context."
199
+
200
+ tables = tables_result.get("tables", [])
201
+ context_info = {
202
+ "available_tables": tables,
203
+ "capabilities": [
204
+ "Full-text search across all content",
205
+ "Semantic search (if embeddings are available)",
206
+ "Structured data queries",
207
+ "Content analytics and insights"
208
+ ],
209
+ "usage_suggestions": [
210
+ "Use search_content() for finding specific information",
211
+ "Use semantic_search() for conceptual queries",
212
+ "Use read_rows() for structured data access",
213
+ "Use explore_tables() to discover available content"
214
+ ]
215
+ }
216
+
217
+ if context_type == "brief":
218
+ prompt = f"""Memory Bank Context (Brief):
219
+ Available tables: {', '.join(tables)}
220
+ Total tables: {len(tables)}
221
+
222
+ This memory bank contains structured information that can be searched and analyzed. Use the available tools to access specific content as needed."""
223
+ else:
224
+ # Get sample content from a few tables
225
+ sample_content = {}
226
+ for table in tables[:3]: # Sample from first 3 tables
227
+ try:
228
+ result = db.read_rows(table, {})
229
+ if result.get("success"):
230
+ rows = result.get("rows", [])
231
+ sample_content[table] = {
232
+ "row_count": len(rows),
233
+ "sample_row": rows[0] if rows else None
234
+ }
235
+ except Exception:
236
+ continue
237
+
238
+ prompt = f"""Memory Bank Context (Full):
239
+
240
+ {json.dumps(context_info, indent=2)}
241
+
242
+ Sample Content:
243
+ {json.dumps(sample_content, indent=2)}
244
+
245
+ This memory bank contains structured information that can be searched, analyzed, and leveraged for various tasks. The content is organized in tables with different types of information. Use the available search and query tools to access specific content as needed for your current task."""
246
+
247
+ return prompt
248
+
249
+
250
+ def setup_mcp_prompts(mcp_app: FastMCP, db_path: str) -> MemoryBankPrompts:
251
+ """Set up MCP Prompts for the memory bank."""
252
+ return MemoryBankPrompts(mcp_app, db_path)
@@ -0,0 +1,164 @@
1
+ """
2
+ MCP Resources Support for SQLite Memory Bank
3
+ ==========================================
4
+
5
+ This module adds MCP Resources support, allowing the memory bank to expose
6
+ stored content as MCP resources that can be consumed by LLM applications.
7
+
8
+ Resources provide context and data that can be accessed by AI models through
9
+ the standardized MCP protocol.
10
+
11
+ Author: Robert Meisner
12
+ """
13
+
14
+ from fastmcp import FastMCP
15
+ from .database import get_database
16
+ import json
17
+
18
+
19
+ class MemoryBankResources:
20
+ """Manages MCP Resources for the SQLite Memory Bank."""
21
+
22
+ def __init__(self, mcp_app: FastMCP, db_path: str):
23
+ self.mcp = mcp_app
24
+ self.db_path = db_path
25
+ self._register_resources()
26
+
27
+ def _register_resources(self):
28
+ """Register MCP resources with the FastMCP app."""
29
+
30
+ @self.mcp.resource("memory://tables/list")
31
+ async def get_tables_list() -> str:
32
+ """Provide a list of all available tables as an MCP resource."""
33
+ db = get_database(self.db_path)
34
+ result = db.list_tables()
35
+
36
+ if not result.get("success"):
37
+ return json.dumps({"error": "Failed to fetch tables", "details": result})
38
+
39
+ resource_content = {
40
+ "resource_type": "table_list",
41
+ "description": "List of all available tables in the memory bank",
42
+ "tables": result.get("tables", []),
43
+ "total_count": len(result.get("tables", [])),
44
+ "last_updated": "dynamic"
45
+ }
46
+
47
+ return json.dumps(resource_content, indent=2)
48
+
49
+ @self.mcp.resource("memory://tables/{table_name}/schema")
50
+ async def get_table_schema(table_name: str) -> str:
51
+ """Provide table schema information as an MCP resource."""
52
+ db = get_database(self.db_path)
53
+ result = db.describe_table(table_name)
54
+
55
+ if not result.get("success"):
56
+ return json.dumps({"error": f"Failed to fetch schema for table '{table_name}'", "details": result})
57
+
58
+ resource_content = {
59
+ "resource_type": "table_schema",
60
+ "table_name": table_name,
61
+ "description": f"Schema definition for table '{table_name}'",
62
+ "columns": result.get("columns", []),
63
+ "column_count": len(result.get("columns", [])),
64
+ "last_updated": "dynamic"
65
+ }
66
+
67
+ return json.dumps(resource_content, indent=2)
68
+
69
+ @self.mcp.resource("memory://tables/{table_name}/data")
70
+ async def get_table_data(table_name: str) -> str:
71
+ """Provide table data as an MCP resource."""
72
+ db = get_database(self.db_path)
73
+ result = db.read_rows(table_name, {})
74
+
75
+ if not result.get("success"):
76
+ return json.dumps({"error": f"Failed to fetch data for table '{table_name}'", "details": result})
77
+
78
+ rows = result.get("rows", [])
79
+ resource_content = {
80
+ "resource_type": "table_data",
81
+ "table_name": table_name,
82
+ "description": f"All data from table '{table_name}'",
83
+ "rows": rows,
84
+ "row_count": len(rows),
85
+ "last_updated": "dynamic"
86
+ }
87
+
88
+ return json.dumps(resource_content, indent=2)
89
+
90
+ @self.mcp.resource("memory://search/{query}")
91
+ async def search_memory_content(query: str) -> str:
92
+ """Provide search results as an MCP resource."""
93
+ db = get_database(self.db_path)
94
+ result = db.search_content(query, None, 50) # Search all tables, limit to 50 results
95
+
96
+ if not result.get("success"):
97
+ return json.dumps({"error": f"Failed to search for '{query}'", "details": result})
98
+
99
+ search_results = result.get("results", [])
100
+ resource_content = {
101
+ "resource_type": "search_results",
102
+ "query": query,
103
+ "description": f"Search results for query: '{query}'",
104
+ "results": search_results,
105
+ "result_count": len(search_results),
106
+ "last_updated": "dynamic"
107
+ }
108
+
109
+ return json.dumps(resource_content, indent=2)
110
+
111
+ @self.mcp.resource("memory://analytics/overview")
112
+ async def get_memory_overview() -> str:
113
+ """Provide memory bank overview analytics as an MCP resource."""
114
+ db = get_database(self.db_path)
115
+
116
+ # Get table list
117
+ tables_result = db.list_tables()
118
+ if not tables_result.get("success"):
119
+ return json.dumps({"error": "Failed to fetch memory overview", "details": tables_result})
120
+
121
+ tables = tables_result.get("tables", [])
122
+ total_rows = 0
123
+ table_stats = {}
124
+
125
+ # Get row counts for each table
126
+ for table in tables:
127
+ try:
128
+ rows_result = db.read_rows(table, {})
129
+ if rows_result.get("success"):
130
+ row_count = len(rows_result.get("rows", []))
131
+ table_stats[table] = {
132
+ "row_count": row_count,
133
+ "status": "accessible"
134
+ }
135
+ total_rows += row_count
136
+ else:
137
+ table_stats[table] = {
138
+ "row_count": 0,
139
+ "status": "error"
140
+ }
141
+ except Exception as e:
142
+ table_stats[table] = {
143
+ "row_count": 0,
144
+ "status": f"error: {str(e)}"
145
+ }
146
+
147
+ resource_content = {
148
+ "resource_type": "memory_overview",
149
+ "description": "Overview of memory bank contents and usage",
150
+ "summary": {
151
+ "total_tables": len(tables),
152
+ "total_rows": total_rows,
153
+ "largest_table": max(table_stats.items(), key=lambda x: x[1]["row_count"])[0] if table_stats else None
154
+ },
155
+ "table_statistics": table_stats,
156
+ "last_updated": "dynamic"
157
+ }
158
+
159
+ return json.dumps(resource_content, indent=2)
160
+
161
+
162
+ def setup_mcp_resources(mcp_app: FastMCP, db_path: str) -> MemoryBankResources:
163
+ """Set up MCP Resources for the memory bank."""
164
+ return MemoryBankResources(mcp_app, db_path)