SQLite Memory
This server gives coding agents persistent, append-only memory with SQL as the tool, plus safe querying, schema management, and audit logging.
Run read-only SQL queries (SELECT, EXPLAIN, PRAGMA) with row/byte/time caps and CSV output.
Execute data modifications and additive schema changes (INSERT, UPDATE, CREATE TABLE/INDEX/VIRTUAL TABLE).
Perform destructive operations (DELETE, DROP, ALTER, triggers) with mandatory reason and automatic snapshot.
List tables/views, describe columns/foreign keys/indexes, and retrieve full schema.
Create reusable named views with descriptions, effectively storing query skills.
Append hash-chained, append-only memory events for decisions and observations.
Verify memory chain integrity, write checkpoints, and get resume context (checkpoint + recent events).
Search text within table columns using ripgrep regex, with SQL filtering and context snippets.
Every call is audit-logged, including failures, for debugging and schema improvement.
Safety rails: one statement per call, protected system tables, row-update guards, read-only connections, and parameterized SQL.
Turns a single SQLite database file into the agent's primary tool: tools for read-only queries, guarded writes, destructive statements with automatic snapshots, schema introspection, and creating reusable views. It also layers an append-only, hash-chained memory log and checkpoint system on top for cross-session state, plus ripgrep-backed text search over table columns.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@SQLite Memory what was I working on last time?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-sqlite-memory
A SQLite MCP server for coding agents, built on one idea: hand the agent SQL as its primary tool and put the safety in the server, not in the prompt.
SQL is the tool. Thirteen tools, all thin wrappers around one database file.
Views are reusable skills.
create_viewstores a SELECT with a description so the next session finds it inget_schemainstead of rediscovering the join.CSV, not JSON. Results come back as CSV with a
# N rowstrailer, roughly half the tokens.Memory the agent cannot rewrite. An append-only, hash-chained event log plus checkpoints, with the hashes computed by the server.
Retrieved data is untrusted. The server never builds SQL from row values, its own tables cannot be modified through any tool, and the connect-time instructions tell the agent that rows are data, not instructions.
Every call is audit-logged, including the ones that failed.
Requires Python 3.12+, uv and, for search_text,
ripgrep on PATH.
Configure it in Claude Code
Setting this up specifically to back the second-brain
plugin? Its own README's "Install" section has the exact recipe, including the plugin's required
flags and the matching .claude/settings.json; the instructions below are for using this server
on its own, or as a starting point for a different consumer.
From the terminal
From the published repository, pinned to a release tag:
claude mcp add sqlite-memory --scope user -- uvx --from git+https://github.com/Diankes/mcp-sqlite-memory@v0.3.0 mcp-sqlite-memory --db F:\data\memory.dbFrom a local clone while developing:
claude mcp add sqlite-memory --scope user -- uv --directory F:\mcp-sqlite-memory run mcp-sqlite-memory --db F:\data\memory.dbPin the tag. @v0.3.0 installs exactly the commit that was tested and released. A bare
repository URL tracks whatever is on main, so a fresh install can pick up an unreviewed
commit, and two machines set up a week apart can run different code under the same name.
Worse, uvx caches the environment it built, so tracking main does not even update
predictably: you get whichever commit was current the last time uv resolved the source, and a
different one after a cache refresh. Pin a tag, and move to a newer tag on purpose.
--db is the only required option; the file and its parent folder are created on first start and
the three server tables are bootstrapped automatically. Every flag can also be given as an
environment variable, for example
claude mcp add sqlite-memory -e MCP_SQLITE_DB=F:\data\memory.db -- uvx ....
uv and uvx are native executables, so no cmd /c wrapper is needed on Windows.
Using this with Claude Code in VS Code
The Claude Code extension has an "Add MCP server" form. For this server the Transport is always Local command (stdio), and the Arguments box takes one argument per line. There are two ways to fill it in.
1. From the published repository (recommended once a version is tagged)
Field | Value |
Name |
|
Transport | Local command (stdio) |
Command |
|
Arguments | see below, one per line |
Environment variables | leave empty |
Scope | Local |
--from
git+https://github.com/Diankes/mcp-sqlite-memory@v0.3.0
mcp-sqlite-memory
--db
F:\data\my-project-memory.dbThe same tag-pinning advice applies here: @v0.3.0 is the tested release, a bare
git+https://github.com/Diankes/mcp-sqlite-memory line silently tracks main.
Scope Local keeps the entry private to you and to the project you have open, which is what
makes the database per project: open another project, add the server again with a different
--db path, and each project gets its own memory and audit log. Choose User instead if you
want one shared database across every project, with one --db path. Project writes the
entry to .mcp.json in the repository, which is checked into git, so the --db path then has to
be valid on every machine that clones it.
2. From a local clone (while developing the server itself)
Field | Value |
Name |
|
Transport | Local command (stdio) |
Command |
|
Arguments | see below, one per line |
Environment variables | leave empty |
Scope | Local |
--directory
F:\mcp-sqlite-memory
run
mcp-sqlite-memory
--db
F:\data\my-project-memory.db--db must be an absolute path in this mode. --directory tells uv to change into the
server's own folder before running, so a relative path such as memory.db or data\memory.db
resolves inside the clone (F:\mcp-sqlite-memory\memory.db), not inside the project you have
open in VS Code. Nothing warns you: the server creates an empty database at the wrong location,
the clone's .gitignore hides *.db files, and your project appears to have no memory at all.
Either way, the two --db lines can be replaced by one line MCP_SQLITE_DB=F:\data\my-project-memory.db
in the Environment variables box.
Recommended permissions
Tools appear to Claude Code as mcp__sqlite-memory__<tool>. The one lock the server cannot
provide is the permission prompt, so allow the twelve ordinary tools and leave
destructive_query on ask. Paste this into .claude/settings.json in the project, or into
~/.claude/settings.json for every project:
{
"permissions": {
"allow": [
"mcp__sqlite-memory__read_query",
"mcp__sqlite-memory__write_query",
"mcp__sqlite-memory__list_tables",
"mcp__sqlite-memory__describe_table",
"mcp__sqlite-memory__get_schema",
"mcp__sqlite-memory__create_view",
"mcp__sqlite-memory__append_event",
"mcp__sqlite-memory__snapshot",
"mcp__sqlite-memory__verify_chain",
"mcp__sqlite-memory__checkpoint",
"mcp__sqlite-memory__get_resume_context",
"mcp__sqlite-memory__search_text"
],
"ask": ["mcp__sqlite-memory__destructive_query"]
}
}If another tool needs to read this database directly
This server has no way to publish the --db path it was registered with ā nothing else on the
machine can discover it automatically. A plugin such as
second-brain, which opens its own direct, read-only
connection to the same file (for validation, cadence checks and rules injection, without going
through the server) to work at all, needs to be told that same path a second time, independently
ā typically as its own MCP_SQLITE_DB environment variable in the consuming project's
.claude/settings.json. Nothing here checks that the two agree: if you move the database or
change one registration and not the other, each side keeps working against its own path,
silently. See second-brain's README, "Where things live, and the one path that must match," for
the full picture and a worked example.
Related MCP server: Axom MCP Server
Tools
Tool | What it does | Returns |
| One SELECT, WITH ... SELECT, EXPLAIN or introspection PRAGMA. Read-only connection, wall-clock timeout, row and byte caps. | CSV plus a |
| One INSERT, UPDATE, upsert, REPLACE, CREATE TABLE, CREATE INDEX or CREATE VIRTUAL TABLE (fts5, rtree). An UPDATE touching more than |
|
| One DELETE, DROP, ALTER TABLE, CREATE/DROP TRIGGER or unfiltered UPDATE. Snapshots the database file first. |
|
| Tables and views, with a | CSV: |
| Columns, foreign keys and indexes of a table or view. | CSV sections |
| Every CREATE statement as stored, view descriptions included. | SQL text |
| Save a SELECT as a named view with a description stored inside it. |
|
| Append to the hash-chained memory log. |
|
| On-demand snapshot of the database file, same online backup API as |
|
| Recompute the whole chain. |
|
| Store a state summary anchored to the current chain head. |
|
| Latest checkpoint, events since it, chain status. Call it first in a new session. | Text block plus CSV |
| SQL narrows the rows, ripgrep matches the pattern over the selected columns. Newlines inside a value appear as | CSV: |
Errors come back verbatim as tool errors (no such table: nope, DROP TABLE is not allowed in write_query; use destructive_query, ...) so the agent can fix the statement and retry.
Memory layer
Three tables are created on first start:
memory_events(id, ts, session, kind, content, prev_hash, hash): append-only. The server computeshash = sha256([id, ts, session, kind, content, prev_hash])and links every row to the one before it; the first row links to 64 zeros.sessionis a per-server-run id, stamped automatically.memory_checkpoints(id, ts, session, summary, last_event_id, head_hash): append-only. A checkpoint points at the chain head it was written against.query_log(id, ts, session, tool, query, ok, rows, error, duration_ms, detail): every tool call, success or failure. The built-in viewquery_errorslists the failures newest first.
get_resume_context verifies the chain on every call, so tampering is noticed at the moment it
matters. Be clear about what that buys on a single-user machine: the chain is tamper-evident, not
tamper-proof. Anyone with the sqlite3 CLI can rewrite it and recompute the hashes. What actually
stops the tools from editing history is the authorizer policy plus BEFORE UPDATE / BEFORE DELETE
triggers on all three tables.
Safety rails
All of these live in the server; none depend on the agent behaving.
Parse-time statement policy. SQLite's authorizer callback classifies every action a statement performs while it is compiled, so comments, CTEs and casing cannot disguise a DROP. Read tools allow SELECT, READ and introspection PRAGMAs only;
write_queryadds INSERT, UPDATE and additive DDL;destructive_queryadds DELETE, DROP, ALTER and triggers. ATTACH, transaction control, PRAGMA writes, temporary objects andload_extensionare refused everywhere.Protected tables. No tool can insert into, update, delete from, drop, alter or attach a trigger to the three server tables, and triggers enforce append-only even for direct connections.
Read-only connections for the read tools (
PRAGMA query_only) and SQLite's defensive mode on every agent connection, as backstops behind the authorizer.Wall-clock timeout per call through SQLite's progress handler; a runaway query is interrupted and reported. The ripgrep subprocess gets the same budget.
Three independent result caps: rows (fetched as
limit + 1, never by rewriting your SQL), total bytes, and characters per cell.UPDATE rowcount guard in
write_query: a forgotten WHERE clause is rolled back instead of rewriting a table.Snapshots before every
destructive_query, and on demand through thesnapshottool, made with SQLite's online backup API and kept in<db>.snapshots/next to the database (or under--snapshot-dir, for example a removable drive), newest five by default. A statement that fails syntax or policy checks costs no snapshot, and a snapshot target that cannot be reached is skipped with a warning rather than failing the call.One statement per call, enforced by Python's sqlite3 module.
Parameterized SQL wherever the server builds statements; identifiers are validated and quoted.
Audit log written in a
finallyblock on its own connection, so an errored call still gets a row.
Configuration
Flag | Environment variable | Default |
|
| required |
|
| 10 |
|
| 100 |
|
| 1000 |
|
| 32768 |
|
| 2000 |
|
| 500 |
|
| 5000 |
|
| 5 (0 disables) |
|
|
|
|
|
|
|
| off |
Long-form notes. The defaults suit tabular data. If the database holds prose with display
math, such as a study log written through the memory tools, raise the two read caps so entries
come back whole: --max-cell-chars 8000 --max-result-bytes 131072. Multi-line cells are valid
CSV and are returned as written; nothing is escaped on the read path.
Restoring a snapshot
Snapshots are plain SQLite files. Stop the server (quit the Claude Code session that started it), copy the snapshot over the database file, start again. There is deliberately no restore tool: swapping the file under an open connection is not safe.
Closing the loop
The audit log is where the schema gets better. SELECT * FROM query_errors LIMIT 20 shows which
statements keep failing; a repeated join becomes a create_view; a column the agent keeps
misreading gets a clearer name. The server's own query_errors view is the first example of the
pattern.
Development
uv sync --all-groups
uv run pytest
uv run ruff check src tests
uv run ruff format src testsTests run the server in-process through mcp.client.Client for the tool surface, and drive the
policy, chain, snapshot and search code directly. The search tests skip when ripgrep is absent.
License
MIT, see LICENSE.
Available Tools
12 toolsappend_eventA
Append one entry to the memory log. The log is append-only and hash-chained by the server, so history cannot be rewritten through SQL. Use it for decisions, results, errors and observations worth remembering across sessions.
Inputs: content (required), kind.
checkpointA
Write a summary of the current state to memory_checkpoints, anchored to the latest event. Do this at milestones and before context runs out; get_resume_context returns the latest one.
Inputs: summary (required).
create_viewA
Save a SELECT as a named view: a reusable query skill. Give it a description so future sessions know what it is for; it is stored inside the view and shown by get_schema. With replace=true an existing view is redefined and its previous definition is kept in query_log.
Inputs: name (required), select_sql (required), replace, description.
describe_tableARead-onlyIdempotent
Columns (cid, name, type, notnull, default, pk), foreign keys and indexes of a table or view, as CSV sections.
Inputs: table (required).
destructive_queryADestructive
Run one destructive statement. A snapshot of the database file is written first (the newest few are kept next to the database), then the statement runs. Requires a short reason, which lands in the audit log. Never use this when write_query would do.
Inputs: query (required), reason (required).
get_resume_contextARead-onlyIdempotent
Call this first in a new session or after context compaction. Returns the latest checkpoint, the events appended since it (oldest first, most recent max_events), and the result of verifying the hash chain.
Inputs: max_events.
get_schemaARead-onlyIdempotent
The CREATE statements of every table, index, view and trigger, exactly as stored. View descriptions appear as '-- description:' comments. The server's own tables are hidden unless include_system is true.
Inputs: include_system.
list_tablesBRead-onlyIdempotent
List tables and views as CSV: name, type, system (1 for the server's own memory and audit tables) and row count (tables only).
Inputs: include_row_counts.
read_queryARead-onlyIdempotent
Run one read-only SQL statement and get CSV back: a header row, data rows, then a '# N rows' trailer that says whether the result was truncated. Time-limited and capped in rows and bytes. Select only the columns you need and keep limit small: results cost context.
Inputs: query (required), limit.
search_textARead-onlyIdempotent
Regex search inside column values in one call: the WHERE filter narrows rows in SQL, then ripgrep matches the pattern over the selected columns. Returns CSV rows of key, column, snippet, one per matching value, plus a trailer with the totals.
Inputs: table (required), pattern (required), limit, where, +5 more.
verify_chainARead-onlyIdempotent
Recompute the memory_events hash chain and report whether it is intact, with the first event id where it breaks if it is not.
Inputs: no named parameters.
write_queryA
Run one data or additive-schema statement and return the affected row count. Refuses DELETE, DROP, ALTER and triggers: those need destructive_query. An UPDATE that touches more rows than the configured cap is rolled back, so a forgotten WHERE clause cannot rewrite a whole table by accident. RETURNING clauses come back as CSV.
Inputs: query (required).
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelogā¦
TDQS
Scored across 12 tools
Each tool has a clearly distinct role: the three query tools are separated by intent (read/write/destructive), and the introspection trio (list_tables, describe_table, get_schema) differs by granularity. Memory tools (checkpoint, append_event, get_resume_context, verify_chain) are distinguishable by write-vs-read purpose.
Most names follow a verb_noun pattern (read_query, write_query, list_tables, describe_table, get_schema, create_view, append_event, verify_chain, search_text). Exceptions are 'checkpoint' (bare noun) and 'destructive_query' (adjective_noun), but both remain readable and predictable.
12 tools is well-scoped for a SQLite memory store that also exposes generic DB operations. Every tool earns its place with no redundancy.
Full lifecycle coverage: read/write/destructive queries, schema introspection, view creation, plus a hash-chained memory log with append, checkpoint, resume, and verification. search_text adds value beyond SQL, and destructive_query covers drops/alters, leaving no obvious gaps.
Maintenance
Related MCP Connectors
Context and control for any AI. Agent identity, scoped access, and proof across tools.
Hosted agent memory with provenance, contradiction surfacing, and snapshot rollback.
Your agents' personal cloud database: records and logs every agent you connect can save and read.
The all-in-one data stack for agents. Upload files, run SQL, evolve tables, and render charts.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceProvides comprehensive SQLite database interaction for AI agents, including data manipulation, schema inspection, and automated query logging. It features a unique context preservation pattern that uses a dedicated meta-table to help autonomous agents maintain self-documenting database architectures.26 npm1MIT
- AlicenseAqualityDmaintenanceProvides persistent SQLite-based memory and unified tool abstraction for AI agents to support long-term context and complex tool chaining. It enables automated code analysis, file operations, and environment discovery through a standardized interface.5MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to safely work with SQLite databases by enforcing read/write separation, dry-run writes with confirmation, automatic backups, and an audit trail.MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to safely query and explore SQLite databases through read-only, guard-protected tools that block writes, sensitive table access, and runaway queries.1MIT