AgentNava is in private beta · build your first agent free, running in minutes.See what you can hire →
AgentNava · Build

Databases

Register a database once. The agents that name it can query it; no other agent can.

Register a database

A database belongs to the workspace and goes by a key you choose. Registering logs in first, so a wrong password fails here, with nothing saved, instead of in the middle of a conversation.

const shop = await ws.databases.create({
  key: 'shop',
  name: 'Shop DB',
  url: process.env.SHOP_DB_URL,   // postgres://reader:[email protected]:5432/shop?sslmode=require
});

Or give the same login as separate fields:

await ws.databases.create({
  key: 'crm',
  engine: 'mysql',
  host: 'crm.example.com',
  port: 3306,
  database: 'crm',
  username: 'reader',
  password: process.env.CRM_DB_PASSWORD,
});
FieldWhat it does
keyWhat agents name it by. Lowercase letters, digits and dashes. Not editable.
urlpostgres://, postgresql:// or mysql://. An sslmode in it sets tls.
tlsrequire (the default), verify-full or disable.
writableOff unless you set it. A read-only database refuses writes whatever SQL is sent.
The login is never read back

It is stored encrypted. Reading a database returns its host, port and name, never the password or the string it came from, and the agent never receives either.

Check a login without saving it

const result = await ws.databases.test({ url: process.env.SHOP_DB_URL });
// { ok: true, engine: 'postgres', tableCount: 3, tables: [...] }
// { ok: false, error: { code: 'connection_failed', message: '...' } }

Give an agent a database

Name the key in the agent's configuration. Naming it is the grant: a database answers only the agents whose configuration names its key.

await ws.agents.create({
  name: 'Ops reporter',
  instructions: 'Answer questions about orders from the shop database.',
  databases: ['shop'],
});

// or, for an agent that already exists
await agent.databases.attach('shop');
await agent.databases.list();     // ['shop']
await agent.databases.detach('shop');

Attaching publishes a version of the agent, like a knowledge base. A key that names no database is refused when you save.

Which conversations get it

A conversation keeps the version it started on, and a database answers only a version that names it. So attaching reaches conversations that start after it (and the version you deploy), not ones already open. Detaching works the other way: it takes effect on the very next query, everywhere, including conversations already under way.

What the agent sees

Its prompt lists its databases by key, with the engine, so it writes the right SQL:

## Databases available to you
- `shop`: Shop DB (PostgreSQL, read-only)

It queries with three tools, each naming the database by key:

ToolDoes
sql_list_tablesLists the tables
sql_describeLists one table's columns and types
sql_queryRuns one statement, with values passed as params

Results are capped, by default at 1,000 rows and 1 MB, and a statement stops after 30 seconds.

Look inside from your code

const shop = ws.database('shop');
await shop.tables();               // [{ schema: 'public', name: 'orders', kind: 'table' }]
await shop.describe('orders');     // [{ name: 'id', dataType: 'int4', ... }]
await ws.databases.list();

Remove a database

await ws.database('shop').delete();

Refused while any agent still names it, with an error that names those agents. Detach it from them first. That refusal is what keeps a key that was valid when you published valid afterwards.