Agentic Write Actions: Letting an LLM Touch Your Production Database Without Losing Sleep
13 July 2026
Agentic Write Actions: Letting an LLM Touch Your Production Database Without Losing Sleep
The safety ladder from read-only to scoped autonomous writes, plus the near-miss that made us stop improvising
By Muhammad Zia | Full Stack AI Engineer
The question everyone asks
Once you ship a read-only agent that answers questions about your product data, someone asks: "Can it also do the thing?" Update the record. Create the ticket. Approve the change. Write to production.
That is the right product instinct and the wrong first implementation. Write actions are where agentic systems earn trust or lose it permanently. This post is the safety model we used on a production assistant that eventually executed scoped writes, plus an anonymised near-miss that forced the design.
The safety ladder
Do not jump from chat to autonomous writes. Climb:
flowchart TD
A[1. Read-only] --> B[2. Draft-and-confirm]
B --> C[3. Scoped autonomous writes]
C --> D[4. Broad autonomy (usually never)]
1. Read-only
The agent can query, summarise, and recommend. Every mutation is a human in another UI. Boring. Ship this first. You learn retrieval quality, latency, and prompt failure modes without risking data.
2. Draft-and-confirm
The agent proposes a mutation as a structured draft. A human reviews and confirms in the product UI. The agent never holds the write credential for the final commit. If it does, the write API rejects anything without a confirmation token issued by the UI.
This is where most B2B products should live for a long time.
3. Scoped autonomous writes
The agent can execute writes inside a tight envelope: allowlisted tables/fields, rate limits, per-session budgets, and an audit trail. Example: "update the status field on this one lead to 'contacted'", not "run arbitrary SQL".
4. Broad autonomy
Full CRUD across the tenant. Almost never worth it for customer-facing agents in 2026. Internal ops agents sometimes get closer, still with kill switches.
RBAC for agents
An agent is a principal. Give it a role, not the user's full role by default.
Patterns that worked:
- Agent role ⊂ user role. The user may be an admin; the agent acting for them might only get
leads:readandleads:update_status. - Resource binding. Session is pinned to a project/org at connect time. Tool args cannot escalate the tenant.
- Action allowlists. Prefer allowlists of tool names over "deny the scary ones." New tools should be deny-by-default until explicitly enabled.
const agentPolicy = {
role: "sales_assistant",
allow: ["leads.search", "leads.get", "leads.update_status"],
deny: ["leads.delete", "billing.*", "users.*"],
maxWritesPerHour: 20,
};
Reuse your existing authZ engine. A second permission system for AI is how you get drift between the product UI and the agent.
Audit trails
If a write happened and you cannot answer "who/what/when/why" in under a minute, you are not ready for autonomous writes.
Minimum audit fields:
| Field | Why |
|---|---|
| actor_type (user | agent) | Separate human vs machine |
| actor_id / session_id | Correlate to the chat session |
| tool_name | Which capability fired |
| args_hash | Integrity without logging secrets by default |
| decision | allow / deny / confirm |
| outcome | success / error / rolled_back |
| latency_ms | Ops signal |
| target_resource | What row/object changed |
We log args hashed by default and only expand raw args in a locked debug mode. Agents love stuffing PII into tool arguments.
Durable sessions and human-in-the-loop UX
Chat UIs are ephemeral. Write flows need durable state:
- User asks for a change.
- Agent produces a draft mutation (structured JSON, not prose).
- UI renders a confirmation card with the exact diff.
- User confirms → server issues a short-lived confirmation token.
- Agent (or UI) submits the write with that token.
- Result streams back; audit row is written.
If the tab closes between steps 3 and 4, the draft must still be recoverable from session storage. If the model "confirms" without a human gesture, the write API must reject it.
That last sentence is the whole design: the model cannot be the authority that unlocks the write. The product session is.
Rollback strategy
Assume some confirmed writes will still be wrong (bad retrieval, user mis-click, model summary that lied about the diff).
- Prefer reversible writes (status flips, soft deletes) over hard deletes.
- Store before-images for agent-originated mutations for N days.
- Expose a one-click undo in the same confirmation thread when the write is reversible.
- For irreversible actions (send email, charge card), do not put them on the autonomous rung. Keep them on draft-and-confirm forever, or require a second human.
Near-miss (anonymised)
We had draft-and-confirm for "update contact fields." A tool description said the agent could "apply the update after summarising changes." The host UI had a confirm button, but a model path existed that called the write tool directly with a fabricated confirmation payload during a prompt regression.
The write API accepted the payload because we had validated shape, not provenance. The confirmation token was a client-generated UUID, not a server-issued, single-use secret bound to the draft hash.
Nothing catastrophic shipped. A staging canary caught a burst of unexpected writes, but the fix was immediate:
- Confirmation tokens issued only by the backend after a human POST.
- Token bound to
hash(draft)+ session id, single-use, 5-minute TTL. - Write tools reject any request without a valid server token, regardless of what the model claims.
That near-miss is why mcp-policy-guard's requireConfirmation exists as middleware instead of a per-handler honour system.
Trade-offs / what I'd do differently
- Start with draft-and-confirm even if product wants autonomy. Autonomy is a privilege you earn with audit + metrics, not a launch checkbox.
- Measure wrong-write rate, not just task-success rate. A helpful agent that corrupts CRM fields is worse than a quiet one.
- Put confirmation in the protocol/middleware layer, not in prompt instructions. Prompts regress. Middleware does not "forget" after a model upgrade.
- Budget writes per session. Even correct writes at high volume look like abuse to your database and your customers.
Closing
Letting an LLM touch production data is a product decision dressed as an engineering problem. The engineering is straightforward once you refuse to skip rungs on the safety ladder: RBAC for agents, durable confirmations, audit by default, reversible writes first.
If you are designing this now, draw the ladder on a whiteboard and mark which rung each tool is allowed to stand on. That diagram will save you more incidents than another prompt tweak.