memvault
v0.0.3
Published
Persistent, tenant-isolated memory tools for AI agents. Drop-in tools for Vercel AI SDK, OpenAI, and Anthropic. Prisma-powered.
Maintainers
Readme
memvault
Persistent, tenant-isolated memory tools for AI agents. Prisma-powered. Drop-in for Vercel AI SDK, OpenAI, and Anthropic.
No cloud. No $249/mo. Just your Postgres.
Install
npm install memvaultSetup
1. Add to your Prisma schema:
model MemvaultMemory {
id String @id @default(cuid())
tenantId String
type String @default("general")
content String
metadata Json?
tags String[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
expiresAt DateTime?
@@index([tenantId])
@@index([tenantId, type])
@@map("memvault_memories")
}2. Migrate:
npx prisma migrate dev --name add-memvault3. Create vault:
import { MemVault } from "memvault"
const vault = new MemVault({ db: prisma.memvaultMemory })Usage
Vercel AI SDK
import { createMemVaultTools } from "memvault/ai-sdk"
import { generateText } from "ai"
import { anthropic } from "@ai-sdk/anthropic"
const tools = createMemVaultTools({ vault, tenantId: user.id })
const result = await generateText({
model: anthropic("claude-sonnet-4-6"),
system: "Always recall user memories before responding. Save preferences with memvault_remember.",
tools,
messages,
maxSteps: 10,
})Anthropic SDK
import { createMemVaultTools } from "memvault/anthropic"
const { tools, handleToolCall } = createMemVaultTools({ vault, tenantId: user.id })
// Agentic loop — keep calling until no more tool use
while (true) {
const response = await anthropic.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1024,
system: "Always recall user memories before responding. Save preferences with memvault_remember.",
tools,
messages,
})
messages.push({ role: "assistant", content: response.content })
const toolUses = response.content.filter((b) => b.type === "tool_use")
if (!toolUses.length) break
const results = []
for (const tu of toolUses) {
const result = await handleToolCall(tu.name, tu.input)
results.push({ type: "tool_result", tool_use_id: tu.id, content: result })
}
messages.push({ role: "user", content: results })
}OpenAI SDK
import { createMemVaultTools } from "memvault/openai"
const { tools, handleToolCall } = createMemVaultTools({ vault, tenantId: user.id })
while (true) {
const response = await openai.chat.completions.create({
model: "gpt-5.4",
tools,
messages,
})
const msg = response.choices[0].message
messages.push(msg)
if (!msg.tool_calls?.length) break
for (const tc of msg.tool_calls) {
const result = await handleToolCall(tc.function.name, JSON.parse(tc.function.arguments))
messages.push({ role: "tool", tool_call_id: tc.id, content: result })
}
}System Prompt
Add this to your system prompt for consistent recall behavior across all models:
Always call memvault_recall at the start of each conversation before responding.
Save anything the user tells you about their preferences or context with memvault_remember.Without this, some models may skip recall unless explicitly instructed.
Tools
| Tool | When the model uses it |
|------|----------------------|
| memvault_recall | Start of conversation, or when user asks about preferences |
| memvault_remember | When user shares preferences, context, or feedback |
| memvault_update | When existing info changes |
| memvault_forget | When user asks to forget something |
Tenant Isolation
Every operation is scoped to a tenantId. No tenant can read or write another's memories — enforced at the query level, not the application level.
const alice = vault.tenant("alice")
const bob = vault.tenant("bob")
await alice.remember({ content: "Alice's preference" })
await bob.recall() // [] — Bob sees nothingStandalone API
const tenant = vault.tenant("user-123")
await tenant.remember({ content: "Prefers dark mode", type: "preference", tags: ["ui"] })
await tenant.recall({ type: "preference" })
await tenant.recall({ search: "dark" })
await tenant.update(id, { content: "Switched to light mode" })
await tenant.forget(id)
await tenant.forgetAll()
await tenant.count()
// TTL — auto-expires after N seconds
await tenant.remember({ content: "Temp session context", ttl: 3600 })Memory Types
Built-in: preference, fact, feedback, project, reference, general
Any string works — types are just a filter. Use whatever makes sense for your app.
API Reference
MemVault
new MemVault({ db: prisma.memvaultMemory })
vault.tenant(tenantId: string): TenantVaultTenantVault
tenant.remember(input: MemoryInput): Promise<Memory>
tenant.recall(filter?: MemoryFilter): Promise<Memory[]>
tenant.get(id: string): Promise<Memory | null>
tenant.update(id: string, input: MemoryUpdate): Promise<Memory>
tenant.forget(id: string): Promise<void>
tenant.forgetAll(): Promise<number>
tenant.count(filter?: MemoryFilter): Promise<number>createMemVaultTools(config)
{ vault: MemVaultInstance, tenantId: string }Returns { tools, handleToolCall } for Anthropic/OpenAI, or a tools object for AI SDK.
License
MIT
