Developer Guide - Building with OpenContext

August 18, 2026 ยท View on GitHub

This guide shows you how to integrate OpenContext into your application. We'll cover common integration patterns, backend selection, and production deployment.

Quick Integration Checklist

Before you integrate, decide:

  • Storage backend: SQLite (desktop), Postgres (server), or Chroma (managed)
  • Embedding provider: Local (no API key) or cloud (OpenRouter, OpenAI)
  • Transport surface: Direct import, HTTP server, or MCP
  • Deployment: Self-hosted or containerized

Integration Patterns

Pattern 1: Embedded in a Node.js App

The simplest integration - import directly into your code:

pnpm add @melandlabs/opencontext
// memory-service.ts
import { createMemoryStore, getRawMessageManager } from "@melandlabs/opencontext";

let store: Awaited<ReturnType<typeof createMemoryStore>>;

export async function initMemory() {
  store = await createMemoryStore({
    dbPath: process.env.MEMORY_DB_PATH || "./memory.db",
  });
}

export async function rememberFact(userId: string, content: string) {
  const messages = await getRawMessageManager();
  const now = Date.now();

  await messages.storeMessages([{
    messageId: `msg-${now}-${userId}`,
    userId,
    content,
    platform: "my-app",
    botId: "default",
    timestamp: now,
    createdAt: now,
  }]);
}

export async function recallFacts(userId: string, query: string, limit = 10) {
  return store.search({ userId, query, limit });
}

Use it in your app:

// app.ts
import { initMemory, rememberFact, recallFacts } from "./memory-service";

async function handleUserMessage(userId: string, message: string) {
  // Remember what the user said
  await rememberFact(userId, message);

  // Recall relevant context
  const context = await recallFacts(userId, `Context for: ${message}`);

  // Use context in your response
  return generateResponse(message, context.results);
}

Pattern 2: HTTP Server (Microservice)

Run OpenContext as a standalone HTTP service:

# Start the server
npx @melandlabs/opencontext http \
  --embedding-provider local \
  --memory-backend sqlite-vec \
  --host 0.0.0.0 \
  --port 7421

Or use npx without installing:

npx -y @melandlabs/opencontext http \
  --embedding-provider local \
  --memory-backend sqlite-vec

Call from your app:

// memory-client.ts
const MEMORY_URL = process.env.MEMORY_URL || "http://127.0.0.1:7421";

async function recallFacts(userId: string, query: string) {
  const response = await fetch(`${MEMORY_URL}/v1/search`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ userId, query, limit: 10 }),
  });

  return response.json();
}

async function rememberFact(userId: string, content: string) {
  const now = Date.now();
  const response = await fetch(`${MEMORY_URL}/v1/raw-messages`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      userId,
      embedOnInsert: true,
      messages: [{
        messageId: `msg-${now}`,
        role: "user",
        content,
        platform: "my-app",
        botId: "default",
        timestamp: now,
        createdAt: now,
      }],
    }),
  });

  return response.json();
}

Pattern 3: MCP Server (for AI Agents)

Integrate with coding agent integration:

Installation (coding agent integration):

Add the OpenContext MCP server to your coding agent's configuration:

{
  "mcpServers": {
    "opencontext": {
      "command": "npx",
      "args": [
        "-y",
        "@melandlabs/opencontext",
        "mcp",
        "--embedding-provider", "local",
        "--memory-backend", "sqlite-vec",
        "--name", "MyMemory",
        "--version", "1.0.0"
      ]
    }
  }
}

Tools exposed:

  • memory.health - Check if the server is running
  • memory.search - Search memory (set synthesize: true for LLM-synthesized answers)
  • memory.writeRawMessage - Store messages
  • memory.getRawMessage - Retrieve a message

Using from an agent:

// Your agent can now call these tools via MCP
// Your coding agent will automatically expose them

Backend Selection Guide

Choose your backend based on your deployment:

Desktop App (Tauri, Electron)

import { createMemoryStore } from "@melandlabs/opencontext";

const store = await createMemoryStore({
  db: {
    type: "sqlite-vec",
    path: "./memory.db",  // Local file
  },
});

Pros: No external dependencies, fast local access Cons: Single-user only

Server / Multi-user

import { createMemoryStore, registerPostgresFactory } from "@melandlabs/opencontext";
import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";

// Register Postgres factory
const client = postgres(process.env.DATABASE_URL!);
const db = drizzle(client);

registerPostgresFactory(async () => ({
  storeMessages: async (messages) => { /* your impl */ },
  getMessages: async (opts) => { /* your impl */ },
  // ... implement PostgresRawMessageManagerLike
}));

const store = await createMemoryStore({
  db: { getDb: () => db },
});

Pros: Multi-user, scalable, backups Cons: Requires Postgres setup

Managed Vector Store (Chroma)

const store = await createMemoryStore({
  dbPath: "./raw.db",
  vector: {
    backend: "chroma",
    chroma: {
      url: process.env.CHROMA_URL || "http://127.0.0.1:8000",
      rawMessagesCollection: "raw_messages",
      insightsCollection: "insights",
    },
  },
});

Pros: Scalable vector search, separate storage Cons: Additional service to run

Configuration Examples

Full Local Setup (No API Keys)

import { createMemoryStore, LocalTransformersEmbeddingProvider } from "@melandlabs/opencontext";

const embedder = new LocalTransformersEmbeddingProvider({
  modelName: "Xenova/all-MiniLM-L6-v2",
});

const store = await createMemoryStore({
  dbPath: "./memory.db",
  unified: {
    embedQuery: async ({ query }) => {
      return await embedder.embedQuery(query);
    },
  },
});

Cloud Embeddings (Better Quality)

const store = await createMemoryStore({
  db: {
    type: "sqlite-vec",
    path: "./memory.db",
  },
  unified: {
    embedQuery: async ({ query }) => {
      const response = await fetch("https://openrouter.ai/api/v1/embeddings", {
        method: "POST",
        headers: {
          "Authorization": `Bearer ${process.env.OPENROUTER_API_KEY}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          model: "text-embedding-3-small",
          input: query,
        }),
      });
      const data = await response.json();
      return data.data[0].embedding;
    },
  },
});

Production Deployment

Docker Compose

# docker-compose.yml
version: '3.8'
services:
  opencontext:
    image: node:22
    working_dir: /app
    command: npx -y @melandlabs/opencontext http --host 0.0.0.0 --port 7421 --embedding-provider local --memory-backend sqlite-vec
    volumes:
      - ./data:/app/data
    environment:
      - MEMORY_STORE_DB_PATH=/app/data/memory.db
    ports:
      - "7421:7421"
    restart: unless-stopped

systemd Service

# /etc/systemd/system/opencontext.service
[Unit]
Description=OpenContext Memory Service
After=network.target

[Service]
Type=simple
User=opencontext
WorkingDirectory=/opt/opencontext
ExecStart=/usr/bin/npx -y @melandlabs/opencontext http --host 0.0.0.0 --port 7421 --embedding-provider local --memory-backend sqlite-vec
Restart=always
RestartSec=10
Environment=MEMORY_STORE_DB_PATH=/var/lib/opencontext/memory.db

[Install]
WantedBy=multi-user.target

Enable and start:

sudo systemctl daemon-reload
sudo systemctl enable opencontext
sudo systemctl start opencontext
sudo systemctl status opencontext

Environment Variables

All CLI flags have environment variable equivalents:

FlagEnvironment VariableDefault
--portMEMORY_HTTP_PORT7421
--hostMEMORY_HTTP_HOST127.0.0.1
--embedding-providerEMBEDDING_PROVIDERnone
--embedding-modelEMBEDDING_MODEL(provider default)
--memory-backendMEMORY_BACKENDnone
--insights-backendINSIGHTS_BACKENDnone
--knowledge-backendKNOWLEDGE_BACKENDnone
--chroma-urlCHROMA_URL(required for chroma)

Testing Your Integration

// test/memory.test.ts
import { createMemoryStore, getRawMessageManager } from "@melandlabs/opencontext";
import { describe, it, expect, beforeAll } from "vitest";

describe("Memory Integration", () => {
  let store: Awaited<ReturnType<typeof createMemoryStore>>;

  beforeAll(async () => {
    process.env.MEMORY_STORE_DB_PATH = ":memory:";  // In-memory SQLite
    store = await createMemoryStore();
  });

  it("should remember and recall facts", async () => {
    const messages = await getRawMessageManager();
    const now = Date.now();

    await messages.storeMessages([{
      messageId: "test-1",
      userId: "test-user",
      content: "Test fact",
      platform: "test",
      botId: "test-bot",
      timestamp: now,
      createdAt: now,
    }]);

    const results = await store.search({
      userId: "test-user",
      query: "test",
      limit: 5,
    });

    expect(results.count).toBe(1);
    expect(results.results[0].content).toContain("Test");
  });
});

Troubleshooting

Module not found errors

# Reinstall dependencies
rm -rf node_modules pnpm-lock.yaml
pnpm install

Native module build failures

# Install build tools (macOS)
xcode-select --install

# Install build tools (Ubuntu)
sudo apt-get install build-essential python3

# Rebuild native modules
pnpm rebuild

Database locked errors

SQLite doesn't support concurrent writes. Use:

// Connection pooling or write queue
// Or switch to Postgres for multi-writer scenarios

Next Steps


Sources: