Skip to content

Guides

Agentic Models

Your tables become safe agent tools in one definition. REST, services, tools, prompt and dashboard views from a Drizzle table.

Giving an agent access to your data usually means writing routes, tools, and permission checks for every table, and keeping them in sync. An Agentic Model is a plain Drizzle table plus defineAgenticModel. From that one definition you get:

  • REST routes under /api/agentic/models/<name>, in the OpenAPI document.
  • Typed services: services.models.customer.list(...) in your server code.
  • Agent and MCP tools such as customer_list and customer_update, with approval rules.
  • Prompt resources, so the dashboard prompt can find and propose changes to records.
  • Automation outcome targets: an agent can write a reviewed field back.
  • A dashboard data view with the columns you choose.

Access rules and workspace scope apply to every one of them.

Defining a Model

The starter's customers model shows every part of a definition:

server/models/customers.ts
import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'
import { defineAgenticModel, timestamps, workspaceColumns } from '@nuxtagentic-pro/server/models'

export const customers = sqliteTable('customers', {
  id: integer().primaryKey({ autoIncrement: true }),
  ...workspaceColumns(),
  name: text().notNull(),
  email: text().notNull(),
  plan: text({ enum: ['free', 'pro'] })
    .notNull()
    .default('free'),
  summary: text(),
  ...timestamps(),
})

export default defineAgenticModel(customers, {
  name: 'customer',
  description: 'Paying customers of our product',
  scope: 'workspace',
  fields: {
    email: { description: 'Billing email', pii: true },
    summary: { description: 'Agent-written account summary' },
  },
  access: { list: 'member', get: 'member', create: 'admin', update: 'admin', delete: false },
  agent: { tools: ['list', 'get', 'update'], approval: { update: 'always' } },
  outcomes: { writable: ['summary'] },
  prompt: { label: 'name', searchable: ['name', 'email'] },
  ui: { icon: 'i-lucide-users', columns: ['name', 'plan', 'updatedAt'] },
})

In Nuxt, files in server/models are discovered. In Void, pass the model to defineVoidAgentic({ models: [customers] }). Then add the table to your schema and migrate it:

pnpm db:generate
pnpm db:migrate

The Nuxt starter's drizzle.config.ts already includes server/models/*.ts; the Void starter re-exports models from db/schema.ts.

Adding Ownership Columns

Scopes need to know who owns a row. Spread the column helpers into your table:

  • workspaceColumns() adds ownerId and workspace. Use it for workspace, workspace-owner, and function scopes.
  • ownerColumns() adds only ownerId, for the owner scope.
  • timestamps() adds createdAt and updatedAt.

id, ownerId, workspace, createdAt, and updatedAt are managed: clients and agents never set them, and ownerId and workspace never appear in responses.

Model Options

OptionDescription
nameSingular name used in routes, services, and tool names
labelDisplay name in the dashboard
descriptionTells agents and the prompt what the records are
scopeWhich rows an actor can see; see below
fieldsPer column: label, description, hidden, readonly, pii, schema (a Zod type)
accessPer action, the minimum role: member, admin, owner, or false
agenttools: the actions agents may call; approval: always or never per action
outcomeswritable: fields an automation record outcome may write
promptlabel: the column that names a record; searchable: columns for search
uiicon and columns for the dashboard data view

Field names are checked against the table at compile time: a typo in fields, outcomes, prompt, or ui is a TypeScript error.

Choosing Which Rows an Actor Sees

scope decides which rows each signed-in user can read and change:

ScopeRows the actor sees
workspaceEvery row in the current workspace (only their own in personal)
workspace-ownerTheir own rows in the current workspace
ownerTheir own rows in any workspace
(actor) => filterA portable filter you build from the actor

Creates set ownerId and workspace from the actor. Updates and deletes check the scope in the same SQL statement as the write, so a row cannot move out of scope between the check and the write.

Limiting Actions by Role

Each action in access needs the listed role or higher. A missing action, or false, means the route, service method, and tool do not exist. The same rule applies to REST, services, agent tools, MCP, and the prompt.

Giving Agents Model Tools

agent.tools: ['list', 'get', 'update'] registers customer_list, customer_get, and customer_update. Enable them on an agent by name:

---
tools: ['customer_list', 'customer_get', 'customer_update']
---

approval: { update: 'always' } holds every update for a person, showing the exact field changes. hidden fields never appear in responses or tool output; pii marks personal data in the OpenAPI schema and data views. A model with outcomes.writable fields also registers customer_update, because automation outcomes write through it.

Querying from Server Code

The services give your own routes the same typed, scoped access:

const { models } = await useAgentic(event)
const page = await models.customer.list({ filter: { plan: 'pro' }, pageSize: 20 })
const customer = await models.customer.get(42)
await models.customer.update(42, { summary: 'Renewed for two years.' })

list accepts page, pageSize (up to 100, default 50), search, sortBy, sortDir, columns, filters with logic (and or or), and the equality shorthand filter, where null matches empty values. Results are typed from the table. The services reference lists every method.

Querying from the Browser

useAgenticClient() calls the same REST routes from your pages:

const client = useAgenticClient()
const { data } = await client('/api/agentic/models/customer', { query: { search: 'acme' } })

The client sends the current workspace and forwards the session during SSR.

Writing Back from Automations

With outcomes: { writable: ['summary'] }, an automation whose payload names a record ({ "record": { "model": "customer", "id": 42 } }) can write its result to summary with a record outcome. See Outcomes.