Skip to main content
Glama
nikships
by nikships

A global, persistent memory layer for AI coding agents — Gemini embeddings × LanceDB × MCP

npm version npm downloads GitHub stars License: MIT Node.js MCP Powered by Gemini Powered by LanceDB

⭐ Star on GitHub · 📦 npm · 💬 Discussions · 🐛 Issues

Why Gemdex

Your agent re-learns everything every session. You explained your deploy flow last week; today it has no idea. Gemdex gives it durable memory you write on purpose — once, recallable everywhere.

A memory layer is a deliberately written, persistent store that is the source of truth. You teach your agent something once, and it remembers forever, across every repo and every session.

  • 🧠 You decide what to remember — explicit save_memory / recall / update_memory. No silent capture, no background recall.

  • 🌍 One global pool — every memory is searchable from everywhere. No scopes, no folders, no tags; embeddings do the disambiguation.

  • 🔎 Sharp recall, whole answers — hybrid semantic + BM25 over internal chunks, but recall always returns the full memory, never a fragment.

  • 🔌 Plug-and-play — speaks MCP over stdio, so any compatible client (Claude Code, Cursor, Codex CLI, Windsurf, Cline, Continue, Zed…) works instantly.

  • 🪶 Local by default, self-hosted when you want more — start with embedded LanceDB at ~/.gemdex, or run the whole stack on infrastructure you own with one command and share one memory pool across every machine.

  • 🌐 Browser manager — the self-hosted stack ships a web UI (behind your Google login) to browse / edit / delete / export / import, and to upload chat transcripts.

Related MCP server: hive-memory

The motivating workflows

During a session:
  "We just figured out how to wire up the Junie review workflow — save that to memory."

Weeks later, a different repo:
  "Set up the Junie review workflow here — check your memory layer for how we do it."

Different machine, different app:
  "Notarize and sign this build — the credentials and steps are in my memory layer."

Two ways to run it

Local

Self-hosted

Store

Embedded LanceDB at ~/.gemdex

Postgres/pgvector + blob storage you own

Setup

Point your agent at npx gemdex-mcp

One command

Needs a Gemini key on each machine

Gemini/media: yes; MLX text: no

No — the server embeds

Shared across machines

No, one pool per machine

Yes, one pool for everything

Human manage surface

The macOS app

The web manager in your browser

Remote agents

stdio only

HTTPS MCP endpoint, OAuth-gated

Start local; move to self-hosted when you want one pool across machines or agents that aren't on your laptop. Both speak the same MCP tools, so nothing about how you use Gemdex changes.

Quickstart — choose your embedding path

There's no setup step for the store — LanceDB is embedded and persists at ~/.gemdex/lance automatically the first time you save a memory.

Wire Gemdex into your agent

Claude Code:

claude mcp add gemdex -- npx -y gemdex-mcp@latest

If you only add the MCP, all six tools remain available and explain setup rather than crashing. Ask Claude to help choose, then run one option on your machine:

npx gemdex-mcp setup gemini # Hidden API-key prompt; validates before saving
npx gemdex-mcp install      # Apple Silicon: download runtime + BGE-M3, set GEMINI_API_KEY=local
# Or connect an existing server (hidden bearer-token prompt):
npx gemdex-mcp init-remote home https://memory.example.com

MLX uses mlx-community/bge-m3-mlx-8bit. It requires macOS 14+ and native arm64 Node (not Rosetta). No Python, uv, Homebrew, HF CLI or compiler setup is required: the explicit installer manages its own runtime and pinned weights. Installation needs the internet; subsequent MLX text embeddings run offline. Allow time for the initial download. See model evidence and platform requirements.

Install is not migration. To move existing text into its separate 1024-dimensional LanceDB bank, run npx gemdex-mcp migrate-text. Progress is reported and the command is safe to re-run. Media rows/blobs stay on Gemini, including attachments belonging to parents whose text moves. Recall searches both banks and returns whole parents. Gemini-backed history/media still needs a working key/network; failure is reported, not silently presented as complete recall. With GEMINI_API_KEY=local (exact lowercase), MLX text runs offline; that sentinel is the only way to enable the local LLM path — any other key value uses Gemini, and a missing key still requires setup (GEMDEX_EMBEDDING_PROVIDER=mlx alone does not activate it).

npx gemdex-mcp embedding gemini switches future text writes back without losing MLX history (needs a real key); embedding mlx writes GEMINI_API_KEY=local again. Settings persist in ~/.gemdex/.env with 0600 permissions and are shared by MCP and the macOS Storage & Gemini panel (install/progress/migrate/provider controls). Launch environment variables override saved settings; remove stale provider/mode/key overrides from your MCP configuration before switching. Retry a tool after setup; if needed reconnect via Claude Code /mcp. npx gemdex-mcp status shows readiness without printing secrets.

Any other MCP client (Cursor, Codex CLI, Windsurf, Cline, Continue, Zed…):

{
  "mcpServers": {
    "gemdex": {
      "command": "npx",
      "args": ["-y", "gemdex-mcp@latest"]
    }
  }
}

Save and recall

Save how we set up the Junie review workflow to memory.

…later, in any repo on any machine:

Set up the Junie review workflow here — check your memory layer.

Done. Your agent now has a tiny, durable knowledge store it writes on purpose and reads on command.

Nudge your agent to actually use it (the single biggest thing you can do)

Agents won't reach for a new MCP tool on their own. Tell your agent, at the top of every session, that it exists and when to use it.

For Claude Code — drop this into CLAUDE.md at the repo root (or ~/.claude/CLAUDE.md to apply globally):

## Memory layer (Gemdex)

`gemdex` MCP exposes `save_memory`, `recall`, `get_memory`, `update_memory`,
`report_outcome`, and `read_attachment` — a global, durable memory store shared
across every repo and session.

- `recall(query)` freely / by default at the start of a task. Returns a cheap
  ranked **title index** (top 10: title + id only). Most recalls need no follow-up.
- `get_memory(id)` only when a title looks clearly task-relevant — the only path
  that returns full body text.
- `save_memory(content, title?)` when you learn something durable and reusable.
- `update_memory(id, content?, edits?, title?)` to revise a stored memory —
  `edits` for a targeted find-and-replace, or `content` for a full rewrite.
- `report_outcome(id, outcome, note?)` right after you act on a fetched memory
  and the result is clear (`worked` / `failed` / `stale`).
- `read_attachment(memory_id, …)` for transcript/attachment bytes after
  `get_memory` shows media.

There's no delete tool — deletion is a human action in the Gemdex desktop app
or web manager. If these tools aren't in your toolset, the MCP isn't connected.

For Codex CLI, Cursor, Windsurf, Cline, Continue, Zed — paste the same snippet into your client's root instructions file (conventionally AGENTS.md).

The 6 MCP tools

Tool

Input

Returns

When the agent calls it

save_memory

content and/or attachments, title (optional)

new id + resolved title (+ a ⚠ similar-memories warning when a near-duplicate is already stored)

when learning something durable/reusable

recall

query (required)

top 10 titles + ids ranked by relevance (+ track-record when stats exist)

freely / by default before work — cheap title index, never bodies

get_memory

id (required)

full parent body (+ age, attachments metadata, track-record)

only when a recall title is clearly task-relevant

update_memory

id (required); content or edits, title, attachments (optional — at least one required)

updated id + title

to revise a stored memory (edits = partial find-and-replace; content = full rewrite)

report_outcome

id (required), outcome (worked|failed|stale, required), note (optional)

confirmation + updated track record

right after acting on a fetched memory, whenever the outcome is clear

read_attachment

memory_id (required); attachment_id, max_chars (optional)

attachment bytes as UTF-8 or base64

after get_memory shows attachments (e.g. chat transcripts)

Deletion is intentionally not an agent tool — it's a deliberate human action in the desktop app or web manager. Embeddings apply where content is written or searched (get_memory, report_outcome, and read_attachment don't embed). Local mode requires GEMINI_API_KEY; remote mode uses the Gemdex Server owner's key.

Multimodal attachments

save_memory and update_memory accept an optional attachments array of inline media — { mimeType, data (base64), caption? } — embedded into the same space as text by gemini-embedding-2. Supported types and per-memory caps: PNG/JPEG images (≤ 6), MP3/WAV audio (≤ 1), MP4/MOV video (≤ 1), and PDF (≤ 1). Each attachment is embedded as its own unit; its caption (or the memory title) backs the keyword branch. Raw bytes are stored as blobs under ~/.gemdex/blobs and round-trip through export/import. Attachments require the gemini-embedding-2 model — supplying them to a text-only model returns a clear error.

recall works both ways: query by text, by media, or both. Each query attachment is embedded into the shared space and runs its own similarity branch, fused with the text branch via Reciprocal Rank Fusion — so you can recall a memory from a screenshot, an audio clip, or a PDF as easily as from a phrase.

Outcome feedback

recall is fire-and-forget by default — no signal about whether a memory actually helped ever flows back. report_outcome(id, outcome, note?) closes that loop: right after acting on a recalled memory, tell gemdex whether it worked, failed (its info was wrong or broken), or was stale (clearly outdated — rotated credentials, moved paths). Every recall hit then shows a track record (recalled 7×, worked 3× (last: worked 2d ago), prefixed with ⚠ once it has failed or gone stale before) so you can judge trustworthiness at a glance.

Stats live in a small per-client ledger (~/.gemdex/stats.json by default, override with GEMDEX_STATS_PATH) — never written into the memory rows themselves, and never shared across machines in v1. Track-record display is always on; actually changing recall ranking by trust is opt-in via GEMDEX_TRUST_RANKING=true (pure relevance ranking otherwise, exactly as before).

How it works

  1. Save — content is split into retrieval chunks; each chunk is embedded with Gemini and stored with a parent_id pointing back to the whole memory.

  2. Recall — hybrid search (dense vector + BM25, fused with Reciprocal Rank Fusion) ranks chunks, then each match resolves to its full parent memory and results are deduped by parent_id. So a query that matches one paragraph of a 300-line playbook gets the entire playbook back, in one shot.

  3. Store — everything lives in a single global LanceDB table under ~/.gemdex. The agent's MCP process and the desktop app's sidecar share the same store, so a memory saved by one shows up in the other.

This is the well-worn parent-document retriever ("small-to-big") pattern: sharp matching on long content, but the agent always gets the whole memory.

Save-time conflict detection

Memory hygiene (below) finds duplicate/contradicted memories after the fact — weeks later, in a manual desktop scan. save_memory now checks at the moment of save instead: the new memory's vectors are already computed before insert, so checking for near-duplicates costs zero extra embedding/network calls — just one local ANN query plus a handful of filtered reads, reusing the exact same centroid-cosine math and default threshold (0.90) as hygiene clustering. When something similar is already stored, the save_memory response carries a similar field and a ⚠ advisory block naming the existing memory — advisory only, the save always succeeds. On by default; disable with GEMDEX_SIMILAR_ON_SAVE=false or loosen/tighten the bar with GEMDEX_SIMILAR_THRESHOLD. Local mode only in v1 — a BYOI remote save simply carries no similar field yet.

The desktop app (maintenance-only)

The macOS app is no longer the primary manage surface. It still works and is still shipped, but it manages a local ~/.gemdex pool on one Mac. New work goes into the web manager, which runs against your self-hosted pool from any browser, behind your Google login, and is the surface that gets features like session upload and memory hygiene at deployment scale. The app is in maintenance mode: bug fixes, no new features.

Use the app if you're local-only on a Mac and want a native window. Otherwise self-host and use the browser.

A native, manage-only SwiftUI app for macOS (Apple Silicon) that opens straight into your memory layer:

  • Browse / list all memories (sorted by recency).

  • View, create, edit, and delete memories — including inline media attachments (drag-and-drop or pick image / audio / video / PDF, caption them, and preview them in place).

  • "Find similar" on any attachment to recall related memories by media.

  • Export all memories to a portable JSONL file; import them back.

  • Distill coding-agent chat history into one memory per new session. Once a session is ingested, Gemdex never reprocesses it—even if the transcript later changes.

  • Memory hygiene — find stale, duplicate, or contradicted memories. A free local scan clusters similar memories using the vectors already in LanceDB; a Gemini judge then marks each cluster member keep / duplicate / superseded / contradicted with quoted evidence. You review the findings and approve every deletion by hand — dismissed clusters are never flagged again.

There's no free-text search box — recall is an agent/MCP capability; the app is a fast local manager (the only recall it surfaces is "Find similar", i.e. recall-by-example from an existing attachment). On launch the app spawns its own Node sidecar (gemdex serve) over localhost and opens directly into the manager. You never run a sidecar command.

First launch

The app will not unlock local memory operations until GEMINI_API_KEY is both present and verified with a real Gemini embedding request. Missing, rejected, or temporarily unverifiable keys produce a prominent blocking screen with retry and replacement controls; an untested candidate is never saved. After Gemini accepts the key, Gemdex stores it locally in ~/.gemdex/.env.

Remote storage can open the memory manager without a local embedding key because the server owns memory embeddings. Chat-history digestion still runs on this Mac, so remote-mode users see a persistent red warning and ingestion remains disabled until a local Gemini key is verified.

Memory manager

After setup, the app opens into the local manager for browsing, editing, exporting, and importing memories.

# from packages/app — requires a Swift 5.9+ toolchain (no Xcode needed)
cd packages/app
bash macos/build-app.sh                 # assemble build/Gemdex Memory.app
open "build/Gemdex Memory.app"          # launch it

Download a signed, notarized DMG from the latest release — it bundles its own Node runtime, so it runs with zero manual dependency installation.

The sidecar is the same package as the MCP server:

npx gemdex serve --port 0   # localhost HTTP/JSON manager API; --port 0 = auto-pick

Method + path

Purpose

GET /health

readiness probe

GET /memories

list (sorted by updatedAt desc)

GET /memories/:id

full memory

POST /memories

create (embeds via Gemini)

PUT /memories/:id

edit (re-chunk + re-embed)

DELETE /memories/:id

delete

GET /export · POST /import

portable backup / restore (upsert by id)

The sidecar binds 127.0.0.1 only — it's a single-user local app.

Self-hosted remote mode (BYOI)

Run Gemdex Server with Postgres/pgvector and file or S3-compatible attachment storage, then connect MCP, CLI, and desktop clients to the same global memory pool. Embedding runs on the server, so remote clients do not need a Gemini key.

Self-host the whole stack (one command)

curl -fsSL https://raw.githubusercontent.com/nikships/gemdex/main/scripts/install.sh | bash

Brings up Postgres, the memory API, the Streamable HTTP MCP endpoint and the web manager; generates every secret; waits for migrations; verifies a real save and recall; then prints a ready-to-paste MCP client config. It asks for one thing — a free Google AI Studio key — or reads GEMINI_API_KEY from the environment.

Loopback-only by default. Add --lan to reach it from your other devices:

curl -fsSL https://raw.githubusercontent.com/nikships/gemdex/main/scripts/install.sh | bash -s -- --lan

Re-running is safe and is the upgrade path: existing secrets are never regenerated and no volume is removed. --help lists the flags (alternate ports, install directory, pinned ref).

Then, when you want more than localhost:

Guide

What it covers

Self-host deploy

The canonical end-to-end setup: Compose stack, Google OAuth, public HTTPS edge, and a proof that the memory plane isn't exposed

Go further

DNS + TLS, running on Render or Railway, a VPS, what stays local vs cloud, and cost/sizing

Security notes

What is actually enforced, where in the code, and the pre-launch checklist

Chat history

The three ingestion paths and which to use

Or just the memory server

If you only want the BYOI backend — no MCP endpoint, no web manager — it's two commands. On the server host:

git clone https://github.com/nikships/gemdex.git
cd gemdex/packages/server && npm run init   # generates secrets, starts Docker, prints the token

On each client (paste the token when prompted; add --import-local to bring your existing local memories along):

npx -y gemdex-mcp@latest init-remote myserver https://memory.example.com

init-remote verifies the server, switches the client to remote mode, and prints the agent command. You can also run a local and a remote pool side by side — see the operations guide.

Start with the BYOI operations guide. The remote mode contract defines the v1 API, auth, attachment handling, compatibility checks, ranking invariants, and non-goals.

Use as a library

Skip the MCP server and embed the memory store directly:

import { MemoryStore, LanceDBVectorDatabase, GeminiEmbedding } from 'gemdex-core';

const embedding = new GeminiEmbedding({
  apiKey: process.env.GEMINI_API_KEY!,
  model: 'gemini-embedding-2',
});

// Pass nothing to use the default ~/.gemdex/lance directory.
const vectorDatabase = new LanceDBVectorDatabase();
const memory = new MemoryStore({ embedding, vectorDatabase });

const { id } = await memory.save({
  content: 'Notarize with: xcrun notarytool submit …',
  title: 'macOS notarization',
});

const hits = await memory.recall('how do we notarize builds', 5);
console.log(hits[0].content); // the full memory, never a fragment

Packages

Package

Description

gemdex-core

Memory store (parent-document chunking), Gemini embedding client, embedded LanceDB hybrid retrieval

gemdex-mcp

MCP server (save_memory/recall/get_memory/update_memory/report_outcome/read_attachment) + gemdex serve localhost sidecar

gemdex-server

Self-hosted BYOI HTTP backend using Postgres/pgvector and file or S3-compatible blobs

gemdex-mcp-http

Python. Streamable HTTP MCP endpoint (/mcp) for remote agents; OAuth 2.1 single-user auth

gemdex-web

Python + React. Browser manager for a self-hosted pool: Google login, CRUD, session upload, hygiene

packages/app

native SwiftUI macOS app to manage a local pool (maintenance-only)

Configuration

Variable

Required

Default

Description

GEMINI_API_KEY

yes

—

Google AI Studio API key (needed to embed on save/recall/update)

LANCEDB_PATH

no

~/.gemdex/lance

Filesystem path for the embedded memory store

EMBEDDING_MODEL

no

gemini-embedding-2

Override Gemini embedding model

EMBEDDING_DIMENSION

no

model default

Force Matryoshka-resized dimension (256/768/1536/3072)

GEMINI_BASE_URL

no

Google default

Custom Gemini endpoint

HYBRID_MODE

no

true

Disable to use dense-only recall

GEMDEX_SERVE_PORT

no

auto (0)

Default port for gemdex serve (the app picks one automatically)

GEMDEX_MODE

no

local

Select the embedded local backend or a configured remote backend

GEMDEX_REMOTE_URL

remote only

—

Gemdex Server root URL

GEMDEX_REMOTE_TOKEN

remote only

—

Gemdex Server bearer token

GEMDEX_STATS_PATH

no

~/.gemdex/stats.json

Where the report_outcome feedback ledger is stored

GEMDEX_TRUST_RANKING

no

false

Set true to re-rank recall results by track record (worked/failed/stale); display of the track-record line stays on either way

GEMDEX_SIMILAR_ON_SAVE

no

true

Set false to disable save-time similar-memory detection

GEMDEX_SIMILAR_THRESHOLD

no

0.90

Centroid cosine-similarity bar for save-time detection (same scale as memory hygiene)

Privacy & safety

Gemdex is a power-dev tool with zero guardrails by design. You may store API keys, credentials, and account details in plaintext. There is no secret redaction, encryption mandate, or safety enforcement. In local mode, records stay on the client except content sent to Gemini for embedding. In BYOI mode, records live in your server/database/blob infrastructure and embedding payloads are sent from that server to Gemini. Gemdex provides no hosted custody or account service. See the BYOI security model, and — if you plan to expose a deployment publicly — the self-host security notes.

Build from source

git clone https://github.com/nikships/gemdex.git
cd gemdex
pnpm install
pnpm build

The MCP entry point lands at packages/mcp/dist/index.js. Point your MCP client at node /absolute/path/to/packages/mcp/dist/index.js to run a local build.

Roadmap

  • Optional encryption-at-rest for sensitive memories

  • Packaged desktop app binaries (macOS / Linux / Windows)

  • Multi-machine sync service (beyond export/import)

  • Memory linking / references

  • CLI (gemdex recall "...") for non-MCP workflows

Have an idea? Open a discussion.

Contributing

First time contributors very welcome. See CONTRIBUTING.md for the dev loop, then check the good-first-issue label.

Star history

Star History Chart


If Gemdex makes your agent remember, give it a ⭐ — it's the single biggest thing that helps the project grow.

License

MIT. See LICENSE.

MCP Registry

mcp-name: io.github.nikships/gemdex

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    Provides persistent memory for AI agents using hybrid search (vector embeddings + BM25) with neural reranking, enabling storage and retrieval of insights, debugging solutions, and patterns across coding sessions.
    8
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Provides AI coding agents with persistent, graph-connected memory across projects, enabling cross-project context retrieval via synaptic connections and hybrid search.
    8 npm
    8
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Provides long-term memory for AI coding agents, enabling them to remember, search, and organize information across sessions and platforms like Claude Code, ChatGPT, and Cursor.
    13 npm
    8
    MIT