ai-hub

ai-hub

A self-hosted server providing shared memory, RAG document search, project maps, and role-based prompts for all AI agents via MCP and REST, enabling persistent context across devices and tools.

Category
Visit Server

README

Mnema

<p align="center"><img src="docs/assets/logo.png" width="160" alt="Mnema logo"/></p>

<p align="center"><strong>One memory for every AI agent.</strong></p>

<p align="center"> <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License: MIT"></a> <a href="package.json"><img src="https://img.shields.io/badge/node-%3E%3D22-339933?logo=node.js&logoColor=white" alt="Node >=22"></a> <a href="https://modelcontextprotocol.io"><img src="https://img.shields.io/badge/MCP-Streamable%20HTTP-6b46c1" alt="MCP"></a> <a href="deploy/setup-pi.sh"><img src="https://img.shields.io/badge/deploy-self--hosted-orange" alt="Self-hosted"></a> </p>

Claude Code, Cursor, opencode, Codex CLI, and custom agents each keep their own context today — decisions made in one tool are invisible to the others, and switching devices means starting from zero. Mnema is a small self-hosted server (MCP + REST) that gives all of them one common brain: structured memory, hybrid document search (RAG), project maps, and role-based system prompts. It runs on a Raspberry Pi 5 on your own network and follows you across every machine and every agent.

Why this exists

Multi-agent workflows have a context problem:

  • A decision made while pairing with Claude Code isn't visible to Cursor an hour later.
  • Switching from your desktop to your laptop (or your phone) means re-explaining what you were doing.
  • Every agent re-derives project context from scratch instead of reading a shared, structured map.
  • "I learned X yesterday" has no durable home — it either lives in scrollback or nowhere.

Mnema is a deliberately small answer to that: one server, one SQLite file, one protocol (MCP) that every agent already speaks, plus REST for anything that doesn't. No vector database cluster, no message queue, no Docker layer — just enough infrastructure to make memory persistent and searchable across tools and devices.

Features

  • Shared memory — facts, preferences, decisions (with rationale), and how-tos, saved by any agent and retrievable by any other. Hybrid search (BM25 via FTS5 + vector via sqlite-vec, merged with Reciprocal Rank Fusion) finds the right memory whether the match is lexical or semantic.
  • RAG document store — ingest notes, READMEs, research write-ups, or learning summaries; markdown-aware chunking, automatic embedding, hybrid retrieval with source references.
  • Project maps — one YAML-backed record per project: summary, stack, decisions, current focus, next steps. Any agent, any device, calls project_get("name") and has full context instantly.
  • Role-based system prompts — a library of prompts (senior software architect, code reviewer, debugging specialist, security engineer, frontend engineer, devops/SRE, ML engineer) with a shared "engineering mindset" core auto-injected into every role, including prompts handed to local models.
  • Local AI orchestration — route simple/bulk text work to a local LLM (LM Studio, zero API cost) and image/video/audio generation to ComfyUI, both driven from any connected agent via local_llm / media_generate.
  • Cross-device sync — a local-first, last-write-wins sync model so memories and project maps created on one machine reconcile cleanly with the primary (Pi) instance.
  • MCP + REST, same core — every capability is exposed both as an MCP tool (for agents that speak MCP over Streamable HTTP) and as a REST endpoint (for scripts, custom agents, and the web UI) — one implementation, two doors.
  • Web UI — a small React dashboard for browsing memory, RAG documents, projects, and prompts from a browser, including your phone.

Architecture

┌──────────────────────────────────────────────────────────────┐
│ Raspberry Pi 5 — "hub"  (Tailscale IP: 100.x.x.x)             │
│                                                                │
│  ┌────────────────────────────────────────────┐               │
│  │ hub-server (Node 22, systemd)                │              │
│  │  ├── MCP endpoint   /mcp   (Streamable HTTP)  │             │
│  │  ├── REST API       /api/* (scripts, agents)  │             │
│  │  └── Web UI         /      (dashboard, PWA)   │             │
│  └───────────────┬────────────────────────────┘               │
│                  │                                            │
│  ┌───────────────▼────────────────────────────┐               │
│  │ SQLite (single file: hub.db)                  │             │
│  │  ├── memories      (structured memory)        │             │
│  │  ├── documents     (RAG source docs)           │             │
│  │  ├── chunks + vec  (sqlite-vec embeddings)     │             │
│  │  ├── chunks_fts    (FTS5 BM25 index)           │             │
│  │  └── projects      (project maps)              │             │
│  └────────────────────────────────────────────┘               │
│                                                                │
│  Nightly backup: sqlite backup + markdown export → git push   │
└──────────────────────────────────────────────────────────────┘
         ▲ Tailscale (private network; Funnel optional for public access)
         │
   ┌─────┴──────────────────────────────────────┐
   │ Devices (desktop, laptop, phone)             │
   │  ├── Claude Code  → MCP (Streamable HTTP)    │
   │  ├── Cursor / Windsurf → MCP (mcp.json)      │
   │  ├── opencode     → MCP (opencode.json)      │
   │  ├── Codex CLI    → MCP (config.toml)        │
   │  ├── claude.ai / ChatGPT / Gemini → MCP (Funnel + ?token=) │
   │  └── custom scripts → REST API               │
   └────────────────────────────────────────────┘

Embedding calls (Gemini API) are made from the Pi only — clients send raw text, so the API key lives in exactly one place.

For the full data model and phased build plan, see PLAN.md.

Screenshots

Dashboard RAG search Prompts Mobile
Dashboard RAG Prompts Mobile

Quick start

Option A: one-click installer

# macOS / Linux
curl -fsSL https://raw.githubusercontent.com/fthsrbst/mnema/main/scripts/install.sh | bash
# Windows
irm https://raw.githubusercontent.com/fthsrbst/mnema/main/scripts/install.ps1 | iex

The installer clones the repo, installs dependencies, builds, and writes a starter .env.

Option B: manual

git clone https://github.com/fthsrbst/mnema.git
cd mnema
npm ci
npm run build
cp .env.example .env   # edit HUB_TOKEN, GEMINI_API_KEY (optional)
npm run dev            # http://127.0.0.1:8033

Without GEMINI_API_KEY the server still runs — it falls back to FTS-only (keyword) search instead of hybrid search. Nothing crashes; you lose semantic recall until you add a key.

Raspberry Pi deploy

curl -fsSL https://raw.githubusercontent.com/fthsrbst/mnema/main/deploy/setup-pi.sh | bash

This installs Node 22, clones the repo, builds server + web UI, generates a .env with a random HUB_TOKEN, installs a systemd unit (hub@<user>), and schedules a nightly backup cron job. See deploy/setup-pi.sh and deploy/clients.md for details.

Connecting agents

Every agent talks to the same /mcp endpoint. Replace <HUB> with your server URL (http://127.0.0.1:8033 locally, http://100.x.x.x:8033 on your tailnet) and <TOKEN> with your HUB_TOKEN.

Claude Code

claude mcp add --transport http --scope user hub <HUB>/mcp \
  --header "Authorization: Bearer <TOKEN>"

Cursor / Windsurf (~/.cursor/mcp.json)

{
  "mcpServers": {
    "hub": {
      "url": "<HUB>/mcp",
      "headers": { "Authorization": "Bearer <TOKEN>" }
    }
  }
}

opencode (~/.config/opencode/opencode.json)

{
  "mcp": {
    "hub": {
      "type": "remote",
      "url": "<HUB>/mcp",
      "headers": { "Authorization": "Bearer <TOKEN>" }
    }
  }
}

Codex CLI (~/.codex/config.toml)

[mcp_servers.hub]
url = "<HUB>/mcp"
http_headers = { "Authorization" = "Bearer <TOKEN>" }

These four entries are not hand-maintained per client — they all come from one file, mcp-servers.json, which also lists auxiliary MCP servers (context7, playwright, sequential-thinking):

{
  "servers": {
    "hub": {
      "url": "$HUB_URL/mcp",
      "headers": { "Authorization": "Bearer $HUB_TOKEN" }
    }
  }
}

Running hub sync on a device resolves $HUB_URL / $HUB_TOKEN from that device's hub config, writes the result into each detected client's own config file (~/.claude.json, ~/.cursor/mcp.json, ~/.config/opencode/opencode.json, ~/.codex/config.toml), copies the shared skill set into ~/.claude/skills/, and updates a managed block in CLAUDE.md / AGENTS.md so every agent knows the hub exists and when to use it. Full client details: deploy/clients.md.

Auto-recall hook

This is the feature that makes the shared memory actually get used instead of sitting unread: Claude Code's UserPromptSubmit hook runs hub recall --hook on every message you send, before the agent sees it. The hook reads the prompt from stdin, skips short messages and slash commands, calls GET /api/recall?q=<prompt>&format=text on the hub with a tight timeout, and prints back whatever relevant memories/RAG chunks it finds — the agent then has that context already in front of it, with zero explicit "search your memory" step from you.

It is deliberately fail-silent: if the hub is unreachable, slow, or returns an error, the hook exits 0 and your prompt goes through untouched. Auto-recall can only add context — it can never block or break a conversation.

Wire it up by merging the hooks block from deploy/claude-code-settings.example.json into ~/.claude/settings.json:

{
  "hooks": {
    "UserPromptSubmit": [
      { "hooks": [{ "type": "command", "command": "hub recall --hook", "timeout": 5 }] }
    ]
  }
}

hub sync also writes a managed block into CLAUDE.md / AGENTS.md that tells the agent when to write back — save decisions with rationale, log sessions before closing, keep project maps current — so recall and capture work as a loop, not just a one-way lookup.

Security notes

Mnema's auth model is intentionally simple — read this before exposing it beyond your own machine:

  • Single bearer token. HUB_TOKEN in .env gates every request except /health. If it's empty, auth is off entirely (fine for local dev, not for anything else). There's no per-agent scoping or rotation tooling — rotating means changing .env, restarting the service, and updating every connected client by hand.
  • Two ways to send the token. Most clients send Authorization: Bearer <token>; platforms that can't attach custom headers (claude.ai, ChatGPT, Gemini) fall back to ?token=<HUB_TOKEN> in the URL. Token-in-URL has real trade-offs (browser history, proxy/access logs) — see docs/connectors.md for the full write-up.
  • Tailscale is the default network boundary. The hub listens on a private tailnet address; nothing is reachable from the open internet unless you explicitly run tailscale funnel. Funnel makes the endpoint fully public — the bearer token becomes the only thing standing between the internet and your memory store, so treat turning it on as a deliberate, temporary action, not a default.
  • /outputs is served without auth, on purpose. Generated media (data/outputs, via image_generate/media_generate) and the web UI's static shell are served unauthenticated (express.static, mounted before the token middleware in src/server/index.ts) so <img>/<video> tags in the dashboard work without token-laced URLs. All data endpoints (/api/*, /mcp) stay behind the bearer check. Don't put anything sensitive in data/outputs.

This is a personal-scale tool, not a hardened multi-tenant service — see the maturity table below for what's actually battle-tested versus rough.

Roadmap

Not built yet, tracked in PLAN.md (Faz 5/6):

  • hub ask "<question>" — RAG + Gemini Flash, direct terminal Q&A without opening an agent.
  • Quick capture — a mobile-friendly single-input web UI for dropping a note or link straight into RAG (with auto-fetch + summarize for links).
  • Watch mode — re-index a notes/docs folder automatically on change.
  • hub timeline <project> — chronological dump of a project's decisions and sessions.
  • Weekly memory-maintenance digest — a cron job that reports stale/conflicting memory entries instead of just letting them accumulate.
  • Qdrant migration path — sqlite-vec is fine to roughly 1M vectors; moving to a dedicated vector store if that ceiling is ever hit is planned, not implemented.

Maturity / honesty table

No inflation — this is what's actually solid versus still rough.

Area Status Notes
Memory CRUD + hybrid search Stable Used daily; FTS-only fallback tested
RAG ingest + search Stable Chunking is markdown-aware but simple; no re-ranking model
Project maps Stable YAML + DB sync works; no conflict UI beyond LWW
MCP server (tools) Stable All tools listed in src/server/mcp.ts are in daily use
REST API Stable Mirrors MCP tools; used by CLI and web UI
Cross-device sync (LWW) Functional, lightly tested Works for the author's two-to-three-device setup; not stress-tested for heavy concurrent writes
Web UI Functional Covers memory/RAG/projects/prompts browsing; not a polished product UI
Local LLM orchestration (LM Studio) Functional Works when LM Studio is reachable; no retry/queueing beyond basic error handling
Media generation (ComfyUI) Experimental Works for the author's own workflows; expect to write your own workflows/*.json
Public connector exposure (Funnel + ?token=) Functional, use with care Token-in-URL is a real trade-off — see docs/connectors.md
Auth model Basic Single bearer token, no per-agent scoping or rotation automation
Backup/restore Functional Nightly cron + markdown export exist; restore path is manual
Qdrant / larger-scale vector store Not built sqlite-vec is fine to roughly 1M vectors; migration path is planned, not implemented

This is a personal-scale tool built for one user running a handful of devices — it is not hardened for multi-tenant or public deployment beyond the token + Funnel model described above.

License

MIT — see LICENSE.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured