@melandlabs/opencontext examples

August 21, 2026 · View on GitHub

Runnable code samples for every package published from the opencontext monorepo that has a Node-callable headline API. One file per package — each one is a self-contained program that imports the published @melandlabs/* package and exercises a real slice of its surface.

No mocks, no fakes, no framework glue. Most demos need no API keys or live network calls; the two that do (search() for live web queries, real embeddings) skip gracefully when the relevant env var or native binding isn't present.

Running them

git clone https://github.com/melandlabs/opencontext.git
cd opencontext/examples
pnpm install
pnpm test

pnpm test walks every demo in src/index.ts, prints one [OK ] / [SKIP] / [INFO] line per check, and exits non-zero if anything regresses. Expected output on a clean checkout:

  • ~140 [OK ] — every demo ran against the real API and asserted on real return values
  • 3 [SKIP]addChunk (needs the host app's Drizzle schema), live search() (needs BRAVE_SEARCH_API_KEY), and the live agent.run(...) call in demo 17 (needs ANTHROPIC_API_KEY / OPENAI_API_KEY / OPENROUTER_API_KEY)
  • 0 [FAIL]

Demos that touch the filesystem sandbox themselves under examples/.tmp/ and clean up after every run — that directory should be empty once pnpm test returns.

Requires Node 22.6+ (--experimental-strip-types is stable there). pnpm test runs the suite directly from .ts source; no build step is involved.

Out of scope here: browser-only packages (hooks, indexeddb, voice-kokoro), CJS-only integration leaves (weixin, whatsapp, …), and pure-namespace utilities (audit, config, db, insights, shared, i18n, api). These don't have a Node-callable headline API to demonstrate and are covered by the per-package vitest suites under packages/*/src/**/*.test.ts.

The demos, one file per package

DemoPackage(s) exercisedSkips
src/simple/00-facade.ts@melandlabs/opencontextchunkText, estimateTokens, getModelPricing, isUserType, createMemoryStore().searchUnifiedMemorymemory search skips if better-sqlite3 cannot build
src/simple/01-rag-chunk.ts@melandlabs/ragchunkText against a real multi-paragraph document
src/simple/02-rag-vector-store.ts@melandlabs/ragSQLiteVecStore opened against a real sqlite file, similaritySearch against vec0addChunk + populated similaritySearch skip (needs the host app's Drizzle schema)
src/simple/03-memory-store.ts@melandlabs/memory-storecreateMemoryStore(), searchUnifiedMemory() with real sources, plus the standalone createUnifiedSearch factoryskips if better-sqlite3 cannot build
src/simple/04-ai.ts@melandlabs/aiestimateTokens, getModelPricing, MODEL_PRICING table inspection
src/simple/05-contracts.ts@melandlabs/contractsUSER_TYPES, INTEGRATION_IDS, isUserType(), isIntegrationId()
src/simple/06-loop.ts@melandlabs/loopLOOP_PATHS constants, ensureDirs, readPreferences, writePreferences round-trip in a sandboxed $HOME
src/simple/07-env-config.ts@melandlabs/env-configisTauriMode, isServerMode, isProductionEnvironment, DEFAULT_AI_MODEL
src/simple/08-cron.ts@melandlabs/cronvalidateCronExpression, computeNextRun, determinism over a fixed instant
src/simple/09-ui-runtime.ts@melandlabs/ui-runtimeisTauri, isClient, isBrowser, getPlatformKind under Node
src/simple/10-storage.ts@melandlabs/storageLocalStorageProvider.save/load/delete, plus path-traversal defense (../../escape becomes .._.._escape inside the root)
src/simple/11-security.ts@melandlabs/securityTokenEncryption (Fernet) with a throwaway key, validateUrlForSSRF (rejects plain HTTP, loopback, RFC1918, cloud-metadata), isTrustedStorageUrl
src/simple/12-search.ts@melandlabs/searchneedsRealTimeInfo classifier on time-sensitive vs timeless queries, then live search()live search() skips without BRAVE_SEARCH_API_KEY
src/simple/13-integrations-core.ts@melandlabs/integrationscreateMinimalContext({}) noop providers, partial overrides, htmlToPlainText, buildSnippet, stripQuotedText
src/simple/14-local-embedding.ts@melandlabs/ai-ragLocalTransformersEmbeddingProvider (ONNX, default Xenova/all-MiniLM-L6-v2, 384 dims), getConfiguredEmbeddingProvider factory routing via EMBEDDING_PROVIDER=local, cosineSimilarity sanity checkinference calls skip if neither the HF cache nor the network is available (first run downloads ~30 MB of ONNX weights)
src/simple/15-http-server.ts@melandlabs/memory-store/httpstartHttpServer booted on a random high port with all three unified.* deps supplied (embedQuery from the local ONNX provider, in-memory searchKnowledge / searchInsights cosine indices), then real GET /health / POST /v1/raw-messages / POST /v1/search round-trips that assert warnings[] is empty and hits come backinference-dependent checks skip on the same condition as demo 14
src/simple/16-mcp-server.ts@melandlabs/opencontext — spawns opencontext mcp with --embedding-provider local --memory-backend sqlite-vec --name --version, then drives the daemon over stdio the way any MCP client would: full JSON-RPC handshake (initializenotifications/initializedtools/list), then memory.writeRawMessage (with embedOnInsert: true), memory.searchUnified (asserting ranked memory hits + no embedQuery warning), memory.getRawMessage, and memory.health. Mirrors the claude_desktop_config.json snippet in the README §4.inference-dependent checks skip on the same condition as demo 14
src/simple/17-ai-agent.ts@melandlabs/aiIAgent / BaseAgent / defineAgentPlugin / AgentRegistry / runAgentRuntimeRequest reachable from the root, plus the built-in StandaloneAgent (single LLM call) round-tripped through getAgentInstancelive agent.run(...) skips without ANTHROPIC_API_KEY / OPENAI_API_KEY / OPENROUTER_API_KEY

Daemon configuration

Both opencontext http and opencontext mcp (plus their standalone bins opencontext-memory-http / opencontext-memory-mcp) take the same --embedding-provider / --*-backend flag surface. Every flag also accepts an env-var equivalent so the same options can be set in docker / systemd units.

FlagEnv varValuesWires
--port <n>MEMORY_HTTP_PORTint (default 7421)HTTP listen port (http only)
--host <h>MEMORY_HTTP_HOSTstring (default 127.0.0.1)HTTP bind host (http only)
--name <s>MEMORY_MCP_NAMEstringMCP server name advertised to clients (mcp only)
--version <s>MEMORY_MCP_VERSIONstringMCP server version (mcp only)
--embedding-provider <name>EMBEDDING_PROVIDERlocal | openrouter | noneunified.embedQuery (default none)
--embedding-model <name>EMBEDDING_MODELstringprovider-specific model id
--memory-backend <name>MEMORY_BACKENDsqlite-vec | chroma | noneunified.searchRawMessagesAnn (default none)
--insights-backend <name>INSIGHTS_BACKENDsqlite-vec | chroma | noneunified.searchInsights (default none)
--insights-collection <name>INSIGHTS_COLLECTIONstring (default opencontext_insights)Chroma collection name for --insights-backend=chroma
--knowledge-backend <name>KNOWLEDGE_BACKENDchroma | noneunified.searchKnowledge (default none)
--knowledge-collection <name>KNOWLEDGE_COLLECTIONstring (default opencontext_knowledge)Chroma collection name for --knowledge-backend=chroma
--chroma-url <url>CHROMA_URLhttp URLrequired by any --*-backend=chroma

Three concrete recipes:

# 1. Local ONNX embedder + sqlite-vec ANN for the memory source. No API
#    key, no extra services. Covers `opencontext http` AND `opencontext mcp`.
opencontext http \
  --embedding-provider local \
  --memory-backend sqlite-vec

# 2. Wire everything via a running Chroma server (uses OpenRouter for
#    embeddings). Requires `OPENROUTER_API_KEY` in the environment.
OPENROUTER_API_KEY=sk-or-v1-... \
opencontext http \
  --embedding-provider openrouter \
  --chroma-url http://127.0.0.1:8000 \
  --memory-backend chroma \
  --insights-backend chroma \
  --knowledge-backend chroma

# 3. Bare daemon (default) — only /health works and /v1/search returns
#    three structured `*_not_configured` / `memory_search_failed`
#    warnings. The bin emits no extra logs.
opencontext http

--embedding-provider local and every --*-backend=chroma value require @melandlabs/ai-rag (a peer install — it pulls in @huggingface/transformers, chromadb, and ~30 MB of ONNX weights on first run). The bin fails with a clear remediation message if the package is missing.

POST /v1/raw-messagesembedOnInsert

When the daemon is booted with --embedding-provider local|openrouter (or the host wires its own unified.embedQuery), the HTTP route auto-fills any missing embedding on incoming messages if the request body carries "embedOnInsert": true. Without that flag, the server stores the row verbatim — clients that pre-embed client-side keep their full pipeline.

curl -X POST http://127.0.0.1:7421/v1/raw-messages \
  -H 'content-type: application/json' \
  -d '{
        "userId": "u-42",
        "embedOnInsert": true,
        "messages": [
          { "role": "user", "messageId": "m-1",
            "content": "User prefers dark mode",
            "platform": "test", "botId": "b-1",
            "timestamp": 1700000000000, "createdAt": 1700000000000 }
        ]
      }'
# → { "ok": true, "count": 1, "result": { "inserted": 1, "ids": [1] } }

The MCP writeRawMessage tool takes the same flag as arguments.embedOnInsert; demo 16 exercises it end-to-end.

Wiring into Claude Desktop / Cursor

The MCP demo (16) exercises the same flag surface the opencontext mcp CLI accepts. Drop this into claude_desktop_config.json (or Cursor → Settings → MCP) — no API key, no extra services:

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

Four tools are exposed: memory.health, memory.searchUnified, memory.writeRawMessage, memory.getRawMessage.

Representative snippets

src/simple/00-facade.ts — chunk and budget an article

import {
	chunkText,
	estimateTokens,
	getModelPricing,
} from "@melandlabs/opencontext";

const article = "OpenContext is a runtime substrate for context-aware agents.";

const chunks = chunkText(article, { maxChunkSize: 80, chunkOverlap: 10 });
const tokens = estimateTokens(article);
const embeddingPrice = getModelPricing("text-embedding-3-small");

The facade re-exports both @melandlabs/rag's and @melandlabs/ai's getModelPricing; the rag version wins the name collision and returns the per-million-token price (a number), not the chat pricing object. Import from @melandlabs/ai directly if you want the chat table — see 04-ai.ts.

import { SQLiteVecStore } from "@melandlabs/rag";

const store = new SQLiteVecStore(dbPath, schemaModule); // opens a real sqlite file
const results = await store.similaritySearch(embedding, k);
//   → { id, documentId, content, distance, metadata }[]  (empty array if nothing matches)
store.close();

SQLiteVecStore provisions a vec0 virtual table on top of sqlite-vec, a loadable extension, so this is real KNN — no service to call, no network involved. addChunk writes through Drizzle and therefore needs the host app's schema module; this is documented inline in the demo along with the parts that stand alone.

src/simple/06-loop.ts — on-disk preferences

import { LOOP_PATHS, ensureDirs, readPreferences, writePreferences } from "@melandlabs/loop";

ensureDirs(); // creates ~/.opencontext/loop/ if missing
const defaults = readPreferences();          // built-in defaults when no config.json yet
const merged = writePreferences({ intervalSec: 42, narrative: false });
//   → full preferences object with the patch applied on top of the defaults

Loop keeps its state in ~/.opencontext/loop/. The demo runs the filesystem-touching calls in a child process whose $HOME is a scratch directory, so the test never writes into your real home. In your own app you call these functions directly.