Can AI Agents Write to Your Database Safely?

Sergii Muliarchuk

datasette-agent 0.3a0 adds execute_write_sql with user approval gates. Here's what it means for AI-driven data ops in production.


# Can AI Agents Write to Your Database Safely?

**TL;DR:** datasette-agent 0.3a0 (released June 15, 2026) introduces `execute_write_sql` — a tool that gates every AI-initiated database write behind explicit user approval and respects the authenticated user's permission set. For teams running AI agents over live data, this is the missing safety layer that makes write-capable agents deployable without a dedicated DBA watching every query.

---

## At a glance

- **Release date:** datasette-agent 0.3a0 published June 15, 2026 on GitHub (datasette/datasette-agent).
- **New tool:** `execute_write_sql` — the first write-capable tool in the datasette-agent toolchain (issue #27).
- **Approval mechanism:** every write is paused for explicit user confirmation before execution — no auto-commit.
- **Permission model:** tool checks the authenticated Datasette user's ACL before even presenting the approval dialog.
- **Protocol compatibility:** datasette-agent exposes tools via the MCP (Model Context Protocol) standard, making it usable with Claude Sonnet, GPT-4o, and any MCP-compatible client.
- **Prior write risk:** before 0.3a0, agents could only read; write operations required manual SQL or external scripts — a gap that forced workarounds in production pipelines.
- **Underlying runtime:** Datasette's SQLite-first architecture means writes are single-file, making approval-gated mutations auditable with WAL-mode snapshots.

---

## Q: Why does human-in-the-loop approval matter for write operations?

Giving an AI agent read access to a database is a calculated risk. Giving it write access without a gate is an incident waiting to happen.

We learned this the hard way running our **scraper MCP server** (`scraper`) against a SQLite-backed product catalog in late 2025. The agent had unrestricted insert permissions through a custom n8n webhook. Between November 12 and November 19, 2025, it created **3 separate duplicate-row incidents** — roughly 4,200 duplicate SKUs — because the model misinterpreted a pagination token as a "new batch" signal. Cleanup took six engineer-hours.

The `execute_write_sql` pattern datasette-agent 0.3a0 ships is structurally identical to what we retrofitted manually: a tool that surfaces a human-readable preview of the SQL mutation, holds execution, and only proceeds on explicit approval. The difference is that datasette-agent bakes this into the tool contract at the protocol level — meaning any MCP client inherits the gate automatically. That's an architectural win. Human-in-the-loop approval adds 2–4 seconds of latency per write, but in our measurement across 340 agent-initiated writes in Q1 2026, **zero** unreviewed destructive queries landed in production after we added approval gates.

---

## Q: How does permission scoping change the agent threat model?

Traditional database agents operate with the credentials of whoever configured them — usually a service account with broad rights. datasette-agent 0.3a0 changes the model: `execute_write_sql` checks the **authenticated Datasette user's** permission set, not a static service credential.

This matters enormously for multi-tenant deployments. In our **crm MCP server** configuration, we map Datasette users 1:1 to CRM roles (viewer, editor, admin). Before 0.3a0-style permission scoping, an agent session authenticated as a "viewer" could theoretically still write if the service account had write rights. With permission-scoped tools, the viewer's agent session is structurally incapable of triggering a write — the tool refuses before the approval dialog even appears.

Concretely: in our June 2026 test against a staging Datasette instance running **datasette 1.0a17**, we authenticated as a read-only user and called `execute_write_sql` with a benign `INSERT` statement. The tool returned `permission-denied` in 180ms — no dialog, no partial execution. That's the right failure mode. It means you can safely expose agent capabilities to lower-privilege users without building a separate permission middleware layer.

---

## Q: What does this mean for MCP-based AI pipelines in practice?

The MCP ecosystem is maturing fast, and write-capable tools are the next frontier. datasette-agent's approach gives us a concrete reference implementation to evaluate against our own MCP servers.

We currently run **12+ MCP servers** in production. The ones that touch persistent state — `crm`, `scraper`, `leadgen`, `memory` — all required custom approval logic we built ad hoc. The pattern datasette-agent formalizes is: **(1) tool receives write intent → (2) tool previews SQL to the user → (3) user approves → (4) tool executes with scoped credentials → (5) result + affected rows returned**.

When we wired datasette-agent 0.3a0 into a Claude Sonnet 4.5 session via MCP in early June 2026, the tool-call flow worked cleanly. Read queries (`execute_sql`) averaged **1.1 seconds** round-trip. Write-approval flows (`execute_write_sql`) averaged **3.2 seconds** including the user-confirmation pause. For async n8n workflows — specifically our **LinkedIn scanner workflow** that periodically writes enriched contact data back to a Datasette instance — this latency is irrelevant. For interactive chat-over-data interfaces, it's a UX consideration worth surfacing to users upfront.

The bigger unlock: because the approval gate is at the tool level, not the application level, you can compose `execute_write_sql` with other MCP tools (`docparse`, `transform`) without rebuilding approval logic in each integration.

---

## Deep dive: The read-write agent gap and why it took this long

For the past two years, most production AI agents have been read-only by design. This wasn't an accident — it was a deliberate constraint born from the failure modes of early LLM-tool integrations.

The core problem is what researchers at Anthropic call "specification gaming" — models finding unintended paths to satisfy a goal. When an agent has write access, specification gaming has consequences: corrupted records, runaway inserts, unintended deletions. OpenAI's early documentation on function calling (published in the GPT-4 API docs, 2023) explicitly recommended limiting write tools to "human-supervised contexts." Datasette creator Simon Willison has written extensively on the Datasette blog about the risks of agentic SQL execution, noting that even well-formed queries can have catastrophic effects when an agent misreads schema context.

The solution the field has converged on is **human-in-the-loop (HITL) approval** — not as a UX pattern, but as a protocol-level primitive. MCP itself, released by Anthropic in late 2024, was designed with this in mind: tools are discrete, inspectable units that a client can intercept before execution. datasette-agent 0.3a0 is one of the first open-source MCP servers to implement HITL approval natively inside a write tool, rather than delegating it to the calling application.

This matters because delegation creates gaps. When approval logic lives in the application layer (an n8n workflow, a Slack bot, a custom UI), every new integration has to re-implement it. We measured this cost directly: across 4 integrations we built in 2025 that needed write-capable agents, we spent an average of **6.5 engineer-hours per integration** on approval-gate logic alone. If datasette-agent's pattern becomes a standard that MCP tool authors adopt, that cost drops to near zero for Datasette-backed data stores.

The permission-scoping piece is equally significant. According to the Datasette documentation (datasette.io/docs, permissions section), Datasette's permission system supports row-level, table-level, and database-level access controls. By threading agent tool calls through this existing ACL layer, datasette-agent avoids the "shadow permissions" problem — where an agent's effective permissions diverge from the user's declared role. This is the same problem that plagues many RAG implementations where retrieval happens under a service account with broader access than the end user should have.

Looking forward, the pattern datasette-agent establishes — tool-level HITL + user-scoped permissions — is likely to become a baseline expectation for any MCP server that touches mutable state. The Model Context Protocol specification (Anthropic, 2024) already includes hooks for `requiresConfirmation` at the tool-definition level; datasette-agent 0.3a0 is an early, concrete implementation of that hook in the wild.

For teams evaluating AI agents for data operations: the question is no longer "should we allow writes?" but "does our write tool implement HITL and permission scoping at the protocol level?" datasette-agent 0.3a0 sets the bar.

---

## Key takeaways

- datasette-agent 0.3a0 ships `execute_write_sql` with mandatory user approval before any database mutation.
- Permission scoping ties agent write access to the authenticated Datasette user's ACL, not a service account.
- HITL approval at the tool level eliminates ~6.5 engineer-hours per integration of custom gate logic.
- Write-approval flows measured at 3.2 seconds average latency with Claude Sonnet 4.5 in June 2026 tests.
- MCP's `requiresConfirmation` hook makes datasette-agent's pattern replicable across any MCP-compatible tool server.

---

## FAQ

**Q: Does execute_write_sql support rollback if the user approves a bad query?**

Not natively in 0.3a0. The tool writes on approval and relies on Datasette's underlying SQLite transaction model. For rollback safety, wrap your Datasette instance behind a WAL-mode SQLite file and snapshot before each agent session. We checkpoint every 15 minutes in our scraper MCP setup — this gives us a clean restore point if an approved write turns out to be wrong after the fact.

**Q: Can I use datasette-agent with Claude via MCP without the web UI?**

Yes. datasette-agent exposes tools over the MCP protocol, so you can wire it directly into a Claude API call using the tools array. We tested this with claude-sonnet-4-5 in June 2026 — tool-call round-trips averaged 1.1 seconds for read queries and 3.2 seconds for write-approval flows. No Datasette web UI is required; the agent communicates with the datasette-agent MCP server directly over stdio or HTTP transport.

**Q: How does user-permission scoping work in execute_write_sql?**

datasette-agent reads the authenticated Datasette user's permission set before allowing the tool to fire. If the user lacks insert or update rights on the target table, the tool refuses and returns a permission-denied error — no approval dialog is shown. This mirrors the RBAC pattern we use in our crm MCP server, where viewer-role users are structurally blocked from write tool calls regardless of the agent's instructions.

---

## About the author

Sergii Muliarchuk — founder of FlipFactory.it.com. Building production AI systems for fintech, e-commerce, and SaaS clients. We run 12+ MCP servers, n8n workflows, and FrontDeskPilot voice agents in production.

*We've been burned by unguarded agent writes in production — so when a tool ships native HITL approval, we pay close attention.*

Frequently Asked Questions

Does execute_write_sql support rollback if the user approves a bad query?

Not natively in 0.3a0. The tool writes on approval and relies on Datasette's underlying SQLite transaction model. For rollback safety, wrap your Datasette instance behind a WAL-mode SQLite file and snapshot before each agent session. We checkpoint every 15 minutes in our scraper MCP setup.

Can I use datasette-agent with Claude via MCP without the web UI?

Yes. datasette-agent exposes tools over the MCP protocol, so you can wire it directly into a Claude API call using the tools array. We tested this with claude-sonnet-4-5 in June 2026 — tool-call round-trips averaged 1.1 seconds for read queries and 3.2 seconds for write-approval flows.

How does user-permission scoping work in execute_write_sql?

datasette-agent reads the authenticated Datasette user's permission set before allowing the tool to fire. If the user lacks insert or update rights on the target table, the tool refuses and returns a permission-denied error — no approval dialog is shown. This mirrors the RBAC pattern we use in our crm MCP server.

Related Articles