mcp-sqlite-memory-bank 1.2.4__py3-none-any.whl → 1.3.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.
@@ -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,146 @@ 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
+ success: bool
186
+ results: List[Dict[str, Any]]
187
+ query: str
188
+ tables_searched: List[str]
189
+ total_results: int
190
+ model: str
191
+ similarity_threshold: float
192
+
193
+
194
+ class RelatedContentResponse(TypedDict, total=False):
195
+ """Response type for find related content operations."""
196
+ success: bool
197
+ results: List[Dict[str, Any]]
198
+ target_row: Dict[str, Any]
199
+ total_results: int
200
+ similarity_threshold: float
201
+ model: str
202
+ message: str # Optional field
203
+
204
+
205
+ class HybridSearchResponse(TypedDict):
206
+ """Response type for hybrid search operations."""
207
+ success: bool
208
+ results: List[Dict[str, Any]]
209
+ query: str
210
+ search_type: str
211
+ semantic_weight: float
212
+ text_weight: float
213
+ total_results: int
214
+ model: str
215
+
216
+
217
+ class EmbeddingStatsResponse(TypedDict):
218
+ """Response type for embedding statistics."""
219
+ success: bool
220
+ table_name: str
221
+ total_rows: int
222
+ embedded_rows: int
223
+ coverage_percent: float
224
+ embedding_dimensions: Optional[int]
225
+ embedding_column: str
226
+
227
+
228
+ class GenerateEmbeddingsResponse(TypedDict):
229
+ """Response type for embedding generation operations."""
230
+ success: bool
231
+ message: str
232
+ processed: int
233
+ model: str
234
+ embedding_dimension: int
235
+
236
+
237
+ class EmbeddingColumnResponse(TypedDict):
238
+ """Response type for adding embedding columns."""
239
+ success: bool
240
+ message: str
241
+
242
+
155
243
  # Type alias for all possible responses
156
244
  ToolResponse = Union[
157
245
  CreateTableResponse,
@@ -165,5 +253,13 @@ ToolResponse = Union[
165
253
  UpdateRowsResponse,
166
254
  DeleteRowsResponse,
167
255
  SelectQueryResponse,
168
- ErrorResponse
256
+ SearchContentResponse,
257
+ ExploreTablesResponse,
258
+ ErrorResponse,
259
+ SemanticSearchResponse,
260
+ RelatedContentResponse,
261
+ HybridSearchResponse,
262
+ EmbeddingStatsResponse,
263
+ GenerateEmbeddingsResponse,
264
+ EmbeddingColumnResponse,
169
265
  ]
@@ -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,11 @@ 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(ToolResponse, DatabaseError(f"Database error in {f.__name__}: {e}", {"sqlite_error": str(e)}).to_dict())
43
35
  except Exception as e:
44
36
  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())
37
+ return cast(ToolResponse, DatabaseError(f"Unexpected error in {f.__name__}: {e}").to_dict())
38
+
48
39
  return cast(T, wrapper)
49
40
 
50
41
 
@@ -57,11 +48,10 @@ def validate_identifier(name: str, context: str = "identifier") -> None:
57
48
  name: The identifier to validate
58
49
  context: Description of what's being validated (for error messages)
59
50
  """
60
- if not bool(re.match(r'^[A-Za-z_][A-Za-z0-9_]*$', name)):
51
+ if not bool(re.match(r"^[A-Za-z_][A-Za-z0-9_]*$", name)):
61
52
  raise ValidationError(
62
- f"Invalid {context}: {name}. Must start with letter/underscore and "
63
- f"contain only letters, numbers, underscores.",
64
- {"invalid_name": name}
53
+ f"Invalid {context}: {name}. Must start with letter/underscore and " f"contain only letters, numbers, underscores.",
54
+ {"invalid_name": name},
65
55
  )
66
56
 
67
57
 
@@ -74,14 +64,11 @@ def validate_column_definition(column: Dict[str, Any]) -> None:
74
64
  column: Dictionary with column definition (must have 'name' and 'type' keys)
75
65
  """
76
66
  if not isinstance(column, dict):
77
- raise ValidationError(
78
- "Column definition must be a dictionary",
79
- {"received": str(type(column))}
80
- )
67
+ raise ValidationError("Column definition must be a dictionary", {"received": str(type(column))})
81
68
  if "name" not in column or "type" not in column:
82
69
  raise ValidationError(
83
70
  "Column definition must have 'name' and 'type' keys",
84
- {"missing_keys": [k for k in ["name", "type"] if k not in column]}
71
+ {"missing_keys": [k for k in ["name", "type"] if k not in column]},
85
72
  )
86
73
  validate_identifier(column["name"], "column name")
87
74
 
@@ -103,17 +90,14 @@ def get_table_columns(conn: sqlite3.Connection, table_name: str) -> List[str]:
103
90
  cur.execute(f"PRAGMA table_info({table_name})")
104
91
  columns = [row[1] for row in cur.fetchall()]
105
92
  if not columns:
106
- raise SchemaError(
107
- f"Table does not exist: {table_name}",
108
- {"table_name": table_name}
109
- )
93
+ raise SchemaError(f"Table does not exist: {table_name}", {"table_name": table_name})
110
94
  return columns
111
95
 
96
+
112
97
  # Compatibility function for direct table_name usage
113
98
 
114
99
 
115
- def get_table_columns_by_name(
116
- table_name: str) -> Union[List[str], Dict[str, Any]]:
100
+ def get_table_columns_by_name(table_name: str) -> Union[List[str], Dict[str, Any]]:
117
101
  """
118
102
  Get list of column names for a table by name.
119
103
  Compatibility function for the old implementation.
@@ -128,14 +112,9 @@ def get_table_columns_by_name(
128
112
  validate_identifier(table_name, "table name")
129
113
  with sqlite3.connect(os.environ.get("DB_PATH", "./test.db")) as conn:
130
114
  cur = conn.cursor()
131
- cur.execute(
132
- "SELECT name FROM sqlite_master WHERE type='table' AND name=?",
133
- (table_name,)
134
- )
115
+ cur.execute("SELECT name FROM sqlite_master WHERE type='table' AND name=?", (table_name,))
135
116
  if not cur.fetchone():
136
- return {
137
- "success": False,
138
- "error": f"Table '{table_name}' does not exist"}
117
+ return {"success": False, "error": f"Table '{table_name}' does not exist"}
139
118
 
140
119
  # Get column information
141
120
  cur.execute(f"PRAGMA table_info({table_name})")
@@ -144,9 +123,7 @@ def get_table_columns_by_name(
144
123
  except MemoryBankError as e:
145
124
  return e.to_dict()
146
125
  except Exception as e:
147
- return {
148
- "success": False,
149
- "error": f"Exception in get_table_columns: {e}"}
126
+ return {"success": False, "error": f"Exception in get_table_columns: {e}"}
150
127
 
151
128
 
152
129
  def validate_table_exists(conn: sqlite3.Connection, table_name: str) -> None:
@@ -159,19 +136,12 @@ def validate_table_exists(conn: sqlite3.Connection, table_name: str) -> None:
159
136
  table_name: Name of table to check
160
137
  """
161
138
  cur = conn.cursor()
162
- cur.execute(
163
- "SELECT name FROM sqlite_master WHERE type='table' AND name=?",
164
- (table_name,)
165
- )
139
+ cur.execute("SELECT name FROM sqlite_master WHERE type='table' AND name=?", (table_name,))
166
140
  if not cur.fetchone():
167
- raise SchemaError(
168
- f"Table does not exist: {table_name}",
169
- {"table_name": table_name}
170
- )
141
+ raise SchemaError(f"Table does not exist: {table_name}", {"table_name": table_name})
171
142
 
172
143
 
173
- def build_where_clause(
174
- where: Dict[str, Any], valid_columns: List[str]) -> Union[Tuple[str, list], Dict[str, Any]]:
144
+ def build_where_clause(where: Dict[str, Any], valid_columns: List[str]) -> Union[Tuple[str, list], Dict[str, Any]]:
175
145
  """
176
146
  Build a WHERE clause from a dictionary of column-value pairs.
177
147
 
@@ -192,13 +162,11 @@ def build_where_clause(
192
162
 
193
163
  for col, val in where.items():
194
164
  if col not in valid_columns:
195
- return {
196
- "success": False,
197
- "error": f"Invalid column in where clause: {col}"}
165
+ return {"success": False, "error": f"Invalid column in where clause: {col}"}
198
166
  conditions.append(f"{col}=?")
199
167
  values.append(val)
200
168
 
201
- clause = ' AND '.join(conditions)
169
+ clause = " AND ".join(conditions)
202
170
  return clause, values
203
171
  except Exception as e:
204
172
  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.0
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
@@ -210,17 +210,39 @@ python -m mcp_sqlite_memory_bank.server main --host 127.0.0.1 --port 8000
210
210
 
211
211
  ## Setup and Configuration
212
212
 
213
- You can configure the database path and other options via environment variables or a `.env` file in your project root.
213
+ ### Database Location
214
+
215
+ **Default Behavior (v1.2.5+):**
216
+ - **User-specific database**: `~/.mcp_sqlite_memory/memory.db`
217
+ - **Isolated per user**: Each user gets their own database
218
+ - **Persistent across projects**: Data is preserved between sessions
219
+
220
+ **Custom Database Paths:**
221
+ You can configure a custom database location via the `DB_PATH` environment variable:
222
+
223
+ - **Project-specific**: `DB_PATH=./project_memory.db`
224
+ - **Shared team database**: `DB_PATH=/shared/team_memory.db`
225
+ - **Temporary database**: `DB_PATH=/tmp/session_memory.db`
214
226
 
215
227
  **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)
228
+ - `DB_PATH`: Path to the SQLite database file (default: `~/.mcp_sqlite_memory/memory.db`)
218
229
 
219
230
  **Example `.env`:**
220
231
  ```env
221
- DB_PATH=./test.db
232
+ # Use project-specific database
233
+ DB_PATH=./project_memory.db
234
+
235
+ # Or use a specific location
236
+ DB_PATH=/path/to/my/memory.db
222
237
  ```
223
238
 
239
+ **Migration Note:**
240
+ 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:
241
+
242
+ 1. Locate your old `test.db` file
243
+ 2. Copy it to the new default location: `~/.mcp_sqlite_memory/memory.db`
244
+ 3. Or set `DB_PATH` to point to your existing database
245
+
224
246
  ---
225
247
 
226
248
  ## Integration with Editors & Agent Platforms
@@ -229,16 +251,45 @@ DB_PATH=./test.db
229
251
 
230
252
  #### Manual Configuration
231
253
 
232
- Add or update `.vscode/mcp.json` in your project root:
254
+ **Option 1: Use Default User Database (Recommended)**
233
255
  ```jsonc
234
256
  {
235
257
  "servers": {
236
258
  "SQLite_Memory": {
237
259
  "type": "stdio",
238
260
  "command": "uvx",
261
+ "args": ["--refresh", "mcp-sqlite-memory-bank"]
262
+ }
263
+ }
264
+ }
265
+ ```
266
+
267
+ **Option 2: Project-Specific Database**
268
+ ```jsonc
269
+ {
270
+ "servers": {
271
+ "SQLite_Memory": {
272
+ "type": "stdio",
273
+ "command": "uvx",
239
274
  "args": ["--refresh", "mcp-sqlite-memory-bank"],
240
275
  "env": {
241
- "DB_PATH": "${workspaceFolder}/.vscode/project_memory.sqlite"
276
+ "DB_PATH": "${workspaceFolder}/.mcp_memory.db"
277
+ }
278
+ }
279
+ }
280
+ }
281
+ ```
282
+
283
+ **Option 3: Custom Database Location**
284
+ ```jsonc
285
+ {
286
+ "servers": {
287
+ "SQLite_Memory": {
288
+ "type": "stdio",
289
+ "command": "uvx",
290
+ "args": ["--refresh", "mcp-sqlite-memory-bank"],
291
+ "env": {
292
+ "DB_PATH": "/path/to/your/custom/memory.db"
242
293
  }
243
294
  }
244
295
  }
@@ -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=OaFHUQdsd9kCdaS1F8f5Z5jdaGmedZUbG-ZvProkvvI,41565
3
+ mcp_sqlite_memory_bank/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ mcp_sqlite_memory_bank/semantic.py,sha256=-QA1XDMlptIwsEDllBzGZS6si26QIdIaIgAiszVkckY,15609
5
+ mcp_sqlite_memory_bank/server.py,sha256=U2iX7JbYBiIwtIWMHET2gZ_M97HrMmskfrvAS5fL8Wg,33775
6
+ mcp_sqlite_memory_bank/types.py,sha256=Uz4CsnIR_MU8LAl2BbAqYWVJTogiYi_8-cr0Gg4foYs,6523
7
+ mcp_sqlite_memory_bank/utils.py,sha256=oF2VNXvxH_yzuipfYZsG6AMB7K0lWFr9Lupz6Cc7sM0,6140
8
+ mcp_sqlite_memory_bank-1.3.0.dist-info/licenses/LICENSE,sha256=KPr7eFgCJqQIjeSAcwRafbjcgm-10zkrJ7MFoTOGJQg,1092
9
+ mcp_sqlite_memory_bank-1.3.0.dist-info/METADATA,sha256=z9MVNNrsnI3So7w72lxoUknyYkM0dFp_jFd85QhdMQg,25864
10
+ mcp_sqlite_memory_bank-1.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
+ mcp_sqlite_memory_bank-1.3.0.dist-info/entry_points.txt,sha256=S9yGWiCe8f_rgcGCgbwEAX2FfJ9jXWxcc4K4Jenbcn8,150
12
+ mcp_sqlite_memory_bank-1.3.0.dist-info/top_level.txt,sha256=xQ8MTGECpWMR-9DV4H8mMqaSoZqE-C8EvpOg9E2U1wM,23
13
+ mcp_sqlite_memory_bank-1.3.0.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,,