Skip to main content

OracleDB storage

The OracleDB storage provider stores Mastra application state in Oracle Database. It implements Mastra's composite storage interface, so one OracleStore instance can back memory, workflow snapshots, observability, scores, scorer definitions, MCP client metadata, and agent registry data.

Installation
Direct link to Installation

npm install @mastra/oracledb@latest

Usage
Direct link to Usage

import { OracleStore } from '@mastra/oracledb'

const storage = new OracleStore({
id: 'oracle-storage',
user: process.env.ORACLE_DATABASE_USER,
password: process.env.ORACLE_DATABASE_PASSWORD,
connectString: process.env.ORACLE_DATABASE_CONNECT_STRING,
})

Use it with Mastra:

import { Mastra } from '@mastra/core/mastra'

export const mastra = new Mastra({
storage,
})

Parameters
Direct link to Parameters

id:

string
Unique identifier for this storage instance.

user?:

string
Oracle Database user. Required unless using pool or externalAuth.

password?:

string
Password for the Oracle Database user. Required unless using pool or externalAuth.

connectString?:

string
Oracle connect string, service name, TNS alias, or Autonomous Database connect descriptor. Required unless using pool.

pool?:

oracledb.Pool
Existing Oracle connection pool. When provided, Mastra uses the pool but doesn't close it when store.close() is called.

poolManager?:

OraclePoolManager
Shared Oracle pool manager. Use this to share one Oracle pool between OracleStore and OracleVector.

schemaName?:

string
Oracle schema name used to qualify storage tables.

poolMin?:

number
= 0
Minimum number of Oracle pool connections.

poolMax?:

number
= 4
Maximum number of Oracle pool connections.

poolIncrement?:

number
= 1
Number of connections to add when the pool grows.

configDir?:

string
Directory containing Oracle Network configuration files such as tnsnames.ora.

walletLocation?:

string
Oracle wallet directory for mTLS connections such as Autonomous Database.

walletPassword?:

string
Password for the Oracle wallet, when required by the wallet configuration.

externalAuth?:

boolean
Use Oracle external authentication instead of username/password authentication.

disableInit?:

boolean
= false
When true, automatic schema initialization is disabled. Use this when schema changes are applied separately before the app starts.

messageBatchSize?:

number
= 200
Number of messages sent per Oracle executeMany call when saving messages. The operation still commits once at the transaction boundary.

skipDefaultIndexes?:

boolean
When true, default storage indexes aren't created during initialization.

indexes?:

OracleCreateIndexOptions[]
Custom Oracle index definitions to create during initialization. Indexes are routed to the storage domain that owns the target table.

migrationTableName?:

string
= 'MASTRA_ORACLE_MIGRATIONS'
Oracle table used to track storage schema migrations.

vectorRegistryTableName?:

string
OracleVector registry table used to discover semantic-recall vector tables when threads or messages are deleted. Set this to match OracleVector's registryTableName when that option is customized.

Connection examples
Direct link to Connection examples

The basic username/password constructor is shown above. For Autonomous Database, add wallet options to the same constructor:

const storage = new OracleStore({
id: 'oracle-storage',
user: process.env.ORACLE_DATABASE_USER,
password: process.env.ORACLE_DATABASE_PASSWORD,
connectString: process.env.ORACLE_DATABASE_CONNECT_STRING,
walletLocation: process.env.ORACLE_DATABASE_WALLET_DIR,
walletPassword: process.env.ORACLE_DATABASE_WALLET_PASSWORD,
configDir: process.env.ORACLE_DATABASE_CONFIG_DIR,
})

For external authentication, set externalAuth: true and omit password. To reuse an existing oracledb.Pool, pass it as pool. Mastra uses it but doesn't close it.

OracleStore backs memory, workflow snapshots, observability, scores, scorer definitions, MCP client metadata, and agent registry data. When using the store outside a Mastra instance, call await storage.init() and access a domain with await storage.getStore('memory').

Initialization
Direct link to Initialization

When you pass OracleStore to Mastra, init() is called automatically before storage operations run. If you use OracleStore directly, call init() before reading or writing:

await storage.init()
warning

If initialization is disabled or skipped, storage operations require the Oracle tables and indexes to already exist.

OracleStore.init() runs repeatable migrations and records the result in the migration ledger table. The default ledger table is MASTRA_ORACLE_MIGRATIONS.

await storage.migrate()
const history = await storage.listMigrations()

Repeatable migrations are idempotent. They reconcile the tables and indexes owned by each storage domain on startup, which lets new domain indexes or compatible schema additions apply without changing application code.

Initialization also creates the provider's default indexes for common Mastra query paths. Use skipDefaultIndexes when indexes are managed separately, or pass indexes for custom Oracle indexes. Custom definitions support Oracle options such as bitmap, online, invisible, parallel, compress, noLogging, and reverse, as well as function-based expressions like JSON_VALUE(...).

Custom indexes are useful when your app repeatedly filters on JSON metadata or when database administrators (DBAs) want to test an index before the optimizer uses it:

const storage = new OracleStore({
id: 'oracle-storage',
user,
password,
connectString,
indexes: [
{
name: 'idx_messages_status',
table: 'mastra_messages',
columns: [
"JSON_VALUE(metadata, '$.status' RETURNING VARCHAR2(32) NULL ON ERROR)",
'thread_id',
],
online: true,
invisible: true,
},
],
})

Use invisible for staged rollout, then remove it after validating query plans. Use skipDefaultIndexes: true only when a DBA-managed indexing strategy replaces the defaults.

Use disableInit: true when schema changes are applied by a separate deployment step or by a database administrator.

Schema export
Direct link to Schema export

Use exportSchemas() to generate Oracle DDL without connecting to a database. This is useful when schema changes are reviewed or applied outside application startup.

import { exportSchemas } from '@mastra/oracledb'

const ddl = exportSchemas({
schemaName: 'MASTRA_APP',
domains: [
'memory',
'workflows',
'observability',
'scores',
'scorerDefinitions',
'mcpClients',
'agents',
],
})

console.log(ddl)

domains defaults to every supported domain, including vector, when omitted.

Operational notes
Direct link to Operational notes

Use the same OraclePoolManager when OracleStore and OracleVector should share one Oracle connection lifecycle:

import { OracleStore, OracleVector } from '@mastra/oracledb'

const storage = new OracleStore({ id: 'oracle-storage', user, password, connectString })
const vector = new OracleVector({
id: 'oracle-vector',
poolManager: storage.getPoolManager(),
})

OracleStore exposes storage.db and await storage.getPool() for advanced use cases. When using these APIs directly, you're responsible for transaction boundaries and connection lifecycle.

JSON metadata, payloads, and snapshots are stored in native Oracle JSON columns and encoded server-side, so the rows are readable directly with standard Oracle JDBC tools such as DBeaver and SQL Developer.

Usage example
Direct link to Usage example

Adding OracleDB memory to an agent
Direct link to Adding OracleDB memory to an agent

src/mastra/agents/oracle-agent.ts
import { Agent } from '@mastra/core/agent'
import { Memory } from '@mastra/memory'
import { OracleStore } from '@mastra/oracledb'

const storage = new OracleStore({
id: 'oracle-storage',
user: process.env.ORACLE_DATABASE_USER,
password: process.env.ORACLE_DATABASE_PASSWORD,
connectString: process.env.ORACLE_DATABASE_CONNECT_STRING,
})

export const oracleAgent = new Agent({
id: 'oracle-agent',
name: 'Oracle Agent',
instructions: 'You are an assistant with persistent OracleDB-backed memory.',
model: 'openai/gpt-5.6-sol',
memory: new Memory({ storage }),
})