christopherkarani

Wax

Built by christopherkarani β€’ 674 stars

What is Wax?

Single-file memory layer for AI agents, sub mili-second RAG on Apple Silicon. Metal Optimized On-Device. No Server. No API. One File. Pure Swift

How to use Wax?

1. Install a compatible MCP client (like Claude Desktop). 2. Open your configuration settings. 3. Add Wax using the following command: npx @modelcontextprotocol/wax 4. Restart the client and verify the new tools are active.
πŸ›‘οΈ Scoped (Restricted)
npx @modelcontextprotocol/wax --scope restricted
πŸ”“ Unrestricted Access
npx @modelcontextprotocol/wax

Key Features

Native MCP Protocol Support
Real-time Tool Activation & Execution
Verified High-performance Implementation
Secure Resource & Context Handling

Optimized Use Cases

Extending AI models with custom local capabilities
Automating system workflows via natural language
Connecting external data sources to LLM context windows

Wax FAQ

Q

Is Wax safe?

Yes, Wax follows the standardized Model Context Protocol security patterns and only executes tools with explicit user-granted permissions.

Q

Is Wax up to date?

Wax is currently active in the registry with 674 stars on GitHub, indicating its reliability and community support.

Q

Are there any limits for Wax?

Usage limits depend on the specific implementation of the MCP server and your system resources. Refer to the official documentation below for technical details.

Official Documentation

View on GitHub
<!-- HEADER:START --> <div align="center"> <picture> <source media="(prefers-color-scheme: dark)" srcset="Resources/docs/assets/banner-dark.svg"> <img src="Resources/docs/assets/banner-light.svg" width="800" alt="Wax Banner"> </picture> </div> <div style="height: 16px;"></div> <p align="center"> <strong>Wax is a high-performance, single-file memory layer for AI agents on Apple platforms.</strong><br/> On-device, private, and portable. No server and no cloud dependency. </p> <p align="center"> <a href="https://github.com/christopherkarani/Wax/releases"><img src="https://img.shields.io/github/v/release/christopherkarani/Wax?style=flat-square&logo=swift&logoColor=white&label=Swift" alt="Swift" /></a> <a href="https://developer.apple.com/ios/"><img src="https://img.shields.io/badge/platform-iOS%20%7C%20macOS-lightgrey?style=flat-square" alt="Platforms" /></a> <a href="https://github.com/christopherkarani/Wax/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square" alt="License" /></a> <a href="https://github.com/christopherkarani/Wax/stargazers"><img src="https://img.shields.io/github/stars/christopherkarani/Wax?style=flat-square&logo=github" alt="Stars" /></a> </p> <p align="center"> <a href="README.md">English</a> Β· <a href="Resources/locales/README.es.md">EspaΓ±ol</a> Β· <a href="Resources/locales/README.fr.md">FranΓ§ais</a> Β· <a href="Resources/locales/README.ja.md">ζ—₯本θͺž</a> Β· <a href="Resources/locales/README.ko.md">ν•œκ΅­μ–΄</a> Β· <a href="Resources/locales/README.pt.md">PortuguΓͺs</a> Β· <a href="Resources/locales/README.zh-CN.md">δΈ­ζ–‡</a> </p> <!-- HEADER:END -->

What is Wax?

Wax is a Swift-native persistence engine for AI agents. It stores documents, embeddings, and structured knowledge in a single portable .wax file.

The goal is simple: keep memory local, keep setup light, and make recall fast enough that it can stay in the loop.

Why Wax?

FeatureWaxSQLite (FTS5)Cloud Vector DBs
SearchHybrid (Text + Vector)Text Only*Vector Only*
Latency~6ms (p95)~10ms (p95)150ms - 500ms+
Privacy100% Local100% LocalCloud-hosted
SetupZero ConfigLowComplex (API Keys)
ArchitectureApple Silicon NativeGenericVaries

Why a single .wax file?

Most RAG setups end up with a database, a vector store, and a file server. Wax keeps the moving pieces smaller by bundling documents, metadata, and indexes into one binary.

  • Less setup: no Docker stack and no separate database to babysit.
  • Portable: move the file with AirDrop, iCloud, or whatever sync layer you already use.
  • Atomic: backup, copy, or delete one file instead of chasing state across services.

Performance

Wax is tuned for M-series hardware and local recall.

Recall Latency (p95)

Lower is better. Measured in milliseconds.

Wax (Hybrid)  |β–ˆβ–ˆ 6.1ms
SQLite (Text) |β–ˆβ–ˆβ–ˆβ–ˆ 12ms
Cloud RAG     |β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ 150ms+

Cold Open Time (p95)

Lower is better. Measured in milliseconds.

Wax           |β–ˆβ–ˆβ–ˆ 9.2ms
Traditional   |β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ 120ms+

[!TIP] Ingest Throughput: Wax handles 85.9 docs/s with full hybrid indexing on an M3 Max. Full benchmark report: Resources/docs/benchmarks/2026-03-06-performance-results.md


Architecture

Wax uses a frame-based container format and embeds the search engines it needs inside the main file: SQLite FTS5 for text and a Metal-accelerated HNSW index for vectors.

Internal File Layout

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                          Dual Header Pages (A/B)                         β”‚
β”‚   (Magic, Version, Generation, Pointers to WAL & TOC, Checksums)         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                          WAL (Write-Ahead Log)                           β”‚
β”‚   (Atomic ring buffer for crash-resilient uncommitted mutations)         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                          Compressed Data Frames                          β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”       β”‚
β”‚   β”‚ Frame 0 (LZ4)    β”‚  β”‚ Frame 1 (LZ4)    β”‚  β”‚ Frame 2 (LZ4)    β”‚ ...   β”‚
β”‚   β”‚ [Raw Document]   β”‚  β”‚ [Metadata/JSON]  β”‚  β”‚ [System Info]    β”‚       β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                          Hybrid Search Indices                           β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”‚
β”‚   β”‚ SQLite FTS5 Blob             β”‚  β”‚ Metal HNSW Index             β”‚     β”‚
β”‚   β”‚ (Text Search + EAV Facts)    β”‚  β”‚ (Vector Search)              β”‚     β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                          TOC (Table of Contents)                         β”‚
β”‚   (Index of all frames, parent-child relations, and engine manifests)    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  1. Atomic resilience: dual headers and the WAL keep the store consistent even if the process dies mid-write.
  2. Unified retrieval: one query fans out to both the BM25 text index and the HNSW vector index.
  3. Structured knowledge: built-in EAV (Entity-Attribute-Value) storage handles durable facts and long-term reasoning.

Quick Start

Swift

import Wax

// Use a sandbox-safe, writable location (works in apps and CLI tools)
let url = URL.documentsDirectory.appending(path: "agent.wax")

// 1. Open a memory store
let memory = try await Memory(at: url)

// 2. Save a memory
try await memory.save("The user is building a habit tracker in SwiftUI.")

// 3. Search with hybrid recall (text + vector)
let results = try await memory.search("What is the user building?")

if let best = results.items.first {
    print("Found: \(best.text)")
    print("Document ID: \(best.metadata["id"] ?? "unknown")")
    // β†’ "Found: The user is building a habit tracker in SwiftUI."
}

try await memory.close()
<details> <summary><strong>SwiftUI example</strong></summary>
import SwiftUI
import Wax

struct ContentView: View {
    @State private var result = "Searching…"

    var body: some View {
        Text(result)
            .task {
                do {
                    let url = URL.documentsDirectory.appending(path: "agent.wax")
                    let memory = try await Memory(at: url)

                    try await memory.save("The user is building a habit tracker in SwiftUI.")
                    let context = try await memory.search("What is the user building?")

                    result = context.items.first?.text ?? "Nothing found"
                    try await memory.close()
                } catch {
                    result = "Error: \(error.localizedDescription)"
                }
            }
    }
}
</details> <details> <summary><strong>CLI tool (main.swift)</strong></summary>
import Wax

@main
struct AgentMemory {
    static func main() async throws {
        let url = URL.documentsDirectory.appending(path: "agent.wax")
        let memory = try await Memory(at: url)

        try await memory.save("The user is building a habit tracker in SwiftUI.")

        let results = try await memory.search("What is the user building?")
        if let best = results.items.first {
            print("Found: \(best.text)")
        }

        try await memory.close()
    }
}
</details>

Looking to store persistent facts and long-term reasoning? See Structured Memory.

For repeated CLI vector work, Wax CLI now auto-starts and reuses a background daemon for vector-capable commands such as remember, recall, and search --mode hybrid.

You can still run the daemon directly when you want an explicit long-lived session:

wax-cli daemon --store-path ~/.wax/memory.wax

Send JSON lines such as:

{"id":"1","command":"remember","content":"An automobile needs periodic maintenance."}
{"id":"2","command":"search","query":"car service","mode":"hybrid","topK":3}
{"id":"3","command":"shutdown"}

Simple text-only usage still runs one-shot. If vector search is unavailable, hybrid/vector commands now fail loudly instead of silently dropping to text-only mode.

AI Coding Assistants

If you use an AI coding assistant like Claude Code, Cursor, or Windsurf, there are two good setup paths:

  • Use the Wax MCP server when you want persistent memory, session handoffs, and cross-session search inside the assistant.
  • Use the bundled Wax skill when you want the assistant to write correct Wax framework code directly against the Swift API.

Install the MCP server (Claude Code):

npx -y waxmcp@latest mcp install --scope user

This install flow stages the bundled Wax runtime into a stable local directory and registers the staged wax-mcp binary with Claude Code. npx is only used for install/bootstrap.

Install the skill (Claude Code):

# From within your project directory
claude install-skill https://github.com/christopherkarani/Wax/tree/main/Resources/skills/public/wax

Once installed, your assistant can work against Memory, VideoRAGOrchestrator, PhotoRAGOrchestrator, hybrid search, structured memory, and the MCP server without extra prompt scaffolding.

Or paste this prompt to get started from scratch:

<details> <summary>Wax starter prompt (click to expand, then copy)</summary>
Use the Wax MCP server for persistent memory in this repo.

Workflow rules:
- At session start, call `wax_handoff_latest` first to load prior context, then call `wax_session_start` once and keep the returned `session_id`.
- Use `wax_remember` to store decisions, discoveries, and short factual notes. If the memory is session-scoped, pass `session_id` as a top-level argument. Do not put `session_id` inside `metadata`.
- Use `wax_recall` for assembled context and `wax_search` for raw ranked hits.
- Prefer `mode: "hybrid"` when semantic retrieval helps. Use `mode: "text"` when I want a fast or deterministic lexical lookup.
- If you batch writes with `commit: false`, call `wax_flush` before any `wax_recall` or `wax_search`.
- Use `wax_handoff` near the end of the session with `content`, optional `project`, and `pending_tasks`, then call `wax_session_end`.
- Use `wax_corpus_search` only when you need cross-session retrieval across many session `.wax` files, such as `~/.wax/sessions`. It rebuilds or refreshes a shared corpus store and returns provenance metadata under `wax.corpus.*` so you can trace hits back to the source session store and frame.
- Use structured memory tools (`wax_entity_upsert`, `wax_fact_assert`, `wax_fact_retract`, `wax_facts_query`, `wax_entity_resolve`) for stable entities and facts, not transient debugging notes.

Behavior expectations:
- Read existing handoffs and recall results before asking me to restate prior context.
- Keep memory writes concise, factual, and scoped to the task.
- When a cross-session result looks relevant, cite the provenance metadata so we know which session store it came from.
</details>

Installation

Swift Package Manager

dependencies: [
    .package(url: "https://github.com/christopherkarani/Wax.git", from: "0.1.8")
]

Ecosystem Tools

πŸ€– MCP Server

Wax provides a first-class Model Context Protocol (MCP) server. Connect your local memory to Claude Code or any MCP-compatible agent.

npx -y waxmcp@latest mcp install --scope user

The published installer stages the bundled runtime into a stable local directory and registers wax-mcp directly, so steady-state MCP sessions do not launch through raw npx. For the recommended Claude Code prompt and setup flow, see Resources/docs/wax-mcp-setup.md.

πŸ” WaxRepo

A semantic search TUI for your git history. Index any repository and find code or commits using natural language.

# From within any git repo
wax-repo index
wax-repo search "where did we implement the WAL?"

License

Wax is released under the Apache License 2.0. See LICENSE for details.

<div align="center"> <sub>Built for developers who believe user data belongs on the user's device.</sub> </div>

Global Ranking

-
Trust ScoreMCPHub Index

Based on codebase health & activity.

Manual Config

{ "mcpServers": { "wax": { "command": "npx", "args": ["wax"] } } }