Spinrun Docs
Build

The agent database

Every agent gets its own SQLite database, and the workspace gets shared tables. The agent creates the tables; people browse and export them.

An agent that runs more than once needs somewhere to keep what it learned: the leads it emailed, the prices it saw yesterday, the tickets it already filed. The agent database is that place. It is not Brain — Brain is knowledge you give the agent to read; this is data the agent writes.

Two scopes

ScopeWho reaches itNamed in the document as
agentThis agent only. Its own SQLite database.tools.database: "agent"
workspaceEvery agent in the workspace granted the shared scope. One database per workspace.tools.database: "workspace" (which includes the agent's own)

"off" (or an absent field) means no database. The switch lives under Abilities in the builder — Own database, then Shared workspace tables — and republishing mints a key that carries toolkit:data, plus the gateway:shared_data capability for the workspace scope.

The workspace scope is a second decision because other agents read what this one writes. Use it to hand work between agents: a research agent fills leads, an outreach agent drains it.

The four tools

The agent sees them as ordinary tools in its list, under the data toolkit. Every call takes an optional scope ("agent" by default).

ToolDoesRisk tier
DATA_DESCRIBE_SCHEMALists tables, columns, keys and row counts. Free.read
DATA_QUERY_SQLOne SELECT or WITH statement. Returns up to max_rows rows (100 by default, 1000 at most) and a truncated flag.read
DATA_WRITE_SQLOne INSERT, UPDATE, DELETE, CREATE TABLE / INDEX / VIEW or ALTER TABLE. RETURNING is supported.write
DATA_DROP_TABLEDrops one table or view by name.destructive

The tiers matter because they are what the agent's approval setting acts on: with approval: "write" every write pauses for a person, and a drop pauses for everyone. Inside a sandbox run, where nothing can pause, DATA_DROP_TABLE is refused outright.

{ "name": "DATA_WRITE_SQL", "arguments": {
  "sql": "CREATE TABLE IF NOT EXISTS leads (id INTEGER PRIMARY KEY, email TEXT UNIQUE, stage TEXT, last_touch TEXT)"
} }
{ "name": "DATA_WRITE_SQL", "arguments": {
  "sql": "INSERT OR IGNORE INTO leads (email, stage) VALUES (?, ?)",
  "params": ["ada@northwind.io", "new"]
} }
{ "name": "DATA_QUERY_SQL", "arguments": {
  "sql": "SELECT email FROM leads WHERE stage = 'emailed' AND last_touch < date('now', '-4 days')",
  "max_rows": 50
} }

Bind values with ? and pass them in params; never quote them into the SQL. params may hold strings, numbers, booleans and null, at most 100 of them.

What the SQL may not do

The dialect is SQLite, one statement per call. The store refuses, whatever the statement looks like:

  • ATTACH, DETACH, PRAGMA, VACUUM and extensions;
  • triggers, virtual tables, temporary objects;
  • the engine's own tables (sqlite_*);
  • a DROP anywhere in a write — dropping is its own tool, so it gets its own tier.

A query that somehow writes is rolled back. The refusal comes back to the agent as the tool's error, with the reason, so it can correct the statement.

Limits

Rows per query100 by default, 1000 at most; larger results are offloaded like any big tool result
Tables per databasePro and Business 50, Enterprise 200
Database sizePro and Business 100 MB, Enterprise 1 GB
Creditsa query 1, a write or drop 2, describe free

From a Connect client

The same four tools are on the workspace endpoint for a person's own MCP client — a spr_ key or an OAuth grant from Claude, ChatGPT or Cursor — on any plan that sells the store. The defaults differ, because a person has no agent:

Agent keyConnect client
scope defaultagent (its own tables)workspace (the shared tables)
scope: "agent"its own tablesrefused: a client has no database of its own
agent: "<slug or name>"that agent's tables, read-only, with the shared-data grantthat agent's tables, read-only
DATA_WRITE_SQL on the shared tableswith the shared-data grantany member
DATA_DROP_TABLE on the shared tableswith the grant, at the destructive tierworkspace admins only

Two rules carry the weight. An agent's own tables are its to write: a client (or another agent) reads them by naming the agent and can never write or drop them, because an edit behind the agent's back would be invisible to it. A drop from a client takes an admin: there is no approval card on the MCP path, so the one irreversible action is fenced by role as well as by any Enhanced Control rule on the destructive tier.

{ "name": "DATA_QUERY_SQL", "arguments": {
  "agent": "outreach",
  "sql": "SELECT email, stage FROM leads WHERE stage = 'replied'"
} }

Team endpoints (/mcp/t/<slug>) do not list the toolkit: a team endpoint exists to narrow what a client reaches, and one database per workspace cannot be narrowed.

Where it lives, and what people see

Each database is a SQLite store pinned to the EU, with thirty days of point-in-time history and a nightly copy to the blob store. Nothing of it is mirrored elsewhere.

People browse it on the agent's Data tab (and the workspace's Data page under Resources): the tables, a paged grid of one table, a CSV export. Nobody edits rows from there — a person's edit would be invisible to the agent that owns the table. An admin can drop a table.

Rows read back into the agent are wrapped as untrusted data, the same as any tool result: an agent may have stored text it scraped from anywhere, and persistence must not turn that into instructions.

On this page