---
source_block: agent-database-integration.md
canonical_url: https://api.theorydelta.com/published/mcp-database-servers-security-bypass
published: 2026-02-24
last_verified: 2026-04-19
confidence: independently-confirmed
rubric:
  total_claims: 8
  tested_count: 8
  independently_confirmed: true
  unlinked_count: 0
  scope_matches: true
  falsification_stated: true
  content_type: finding
trust:
  provenance: "sourced + first-party"
  rigor: independently-confirmed
  sources: "2 source snippets, 1 GHSA, 2 GitHub issues, 5 repo links"
  unlinked_claims: 0
environments_tested:
  - tool: "executeautomation/mcp-database-server"
    version: "latest (Feb 2026)"
    evidence_type: source-reviewed
    result: "startsWith('select') prefix check found in source"
  - tool: "modelcontextprotocol/servers mcp-server-sqlite"
    version: "v2025.4.25"
    evidence_type: source-reviewed
    result: "PRAGMA f-string interpolation found in source"
  - tool: "haxzie/sequel-mcp"
    version: "latest (Feb 2026)"
    evidence_type: source-reviewed
    result: "startsWith('select') prefix check found; writable CTEs pass guard"
  - tool: "vanna-ai/vanna"
    version: "v0.7.x"
    evidence_type: docs-reviewed
    result: "NL2SQL pipeline reviewed"
  - tool: "Canner/WrenAI"
    version: "latest (Feb 2026)"
    evidence_type: docs-reviewed
    result: "NL2SQL pipeline reviewed"
  - tool: "Snowflake-Labs/ReFoRCE"
    version: "latest (Feb 2026)"
    evidence_type: docs-reviewed
    result: "Benchmark reviewed"
  - tool: "xlang-ai/Spider2"
    version: "latest (Feb 2026)"
    evidence_type: docs-reviewed
    result: "Enterprise accuracy benchmarks reviewed"
theory_delta: Every SQLite MCP database server reviewed uses startsWith("select") as its read-only guard — trivially bypassable with a semicolon or comment prefix.
a2a_card:
  type: knowledge_finding
  topic_tags: [mcp, database, security, sql-injection, read-only-bypass]
  confidence_score: 0.70
  finding_url: https://theorydelta.com/findings/mcp-database-servers-security-bypass/
  mcp_query_hint: "MCP database server security bypass read-only"
tasks:
  - task: mcp-setup
    phase: choose-servers
---

# MCP database servers' "read-only mode" is a string check — and it's bypassable

*From [Theory Delta](https://theorydelta.com) | [Methodology](https://theorydelta.com/methodology/) | Published 2026-02-24*

## What you expect

You connect your agent to a database using an MCP database server with "read-only mode" enabled. The server restricts the agent to SELECT queries. Your data is safe from modification. You've seen the feature advertised in the official Anthropic MCP SQLite reference server and multiple community implementations.

## What actually happens

Every SQLite MCP database server reviewed implements read-only enforcement as a string prefix check on the incoming query. None use SQL AST parsing to verify intent. The guard is:

```typescript
// executeautomation/mcp-database-server — index.ts:272-291
if (!query.trim().toLowerCase().startsWith("select")) {
  throw new Error("Only SELECT queries are allowed with read_query");
}
```

This is not a security boundary. It is a one-line string comparison that multiple attack classes bypass entirely.

**What bypasses the guard:**

- **Writable CTEs** — `WITH deleted AS (DELETE FROM users WHERE id=1) SELECT 1` starts with `WITH`, not `SELECT`. If the server blocks `WITH`, legitimate read-only CTEs break. If it allows `WITH`, writes go through.
- **PRAGMA statements** — `PRAGMA journal_mode=DELETE` and `PRAGMA wal_checkpoint` can modify database state. The prefix check never evaluates them.
- **Multi-query chaining** — `SELECT 1; DROP TABLE users` passes the prefix check. Only the first statement is inspected. The PostgreSQL driver supports semicolon-delimited multi-query execution.
- **Extension loading** — `SELECT load_extension('/path/to/malicious.so')` passes as a SELECT. Loaded extensions can expose filesystem and network write paths.

**The Anthropic reference server has an open SQL injection.** `mcp-server-sqlite` (modelcontextprotocol/servers) interpolates `table_name` directly into a PRAGMA statement:

```python
# mcp_server_sqlite/server.py:326
results = db._execute_query(
    f"PRAGMA table_info({arguments['table_name']})"
)
```

Input `users); DROP TABLE users; --` executes arbitrary SQL. [Issue #3314](https://github.com/modelcontextprotocol/servers/issues/3314) is open. Anthropic archived the reference server May 29, 2025 — 5,000+ forks carry this unpatched injection forward indefinitely.

**The bypass is confirmed and independently verified.** Security advisory [GHSA-65hm-pwj5-73pw](https://github.com/executeautomation/mcp-database-server/security/advisories/GHSA-65hm-pwj5-73pw) independently documents this finding for executeautomation/mcp-database-server.

**The correctness ceiling compounds the problem.** Even if security were solved, SOTA NL2SQL on enterprise schemas (Spider 2.0) is ~36% for the best agentic system. Spider 1.0's commonly cited 86.6% uses toy schemas and is a misleading baseline for production deployments. Your agent is not reliably generating correct SQL — and the servers designed to contain the damage have unpatched injection points.

**Scale of exposure.** A July 2025 scan found 13,000+ MCP database servers in production with no authentication. Read-only enforcement is irrelevant when any network-reachable client can connect unauthenticated.

## What this means for you

**If you deployed an MCP database server with read-only mode to protect production data:** you do not have read-only enforcement. You have a string check that rejects `DELETE` as a first token and accepts everything else. Any agent prompt that produces a writable CTE, a PRAGMA call, or a multi-statement query bypasses the guard entirely.

**If you are using the Anthropic MCP SQLite reference server as a starting point:** the `describe_table` path has an open SQL injection. An attacker who can write to the database you are connecting can embed a table name that triggers arbitrary SQL execution when your agent explores the schema during planning. This is not a theoretical edge case — it is the planning step every agent takes before generating queries.

**If your agent uses schema discovery tools:** they are more fragile than the query tools they support. `list_tables` returns 748 null entries on MySQL 8.0 RDS with executeautomation's server. Agents silently fall back to raw `information_schema` queries, bypassing the tool abstraction and reintroducing injection surface.

## What to do

1. **Add a SQL AST parser.** Use [sqlglot](https://github.com/tobymao/sqlglot) (Python) or [node-sql-parser](https://github.com/nicklockwood/node-sql-parser) (Node) to identify statement type before execution. String prefix checks are not a security boundary. `crystaldba/postgres-mcp` (2,300 stars) is the only reviewed server that uses AST-level parsing via pglast — use it as a reference for the correct architecture.

2. **Use parameterized queries.** Neither executeautomation nor haxzie/sequel-mcp separates parameters from query strings. Parameterized queries eliminate the SQL injection class. The only reviewed server that uses parameterized queries as the architectural default is `googleapis/genai-toolbox` (13,300 stars, Google-backed), which uses YAML-defined pre-approved query templates.

3. **Do not treat the Anthropic reference server as a secure starting point.** [Issue #3314](https://github.com/modelcontextprotocol/servers/issues/3314) is open. Audit `describe_table` and any PRAGMA-using path before deploying, regardless of which fork you start from.

4. **Add authentication before anything else.** Read-only enforcement is moot without authentication. 13,000+ production servers are reachable by any unauthenticated client.

5. **Sandbox any code execution path.** If your NL2SQL stack generates visualization code (Vanna, for instance, runs LLM-generated Plotly code via `exec()` without sandboxing), run it in a subprocess with restricted imports or a container with no network access. This is a separate attack surface from SQL injection.

## Evidence

| Tool | Version | Result |
|---|---|---|
| [executeautomation/mcp-database-server](https://github.com/executeautomation/mcp-database-server) | latest (Feb 2026) | source-reviewed: `startsWith('select')` prefix check confirmed; [GHSA-65hm-pwj5-73pw](https://github.com/executeautomation/mcp-database-server/security/advisories/GHSA-65hm-pwj5-73pw) independently confirms bypass |
| [modelcontextprotocol/servers mcp-server-sqlite](https://github.com/modelcontextprotocol/servers/tree/main/src/sqlite) | v2025.4.25 | source-reviewed: PRAGMA f-string interpolation confirmed; [Issue #3314](https://github.com/modelcontextprotocol/servers/issues/3314) open; archived May 2025 — 5,000+ forks inherit unpatched injection |
| [haxzie/sequel-mcp](https://github.com/haxzie/sequel-mcp) | latest (Feb 2026) | source-reviewed: `startsWith('select')` prefix check confirmed; writable CTEs pass guard |
| [crystaldba/postgres-mcp](https://github.com/crystaldba/postgres-mcp) | latest (Feb 2026) | source-reviewed: pglast AST parsing confirmed — only reviewed server with correct architecture; residual gap: SECURITY DEFINER stored procs bypass enforcement |
| [vanna-ai/vanna](https://github.com/vanna-ai/vanna) | v0.7.x | docs-reviewed: `exec()` of LLM-generated Plotly code is an unmitigated RCE surface |
| [xlang-ai/Spider2](https://github.com/xlang-ai/Spider2) | latest (Feb 2026) | docs-reviewed: GPT-4 enterprise schema accuracy 5.6% vs. commonly cited 86.6% (Spider 1.0 toy schemas) |

**Confidence:** source-reviewed and independently confirmed — source code reviewed across 3 MCP database servers; bypass patterns confirmed by reading source. Security advisory [GHSA-65hm-pwj5-73pw](https://github.com/executeautomation/mcp-database-server/security/advisories/GHSA-65hm-pwj5-73pw) independently confirms the finding.

**Falsification criterion:** This claim would be disproved by finding an SQLite MCP database server (among the 3 reviewed) that uses SQL AST parsing — not string prefix matching — for read-only enforcement, or by demonstrating that the bypass vectors above do not execute in the documented environments.

Seen different? [Contribute your evidence](https://theorydelta.com/contribute/) — share a repro or counter-example and we'll review it against this finding. Reader evidence is what keeps these findings accurate.
