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.
@@ -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.
@@ -0,0 +1,12 @@
1
+ .venv/
2
+ venv/
3
+ __pycache__/
4
+ *.pyc
5
+ *.egg-info/
6
+ dist/
7
+ build/
8
+ .env
9
+ .pytest_cache/
10
+ .coverage
11
+ htmlcov/
12
+ .DS_Store
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).
@@ -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).