sqlgate-mcp
Provides read-only SQL access to DuckDB database files, opening them read-only with external access disabled, query timeouts, and the same guard and masking policies.
Provides read-only SQL access to PostgreSQL databases for AI agents, with policy-based table and column restrictions, query guardrails, sensitive-data masking, cost checks, and audit logging.
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
sqlgate-mcp
Read-only SQL access to your database for AI agents, over the Model Context Protocol, with guardrails you can read and an audit log you can check.
Giving an agent a database connection string is easy. Knowing what it can do with it is not:
pg_read_file('/etc/passwd'), a DELETE hidden in a CTE, SELECT * on the users table with
its password hashes, or a cross join that runs for an hour. sqlgate sits between the agent and
the database and only lets through what your policy allows.
agent ──MCP──▶ sqlgate ──▶ guard (sqlglot AST) ──▶ database (read-only role, read-only txn, timeout)
│ │
└── audit log ◀───────────────────────┴── masking ◀── resultsDefence in depth
No single layer is trusted to be perfect:
The guard parses every query with sqlglot in the database's dialect and refuses anything that isn't exactly one read-only query:
no INSERT, UPDATE, DELETE, DDL or COPY anywhere in the tree, including data-modifying CTEs; no
SELECT ... INTOand noFOR UPDATE/FOR SHARElocksno functions that read files or the network, sleep, run SQL from strings or change state (
pg_read_file,dblink,query_to_xml,nextval, DuckDB'sread_csv,getenv, ...)only tables your policy allows, never the system catalogs
denied columns can't be selected, filtered on, or reached through
SELECT *or a whole-row reference likerow_to_json(u)an outer
LIMITis added or tightened
The database enforces it again. Postgres queries run in a
READ ONLYtransaction with astatement_timeoutand are always rolled back, optionally after anEXPLAINcost check. BigQuery queries are dry-run against a bytes limit and carrymaximum_bytes_billed. DuckDB files are opened read-only with external access switched off and a query timeout.Masking transforms sensitive values on the way out. Lineage is followed through expressions, CTEs and subqueries, so
lower(email) AS eis masked likeemail. Responses are also kept within a byte budget, so one wide row can't flood an agent's context.The audit log records every query, what actually ran, and why anything was refused.
You should still connect with a role that can only SELECT what the agent is meant to see.
Related MCP server: MCP Data Bridge
Install
uv tool install 'sqlgate-mcp[postgres]' # or [bigquery], or bothPython 3.11+, MCP Python SDK 2.x. DuckDB support is built in.
Configure
# sqlgate.yaml
database:
type: postgres
dsn: ${ANALYTICS_RO_DSN} # from the environment, never the file
statement_timeout_ms: 15000
max_cost: 500000 # planner cost; EXPLAIN a few heavy queries to pick yours
policy:
max_rows: 500
allow_tables: ["public.*", "reporting.*"]
deny_tables: ["public.*_audit"]
deny_columns: ["*.password_hash", "users.mfa_secret"]
mask_columns:
"*.email": partial # s•••@example.com
"customers.phone": partial # •••4567
"customers.full_name": hash # h:3f9a…, stable, so it still groups and joins
deny_functions: [expensive_udf]
masking:
key: ${SQLGATE_MASK_KEY} # HMAC key for hash masking
audit:
path: /var/log/sqlgate/audit.jsonlFor BigQuery:
database:
type: bigquery
project: acme-analytics
datasets: [reporting, marketing] # unqualified tables resolve to the first
location: EU
max_gib_billed: 5For a DuckDB file: type: duckdb, path: warehouse.duckdb, timeout_s: 30.
Table patterns are globs over schema.table (BigQuery: project.dataset.table); column
patterns over table.column or schema.table.column. Unqualified tables resolve to public
for Postgres and main for DuckDB unless policy.default_schema says otherwise.
Names are compared the way the database compares them. In Postgres "Orders" (quoted) is a
different table from orders, so allow patterns match it exactly; deny patterns and masks
ignore case everywhere, so any ambiguity ends in a refusal.
Check what the guard does with a query before an agent ever sends it:
$ sqlgate-mcp check -c sqlgate.yaml "select email, count(*) from customers group by 1"
SELECT email, COUNT(*) FROM customers GROUP BY 1 LIMIT 501
masked output columns: #1 (partial)
$ sqlgate-mcp check -c sqlgate.yaml "with gone as (delete from orders returning *) select * from gone"
refused: DELETE is not allowed anywhere in the query, including inside WITHConnect a client
Claude Code:
claude mcp add analytics -- sqlgate-mcp serve -c /path/to/sqlgate.yamlClaude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"analytics": {
"command": "sqlgate-mcp",
"args": ["serve", "-c", "/path/to/sqlgate.yaml"],
"env": { "ANALYTICS_RO_DSN": "postgresql://agent_ro@db/analytics" }
}
}
}Or with Docker, where the container only ever sees the config and the secrets you pass it:
docker build -t sqlgate-mcp .
claude mcp add analytics -- docker run -i --rm \
-v /path/to/sqlgate.yaml:/config/sqlgate.yaml:ro -e ANALYTICS_RO_DSN sqlgate-mcpOver HTTP
One server for a team, instead of one per laptop:
SQLGATE_TOKEN=$(openssl rand -hex 32) sqlgate-mcp serve -c sqlgate.yaml --transport http --host 0.0.0.0 --port 8000
claude mcp add --transport http analytics https://sqlgate.internal/mcp --header "Authorization: Bearer $TOKEN"On 127.0.0.1 (the default) the SDK's DNS-rebinding protection is on; any other address
requires SQLGATE_TOKEN, and the server won't start without it. Put TLS in front.
Tools
tool | |
| Tables the policy allows, with row estimates and descriptions |
| Columns, types and comments of one table (denied columns left out) |
| One read-only SELECT; returns columns, rows, |
| What would run, what would be masked, and the cost estimate, without running it |
| Up to 20 rows of a table through the same guard and masking, to see what values look like |
All five are annotated readOnlyHint and declare output schemas, so clients get
structured_content as well as text and can skip confirmation prompts. The visible schema is
also available as the schema://overview resource, for clients that attach context.
Refusals come back as tool errors that say what to change ("table hr.salaries is not available; use list_tables..."), so agents recover instead of retrying blindly. Table and column comments are passed through, so a well-commented schema is the best prompt you can give an agent.
A read-only Postgres role
CREATE ROLE agent_ro LOGIN PASSWORD '...' NOSUPERUSER NOCREATEDB NOCREATEROLE;
ALTER ROLE agent_ro SET default_transaction_read_only = on;
GRANT USAGE ON SCHEMA public, reporting TO agent_ro;
GRANT SELECT ON ALL TABLES IN SCHEMA public, reporting TO agent_ro;
REVOKE SELECT ON public.users FROM agent_ro;
GRANT SELECT (id, email, created_at) ON public.users TO agent_ro;Audit log
One JSON object per line:
{"ts": "2025-12-28T07:25:11.402+00:00", "tool": "run_query", "sql": "select count(*) from orders",
"executed": "SELECT COUNT(*) FROM orders LIMIT 501", "outcome": "ok", "rows": 1, "truncated": false,
"elapsed_ms": 3.8, "estimated_cost": 1834.5}outcome is ok, refused (guard or cost check) or failed (database error). It's written
per line and survives logrotate.
Development
uv sync --all-extras
uv run pytest # Postgres tests run when SQLGATE_TEST_PG_DSN is set
uv run ruff check && uv run ruff format --checkLicense
MIT
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
This server cannot be deployed
Maintenance
Related MCP Connectors
Safe, read-only Postgres and MySQL access for AI agents. Audit log + column-level controls.
Deterministic safety, correctness & cost gate that vets Postgres SQL before your AI agent runs it.
Query 40 databases from Claude, ChatGPT, or Cursor — on any device. Read-only, encrypted, audited.
Query PostgreSQL databases in plain English — LLM-generated, safety-validated SQL.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to query a Postgres database read-only through a policy layer rather than a raw connection string. Enforces table and column allowlists, row limits, mandatory predicates, PII masking and append-only audit logging, while refusing writes, joins, subqueries and unlisted functions.MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to securely discover database schemas and execute read-only SQL queries through guardrailed tools that block mutations and clamp result sizes.MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to explore and query a SQL database safely, answering questions with real data while blocking writes, hiding protected columns, restricting results to one tenant's rows, and logging every call to an audit trail.MIT
- AlicenseAqualityCmaintenanceEnables AI assistants to safely explore and query MySQL, MariaDB, PostgreSQL, SQLite and StarRocks databases through read-only tools for schema listing, table description, schema search, row sampling, query execution, and plan explanation. Enforces least-privilege accounts, engine-level read-only sessions with timeouts, parsed SQL guardrails, and column masking so runaway or unsafe queries are refused.8MIT