Show HN: Open-source toolkit for AI memory that scales Lithium, an open-source toolkit for AI memory, launched with a PostgreSQL-based storage engine that uses hierarchical versioned storage to scale tree-structured data. The toolkit, which includes zero runtime dependencies and built-in versioning, claims to outperform graph and vector databases for tree queries by using PostgreSQL's ltree extension for index-backed subtree lookups instead of graph traversal. Hierarchical versioned storage on PostgreSQL ltree. Scoped retrieval, built-in versioning, zero runtime deps. js const lithium = new Lithium drizzleAdapter db ; await lithium.clusters.create { name: "infra" } ; await lithium.clusters.create { name: "database", parentPath: "infra" } ; const context = await lithium.getContext { path: "infra" } ; Memory graphs don't scale for tree-structured data. Graph traversal becomes a bottleneck. Vector search gives you "similar to X" when you need "everything under X." PostgreSQL's ltree handles tree queries significantly faster. Index-backed subtree lookups, not traversal. Lithium wraps it in a clean TypeScript API with built-in versioning. | Lithium | Graph DBs | Vector DBs | | |---|---|---|---| Structure | Tree hierarchy | Arbitrary graph | Flat | Query speed | ltree index-backed | Graph traversal | ANN search | Retrieval | Deterministic, scoped | Pattern matching | Fuzzy, similarity | Versioning | Built-in, immutable | Manual | Overwrite | Infrastructure | Your existing Postgres | Separate service | Separate service | | Package | What | Size | |---|---|---| @lithium-ai/core | Zero-dep storage engine | | @lithium-ai/postgres | PostgreSQL ltree adapter | | @lithium-ai/drizzle | Drizzle ORM adapter | | @lithium-ai/mcp | MCP server for AI tools | Prerequisites: PostgreSQL with ltree extension. With Drizzle: npm install @lithium-ai/core @lithium-ai/drizzle drizzle-orm js import { Lithium } from "@lithium-ai/core"; import { drizzleAdapter } from "@lithium-ai/drizzle"; const lithium = new Lithium drizzleAdapter db ; With raw postgres: npm install @lithium-ai/core @lithium-ai/postgres postgres js import { Lithium } from "@lithium-ai/core"; import { postgresAdapter } from "@lithium-ai/postgres"; import postgres from "postgres"; const sql = postgres "postgres://..." ; const lithium = new Lithium postgresAdapter sql ; Then: js // Create hierarchy const infra = await lithium.clusters.create { name: "infra" } ; await lithium.clusters.create { name: "database", parentPath: "infra" } ; // Create versioned entries const entry = await lithium.entries.create { clusterId: infra.value.id } ; await lithium.entries.update { id: entry.value.entry.id } ; // Scoped retrieval: everything under "infra" const context = await lithium.getContext { path: "infra" } ; npm install @lithium-ai/mcp js // server.ts import { Lithium } from "@lithium-ai/core"; import { postgresAdapter } from "@lithium-ai/postgres"; import { serveMcp } from "@lithium-ai/mcp"; import postgres from "postgres"; const sql = postgres process.env.DATABASE URL ; const lithium = new Lithium postgresAdapter sql , async versionIds = { const rows = await sql SELECT entry version id, title, content FROM your content table WHERE entry version id = ANY ${versionIds} ; return new Map rows.map r = r.entry version id, r ; } ; serveMcp lithium ; Add to Claude Code: { "mcpServers": { "lithium": { "command": "npx", "args": "tsx", "server.ts" } } } Entries are pure structure. Your content lives in your own tables, referenced by entry version IDs. Cluster id, parentId, path "infra.database" , name, description, createdAt Entry id, clusterId, createdAt EntryVersion id, entryId, version auto-incremented , createdAt Your Content Table entryVersionId FK , title, content, ...whatever you want | Method | What | |---|---| create { name, parentPath?, description? } | Create cluster, resolve parent | findByPath { path } | Find by dot-path | list | All clusters ordered by path | listDescendantIds { path } | ltree subtree query | | Method | What | |---|---| create { clusterId } | New entry + version 1 | update { id } | Auto-increment version | get { id, version? } | Entry + version latest or specific | list { clusterIds } | Entries by cluster IDs | listWithLatestVersion { clusterIds } | Entries + latest versions batch | | Method | What | |---|---| getContext { path } | Scoped retrieval with optional content resolver | Every method returns Result