Data agents
A data agent is a scheduled LLM loop bound to a single database. You
give it a natural-language standing goal (“keep the metrics rollup
fresh”, “triage new tickets rows and tag them”) and a cadence; it wakes
on that cadence, inspects the database, calls tools, optionally fetches
the web, and writes rows back. It is the LLM analog of a
scheduled query: same per-database, no-central-cron
design, but the body is a model with tools instead of a fixed SQL string.
Data agents are a feature, not a plan tier. They drain the same prepaid workspace balance as everything else — requests, rows, and AI tokens.
How it works
Section titled “How it works”Each agent’s config lives in the control plane; its execution state lives on the database’s own isolated instance, which owns the schedule. There is no central worker.
- On the cadence, the database wakes the agent.
- The agent runs a bounded tool-use loop (default 6 rounds per wake, 1–12) against your data, carrying a one-line summary of its previous run as working memory.
- Any write the agent makes can pause for human approval (see
Approval modes). A queued write fires a push and
lands in the same
/approve/:tokensurface as a/v1approval. - After each wake it records
last_status,last_summary,last_rows_read/written,last_rounds,last_duration_ms, andlast_error, then re-arms fornow + intervalSec.
The agent’s tools each wake: web_search, fetch_url, list_tables,
describe_schema, sample_table, validate_sql, explain,
run_select, run_write, run_batch, query_log, create_snapshot,
list_endpoints / call_endpoint, and ask_human (pause for a person).
Mount extra tools from external MCP servers.
A wake that fails doesn’t disable the agent — the next one tries again.
An agent parked on an approval skips its schedule until every pending
write (or ask_human question) is decided, so it never piles up duplicate
proposals.
Configuration
Section titled “Configuration”| Field | Type | Notes |
|---|---|---|
name | string | 1–80 chars. |
goal | string | 1–4000 chars. The standing instruction run each wake. |
intervalSec | number | Cadence. 60–604800 (1 min – 7 days). |
approvalMode | auto | destructive | writes | Default destructive. See below. |
maxIters | number | Tool-executing rounds per wake. 1–12, default 6. |
model | string | null | Workers AI model id; null uses the platform default. |
until | ISO 8601 | null | Stop instant — past it the agent disables itself. |
mcpServers | array | External MCP servers to mount as tools (≤4). |
enabled | boolean | Start running. Default true. |
Approval modes
Section titled “Approval modes”The agent’s own writes can be gated before they execute:
auto— fully autonomous, no writes pause.destructive(default) —DROP/DELETE/ALTER/TRUNCATEpause for approval; ordinaryINSERT/UPDATErun.writes— every mutation pauses for approval.
A paused write pushes a notification and waits in the
approval surface. Approve it from the console, the
PerSQL mobile app, or POST /api/approvals/:token/decide; the write then
runs server-side through the same auto-snapshot + query-log + pricing
pipeline as any other.
External MCP tools
Section titled “External MCP tools”Mount up to four external MCP servers as extra tools — the agent can call out to a search index, a ticketing system, or your own MCP worker mid-loop:
{ "mcpServers": [ { "label": "tickets", "url": "https://mcp.example.com", "headers": { "Authorization": "Bearer …" } } ]}headers (call-time auth) is write-only — it is accepted on create /
update but never returned. The DTO echoes label and url only.
Create one
Section titled “Create one”Four surfaces, same agent. Create / update / delete / run require an admin-role token (or workspace admin in the console); list is open to any read access.
Console
Section titled “Console”Open a database → the Agents tab → New agent. Fill in the goal, cadence, and approval mode; runs, last summary, and pending approvals show inline.
import { PerSQL } from "@persql/sdk";
const persql = new PerSQL({ token: process.env.PERSQL_TOKEN! }); // admin tokenconst db = persql.database("acme/metrics");
const agent = await db.agents.create({ name: "rollup-refresher", goal: "Recompute the daily_revenue rollup from orders for any day that changed since the last run.", intervalSec: 3600, // hourly approvalMode: "destructive", // INSERT/UPDATE run; DROP/DELETE pause maxIters: 6,});
// Run one cycle now (doesn't shift the schedule):const ran = await db.agents.run(agent.id);console.log(ran.lastStatus, ran.lastSummary);
await db.agents.update(agent.id, { intervalSec: 1800 });const { data } = await db.agents.list();await db.agents.delete(agent.id);See the SDK reference and the runnable scheduled data agent recipe.
persql agent create acme/metrics \ --name rollup-refresher \ --goal "Recompute the daily_revenue rollup for any day that changed." \ --interval 1h --approval destructive
persql agent list acme/metricspersql agent run acme/metrics rollup-refresher # run one cycle nowpersql agent delete acme/metrics rollup-refresher--interval accepts 3600, 30m, 1h, or 1d. Reference an agent by
name or id. See the CLI reference.
Any MCP runtime gets list_agents, create_agent,
update_agent, delete_agent, and run_agent — drive the same lifecycle
from Claude, Cursor, or your own agent.
Two parallel surfaces — cookie-authed for the console, bearer-authed for agents and SDK callers:
| Method | Console (cookie / CLI bearer) | Agent (/v1 bearer) |
|---|---|---|
GET | /api/namespaces/:ns/databases/:db/agents | /v1/db/:ns/:db/agents |
POST | /api/namespaces/:ns/databases/:db/agents | /v1/db/:ns/:db/agents |
PATCH | /api/namespaces/:ns/databases/:db/agents/:id | /v1/db/:ns/:db/agents/:id |
DELETE | /api/namespaces/:ns/databases/:db/agents/:id | /v1/db/:ns/:db/agents/:id |
POST | /api/namespaces/:ns/databases/:db/agents/:id/run | /v1/db/:ns/:db/agents/:id/run |
The /v1 mutating routes require an admin-role bearer. A
workspace-wide read is also available at
GET /api/namespaces/:ns/agents, which aggregates every database’s agents
(used by the dashboard “needs attention” view).
Limits
Section titled “Limits”- One bounded loop per wake (default 6 rounds, max 12) — the agent can’t spin forever inside a single cycle.
- Cadence floor is 60s, ceiling 7 days — same bounds as scheduled queries.
- Up to four external MCP servers per agent.
- No per-tier message cap and no per-token throughput cap. Every wake
drains the prepaid balance (requests, rows, and AI tokens); when it runs
out the agent’s next wake gets a
402and skips. Top up to resume. - A model is non-deterministic: a wake may legitimately do nothing if the
goal is already satisfied.
last_summaryrecords what it concluded.