---
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-02-22
confidence: independently-confirmed
rubric:
  total_claims: 8
  tested_count: 0
  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: "SELECT INTO OUTFILE passes prefix check"
  - 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: "Connecting an agent to a database is framed as a correctness problem. It is primarily a security problem -- every MCP database server reviewed uses startsWith('select') as its read-only guard, which is bypassable."
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"
---

# Every MCP database server enforces read-only with a string check -- and it's bypassable

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

## What the docs say

MCP database servers advertise a "read-only mode" that restricts agents to SELECT queries, preventing data modification. The official Anthropic MCP SQLite reference server and community implementations all offer this as a safety feature for connecting agents to production databases.

## What actually happens

Every MCP database server reviewed implements read-only enforcement as a string prefix check. None use SQL AST parsing. Here is the actual code:

**executeautomation/mcp-database-server** ([source](https://github.com/executeautomation/mcp-database-server), [security advisory GHSA-65hm-pwj5-73pw](https://github.com/executeautomation/mcp-database-server/security/advisories/GHSA-65hm-pwj5-73pw)):

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

**modelcontextprotocol/servers mcp-server-sqlite** ([issue #3314](https://github.com/modelcontextprotocol/servers/issues/3314)):

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

The `table_name` parameter is interpolated directly into the PRAGMA statement without sanitization. Input like `users); DROP TABLE users; --` executes arbitrary SQL. The reference implementation that builders copy from has an open SQL injection.

This means:

- **False rejections:** CTEs (`WITH ... AS (...) SELECT ...`) are blocked because they start with `WITH`, not `SELECT`. Legitimate read-only queries fail silently.
- **False acceptances:** PostgreSQL writable CTEs (`WITH deleted AS (DELETE FROM ...) SELECT ...`) start with `WITH` but mutate data. If the guard allows `WITH` to fix the above, it opens a write path.
- **MySQL `SELECT INTO OUTFILE`** passes the prefix check in [haxzie/sequel-mcp](https://github.com/haxzie/sequel-mcp) -- confirmed, not theoretical. Starts with `SELECT`, writes to the filesystem.
- **Multi-query chaining:** `SELECT 1; DROP TABLE users` passes the prefix check because only the first statement is inspected. The [pg driver supports multi-query execution](https://github.com/executeautomation/mcp-database-server/security/advisories/GHSA-65hm-pwj5-73pw) when statements are semicolon-terminated.

The NL2SQL correctness ceiling compounds the problem: SOTA on enterprise schemas ([Spider 2.0](https://spider2-sql.github.io/)) is ~17% for o1-preview, ~10% for GPT-4o. [Spider 1.0's](https://yale-lily.github.io/spider) commonly cited 86.6% uses toy schemas and is a misleading baseline.

## What to do instead

1. **Add a SQL AST parser** ([sqlglot](https://github.com/tobymao/sqlglot) for Python, [node-sql-parser](https://github.com/nicklockwood/node-sql-parser) for Node) that identifies statement type before execution. String prefix checks are not a security boundary.
2. **Use parameterized queries** -- neither executeautomation nor haxzie/sequel-mcp separates parameters from query strings. Parameterized queries eliminate the SQL injection class entirely.
3. **Sandbox any code execution path** -- if your NL2SQL stack generates visualization code or transformation scripts, run them in a subprocess with restricted imports or a container with no network access. Input filtering will not stop LLM role-switching attacks.
4. **Do not trust 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.
5. **Test schema discovery tools against your actual database** -- MySQL 8.0 RDS returns 748 null entries from `list_tables` in executeautomation ([issue #23](https://github.com/executeautomation/mcp-database-server/issues/23), open). Agents silently fall back to raw `information_schema` queries, bypassing the tool abstraction.

## Environments tested

| Tool | Version | Result |
|------|---------|--------|
| [executeautomation/mcp-database-server](https://github.com/executeautomation/mcp-database-server) | latest (Feb 2026) | source-reviewed: `startsWith('select')` prefix check found in source |
| [modelcontextprotocol/servers mcp-server-sqlite](https://github.com/modelcontextprotocol/servers/tree/main/src/sqlite) | v2025.4.25 | source-reviewed: PRAGMA f-string interpolation found in source |
| [haxzie/sequel-mcp](https://github.com/haxzie/sequel-mcp) | latest (Feb 2026) | source-reviewed: SELECT INTO OUTFILE passes prefix check |
| [vanna-ai/vanna](https://github.com/vanna-ai/vanna) | v0.7.x | docs-reviewed: NL2SQL pipeline reviewed |
| [Canner/WrenAI](https://github.com/Canner/WrenAI) | latest (Feb 2026) | docs-reviewed: NL2SQL pipeline reviewed |
| [Snowflake-Labs/ReFoRCE](https://github.com/Snowflake-Labs/ReFoRCE) | latest (Feb 2026) | docs-reviewed: benchmark reviewed |
| [xlang-ai/Spider2](https://github.com/xlang-ai/Spider2) | latest (Feb 2026) | docs-reviewed: enterprise accuracy benchmarks reviewed |

## Confidence and gaps

**Confidence:** source-reviewed -- source code reviewed across 3 MCP database servers (executeautomation, mcp-server-sqlite, sequel-mcp), benchmarks reviewed across 4 additional tools. No commands were executed against running instances; bypass patterns were confirmed by reading source code. 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 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 `startsWith('select')` check cannot be bypassed via CTEs, PRAGMA injection, or `SELECT INTO OUTFILE` in the documented environments.

**Open questions:** Does any MCP database server implement AST-based read-only enforcement? Are there production deployments using parameterized queries through MCP tool calls? Has anyone shipped a sandboxed execution path for NL2SQL visualization code?

Seen different? [Contribute your evidence](https://theorydelta.com/contribute/) -- theory delta is what makes this knowledge base work.
