Persistent semantic memory for SQL databases that survives across AI sessions. Point it at Postgres, MySQL, MSSQL, or SQLite and annotate tables, columns, or enums once. Those annotations live in a local SQLite knowledge store that every future conversation can read. The irony in the name is intentional: it's read-only by design with two enforcement layers (static SQL analysis plus transaction rollback), so you can safely aim it at production. Ships with an interactive setup wizard that tests connections before saving credentials. Install via pipx, run the init wizard, add the server block to your MCP client config, and your AI stops asking what a status code means every single session.
Persistent semantic memory for your SQL databases. The name is ironic — it remembers everything.
"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."
Works with Claude Code · Claude Desktop · Cursor · VS Code · Cline · Windsurf — any MCP-compatible client.
Available on Official MCP Registry · Claude Code plugin marketplace
🔒 Read-only by design. amnesic refuses to execute
INSERT,UPDATE,DELETE,DROP,TRUNCATE,ALTER,CREATE,EXEC,MERGE,GRANT,REVOKE— and any write statement smuggled inside aWITHCTE. Two layers of defense: static SQL analysis rejects the statement before connecting, and every query runs inside a transaction that is immediately rolled back. Safe to point at prod. Details ↓
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.
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.
pipx install amnesic # install the core
amnesic init # interactive wizard
⚡ Try it without credentials. Run
amnesic init --demoinstead — it adds a self-contained SQLite sample DB (e-commerce schema: customers / products / orders with FKs and an enum column) so you can exercise every tool in under a minute. Great for a first look before pointing amnesic at a real database.
The wizard asks which database type you're connecting to and tells you the one command to run if its driver isn't installed yet — you never need to guess extras up front.
The wizard:
~/.config/amnesic/.env (chmod 600)~/.config/amnesic/connections.tomlThen add amnesic to your AI client and restart.
pipx? Or prefer uv / plain pip?Install pipx (one-time):
brew install pipx # macOS
sudo apt install pipx # Linux (Debian/Ubuntu)
python -m pip install --user pipx # Windows / generic
Or use uv (single-binary alternative — fast, no Python required):
brew install uv # macOS
curl -LsSf https://astral.sh/uv/install.sh | sh # Linux / macOS
powershell -c "irm https://astral.sh/uv/install.ps1 | iex" # Windows
uv tool install amnesic
Or plain pip (installs into your active Python env):
pip install amnesic
Whichever you pick,
amnesic initasks which database you'll connect to and prints the one extra command to install that driver — no need to commit to extras up front.
After install, amnesic --help works from any terminal.
| File | macOS / Linux | Windows |
|---|---|---|
| Config | ~/.config/amnesic/connections.toml | %APPDATA%\amnesic\connections.toml |
| Secrets | ~/.config/amnesic/.env (chmod 600) | %APPDATA%\amnesic\.env (user profile ACL) |
| Knowledge | ~/.config/amnesic/knowledge_<name>.db | %APPDATA%\amnesic\knowledge_<name>.db |
Set $AMNESIC_HOME (or $XDG_CONFIG_HOME on Linux) to override the location.
amnesic add # add another connection to existing config
amnesic test # verify all connections
amnesic test orders.prod # verify one connection
amnesic init and amnesic add save your password automatically — for the typical setup flow, you never need to think about this section.
Use set-secret when you need to change a stored password later — IT rotated it, you mistyped it during setup, or you're hand-editing the config.
$ amnesic set-secret ORDERS_PROD_PASSWORD
Value: **** ← hidden input (your typing is invisible)
Confirm: ****
✓ Set ORDERS_PROD_PASSWORD in ~/.config/amnesic/.env
What's the variable name? It's the env var your connections.toml references for that connection's password. The wizard auto-generates these as <CONNECTION_NAME_UPPERCASE_WITH_UNDERSCORES>_PASSWORD:
| Connection name | Generated env var |
|---|---|
orders.prod | ORDERS_PROD_PASSWORD |
analytics | ANALYTICS_PASSWORD |
drive.staging | DRIVE_STAGING_PASSWORD |
To see the exact name your config uses, check ~/.config/amnesic/connections.toml — anything inside ${...} is the variable to pass to set-secret.
Under the hood: writes (or replaces) the line in ~/.config/amnesic/.env, sets file permission to chmod 600 (only your user can read it), preserves all other entries.
Knowledge accumulates per connection in a local SQLite file. These commands let you move it between machines and clean up:
# Hand off everything you've taught amnesic about a database (annotations +
# relationships, not the re-derivable schema cache) as portable JSON:
amnesic export orders.prod -o orders-knowledge.json
amnesic export orders.prod # or print to stdout to pipe/redirect
# Load that knowledge into another connection (e.g. promote staging → prod,
# or onboard a teammate). Unconditional upsert — existing entries are overwritten:
amnesic import orders.prod orders-knowledge.json
# Wipe stored knowledge for a connection but keep the config entry:
amnesic clear orders.staging
# Drop a connection from connections.toml entirely (knowledge file kept
# unless you pass --delete-knowledge):
amnesic remove old.connection
amnesic remove old.connection --delete-knowledge
export/import/clear/remove operate purely on local files — they never connect to the database, so they work even if a connection's credentials aren't set. remove edits connections.toml with surgical string edits, leaving every other block's formatting and comments byte-for-byte intact.
Once amnesic is installed with the right driver extras (see Quickstart), the amnesic command is on your PATH. Use the same snippet across every MCP client:
One-line install (recommended — no JSON editing). Inside Claude Code:
/plugin marketplace add SurajKGoyal/amnesic-marketplace
/plugin install amnesic@amnesic
That wires amnesic as an MCP server automatically. Source: SurajKGoyal/amnesic-marketplace.
~/.claude/mcp.json{
"mcpServers": {
"amnesic": {
"command": "amnesic"
}
}
}
Add to your platform's Claude Desktop config:
~/Library/Application Support/Claude/claude_desktop_config.json%APPDATA%\Claude\claude_desktop_config.json~/.config/Claude/claude_desktop_config.json{
"mcpServers": {
"amnesic": {
"command": "amnesic"
}
}
}
One-click install — click the button below and Cursor wires it up for you:
.cursor/mcp.jsonAdd to .cursor/mcp.json in your project (or ~/.cursor/mcp.json globally):
{
"mcpServers": {
"amnesic": {
"command": "amnesic"
}
}
}
If you'd rather not install amnesic on your system, use uvx or pipx to fetch it each time the MCP client starts. Note the driver extras must be passed explicitly:
// uvx — requires `uv` installed (see Install section for per-OS instructions)
{
"mcpServers": {
"amnesic": {
"command": "uvx",
"args": ["--from", "amnesic[mssql]", "amnesic"]
}
}
}
// pipx — usually pre-installed via Homebrew or system package manager
{
"mcpServers": {
"amnesic": {
"command": "pipx",
"args": ["run", "--spec", "amnesic[mssql]", "amnesic"]
}
}
}
For multiple drivers, comma-separate inside the brackets — e.g. amnesic[postgres,mssql] or use amnesic[all] for everything.
Add to .vscode/mcp.json:
{
"servers": {
"amnesic": {
"type": "stdio",
"command": "amnesic"
}
}
}
amnesic ships often. Upgrade with the same tool you installed it with:
| Installed via | Upgrade command |
|---|---|
pipx | pipx upgrade amnesic |
uv tool | uv tool upgrade amnesic |
pip | pip install --upgrade amnesic |
uvx (ephemeral, in your MCP config) | uvx caches builds — run uv cache clean amnesic to pull the newest |
Then restart your MCP client (Claude Code, Cursor, …) so it relaunches the amnesic server and picks up any new tools.
Upgrading is safe — you won't lose annotations. Your knowledge files auto-migrate to the new schema on first load; amnesic only ever adds columns, never drops your data.
To check the installed version: amnesic --version. Latest release: PyPI · Releases.
| Tool | Description |
|---|---|
db_list_connections() | List all configured connections (no secrets exposed) |
db_list_tables(connection) | All known tables with descriptions and column counts |
db_search(query, connection, target, limit) | BM25 search over table/column descriptions and aliases |
db_get_schema(table, connection) | Column schema merged with saved annotations |
db_query(sql, connection) | Execute a read-only SELECT query |
db_annotate(table, connection, ...) | Persist semantic annotations for tables/columns |
db_deprecate(table, connection, column?, reason?, undo?) | Soft-retire a stale annotation — flagged (and warned) but kept, reversible |
db_detect_drift(connection) | Audit annotations vs the live schema — find orphaned annotations + undocumented tables |
db_forget(table, connection, column?, cascade?) | Hard-delete an annotation (cascade opt-in) — permanent |
db_sync_knowledge(from, to) | Copy annotations between connections (e.g. staging → prod) |
db_discover_relationships(connection) | Discover all FK relationships from the live DB |
db_get_relationships(table, connection) | Navigate the FK graph for JOIN planning |
For large schemas, db_list_tables is impractical — you'd dump 500+ rows into Claude's context. Use db_search to find the relevant tables/columns by keyword instead:
"What table tracks customer payments?"
→ db_search("payments")
Top results:
- dbo.payments (table) "Customer payment records..."
- dbo.orders.payment_method (column) "Mode of payment..."
db_search uses SQLite FTS5 with BM25 ranking — fast, local, no embeddings or external services. Search syntax supports:
| Syntax | Effect |
|---|---|
payment | Match the word (with stemming — also matches "payments", "paying") |
"payment method" | Exact phrase |
pay* | Prefix match — "payment", "payable", etc. |
payment AND status | Both terms required |
payment OR refund | Either term |
Results return ranked table/column rows with descriptions and highlighted snippets.
The core differentiator. Every annotation survives restarts, model updates, and new sessions.
You: What does status=3 mean in the orders table?
AI: Let me check. [runs db_query: SELECT DISTINCT status FROM dbo.orders]
I see values 1, 2, 3, 4. Let me look at some examples...
Based on the data, 3 appears to be "cancelled".
You: Save that. And status=1 is "pending", 2 is "confirmed", 4 is "delivered".
AI: [calls db_annotate]
db_annotate(
table="dbo.orders",
column="status",
column_description="Order lifecycle status",
enum_values={"1": "pending", "2": "confirmed", "3": "cancelled", "4": "delivered"}
)
Saved. Future sessions will see these labels automatically.
You: How many cancelled orders are there this month?
AI: [calls db_get_schema("dbo.orders")]
Schema response includes:
column: "status"
description: "Order lifecycle status"
enum_values: {"1": "pending", "2": "confirmed", "3": "cancelled", "4": "delivered"}
[writes correct SQL immediately]
SELECT COUNT(*) FROM dbo.orders WHERE status = 3 AND ...
No re-discovery. No wasted turns. The annotation persisted.
Understand your schema's JOIN structure once, reuse it forever.
AI: [db_discover_relationships(connection="orders.prod")]
Discovered 47 foreign key relationships.
AI: [db_get_relationships(table="orders", depth=2)]
neighbors:
orders → users (via user_id → id)
orders → order_items (via id ← order_id)
paths:
orders -> users
orders -> order_items
order_items -> products
Now the AI knows exactly how to JOIN across your schema without guessing.
Build up annotations in staging, then promote to prod:
db_sync_knowledge(from_connection="orders.staging", to_connection="orders.prod")
Returns {synced: [...], skipped: [{table, reason}], warnings: [{table, column, reason}]}.
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.
If you prefer to manage the config file yourself, generate a blank template:
amnesic init --template
This writes ~/.config/amnesic/connections.toml with commented examples and exits — no wizard. Edit the file directly:
# ~/.config/amnesic/connections.toml
# Nested style: [connections.product.env]
[connections.orders.prod]
driver = "mssql"
server = "localhost"
port = 11433
database = "OrdersDB"
user = "${ORDERS_USER}"
password = "${ORDERS_PROD_PASSWORD}"
tunnel_script = "~/.scripts/mssql-tunnel.sh" # macOS / Linux (bash)
# tunnel_script = "C:/scripts/mssql-tunnel.ps1" # Windows (PowerShell)
[connections.orders.staging]
driver = "mssql"
server = "localhost"
port = 11434
database = "OrdersDB_Staging"
user = "${ORDERS_USER}"
password = "${ORDERS_STAGING_PASSWORD}"
# Flat style: [connections.name]
[connections.analytics]
driver = "postgres"
server = "analytics.company.com"
port = 5432
database = "warehouse"
user = "${ANALYTICS_DB_USER}"
password = "${ANALYTICS_DB_PASSWORD}"
# SQLite — no credentials needed
[connections.local]
driver = "sqlite"
database = "/absolute/path/to/local.db" # macOS / Linux
# database = "C:/path/to/local.db" # Windows (use forward slashes)
Use ${ENV_VAR} for credentials — never hardcode passwords.
Secrets are loaded from ~/.config/amnesic/.env automatically (format: KEY=VALUE, one per line, # for comments). For each ${VAR_NAME} referenced in your TOML, populate the matching .env entry with amnesic set-secret VAR_NAME (hidden input, chmod 600), or write .env yourself.
Canonical connection names use dot notation: orders.prod, orders.staging, analytics, local.
| Database | Python driver | Installed by |
|---|---|---|
| PostgreSQL | psycopg2-binary | wizard nudge when you pick Postgres, or amnesic[postgres] extras |
| MySQL / MariaDB | pymysql | wizard nudge when you pick MySQL, or amnesic[mysql] extras |
| Microsoft SQL Server | pymssql | wizard nudge when you pick MSSQL, or amnesic[mssql] extras |
| SQLite | stdlib sqlite3 | always available — no extra |
amnesic is built to be safe to point at production databases.
Every query passes through two independent layers before reaching the database:
amnesic/readonly.py) — the SQL is tokenized and rejected if it contains any of:
INSERT, UPDATE, DELETE, DROP, TRUNCATE, ALTER, CREATE, EXEC, EXECUTE, MERGE, BULK, GRANT, REVOKE, DENY.
This includes write statements smuggled inside CTEs (WITH x AS (SELECT ...) UPDATE ... is caught and refused).BEGIN TRANSACTION ... ROLLBACK so nothing is ever committed. Belt and suspenders.Only SELECT and WITH ... SELECT reach the database. Comments are stripped before analysis so /* DELETE FROM users */ can't be used to hide an attack.
db_list_connections strips passwords and usernames from its output. The AI can see which connections exist, never how to authenticate to them.${ENV_VAR} expansion at config-load time — passwords never touch connections.toml on disk..env storage: on macOS/Linux chmod 0o600 (owner read/write only); on Windows the .env lives in %APPDATA% which is restricted to your user profile by Windows ACL.[A-Za-z0-9_]+ before any string interpolation into SQL.tests/test_readonly.py cover every write keyword, comment-stripping edge case, CTE-with-write attempts, semicolon-separated multi-statements, and identifier injection attempts. pytest tests/test_readonly.py to verify on your machine.amnesic is local-only and protocol-only. It doesn't introduce a new external trust boundary — the trust boundary is wherever your MCP client sends data, not amnesic itself. Choosing the AI client decides the policy that applies to your rows.
your DB → amnesic (local) → MCP client → your AI deployment
↑ trust boundary lives here
The honest question to ask, whether you're indie or enterprise:
Do I trust my AI client with the data in this database?
If yes — and for most setups, the answer is yes — you're good. That covers:
A property of the design, not an afterthought: the annotation layer means the AI answers most schema questions from a local SQLite knowledge file — no db_query runs, no row data is sent anywhere.
created_at column?" → resolves from schema cacheFor purely structural exploration, six tools never touch your data: db_list_tables, db_get_schema, db_search, db_annotate, db_discover_relationships, db_get_relationships. They return metadata only.
That's measurably less data movement than a "naked" SQL MCP — which has to run SELECT DISTINCT status FROM orders every time the AI is confused about an enum. amnesic answers it once from local annotations.
Disclaimer: amnesic is provided as-is under the MIT License (no warranty, no liability — see LICENSE). This section is not legal or compliance advice. Your use of amnesic, and the AI client you connect it to, is your responsibility. If you handle regulated data, consult your security / compliance team before pointing it at production.
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 for the full picture.
Have an idea? Open an issue.
pypistats.org/packages/amnesic
MIT — see LICENSE.
This server is registered on the official MCP Registry.
mcp-name: io.github.SurajKGoyal/amnesic
hovecapital/read-only-local-postgres-mcp-server
cocaxcode/database-mcp
io.github.infoinlet-marketplace/mcp-mysql
io.github.cybeleri/database-admin
io.github.yash-0620/postgres-mcp-secured