amnesic 0.1.0__tar.gz
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.
- amnesic-0.1.0/.github/workflows/ci.yml +29 -0
- amnesic-0.1.0/.github/workflows/publish.yml +35 -0
- amnesic-0.1.0/.gitignore +12 -0
- amnesic-0.1.0/LICENSE +21 -0
- amnesic-0.1.0/PKG-INFO +326 -0
- amnesic-0.1.0/README.md +289 -0
- amnesic-0.1.0/ROADMAP.md +115 -0
- amnesic-0.1.0/amnesic/__init__.py +7 -0
- amnesic-0.1.0/amnesic/cli.py +160 -0
- amnesic-0.1.0/amnesic/config.py +199 -0
- amnesic-0.1.0/amnesic/drivers.py +86 -0
- amnesic-0.1.0/amnesic/readonly.py +95 -0
- amnesic-0.1.0/amnesic/server.py +250 -0
- amnesic-0.1.0/amnesic/store.py +485 -0
- amnesic-0.1.0/amnesic/tools/__init__.py +1 -0
- amnesic-0.1.0/amnesic/tools/connections.py +28 -0
- amnesic-0.1.0/amnesic/tools/knowledge.py +207 -0
- amnesic-0.1.0/amnesic/tools/query.py +55 -0
- amnesic-0.1.0/amnesic/tools/relationships.py +211 -0
- amnesic-0.1.0/amnesic/tools/schema.py +257 -0
- amnesic-0.1.0/amnesic/tunnel.py +61 -0
- amnesic-0.1.0/pyproject.toml +45 -0
- amnesic-0.1.0/tests/__init__.py +0 -0
- amnesic-0.1.0/tests/test_config.py +320 -0
- amnesic-0.1.0/tests/test_readonly.py +246 -0
- amnesic-0.1.0/tests/test_store.py +257 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
name: Python ${{ matrix.python-version }}
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
strategy:
|
|
14
|
+
matrix:
|
|
15
|
+
python-version: ["3.11", "3.12"]
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
21
|
+
uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: ${{ matrix.python-version }}
|
|
24
|
+
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
run: pip install -e .[dev]
|
|
27
|
+
|
|
28
|
+
- name: Run tests
|
|
29
|
+
run: pytest tests/ -v
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
name: Build and publish
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
# Trusted Publishing — no token, no secrets to manage.
|
|
14
|
+
# Configure once at https://pypi.org/manage/account/publishing/
|
|
15
|
+
permissions:
|
|
16
|
+
id-token: write # required for OIDC to PyPI
|
|
17
|
+
contents: read
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- name: Set up Python
|
|
23
|
+
uses: actions/setup-python@v5
|
|
24
|
+
with:
|
|
25
|
+
python-version: "3.11"
|
|
26
|
+
|
|
27
|
+
- name: Install build tools
|
|
28
|
+
run: pip install build
|
|
29
|
+
|
|
30
|
+
- name: Build distribution
|
|
31
|
+
run: python -m build
|
|
32
|
+
|
|
33
|
+
- name: Publish to PyPI
|
|
34
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
35
|
+
# No password/token needed — OIDC handles authentication via Trusted Publisher.
|
amnesic-0.1.0/.gitignore
ADDED
amnesic-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Suraj Goyal
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
amnesic-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: amnesic
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: The MCP server with the most ironic name in the registry — persistent semantic memory for your SQL databases
|
|
5
|
+
Project-URL: Homepage, https://github.com/SurajKGoyal/amnesic
|
|
6
|
+
Project-URL: Repository, https://github.com/SurajKGoyal/amnesic
|
|
7
|
+
Project-URL: Issues, https://github.com/SurajKGoyal/amnesic/issues
|
|
8
|
+
Author-email: Suraj Goyal <sgoyal275@gmail.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: ai-tools,claude,database,llm,mcp,mssql,mysql,postgres,sql
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Database
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Requires-Dist: click>=8.0
|
|
20
|
+
Requires-Dist: mcp[cli]>=1.0.0
|
|
21
|
+
Requires-Dist: rich>=13.0
|
|
22
|
+
Requires-Dist: sqlalchemy>=2.0
|
|
23
|
+
Provides-Extra: all
|
|
24
|
+
Requires-Dist: psycopg2-binary>=2.9; extra == 'all'
|
|
25
|
+
Requires-Dist: pymssql>=2.2; extra == 'all'
|
|
26
|
+
Requires-Dist: pymysql>=1.1; extra == 'all'
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
30
|
+
Provides-Extra: mssql
|
|
31
|
+
Requires-Dist: pymssql>=2.2; extra == 'mssql'
|
|
32
|
+
Provides-Extra: mysql
|
|
33
|
+
Requires-Dist: pymysql>=1.1; extra == 'mysql'
|
|
34
|
+
Provides-Extra: postgres
|
|
35
|
+
Requires-Dist: psycopg2-binary>=2.9; extra == 'postgres'
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
# amnesic — the MCP server with the most ironic name in the registry
|
|
39
|
+
|
|
40
|
+
**Persistent semantic memory for your SQL databases. The name is ironic — it remembers everything.**
|
|
41
|
+
|
|
42
|
+
*"The MCP server with the most ironic name in the registry. It's anything but amnesic — it remembers your database so your AI doesn't have to."*
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## The problem
|
|
47
|
+
|
|
48
|
+
Every session with an AI starts cold. You spend the first few minutes re-explaining what tables exist, what a `status` column value of `3` means, which FK connects `orders` to `users`. Then the session ends, and you do it all over again tomorrow.
|
|
49
|
+
|
|
50
|
+
**amnesic fixes this.** It gives your AI a persistent SQLite knowledge store — one per database — that survives across sessions. Annotate a status enum once; every future session sees those labels automatically. Discover FK relationships once; every future JOIN query uses that graph.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Install
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# Core only (SQLite works out of the box)
|
|
58
|
+
pip install amnesic
|
|
59
|
+
|
|
60
|
+
# With driver extras
|
|
61
|
+
pip install "amnesic[postgres]"
|
|
62
|
+
pip install "amnesic[mssql]"
|
|
63
|
+
pip install "amnesic[mysql]"
|
|
64
|
+
pip install "amnesic[all]"
|
|
65
|
+
|
|
66
|
+
# Or run directly with uvx (no install needed)
|
|
67
|
+
uvx amnesic
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Setup
|
|
73
|
+
|
|
74
|
+
### 1. Create config
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
amnesic init
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
This creates `~/.config/amnesic/connections.toml` with a commented template.
|
|
81
|
+
|
|
82
|
+
### 2. Edit the config
|
|
83
|
+
|
|
84
|
+
```toml
|
|
85
|
+
# ~/.config/amnesic/connections.toml
|
|
86
|
+
|
|
87
|
+
# Nested style: [connections.product.env]
|
|
88
|
+
[connections.orders.prod]
|
|
89
|
+
driver = "mssql"
|
|
90
|
+
server = "localhost"
|
|
91
|
+
port = 11433
|
|
92
|
+
database = "OrdersDB"
|
|
93
|
+
user = "${ORDERS_PROD_USER}"
|
|
94
|
+
password = "${ORDERS_PROD_PASSWORD}"
|
|
95
|
+
tunnel_script = "~/.scripts/mssql-tunnel.sh" # optional SSH tunnel
|
|
96
|
+
|
|
97
|
+
[connections.orders.staging]
|
|
98
|
+
driver = "mssql"
|
|
99
|
+
server = "localhost"
|
|
100
|
+
port = 11434
|
|
101
|
+
database = "OrdersDB_Staging"
|
|
102
|
+
user = "${ORDERS_STAGING_USER}"
|
|
103
|
+
password = "${ORDERS_STAGING_PASSWORD}"
|
|
104
|
+
|
|
105
|
+
# Flat style: [connections.name]
|
|
106
|
+
[connections.analytics]
|
|
107
|
+
driver = "postgres"
|
|
108
|
+
server = "analytics.company.com"
|
|
109
|
+
port = 5432
|
|
110
|
+
database = "warehouse"
|
|
111
|
+
user = "${ANALYTICS_DB_USER}"
|
|
112
|
+
password = "${ANALYTICS_DB_PASSWORD}"
|
|
113
|
+
|
|
114
|
+
# SQLite — no credentials needed
|
|
115
|
+
[connections.local]
|
|
116
|
+
driver = "sqlite"
|
|
117
|
+
database = "/Users/me/data/local.db"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Use `${ENV_VAR}` for credentials — never hardcode passwords.
|
|
121
|
+
|
|
122
|
+
Canonical connection names use dot notation: `orders.prod`, `orders.staging`, `analytics`, `local`.
|
|
123
|
+
|
|
124
|
+
### 3. Test connectivity
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
amnesic test # tests all connections
|
|
128
|
+
amnesic test orders.prod # tests one connection
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Add to your AI client
|
|
134
|
+
|
|
135
|
+
### Claude Code
|
|
136
|
+
|
|
137
|
+
Add to `~/.claude/mcp.json`:
|
|
138
|
+
|
|
139
|
+
```json
|
|
140
|
+
{
|
|
141
|
+
"mcpServers": {
|
|
142
|
+
"amnesic": {
|
|
143
|
+
"command": "uvx",
|
|
144
|
+
"args": ["amnesic"]
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Claude Desktop
|
|
151
|
+
|
|
152
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
153
|
+
|
|
154
|
+
```json
|
|
155
|
+
{
|
|
156
|
+
"mcpServers": {
|
|
157
|
+
"amnesic": {
|
|
158
|
+
"command": "uvx",
|
|
159
|
+
"args": ["amnesic"]
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Cursor
|
|
166
|
+
|
|
167
|
+
Add to `.cursor/mcp.json` in your project (or `~/.cursor/mcp.json` globally):
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"mcpServers": {
|
|
172
|
+
"amnesic": {
|
|
173
|
+
"command": "uvx",
|
|
174
|
+
"args": ["amnesic"]
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### VS Code (with MCP extension)
|
|
181
|
+
|
|
182
|
+
Add to `.vscode/mcp.json`:
|
|
183
|
+
|
|
184
|
+
```json
|
|
185
|
+
{
|
|
186
|
+
"servers": {
|
|
187
|
+
"amnesic": {
|
|
188
|
+
"type": "stdio",
|
|
189
|
+
"command": "uvx",
|
|
190
|
+
"args": ["amnesic"]
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Tools
|
|
199
|
+
|
|
200
|
+
| Tool | Description |
|
|
201
|
+
|------|-------------|
|
|
202
|
+
| `db_list_connections()` | List all configured connections (no secrets exposed) |
|
|
203
|
+
| `db_list_tables(connection)` | All known tables with descriptions and column counts |
|
|
204
|
+
| `db_get_schema(table, connection)` | Column schema merged with saved annotations |
|
|
205
|
+
| `db_query(sql, connection)` | Execute a read-only SELECT query |
|
|
206
|
+
| `db_annotate(table, connection, ...)` | Persist semantic annotations for tables/columns |
|
|
207
|
+
| `db_sync_knowledge(from, to)` | Copy annotations between connections (e.g. staging → prod) |
|
|
208
|
+
| `db_discover_relationships(connection)` | Discover all FK relationships from the live DB |
|
|
209
|
+
| `db_get_relationships(table, connection)` | Navigate the FK graph for JOIN planning |
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## The knowledge layer
|
|
214
|
+
|
|
215
|
+
The core differentiator. Every annotation survives restarts, model updates, and new sessions.
|
|
216
|
+
|
|
217
|
+
### Session 1 — you discover something
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
You: What does status=3 mean in the orders table?
|
|
221
|
+
AI: Let me check. [runs db_query: SELECT DISTINCT status FROM dbo.orders]
|
|
222
|
+
I see values 1, 2, 3, 4. Let me look at some examples...
|
|
223
|
+
Based on the data, 3 appears to be "cancelled".
|
|
224
|
+
|
|
225
|
+
You: Save that. And status=1 is "pending", 2 is "confirmed", 4 is "delivered".
|
|
226
|
+
AI: [calls db_annotate]
|
|
227
|
+
db_annotate(
|
|
228
|
+
table="dbo.orders",
|
|
229
|
+
column="status",
|
|
230
|
+
column_description="Order lifecycle status",
|
|
231
|
+
enum_values={"1": "pending", "2": "confirmed", "3": "cancelled", "4": "delivered"}
|
|
232
|
+
)
|
|
233
|
+
Saved. Future sessions will see these labels automatically.
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Session 2 — the knowledge is already there
|
|
237
|
+
|
|
238
|
+
```
|
|
239
|
+
You: How many cancelled orders are there this month?
|
|
240
|
+
AI: [calls db_get_schema("dbo.orders")]
|
|
241
|
+
Schema response includes:
|
|
242
|
+
column: "status"
|
|
243
|
+
description: "Order lifecycle status"
|
|
244
|
+
enum_values: {"1": "pending", "2": "confirmed", "3": "cancelled", "4": "delivered"}
|
|
245
|
+
|
|
246
|
+
[writes correct SQL immediately]
|
|
247
|
+
SELECT COUNT(*) FROM dbo.orders WHERE status = 3 AND ...
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
No re-discovery. No wasted turns. The annotation persisted.
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Relationship graph
|
|
255
|
+
|
|
256
|
+
Understand your schema's JOIN structure once, reuse it forever.
|
|
257
|
+
|
|
258
|
+
```
|
|
259
|
+
AI: [db_discover_relationships(connection="orders.prod")]
|
|
260
|
+
Discovered 47 foreign key relationships.
|
|
261
|
+
|
|
262
|
+
AI: [db_get_relationships(table="orders", depth=2)]
|
|
263
|
+
neighbors:
|
|
264
|
+
orders → users (via user_id → id)
|
|
265
|
+
orders → order_items (via id ← order_id)
|
|
266
|
+
paths:
|
|
267
|
+
orders -> users
|
|
268
|
+
orders -> order_items
|
|
269
|
+
order_items -> products
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Now the AI knows exactly how to JOIN across your schema without guessing.
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## Sync between environments
|
|
277
|
+
|
|
278
|
+
Build up annotations in staging, then promote to prod:
|
|
279
|
+
|
|
280
|
+
```
|
|
281
|
+
db_sync_knowledge(from_connection="orders.staging", to_connection="orders.prod")
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Returns `{synced: [...], skipped: [{table, reason}], warnings: [{table, column, reason}]}`.
|
|
285
|
+
|
|
286
|
+
Tables missing from the target schema cache are skipped with a clear reason. Columns missing from target schema are warned but don't block the rest of the sync.
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## Supported databases
|
|
291
|
+
|
|
292
|
+
| Database | Driver | Extra |
|
|
293
|
+
|----------|--------|-------|
|
|
294
|
+
| PostgreSQL | psycopg2 | `pip install "amnesic[postgres]"` |
|
|
295
|
+
| MySQL / MariaDB | pymysql | `pip install "amnesic[mysql]"` |
|
|
296
|
+
| Microsoft SQL Server | pymssql | `pip install "amnesic[mssql]"` |
|
|
297
|
+
| SQLite | built-in | no extra needed |
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## Security
|
|
302
|
+
|
|
303
|
+
- **Read-only enforcement**: two layers — static SQL analysis rejects any write/DDL statement before a connection opens, plus every query runs inside an immediately-rolled-back transaction.
|
|
304
|
+
- **No credentials in responses**: `db_list_connections` strips passwords and usernames from output.
|
|
305
|
+
- **Credentials via env vars**: `${ENV_VAR}` expansion at load time — secrets never touch the config file on disk.
|
|
306
|
+
- **Identifier validation**: table names, schema names, and database names are validated against `[A-Za-z0-9_]` before any interpolation into SQL.
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## Roadmap
|
|
311
|
+
|
|
312
|
+
What's coming: knowledge lifecycle management (v0.2 — `db_deprecate`, drift detection, export/import for team handoff), query intelligence (v0.3 — `db_explain`, query history), team sharing (v0.4), and more. See [ROADMAP.md](./ROADMAP.md) for the full picture.
|
|
313
|
+
|
|
314
|
+
Have an idea? [Open an issue.](https://github.com/SurajKGoyal/amnesic/issues/new)
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## Track usage
|
|
319
|
+
|
|
320
|
+
[pypistats.org/packages/amnesic](https://pypistats.org/packages/amnesic)
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## License
|
|
325
|
+
|
|
326
|
+
MIT — see [LICENSE](LICENSE).
|
amnesic-0.1.0/README.md
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
# amnesic — the MCP server with the most ironic name in the registry
|
|
2
|
+
|
|
3
|
+
**Persistent semantic memory for your SQL databases. The name is ironic — it remembers everything.**
|
|
4
|
+
|
|
5
|
+
*"The MCP server with the most ironic name in the registry. It's anything but amnesic — it remembers your database so your AI doesn't have to."*
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## The problem
|
|
10
|
+
|
|
11
|
+
Every session with an AI starts cold. You spend the first few minutes re-explaining what tables exist, what a `status` column value of `3` means, which FK connects `orders` to `users`. Then the session ends, and you do it all over again tomorrow.
|
|
12
|
+
|
|
13
|
+
**amnesic fixes this.** It gives your AI a persistent SQLite knowledge store — one per database — that survives across sessions. Annotate a status enum once; every future session sees those labels automatically. Discover FK relationships once; every future JOIN query uses that graph.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Core only (SQLite works out of the box)
|
|
21
|
+
pip install amnesic
|
|
22
|
+
|
|
23
|
+
# With driver extras
|
|
24
|
+
pip install "amnesic[postgres]"
|
|
25
|
+
pip install "amnesic[mssql]"
|
|
26
|
+
pip install "amnesic[mysql]"
|
|
27
|
+
pip install "amnesic[all]"
|
|
28
|
+
|
|
29
|
+
# Or run directly with uvx (no install needed)
|
|
30
|
+
uvx amnesic
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Setup
|
|
36
|
+
|
|
37
|
+
### 1. Create config
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
amnesic init
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
This creates `~/.config/amnesic/connections.toml` with a commented template.
|
|
44
|
+
|
|
45
|
+
### 2. Edit the config
|
|
46
|
+
|
|
47
|
+
```toml
|
|
48
|
+
# ~/.config/amnesic/connections.toml
|
|
49
|
+
|
|
50
|
+
# Nested style: [connections.product.env]
|
|
51
|
+
[connections.orders.prod]
|
|
52
|
+
driver = "mssql"
|
|
53
|
+
server = "localhost"
|
|
54
|
+
port = 11433
|
|
55
|
+
database = "OrdersDB"
|
|
56
|
+
user = "${ORDERS_PROD_USER}"
|
|
57
|
+
password = "${ORDERS_PROD_PASSWORD}"
|
|
58
|
+
tunnel_script = "~/.scripts/mssql-tunnel.sh" # optional SSH tunnel
|
|
59
|
+
|
|
60
|
+
[connections.orders.staging]
|
|
61
|
+
driver = "mssql"
|
|
62
|
+
server = "localhost"
|
|
63
|
+
port = 11434
|
|
64
|
+
database = "OrdersDB_Staging"
|
|
65
|
+
user = "${ORDERS_STAGING_USER}"
|
|
66
|
+
password = "${ORDERS_STAGING_PASSWORD}"
|
|
67
|
+
|
|
68
|
+
# Flat style: [connections.name]
|
|
69
|
+
[connections.analytics]
|
|
70
|
+
driver = "postgres"
|
|
71
|
+
server = "analytics.company.com"
|
|
72
|
+
port = 5432
|
|
73
|
+
database = "warehouse"
|
|
74
|
+
user = "${ANALYTICS_DB_USER}"
|
|
75
|
+
password = "${ANALYTICS_DB_PASSWORD}"
|
|
76
|
+
|
|
77
|
+
# SQLite — no credentials needed
|
|
78
|
+
[connections.local]
|
|
79
|
+
driver = "sqlite"
|
|
80
|
+
database = "/Users/me/data/local.db"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Use `${ENV_VAR}` for credentials — never hardcode passwords.
|
|
84
|
+
|
|
85
|
+
Canonical connection names use dot notation: `orders.prod`, `orders.staging`, `analytics`, `local`.
|
|
86
|
+
|
|
87
|
+
### 3. Test connectivity
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
amnesic test # tests all connections
|
|
91
|
+
amnesic test orders.prod # tests one connection
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Add to your AI client
|
|
97
|
+
|
|
98
|
+
### Claude Code
|
|
99
|
+
|
|
100
|
+
Add to `~/.claude/mcp.json`:
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
{
|
|
104
|
+
"mcpServers": {
|
|
105
|
+
"amnesic": {
|
|
106
|
+
"command": "uvx",
|
|
107
|
+
"args": ["amnesic"]
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Claude Desktop
|
|
114
|
+
|
|
115
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"mcpServers": {
|
|
120
|
+
"amnesic": {
|
|
121
|
+
"command": "uvx",
|
|
122
|
+
"args": ["amnesic"]
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Cursor
|
|
129
|
+
|
|
130
|
+
Add to `.cursor/mcp.json` in your project (or `~/.cursor/mcp.json` globally):
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"mcpServers": {
|
|
135
|
+
"amnesic": {
|
|
136
|
+
"command": "uvx",
|
|
137
|
+
"args": ["amnesic"]
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### VS Code (with MCP extension)
|
|
144
|
+
|
|
145
|
+
Add to `.vscode/mcp.json`:
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
{
|
|
149
|
+
"servers": {
|
|
150
|
+
"amnesic": {
|
|
151
|
+
"type": "stdio",
|
|
152
|
+
"command": "uvx",
|
|
153
|
+
"args": ["amnesic"]
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Tools
|
|
162
|
+
|
|
163
|
+
| Tool | Description |
|
|
164
|
+
|------|-------------|
|
|
165
|
+
| `db_list_connections()` | List all configured connections (no secrets exposed) |
|
|
166
|
+
| `db_list_tables(connection)` | All known tables with descriptions and column counts |
|
|
167
|
+
| `db_get_schema(table, connection)` | Column schema merged with saved annotations |
|
|
168
|
+
| `db_query(sql, connection)` | Execute a read-only SELECT query |
|
|
169
|
+
| `db_annotate(table, connection, ...)` | Persist semantic annotations for tables/columns |
|
|
170
|
+
| `db_sync_knowledge(from, to)` | Copy annotations between connections (e.g. staging → prod) |
|
|
171
|
+
| `db_discover_relationships(connection)` | Discover all FK relationships from the live DB |
|
|
172
|
+
| `db_get_relationships(table, connection)` | Navigate the FK graph for JOIN planning |
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## The knowledge layer
|
|
177
|
+
|
|
178
|
+
The core differentiator. Every annotation survives restarts, model updates, and new sessions.
|
|
179
|
+
|
|
180
|
+
### Session 1 — you discover something
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
You: What does status=3 mean in the orders table?
|
|
184
|
+
AI: Let me check. [runs db_query: SELECT DISTINCT status FROM dbo.orders]
|
|
185
|
+
I see values 1, 2, 3, 4. Let me look at some examples...
|
|
186
|
+
Based on the data, 3 appears to be "cancelled".
|
|
187
|
+
|
|
188
|
+
You: Save that. And status=1 is "pending", 2 is "confirmed", 4 is "delivered".
|
|
189
|
+
AI: [calls db_annotate]
|
|
190
|
+
db_annotate(
|
|
191
|
+
table="dbo.orders",
|
|
192
|
+
column="status",
|
|
193
|
+
column_description="Order lifecycle status",
|
|
194
|
+
enum_values={"1": "pending", "2": "confirmed", "3": "cancelled", "4": "delivered"}
|
|
195
|
+
)
|
|
196
|
+
Saved. Future sessions will see these labels automatically.
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Session 2 — the knowledge is already there
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
You: How many cancelled orders are there this month?
|
|
203
|
+
AI: [calls db_get_schema("dbo.orders")]
|
|
204
|
+
Schema response includes:
|
|
205
|
+
column: "status"
|
|
206
|
+
description: "Order lifecycle status"
|
|
207
|
+
enum_values: {"1": "pending", "2": "confirmed", "3": "cancelled", "4": "delivered"}
|
|
208
|
+
|
|
209
|
+
[writes correct SQL immediately]
|
|
210
|
+
SELECT COUNT(*) FROM dbo.orders WHERE status = 3 AND ...
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
No re-discovery. No wasted turns. The annotation persisted.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Relationship graph
|
|
218
|
+
|
|
219
|
+
Understand your schema's JOIN structure once, reuse it forever.
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
AI: [db_discover_relationships(connection="orders.prod")]
|
|
223
|
+
Discovered 47 foreign key relationships.
|
|
224
|
+
|
|
225
|
+
AI: [db_get_relationships(table="orders", depth=2)]
|
|
226
|
+
neighbors:
|
|
227
|
+
orders → users (via user_id → id)
|
|
228
|
+
orders → order_items (via id ← order_id)
|
|
229
|
+
paths:
|
|
230
|
+
orders -> users
|
|
231
|
+
orders -> order_items
|
|
232
|
+
order_items -> products
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Now the AI knows exactly how to JOIN across your schema without guessing.
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Sync between environments
|
|
240
|
+
|
|
241
|
+
Build up annotations in staging, then promote to prod:
|
|
242
|
+
|
|
243
|
+
```
|
|
244
|
+
db_sync_knowledge(from_connection="orders.staging", to_connection="orders.prod")
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Returns `{synced: [...], skipped: [{table, reason}], warnings: [{table, column, reason}]}`.
|
|
248
|
+
|
|
249
|
+
Tables missing from the target schema cache are skipped with a clear reason. Columns missing from target schema are warned but don't block the rest of the sync.
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Supported databases
|
|
254
|
+
|
|
255
|
+
| Database | Driver | Extra |
|
|
256
|
+
|----------|--------|-------|
|
|
257
|
+
| PostgreSQL | psycopg2 | `pip install "amnesic[postgres]"` |
|
|
258
|
+
| MySQL / MariaDB | pymysql | `pip install "amnesic[mysql]"` |
|
|
259
|
+
| Microsoft SQL Server | pymssql | `pip install "amnesic[mssql]"` |
|
|
260
|
+
| SQLite | built-in | no extra needed |
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Security
|
|
265
|
+
|
|
266
|
+
- **Read-only enforcement**: two layers — static SQL analysis rejects any write/DDL statement before a connection opens, plus every query runs inside an immediately-rolled-back transaction.
|
|
267
|
+
- **No credentials in responses**: `db_list_connections` strips passwords and usernames from output.
|
|
268
|
+
- **Credentials via env vars**: `${ENV_VAR}` expansion at load time — secrets never touch the config file on disk.
|
|
269
|
+
- **Identifier validation**: table names, schema names, and database names are validated against `[A-Za-z0-9_]` before any interpolation into SQL.
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Roadmap
|
|
274
|
+
|
|
275
|
+
What's coming: knowledge lifecycle management (v0.2 — `db_deprecate`, drift detection, export/import for team handoff), query intelligence (v0.3 — `db_explain`, query history), team sharing (v0.4), and more. See [ROADMAP.md](./ROADMAP.md) for the full picture.
|
|
276
|
+
|
|
277
|
+
Have an idea? [Open an issue.](https://github.com/SurajKGoyal/amnesic/issues/new)
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Track usage
|
|
282
|
+
|
|
283
|
+
[pypistats.org/packages/amnesic](https://pypistats.org/packages/amnesic)
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## License
|
|
288
|
+
|
|
289
|
+
MIT — see [LICENSE](LICENSE).
|