SQLite Access Control
БесплатноНе проверенQuery SQLite databases with fine-grained column-level access control in Rust.
Описание
Query SQLite databases with fine-grained column-level access control in Rust.
README
Why this exists
Every existing MCP server for SQLite falls short in at least one critical way. The official reference server from Anthropic is a ~300-line Python script with known unpatched SQL injection vulnerabilities, no parameterized queries, no transactions, no read-only mode, and a hardcoded demo prompt. It was archived in May 2025 with Anthropic declining to fix the security issues, yet it was forked over 5,000 times. The community alternatives each solve one or two problems but none solves them comprehensively.
This server was built to be the SQLite MCP server you can actually trust in production. It is written in Rust, ships as a single binary with no runtime dependencies, and uses SQLite's own sqlite3_set_authorizer API to enforce access control at the query-preparation level, before any SQL ever executes. The access control system supports column-level granularity, specificity-based rule resolution, and five built-in presets ranging from deny-everything to allow-everything.
Features
- Authorizer-based access control. Uses SQLite's native authorizer callback to intercept every operation at prepare time. Supports 30+ selector types covering reads, writes, DDL, pragmas, functions, transactions, attach/detach, and virtual tables, all with per-table, per-column, and per-function granularity.
- Five permission presets.
deny-everything,read-only(default),read-write,full-ddl, andallow-everything. Layer--allowand--denyoverrides on top with a specificity-based resolution algorithm where deny wins ties. - 13 tools. A single
executetool for arbitrary SQL, plus purpose-built tools for schema introspection, full-text search, query planning, backups, and database maintenance. - Full-text search. First-class FTS5 support with dedicated tools for creating indexes and searching with BM25 ranking and highlighted snippets.
- Query timeouts. Optional per-server timeout that uses SQLite's progress handler to abort runaway queries before they can tie up resources.
- Connection pooling. Built on
r2d2for connection reuse with per-connection authorizer and timeout installation. - Init scripts. Seed schemas and data on first run with
--init-sqlfiles that are skipped when the database already exists. - Single binary. Compiles SQLite statically via
rusqlite'sbundledfeature. No Python, no Node, no runtime dependencies.
Installation
From crates.io
cargo install mcp-server-sqlite
From source
git clone https://github.com/0xOmarA/mcp-server-sqlite.git
cd mcp-server-sqlite
cargo install --path .
Quick start
Start a read-only server with an in-memory database:
mcp-server-sqlite
Start a read-write server on a persistent database with a schema init file:
mcp-server-sqlite --database ./app.db --preset read-write --init-sql schema.sql
Claude Desktop configuration
Add the following to your Claude Desktop MCP config file:
{
"mcpServers": {
"sqlite": {
"command": "mcp-server-sqlite",
"args": [
"--database", "./my-database.db",
"--preset", "read-only"
]
}
}
}
Tools
The server exposes 13 tools over MCP. Each tool validates inputs, enforces the configured access control policy, and returns structured typed output.
execute
Runs any SQL query against the database. This is the primary tool. It handles SELECT, INSERT, UPDATE, DELETE, DDL, and everything else SQLite supports. Returns typed result rows (preserving NULL, Integer, Real, Text, and Blob types) and a count of rows changed.
execute({ "query": "SELECT name, age FROM students WHERE age > 20" })
Access control is enforced at the SQLite authorizer level, so denied operations never execute. If the configured policy denies any part of the query (a table read, a column access, a function call), the entire statement is rejected with a clear error message.
list_tables
Lists all tables in the database with their names and CREATE TABLE definitions.
describe_table
Returns column metadata for a specific table: column name, declared type, NOT NULL constraint, default value, and whether it is part of the primary key.
describe_table({ "table_name": "students" })
list_indexes
Lists indexes in the database. Optionally filter by table. Returns the index name, table name, uniqueness flag, ordered column list, and the WHERE clause for partial indexes.
list_indexes({ "table_name": "students" })
list_foreign_keys
Returns foreign key constraints for a given table, including the local column, referenced table and column, and the ON UPDATE/ON DELETE actions.
list_foreign_keys({ "table_name": "enrollments" })
list_views
Lists all views with their names and defining SQL.
list_triggers
Lists triggers in the database. Optionally filter by table. Returns the trigger name, attached table, event (INSERT/UPDATE/DELETE), timing (BEFORE/AFTER/INSTEAD OF), and full SQL definition.
list_triggers({ "table_name": "students" })
explain_query
Returns the EXPLAIN QUERY PLAN output for a query without executing it. Shows how SQLite will access tables, which indexes it will use, and how it will join data. Useful for understanding query performance.
explain_query({ "query": "SELECT * FROM students WHERE name = 'Omar'" })
backup
Creates a full backup of the database to a file path using SQLite's online backup API. Runs incrementally in pages so it does not block other operations for long. Returns the number of pages copied and the destination path.
backup({ "destination": "/tmp/app-backup.db" })
create_fts_index
Creates an FTS5 virtual table over specified columns of an existing table. The virtual table name defaults to {table}_fts but can be customized.
create_fts_index({
"table_name": "articles",
"columns": ["title", "body"]
})
search_fts
Runs a full-text search query against an FTS5 virtual table. Returns results ranked by BM25 relevance with highlighted snippets showing where the query matched. Configurable result limit, snippet length, and highlight markers.
search_fts({
"fts_table": "articles_fts",
"query": "sqlite AND indexing",
"limit": 5
})
vacuum
Runs the SQLite VACUUM command to rebuild the database file, reclaim unused space, and defragment storage.
database_info
Returns database metadata: SQLite version, page size, page count, total size in bytes, journal mode, WAL checkpoint status (when in WAL mode), freelist page count, and the number of tables and indexes.
Access control
The access control system is the core differentiator of this server. It is built on SQLite's sqlite3_set_authorizer callback, which means SQLite itself parses every query and reports every operation it will perform. The server then evaluates each operation against the configured policy. Denied operations cause the entire statement to be rejected at prepare time, before any data is read or modified.
This approach has several important properties:
- Zero parsing gaps. SQLite does the parsing, not a third-party SQL parser. Every valid SQLite statement is handled correctly, including edge cases like triggers, views, CTEs, and virtual tables.
- Column-level granularity. The
ReadandUpdateselectors accept both a table name and a column name, so you can deny access to specific columns (e.g.--deny Read(Users.ssn)). - Indirect operation tracking. If a trigger on table A fires an INSERT into table B, the authorizer catches the INSERT on table B. Operations hidden inside triggers and views are not invisible to the policy.
- Fail-closed by default. The default preset is
read-only. When allow and deny rules conflict at the same specificity level, deny wins.
Presets
Presets define the baseline permissions before any --allow/--deny overrides are applied.
| Preset | Allows | Denies |
|---|---|---|
deny-everything |
Nothing | Everything |
read-only (default) |
Read, Select, Transaction, Function, Recursive, Pragma | Insert, Update, Delete, all DDL, Attach, Detach |
read-write |
Extends read-only with Insert, Update, Delete, Savepoint, Analyze, Reindex, temp objects | Permanent DDL, Attach, Detach |
full-ddl |
Extends read-write with CreateTable, DropTable, AlterTable, CreateIndex, DropIndex, CreateTrigger, DropTrigger, CreateView, DropView | Attach, Detach, virtual tables |
allow-everything |
Everything | Nothing |
Selectors
Selectors identify the operation and, optionally, the exact resources a rule applies to. The syntax is Action or Action(field1.field2) where * is a wildcard.
Data operations:
| Selector | Fields | Example |
|---|---|---|
Read |
table_name.column_name |
Read(Users.email) |
Insert |
table_name |
Insert(AuditLog) |
Update |
table_name.column_name |
Update(Users.password) |
Delete |
table_name |
Delete(Sessions) |
Select |
(none) | Select |
Transactions:
| Selector | Fields | Example |
|---|---|---|
Transaction |
operation |
Transaction(BEGIN) |
Savepoint |
operation.savepoint_name |
Savepoint(*.my_sp) |
DDL (permanent):
| Selector | Fields | Example |
|---|---|---|
CreateTable |
table_name |
CreateTable(TempData) |
DropTable |
table_name |
DropTable(OldRecords) |
AlterTable |
database_name.table_name |
AlterTable(*.Users) |
CreateIndex |
table_name.index_name |
CreateIndex(Users.idx_email) |
DropIndex |
table_name.index_name |
DropIndex |
CreateView |
view_name |
CreateView(ActiveUsers) |
DropView |
view_name |
DropView |
CreateTrigger |
table_name.trigger_name |
CreateTrigger(Users.*) |
DropTrigger |
table_name.trigger_name |
DropTrigger |
DDL (temporary):
| Selector | Fields | Example |
|---|---|---|
CreateTempTable |
table_name |
CreateTempTable |
DropTempTable |
table_name |
DropTempTable |
CreateTempIndex |
table_name.index_name |
CreateTempIndex |
DropTempIndex |
table_name.index_name |
DropTempIndex |
CreateTempView |
view_name |
CreateTempView |
DropTempView |
view_name |
DropTempView |
CreateTempTrigger |
table_name.trigger_name |
CreateTempTrigger |
DropTempTrigger |
table_name.trigger_name |
DropTempTrigger |
Other operations:
| Selector | Fields | Example |
|---|---|---|
Pragma |
pragma_name |
Pragma(journal_mode) |
Function |
function_name |
Function(count) |
Attach |
filename |
Attach |
Detach |
database_name |
Detach |
Reindex |
index_name |
Reindex |
Analyze |
table_name |
Analyze(Users) |
CreateVtable |
table_name.module_name |
CreateVtable(*.fts5) |
DropVtable |
table_name.module_name |
DropVtable |
Recursive |
(none) | Recursive |
Specificity and resolution
Each selector has a specificity equal to the number of fields pinned to concrete values (not *). When SQLite asks whether an operation is allowed:
- All matching rules are collected. Globs match anything; concrete values must match exactly.
- Rules are evaluated from the highest specificity to the lowest. The first level with at least one matching rule determines the outcome.
- If both allow and deny rules match at the same specificity, deny wins.
- If no rule matches at any level, the per-action default from the preset applies.
Example: read-only with a sensitive column denied
mcp-server-sqlite --database ./app.db --deny Read(Users.ssn)
The read-only preset allows all reads. The --deny Read(Users.ssn) rule has specificity 2 (both table and column pinned), which beats the preset's blanket allow at specificity 0. Reading Users.name is fine. Reading Users.ssn is denied.
Example: deny a table but allow one column
mcp-server-sqlite --database ./app.db \
--deny Read(Secrets) \
--allow Read(Secrets.id)
The deny on Read(Secrets) has specificity 1. The allow on Read(Secrets.id) has specificity 2. Reads on Secrets.id are allowed because specificity 2 beats specificity 1. All other columns in Secrets remain denied.
Example: deny everything, allow only specific functions
mcp-server-sqlite --preset deny-everything \
--allow Read \
--allow Select \
--allow Transaction \
--allow Function(count) \
--allow Function(sum)
Starting from a deny-everything baseline, only reads and the count() and sum() SQL functions are permitted. All other functions (e.g. load_extension) remain denied.
CLI reference
Usage: mcp-server-sqlite [OPTIONS]
Options:
--database <DATABASE>
The SQLite database URI. Defaults to a shared in-memory database.
Use a file URI for persistence (e.g. `file:./app.db`). Query
parameters like `?mode=ro` and `?cache=shared` are supported.
[default: file::memory:?cache=shared]
--init-sql <INIT_SQL>
Paths to SQL files executed once when creating a new database.
Skipped entirely if the database file already exists. Use this
to set up schemas and seed data on first run. May be specified
multiple times.
-p, --preset <PRESET>
The baseline permission preset.
[default: read-only]
[possible values: deny-everything, read-only, read-write,
full-ddl, allow-everything]
-a, --allow <ALLOW>
Allow a specific SQL operation. Accepts a selector in the form
Action or Action(field1.field2) where * is a wildcard. More
specific rules override less specific ones. May be specified
multiple times.
-d, --deny <DENY>
Deny a specific SQL operation. Same selector syntax as --allow.
When an allow and deny rule match at the same specificity level,
deny wins. May be specified multiple times.
--timeout-ms <TIMEOUT_MS>
Maximum time in milliseconds that any single SQL operation is
allowed to run before being interrupted. Omit for no timeout.
-h, --help
Print help (including more examples with --help)
-V, --version
Print version
Examples
Read-only analytics database
Expose a database for read-only analytics, denying access to PII columns:
mcp-server-sqlite \
--database ./analytics.db \
--deny Read(*.email) \
--deny Read(*.phone) \
--deny Read(*.ssn)
Application backend with restricted writes
Allow reads and writes, but protect the audit log and prevent schema changes:
mcp-server-sqlite \
--database ./app.db \
--preset read-write \
--deny Delete(AuditLog) \
--deny Update(AuditLog)
Locked-down server for untrusted agents
Start from deny-everything and allow only what is needed:
mcp-server-sqlite \
--database ./data.db \
--preset deny-everything \
--allow Read(Products) \
--allow Read(Categories) \
--allow Select \
--allow Transaction \
--allow Function(count) \
--allow Function(sum) \
--allow Function(avg) \
--timeout-ms 5000
Development server with full access
For local development where convenience matters more than safety:
mcp-server-sqlite \
--database ./dev.db \
--preset allow-everything \
--init-sql schema.sql \
--init-sql seed.sql
Full-text search workflow
Set up a database with FTS5 for document search:
mcp-server-sqlite \
--database ./docs.db \
--preset full-ddl \
--allow CreateVtable(*.fts5)
Then through the MCP tools:
create_fts_index({ "table_name": "articles", "columns": ["title", "body"] })
search_fts({ "fts_table": "articles_fts", "query": "rust AND sqlite" })
Logging
The server logs to stderr using the tracing framework. Control the log level with the RUST_LOG environment variable:
# Default (info)
mcp-server-sqlite --database ./app.db
# Verbose (includes tool inputs/outputs)
RUST_LOG=debug mcp-server-sqlite --database ./app.db
# Quiet (errors only)
RUST_LOG=error mcp-server-sqlite --database ./app.db
Comparison with existing servers
| Feature | Official (Python) | jparkerweb (JS) | sqlite-explorer (Python) | dbhub (TS) | This server |
|---|---|---|---|---|---|
| Authorizer-based access control | No | No | No | No | Yes |
| Column-level permissions | No | No | No | No | Yes |
| Permission presets | No | No | No | Read-only toggle | 5 presets + overrides |
| Query timeouts | No | No | No | Yes | Yes |
| Full-text search (FTS5) | No | No | No | No | Yes |
| Query plan explanation | No | No | No | No | Yes |
| Database backup | No | No | No | No | Yes |
| Schema introspection depth | Tables + columns | Tables + columns | Tables + columns | Tables + columns | Tables, columns, indexes, foreign keys, views, triggers |
| SQL injection protection | Vulnerable | N/A | Parameter binding | N/A | Authorizer rejects at prepare time |
| Connection pooling | No | No | No | No | Yes (r2d2) |
| Init scripts | No | No | No | No | Yes |
| Single binary, no runtime | No (Python) | No (Node.js) | No (Python) | No (Node.js) | Yes (Rust) |
License
Licensed under either of Apache License, Version 2.0 or MIT License at your option.
Установка SQLite Access Control
У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.
▸ github.com/0xomara/mcp-server-sqliteFAQ
SQLite Access Control MCP бесплатный?
Да, SQLite Access Control MCP бесплатный — установка в пару кликов через Unyly без оплаты.
Нужен ли API-ключ для SQLite Access Control?
Нет, SQLite Access Control работает без API-ключей и переменных окружения.
SQLite Access Control — hosted или self-hosted?
Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.
Как установить SQLite Access Control в Claude Desktop, Claude Code или Cursor?
Открой SQLite Access Control на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.
Похожие MCP
wenb1n-dev/SmartDB_MCP
A universal database MCP server supporting simultaneous connections to multiple databases. It provides tools for database operations, health analysis, SQL optim
автор: wenb1n-devPostgres Server
This server enables interaction with PostgreSQL databases through the Model Context Protocol, optimized for the AWS Bedrock AgentCore Runtime. It provides tools
автор: madhurprashPostgres
Query your database in natural language
автор: AnthropicPostgreSQL
Read-only database access with schema inspection.
автор: modelcontextprotocolCompare SQLite Access Control with
Не уверен что выбрать?
Найди свой стек за 60 секунд
Автор?
Embed-бейдж для README
Похожее
Все в категории data
