mcp-sqlite-memory-bank 1.2.4__py3-none-any.whl → 1.3.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.
@@ -12,17 +12,19 @@ from enum import Enum, auto
12
12
 
13
13
  class SqliteType(str, Enum):
14
14
  """Valid SQLite column types."""
15
+
15
16
  TEXT = "TEXT"
16
17
  INTEGER = "INTEGER"
17
18
  REAL = "REAL"
18
19
  BLOB = "BLOB"
19
20
  NULL = "NULL"
20
21
  TIMESTAMP = "TIMESTAMP" # Common extension
21
- BOOLEAN = "BOOLEAN" # Common extension
22
+ BOOLEAN = "BOOLEAN" # Common extension
22
23
 
23
24
 
24
25
  class ErrorCategory(Enum):
25
26
  """Categories for SQLite Memory Bank errors."""
27
+
26
28
  VALIDATION = auto()
27
29
  DATABASE = auto()
28
30
  SCHEMA = auto()
@@ -33,18 +35,14 @@ class ErrorCategory(Enum):
33
35
  @dataclass
34
36
  class MemoryBankError(Exception):
35
37
  """Base class for SQLite Memory Bank errors."""
38
+
36
39
  message: str
37
40
  category: ErrorCategory
38
41
  details: Optional[Dict[str, Any]] = None
39
42
 
40
43
  def to_dict(self) -> Dict[str, Any]:
41
44
  """Convert error to a dict format suitable for FastMCP responses."""
42
- return {
43
- "success": False,
44
- "error": self.message,
45
- "category": self.category.name,
46
- "details": self.details or {}
47
- }
45
+ return {"success": False, "error": self.message, "category": self.category.name, "details": self.details or {}}
48
46
 
49
47
 
50
48
  class ValidationError(MemoryBankError):
@@ -77,6 +75,7 @@ class DataError(MemoryBankError):
77
75
 
78
76
  class TableColumn(TypedDict):
79
77
  """Structure for table column definitions."""
78
+
80
79
  name: str
81
80
  type: str
82
81
  notnull: bool
@@ -86,11 +85,13 @@ class TableColumn(TypedDict):
86
85
 
87
86
  class SuccessResponse(TypedDict):
88
87
  """Base structure for successful responses."""
88
+
89
89
  success: Literal[True]
90
90
 
91
91
 
92
92
  class ErrorResponse(TypedDict):
93
93
  """Structure for error responses."""
94
+
94
95
  success: Literal[False]
95
96
  error: str
96
97
  category: str
@@ -99,59 +100,152 @@ class ErrorResponse(TypedDict):
99
100
 
100
101
  class CreateTableResponse(SuccessResponse):
101
102
  """Response for create_table tool."""
103
+
102
104
  pass
103
105
 
104
106
 
105
107
  class DropTableResponse(SuccessResponse):
106
108
  """Response for drop_table tool."""
109
+
107
110
  pass
108
111
 
109
112
 
110
113
  class RenameTableResponse(SuccessResponse):
111
114
  """Response for rename_table tool."""
115
+
112
116
  pass
113
117
 
114
118
 
115
119
  class ListTablesResponse(SuccessResponse):
116
120
  """Response for list_tables tool."""
121
+
117
122
  tables: List[str]
118
123
 
119
124
 
120
125
  class DescribeTableResponse(SuccessResponse):
121
126
  """Response for describe_table tool."""
122
- columns: List[TableColumn]
127
+
128
+ columns: List[Dict[str, Any]]
123
129
 
124
130
 
125
131
  class ListAllColumnsResponse(SuccessResponse):
126
132
  """Response for list_all_columns tool."""
133
+
127
134
  schemas: Dict[str, List[str]]
128
135
 
129
136
 
130
137
  class CreateRowResponse(SuccessResponse):
131
138
  """Response for create_row tool."""
139
+
132
140
  id: int
133
141
 
134
142
 
135
143
  class ReadRowsResponse(SuccessResponse):
136
144
  """Response for read_rows tool."""
145
+
137
146
  rows: List[Dict[str, Any]]
138
147
 
139
148
 
140
149
  class UpdateRowsResponse(SuccessResponse):
141
150
  """Response for update_rows tool."""
151
+
142
152
  rows_affected: int
143
153
 
144
154
 
145
155
  class DeleteRowsResponse(SuccessResponse):
146
156
  """Response for delete_rows tool."""
157
+
147
158
  rows_affected: int
148
159
 
149
160
 
150
161
  class SelectQueryResponse(SuccessResponse):
151
162
  """Response for run_select_query tool."""
163
+
152
164
  rows: List[Dict[str, Any]]
153
165
 
154
166
 
167
+ class SearchContentResponse(SuccessResponse):
168
+ """Response for search_content tool."""
169
+
170
+ results: List[Dict[str, Any]]
171
+ query: str
172
+ tables_searched: List[str]
173
+ total_results: int
174
+
175
+
176
+ class ExploreTablesResponse(SuccessResponse):
177
+ """Response for explore_tables tool."""
178
+
179
+ exploration: Dict[str, Any]
180
+
181
+
182
+ # Semantic Search Response Types
183
+ class SemanticSearchResponse(TypedDict):
184
+ """Response type for semantic search operations."""
185
+
186
+ success: bool
187
+ results: List[Dict[str, Any]]
188
+ query: str
189
+ tables_searched: List[str]
190
+ total_results: int
191
+ model: str
192
+ similarity_threshold: float
193
+
194
+
195
+ class RelatedContentResponse(TypedDict, total=False):
196
+ """Response type for find related content operations."""
197
+
198
+ success: bool
199
+ results: List[Dict[str, Any]]
200
+ target_row: Dict[str, Any]
201
+ total_results: int
202
+ similarity_threshold: float
203
+ model: str
204
+ message: str # Optional field
205
+
206
+
207
+ class HybridSearchResponse(TypedDict):
208
+ """Response type for hybrid search operations."""
209
+
210
+ success: bool
211
+ results: List[Dict[str, Any]]
212
+ query: str
213
+ search_type: str
214
+ semantic_weight: float
215
+ text_weight: float
216
+ total_results: int
217
+ model: str
218
+
219
+
220
+ class EmbeddingStatsResponse(TypedDict):
221
+ """Response type for embedding statistics."""
222
+
223
+ success: bool
224
+ table_name: str
225
+ total_rows: int
226
+ embedded_rows: int
227
+ coverage_percent: float
228
+ embedding_dimensions: Optional[int]
229
+ embedding_column: str
230
+
231
+
232
+ class GenerateEmbeddingsResponse(TypedDict):
233
+ """Response type for embedding generation operations."""
234
+
235
+ success: bool
236
+ message: str
237
+ processed: int
238
+ model: str
239
+ embedding_dimension: int
240
+
241
+
242
+ class EmbeddingColumnResponse(TypedDict):
243
+ """Response type for adding embedding columns."""
244
+
245
+ success: bool
246
+ message: str
247
+
248
+
155
249
  # Type alias for all possible responses
156
250
  ToolResponse = Union[
157
251
  CreateTableResponse,
@@ -165,5 +259,13 @@ ToolResponse = Union[
165
259
  UpdateRowsResponse,
166
260
  DeleteRowsResponse,
167
261
  SelectQueryResponse,
168
- ErrorResponse
262
+ SearchContentResponse,
263
+ ExploreTablesResponse,
264
+ ErrorResponse,
265
+ SemanticSearchResponse,
266
+ RelatedContentResponse,
267
+ HybridSearchResponse,
268
+ EmbeddingStatsResponse,
269
+ GenerateEmbeddingsResponse,
270
+ EmbeddingColumnResponse,
169
271
  ]
@@ -11,15 +11,9 @@ import sqlite3
11
11
  import logging
12
12
  from functools import wraps
13
13
  from typing import Any, Callable, Dict, List, TypeVar, cast, Union, Tuple
14
- from .types import (
15
- ValidationError,
16
- DatabaseError,
17
- SchemaError,
18
- MemoryBankError,
19
- ToolResponse
20
- )
14
+ from .types import ValidationError, DatabaseError, SchemaError, MemoryBankError, ToolResponse
21
15
 
22
- T = TypeVar('T', bound=Callable[..., ToolResponse])
16
+ T = TypeVar("T", bound=Callable[..., ToolResponse])
23
17
 
24
18
 
25
19
  def catch_errors(f: T) -> T:
@@ -27,6 +21,7 @@ def catch_errors(f: T) -> T:
27
21
  Decorator to standardize error handling across tools.
28
22
  Catches exceptions and converts them to appropriate error responses.
29
23
  """
24
+
30
25
  @wraps(f)
31
26
  def wrapper(*args: Any, **kwargs: Any) -> ToolResponse:
32
27
  try:
@@ -36,15 +31,13 @@ def catch_errors(f: T) -> T:
36
31
  return cast(ToolResponse, e.to_dict())
37
32
  except sqlite3.Error as e:
38
33
  logging.error(f"{f.__name__} database error: {e}")
39
- return cast(ToolResponse, DatabaseError(
40
- f"Database error in {f.__name__}: {e}",
41
- {"sqlite_error": str(e)}
42
- ).to_dict())
34
+ return cast(
35
+ ToolResponse, DatabaseError(f"Database error in {f.__name__}: {e}", {"sqlite_error": str(e)}).to_dict()
36
+ )
43
37
  except Exception as e:
44
38
  logging.error(f"Unexpected error in {f.__name__}: {e}")
45
- return cast(ToolResponse, DatabaseError(
46
- f"Unexpected error in {f.__name__}: {e}"
47
- ).to_dict())
39
+ return cast(ToolResponse, DatabaseError(f"Unexpected error in {f.__name__}: {e}").to_dict())
40
+
48
41
  return cast(T, wrapper)
49
42
 
50
43
 
@@ -57,11 +50,11 @@ def validate_identifier(name: str, context: str = "identifier") -> None:
57
50
  name: The identifier to validate
58
51
  context: Description of what's being validated (for error messages)
59
52
  """
60
- if not bool(re.match(r'^[A-Za-z_][A-Za-z0-9_]*$', name)):
53
+ if not bool(re.match(r"^[A-Za-z_][A-Za-z0-9_]*$", name)):
61
54
  raise ValidationError(
62
55
  f"Invalid {context}: {name}. Must start with letter/underscore and "
63
56
  f"contain only letters, numbers, underscores.",
64
- {"invalid_name": name}
57
+ {"invalid_name": name},
65
58
  )
66
59
 
67
60
 
@@ -74,14 +67,11 @@ def validate_column_definition(column: Dict[str, Any]) -> None:
74
67
  column: Dictionary with column definition (must have 'name' and 'type' keys)
75
68
  """
76
69
  if not isinstance(column, dict):
77
- raise ValidationError(
78
- "Column definition must be a dictionary",
79
- {"received": str(type(column))}
80
- )
70
+ raise ValidationError("Column definition must be a dictionary", {"received": str(type(column))})
81
71
  if "name" not in column or "type" not in column:
82
72
  raise ValidationError(
83
73
  "Column definition must have 'name' and 'type' keys",
84
- {"missing_keys": [k for k in ["name", "type"] if k not in column]}
74
+ {"missing_keys": [k for k in ["name", "type"] if k not in column]},
85
75
  )
86
76
  validate_identifier(column["name"], "column name")
87
77
 
@@ -103,17 +93,14 @@ def get_table_columns(conn: sqlite3.Connection, table_name: str) -> List[str]:
103
93
  cur.execute(f"PRAGMA table_info({table_name})")
104
94
  columns = [row[1] for row in cur.fetchall()]
105
95
  if not columns:
106
- raise SchemaError(
107
- f"Table does not exist: {table_name}",
108
- {"table_name": table_name}
109
- )
96
+ raise SchemaError(f"Table does not exist: {table_name}", {"table_name": table_name})
110
97
  return columns
111
98
 
99
+
112
100
  # Compatibility function for direct table_name usage
113
101
 
114
102
 
115
- def get_table_columns_by_name(
116
- table_name: str) -> Union[List[str], Dict[str, Any]]:
103
+ def get_table_columns_by_name(table_name: str) -> Union[List[str], Dict[str, Any]]:
117
104
  """
118
105
  Get list of column names for a table by name.
119
106
  Compatibility function for the old implementation.
@@ -128,14 +115,9 @@ def get_table_columns_by_name(
128
115
  validate_identifier(table_name, "table name")
129
116
  with sqlite3.connect(os.environ.get("DB_PATH", "./test.db")) as conn:
130
117
  cur = conn.cursor()
131
- cur.execute(
132
- "SELECT name FROM sqlite_master WHERE type='table' AND name=?",
133
- (table_name,)
134
- )
118
+ cur.execute("SELECT name FROM sqlite_master WHERE type='table' AND name=?", (table_name,))
135
119
  if not cur.fetchone():
136
- return {
137
- "success": False,
138
- "error": f"Table '{table_name}' does not exist"}
120
+ return {"success": False, "error": f"Table '{table_name}' does not exist"}
139
121
 
140
122
  # Get column information
141
123
  cur.execute(f"PRAGMA table_info({table_name})")
@@ -144,9 +126,7 @@ def get_table_columns_by_name(
144
126
  except MemoryBankError as e:
145
127
  return e.to_dict()
146
128
  except Exception as e:
147
- return {
148
- "success": False,
149
- "error": f"Exception in get_table_columns: {e}"}
129
+ return {"success": False, "error": f"Exception in get_table_columns: {e}"}
150
130
 
151
131
 
152
132
  def validate_table_exists(conn: sqlite3.Connection, table_name: str) -> None:
@@ -159,19 +139,12 @@ def validate_table_exists(conn: sqlite3.Connection, table_name: str) -> None:
159
139
  table_name: Name of table to check
160
140
  """
161
141
  cur = conn.cursor()
162
- cur.execute(
163
- "SELECT name FROM sqlite_master WHERE type='table' AND name=?",
164
- (table_name,)
165
- )
142
+ cur.execute("SELECT name FROM sqlite_master WHERE type='table' AND name=?", (table_name,))
166
143
  if not cur.fetchone():
167
- raise SchemaError(
168
- f"Table does not exist: {table_name}",
169
- {"table_name": table_name}
170
- )
144
+ raise SchemaError(f"Table does not exist: {table_name}", {"table_name": table_name})
171
145
 
172
146
 
173
- def build_where_clause(
174
- where: Dict[str, Any], valid_columns: List[str]) -> Union[Tuple[str, list], Dict[str, Any]]:
147
+ def build_where_clause(where: Dict[str, Any], valid_columns: List[str]) -> Union[Tuple[str, list], Dict[str, Any]]:
175
148
  """
176
149
  Build a WHERE clause from a dictionary of column-value pairs.
177
150
 
@@ -192,13 +165,11 @@ def build_where_clause(
192
165
 
193
166
  for col, val in where.items():
194
167
  if col not in valid_columns:
195
- return {
196
- "success": False,
197
- "error": f"Invalid column in where clause: {col}"}
168
+ return {"success": False, "error": f"Invalid column in where clause: {col}"}
198
169
  conditions.append(f"{col}=?")
199
170
  values.append(val)
200
171
 
201
- clause = ' AND '.join(conditions)
172
+ clause = " AND ".join(conditions)
202
173
  return clause, values
203
174
  except Exception as e:
204
175
  logging.error(f"Error in build_where_clause: {e}")
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp_sqlite_memory_bank
3
- Version: 1.2.4
4
- Summary: A dynamic, agent/LLM-friendly SQLite memory bank for MCP servers.
3
+ Version: 1.3.1
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
7
7
  Project-URL: Homepage, https://github.com/robertmeisner/mcp_sqlite_memory_bank
@@ -16,6 +16,10 @@ Requires-Dist: fastapi>=0.100.0
16
16
  Requires-Dist: uvicorn>=0.22.0
17
17
  Requires-Dist: pydantic>=1.10.0
18
18
  Requires-Dist: fastmcp
19
+ Requires-Dist: sqlalchemy>=2.0.0
20
+ Requires-Dist: sentence-transformers>=2.2.0
21
+ Requires-Dist: torch>=1.9.0
22
+ Requires-Dist: numpy>=1.21.0
19
23
  Provides-Extra: test
20
24
  Requires-Dist: pytest; extra == "test"
21
25
  Dynamic: license-file
@@ -210,17 +214,39 @@ python -m mcp_sqlite_memory_bank.server main --host 127.0.0.1 --port 8000
210
214
 
211
215
  ## Setup and Configuration
212
216
 
213
- You can configure the database path and other options via environment variables or a `.env` file in your project root.
217
+ ### Database Location
218
+
219
+ **Default Behavior (v1.2.5+):**
220
+ - **User-specific database**: `~/.mcp_sqlite_memory/memory.db`
221
+ - **Isolated per user**: Each user gets their own database
222
+ - **Persistent across projects**: Data is preserved between sessions
223
+
224
+ **Custom Database Paths:**
225
+ You can configure a custom database location via the `DB_PATH` environment variable:
226
+
227
+ - **Project-specific**: `DB_PATH=./project_memory.db`
228
+ - **Shared team database**: `DB_PATH=/shared/team_memory.db`
229
+ - **Temporary database**: `DB_PATH=/tmp/session_memory.db`
214
230
 
215
231
  **Environment Variables:**
216
- - `DB_PATH`: Path to the SQLite database file (default: `./test.db`)
217
- - Any other options supported by the server (see API docs)
232
+ - `DB_PATH`: Path to the SQLite database file (default: `~/.mcp_sqlite_memory/memory.db`)
218
233
 
219
234
  **Example `.env`:**
220
235
  ```env
221
- DB_PATH=./test.db
236
+ # Use project-specific database
237
+ DB_PATH=./project_memory.db
238
+
239
+ # Or use a specific location
240
+ DB_PATH=/path/to/my/memory.db
222
241
  ```
223
242
 
243
+ **Migration Note:**
244
+ If you were using v1.2.4 or earlier, your data was stored in `./test.db` in the current working directory. To migrate your data:
245
+
246
+ 1. Locate your old `test.db` file
247
+ 2. Copy it to the new default location: `~/.mcp_sqlite_memory/memory.db`
248
+ 3. Or set `DB_PATH` to point to your existing database
249
+
224
250
  ---
225
251
 
226
252
  ## Integration with Editors & Agent Platforms
@@ -229,16 +255,45 @@ DB_PATH=./test.db
229
255
 
230
256
  #### Manual Configuration
231
257
 
232
- Add or update `.vscode/mcp.json` in your project root:
258
+ **Option 1: Use Default User Database (Recommended)**
233
259
  ```jsonc
234
260
  {
235
261
  "servers": {
236
262
  "SQLite_Memory": {
237
263
  "type": "stdio",
238
264
  "command": "uvx",
265
+ "args": ["--refresh", "mcp-sqlite-memory-bank"]
266
+ }
267
+ }
268
+ }
269
+ ```
270
+
271
+ **Option 2: Project-Specific Database**
272
+ ```jsonc
273
+ {
274
+ "servers": {
275
+ "SQLite_Memory": {
276
+ "type": "stdio",
277
+ "command": "uvx",
239
278
  "args": ["--refresh", "mcp-sqlite-memory-bank"],
240
279
  "env": {
241
- "DB_PATH": "${workspaceFolder}/.vscode/project_memory.sqlite"
280
+ "DB_PATH": "${workspaceFolder}/.mcp_memory.db"
281
+ }
282
+ }
283
+ }
284
+ }
285
+ ```
286
+
287
+ **Option 3: Custom Database Location**
288
+ ```jsonc
289
+ {
290
+ "servers": {
291
+ "SQLite_Memory": {
292
+ "type": "stdio",
293
+ "command": "uvx",
294
+ "args": ["--refresh", "mcp-sqlite-memory-bank"],
295
+ "env": {
296
+ "DB_PATH": "/path/to/your/custom/memory.db"
242
297
  }
243
298
  }
244
299
  }
@@ -0,0 +1,13 @@
1
+ mcp_sqlite_memory_bank/__init__.py,sha256=6Y9_iSiQIWOPJYMcMkbrqmaWiM-ymRHdNR6W-8jHj-k,2403
2
+ mcp_sqlite_memory_bank/database.py,sha256=kBHiibDV2ucm9alaDrNB-txm7v-hGt-uTNHIFKuFlKI,41873
3
+ mcp_sqlite_memory_bank/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ mcp_sqlite_memory_bank/semantic.py,sha256=XVfM1C95TdZDtXOrwrGxR5JZnmeeO4O45gxniPWa6go,14791
5
+ mcp_sqlite_memory_bank/server.py,sha256=lt_1LSXDDJoSATNd4T07GmfPqlYcdQlmwDGR_TySboM,33896
6
+ mcp_sqlite_memory_bank/types.py,sha256=2rNhd6dbvEFsey9QGHQ0VPGSB3U0RaXw8fKVfiBuUJw,6535
7
+ mcp_sqlite_memory_bank/utils.py,sha256=wHbR0cUlV-AWBk8ToI5ZgCrfrMp380ofyEc_GLB0l4g,6185
8
+ mcp_sqlite_memory_bank-1.3.1.dist-info/licenses/LICENSE,sha256=KPr7eFgCJqQIjeSAcwRafbjcgm-10zkrJ7MFoTOGJQg,1092
9
+ mcp_sqlite_memory_bank-1.3.1.dist-info/METADATA,sha256=xgs7hG0Uu1Sr6Qpj_HR6wHz48P4wxTQmD_CbSknsLus,26002
10
+ mcp_sqlite_memory_bank-1.3.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
+ mcp_sqlite_memory_bank-1.3.1.dist-info/entry_points.txt,sha256=S9yGWiCe8f_rgcGCgbwEAX2FfJ9jXWxcc4K4Jenbcn8,150
12
+ mcp_sqlite_memory_bank-1.3.1.dist-info/top_level.txt,sha256=xQ8MTGECpWMR-9DV4H8mMqaSoZqE-C8EvpOg9E2U1wM,23
13
+ mcp_sqlite_memory_bank-1.3.1.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- mcp_sqlite_memory_bank/__init__.py,sha256=l2045g6NL0NYoX0zMUjL5gDJHb4rI3l3gNUOYeACyBU,2293
2
- mcp_sqlite_memory_bank/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- mcp_sqlite_memory_bank/server.py,sha256=xZFbFtR40Fq5mK_7B0dQNKtrZhQD8DvHkQlVTDrXTLw,38998
4
- mcp_sqlite_memory_bank/types.py,sha256=86xN1bv-WaIu_Oz1_RFjRP7wsZdc6CHL_ntCGqn0eDI,4439
5
- mcp_sqlite_memory_bank/utils.py,sha256=YHUFbZL0AJ0jjQnlQSVYJpBi4Bmeu7M-7tcyS82gxts,6574
6
- mcp_sqlite_memory_bank-1.2.4.dist-info/licenses/LICENSE,sha256=KPr7eFgCJqQIjeSAcwRafbjcgm-10zkrJ7MFoTOGJQg,1092
7
- mcp_sqlite_memory_bank-1.2.4.dist-info/METADATA,sha256=yuO3ewomT2ZJrimZ6uoLlnfbrTilycDBLAJwHJcG0D0,24513
8
- mcp_sqlite_memory_bank-1.2.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
- mcp_sqlite_memory_bank-1.2.4.dist-info/entry_points.txt,sha256=S9yGWiCe8f_rgcGCgbwEAX2FfJ9jXWxcc4K4Jenbcn8,150
10
- mcp_sqlite_memory_bank-1.2.4.dist-info/top_level.txt,sha256=xQ8MTGECpWMR-9DV4H8mMqaSoZqE-C8EvpOg9E2U1wM,23
11
- mcp_sqlite_memory_bank-1.2.4.dist-info/RECORD,,