Community Project - Built in collaboration with Claude Opus 4.5 to rapidly prototype Mastra with SurrealDB, following established Mastra store patterns.
This is a lightweight Mastra agent starter with SurrealDB as the agent's store that we used in a handful of other projects while testing. SurrealDB is a multi-model database with lots of powerful features for agents.
- Mastra v1 Compatible — Built for Mastra v1.0+ with subpath imports and domain-based storage
- SurrealDB Storage Adapter —
SurrealStoreextendsMastraCompositeStorewith domain classes (matches official Mastra store patterns) - SurrealDB Vector Store —
SurrealVectorextendsMastraVectorwith native HNSW indexing - Cross-Thread Semantic Recall — Agent recalls information across different conversation threads via
scope: 'resource' - Working Memory — Persistent user context and preferences across sessions
- Workflow Persistence — Snapshot and resume workflow executions
- Vector Similarity Search — HNSW-powered semantic search with metadata filtering
- Docker Setup — One command to start SurrealDB locally
- Example Agent — Working agent with tools, memory, and semantic recall
- Bun Compatible — Fast development with Bun runtime
- Node.js >=22.13.0
- Bun (recommended)
- Docker (for local SurrealDB)
# 1. Clone and setup
git clone https://github.com/jonathanprozzi/mastra-agent-surreal-starter.git
cd mastra-agent-surreal-starter
cp .env.example .env
# Add your ANTHROPIC_API_KEY or OPENAI_API_KEY to .env
# 2. Install dependencies
bun install
# 3. Start SurrealDB
docker-compose up -d
# 4. Apply database schema
bun run db:setup
# 5. Test the SurrealDB adapter
bun run scripts/test-surreal.ts
# 6. Run Mastra Studio
bun run dev
# Open http://localhost:4111├── src/mastra/
│ ├── agents/ # Agent definitions
│ ├── tools/ # Tool definitions
│ ├── workflows/ # Workflow definitions
│ ├── memory/ # Memory configuration (semantic recall, working memory)
│ ├── storage/ # SurrealDB adapters
│ │ ├── surreal-store.ts # Storage adapter facade
│ │ ├── domains/ # Domain classes (matches official Mastra patterns)
│ │ │ ├── memory.ts # Thread/message persistence
│ │ │ ├── workflows.ts # Workflow state management
│ │ │ ├── scores.ts # Evaluation scoring
│ │ │ └── observability.ts # Tracing and spans
│ │ ├── shared/ # Shared utilities and config
│ │ └── schema.surql # Database schema
│ ├── vector/ # Vector store
│ │ └── surreal-vector.ts # HNSW vector adapter
│ └── index.ts # Mastra instance
├── scripts/
│ ├── setup-db.ts # Apply schema to SurrealDB
│ ├── reset-db.sh # Reset database
│ ├── test-surreal.ts # Test storage adapter
│ └── test-vector.ts # Test vector adapter
├── examples/
│ ├── test-cross-thread-recall.ts # Cross-thread semantic recall demo
│ └── test-semantic-recall.ts # Full semantic recall example
├── docker-compose.yml # SurrealDB container
└── .env.example # Environment template
The adapter implements storage for all Mastra data types (9 tables):
| Table | Purpose |
|---|---|
mastra_threads |
Conversation threads |
mastra_messages |
Messages with optional embeddings |
mastra_workflow_snapshot |
Suspended workflow state |
mastra_ai_spans |
Observability spans (traces) |
mastra_evals |
Evaluation results |
mastra_scorers |
Scorer definitions |
mastra_scores |
Scoring run data |
mastra_resources |
Working memory |
mastra_agents |
Agent configurations |
import { SurrealStore } from "./src/mastra/storage";
const store = new SurrealStore();
await store.init();
// Get domain stores (v1 API)
const memory = await store.getStore('memory');
const workflows = await store.getStore('workflows');
// Save a thread
const thread = await memory.saveThread({
thread: {
id: "thread-1",
resourceId: "user-123",
title: "My Conversation",
metadata: {},
createdAt: new Date(),
updatedAt: new Date(),
},
});
// Save messages
await memory.saveMessages({
messages: [
{
id: "msg-1",
threadId: "thread-1",
role: "user",
content: [{ type: "text", text: "Hello!" }],
createdAt: new Date(),
type: "text",
},
],
});
// List messages from a thread
const { messages } = await memory.listMessages({ threadId: "thread-1" });
// Save resource (working memory)
await memory.saveResource({
resource: {
id: "user-123",
workingMemory: JSON.stringify({ theme: "dark" }),
metadata: {},
createdAt: new Date(),
updatedAt: new Date(),
},
});
// Close when done
await store.close();The SurrealVector class implements MastraVector for native HNSW vector search:
import { SurrealVector } from "./src/mastra/vector";
const vector = new SurrealVector();
// Create an index with HNSW
await vector.createIndex({
indexName: "embeddings",
dimension: 1536, // OpenAI embedding dimension
metric: "cosine",
});
// Upsert vectors with metadata
await vector.upsert({
indexName: "embeddings",
vectors: [embedding1, embedding2],
metadata: [{ label: "doc1" }, { label: "doc2" }],
ids: ["id1", "id2"],
});
// Query similar vectors
const results = await vector.query({
indexName: "embeddings",
queryVector: queryEmbedding,
topK: 10,
filter: { category: "docs" }, // Optional metadata filter
});
// results: [{ id, score, metadata }]Test the vector store:
bun run scripts/test-vector.tsDemonstrates that the agent can recall information from a different conversation thread:
bun run examples/test-cross-thread-recall.tsThis test:
- Creates Thread 1 with a cooking conversation (lasagna recipe)
- Creates Thread 2 and asks about cooking
- Verifies the agent recalls lasagna from Thread 1 while in Thread 2
This works because memory is configured with scope: 'resource' — the agent searches across all threads for a given user, not just the current thread.
Note on Mastra v1 Memory Defaults:
- Semantic recall is disabled by default in v1 (must opt-in via
semanticRecallconfig) - Default
lastMessagesis 10 (can be increased) options.generateTitleis top-level and disabled by default (opt-in required)- This project explicitly enables semantic recall for cross-thread knowledge retrieval
A comprehensive test covering multiple topics and cross-domain recall:
bun run examples/test-semantic-recall.tsThis test:
- Creates threads with programming topics (TypeScript, React)
- Creates threads with cooking topics (pasta carbonara, soufflé)
- Tests same-thread recall and cross-thread recall
- Verifies vector index statistics
# SurrealDB Connection
SURREALDB_URL=http://localhost:8000
SURREALDB_NS=mastra
SURREALDB_DB=development
SURREALDB_USER=root
SURREALDB_PASS=root
# LLM Provider (for agent reasoning)
ANTHROPIC_API_KEY=sk-ant-... # Recommended for Claude
# or
OPENAI_API_KEY=sk-... # Also works for GPT models
# Embeddings (required for semantic recall)
OPENAI_API_KEY=sk-... # Required - OpenAI embeddings for vector searchNote: Semantic recall requires OpenAI API key for embeddings (Claude doesn't have an embedding model). You can use Claude for reasoning and OpenAI for embeddings — this is a common pattern.
If you want to run schema setup in CI/CD and skip runtime DDL, pass disableInit and run bun run db:setup during deploy:
import { SurrealStore } from "./src/mastra/storage";
const store = new SurrealStore({ disableInit: true });| Command | Description |
|---|---|
bun run dev |
Start Mastra Studio |
bun run build |
Build for production |
bun run db:setup |
Apply schema to SurrealDB |
bun run db:reset |
Reset database (removes all data) |
| Command | Description |
|---|---|
bun run test |
Run all vitest tests |
bun run test:watch |
Run tests in watch mode |
bun run test:coverage |
Run tests with coverage report |
bun run test:storage |
Run only storage adapter tests |
bun run test:vector |
Run only vector store tests |
bun run test:quick |
Run quick validation scripts |
The test suite includes:
- Storage tests (
tests/storage.test.ts) - Thread, message, resource, workflow operations - Vector tests (
tests/vector.test.ts) - Index management, HNSW queries, metadata filtering - Integration tests (
tests/integration.test.ts) - Cross-thread recall, concurrent operations
If running alongside other projects:
| Project | SurrealDB | Redis |
|---|---|---|
| mastra-agent-surreal-starter | 8000 |
— |
| my-other-project | — | 6378 |
The SurrealStore class extends MastraCompositeStore from @mastra/core/storage, providing a SurrealDB-backed implementation of all storage operations. This follows the same pattern as official Mastra stores:
- PostgresStore (
@mastra/pg) - Uses domain classes (MemoryPG, WorkflowsPG, etc.) - LibSQLStore (
@mastra/libsql) - SQLite-compatible with WAL mode - MongoDBStore (
@mastra/mongodb) - Document-oriented NoSQL
Following the official Mastra store patterns, storage is organized into domain classes:
| Domain Class | Responsibility |
|---|---|
MemorySurreal |
Thread/message CRUD, context retrieval with vector search |
WorkflowsSurreal |
Workflow snapshot persistence and resume |
ScoresSurreal |
Evaluation and scoring data |
ObservabilitySurreal |
Tracing and span management |
AgentsSurreal |
Agent configuration persistence |
OperationsSurreal |
Core table operations (insert, update, delete) |
The SurrealStore facade composes these 6 domain classes and delegates operations accordingly.
- Record ID Handling: SurrealDB returns IDs like
table:⟨uuid⟩. We usetype::thing()in queries andnormalizeId()helper to handle this. - HNSW Vector Search: Uses
<|topK,effort|>syntax for O(log n) performance. The effort parameter (e.g., 500) is required. - Semantic Recall: Memory configured with
scope: 'resource'enables cross-thread knowledge retrieval.
- Retry Mechanism - Implement exponential backoff for connection issues
- Graph Relationships - Leverage SurrealDB's graph capabilities for agent relationships
This project was migrated from Mastra v0.24.9 to v1.0. For details on v1 breaking changes and migration steps, see the official Mastra v1 migration guide.
Feedback welcome!
Apache 2.0 — aligned with Mastra's licensing.