Metadata-Version: 2.4
Name: mem-mesh
Version: 1.12.1
Summary: Centralized memory system for AI development tools
Author: mem-mesh
Project-URL: Homepage, https://github.com/x-mesh/mem-mesh
Project-URL: Repository, https://github.com/x-mesh/mem-mesh
Project-URL: Documentation, https://github.com/x-mesh/mem-mesh/blob/main/README.md
Project-URL: Bug Tracker, https://github.com/x-mesh/mem-mesh/issues
Keywords: mcp,memory,ai-agent,sqlite,vector-search,llm,claude,cursor,kiro
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pydantic>=2.5.0
Requires-Dist: pydantic-settings>=2.1.0
Requires-Dist: python-dotenv>=1.0.0
Provides-Extra: server
Requires-Dist: fastapi>=0.104.0; extra == "server"
Requires-Dist: uvicorn[standard]>=0.24.0; extra == "server"
Requires-Dist: sentence-transformers>=2.2.2; extra == "server"
Requires-Dist: sqlite-vec>=0.0.1a12; extra == "server"
Requires-Dist: mcp>=1.0.0; extra == "server"
Requires-Dist: sqlmodel>=0.0.14; extra == "server"
Requires-Dist: python-multipart>=0.0.6; extra == "server"
Requires-Dist: fastmcp>=2.14.2; extra == "server"
Requires-Dist: httpx>=0.25.0; extra == "server"
Requires-Dist: sse-starlette>=2.0.0; extra == "server"
Requires-Dist: tiktoken>=0.5.0; extra == "server"
Requires-Dist: requests>=2.31.0; extra == "server"
Requires-Dist: urllib3>=1.26.0; extra == "server"
Requires-Dist: pysqlite3-binary>=0.5.3; sys_platform == "linux" and extra == "server"
Provides-Extra: dev
Requires-Dist: pytest>=7.4.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
Requires-Dist: hypothesis>=6.88.0; extra == "dev"
Requires-Dist: black>=23.0.0; extra == "dev"
Requires-Dist: isort>=5.12.0; extra == "dev"
Requires-Dist: ruff>=0.4.0; extra == "dev"
Requires-Dist: mypy>=1.7.0; extra == "dev"
Requires-Dist: types-requests>=2.31.0; extra == "dev"
Requires-Dist: coverage>=7.3.0; extra == "dev"
Requires-Dist: pre-commit>=3.5.0; extra == "dev"
Requires-Dist: tiktoken>=0.5.0; extra == "dev"
Requires-Dist: matplotlib>=3.8.0; extra == "dev"
Requires-Dist: seaborn>=0.13.0; extra == "dev"
Dynamic: license-file

# mem-mesh

[![PyPI version](https://img.shields.io/pypi/v/mem-mesh.svg)](https://pypi.org/project/mem-mesh/)
[![CI](https://github.com/x-mesh/mem-mesh/actions/workflows/ci.yml/badge.svg)](https://github.com/x-mesh/mem-mesh/actions/workflows/ci.yml)
[![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![MCP Protocol](https://img.shields.io/badge/MCP-2024--11--05%20%7C%202025--03--26-green.svg)](https://modelcontextprotocol.io/)

> Persistent memory for AI agents — hybrid vector + FTS5 search, pin-based session tracking, and NLI conflict detection. Zero external dependencies.

[한국어](./README.ko.md) · [Quick Start](#quick-start) · [MCP Setup](#mcp-setup) · [MCP Tools](#mcp-tools-15) · [Session & Pins](#session--pins) · [Architecture](#architecture) · [Docker](#docker) · [Contributing](#contributing)

---

## Why mem-mesh?

Most MCP memory servers are glorified key-value stores. mem-mesh is built for how AI agents actually work — sessions with multiple steps, decisions that need to survive reboots, and cross-machine context that has to stay coherent.

| Differentiator | What it means |
|---|---|
| **Pin lifecycle** | Lightweight kanban inside every session: `pin_add` → `pin_complete` → `pin_promote`. No other MCP memory server has this. |
| **Hybrid search** | sqlite-vec vector embeddings + FTS5 full-text fused with Reciprocal Rank Fusion (RRF). Korean n-gram optimized out of the box. |
| **NLI conflict detection** | 2-stage pipeline: vector similarity pre-filter → mDeBERTa NLI model catches contradictory memories before they're stored. |
| **4-Tier Smart Expand** | `session_resume(expand="smart")` uses an importance × status matrix to load only what matters — ~60% token savings. |
| **Zero external services** | Single SQLite file. `pip install mem-mesh` and you're running. No Postgres, no Redis, no cloud. |
| **Dual MCP transport** | stdio (Cursor, Claude Desktop, Kiro) + Streamable HTTP/SSE (MCP spec 2025-03-26). |
| **25+ client auto-detection** | Identifies the calling IDE/AI platform from MCP handshake or User-Agent. |
| **Batch operations** | Pack multiple memory ops into one round-trip: 30–50% token savings. |

---

## Features

- **Memory CRUD** — `add`, `search`, `context`, `update`, `delete`
- **Hybrid search** — sentence-transformers vectors + FTS5 RRF fusion, Korean n-gram support
- **Session & pins** — short-lived work tracking with importance-based promotion to permanent memory
- **Memory relations** — `link`, `unlink`, `get_links` across 7 relation types
- **Conflict detection** — mDeBERTa NLI prevents storing contradictory facts
- **Batch operations** — 30–50% fewer tokens per multi-op workflow
- **Web dashboard** — FastAPI REST API + real-time UI at `localhost:8000`

---

## Quick Start

### Recommended: uvx (zero Python management)

One tool to install — [uv](https://github.com/astral-sh/uv) — and mem-mesh handles the rest. No virtualenv, no pyenv tweaks, no `sqlite-vec` compile errors. Your MCP client spawns a cached, isolated mem-mesh on-demand.

```bash
# 1. Install uv (one-time, ~15 seconds)
curl -LsSf https://astral.sh/uv/install.sh | sh

# 2. Run the interactive installer — writes MCP config for detected tools,
#    offers to install hooks, warms the uv cache.
uvx --from "mem-mesh[server]" mem-mesh install
```

That's it. Restart Cursor / Claude Desktop / Kiro and mem-mesh MCP tools are live.

Want the web dashboard too? `uvx --from "mem-mesh[server]" mem-mesh serve` — open http://localhost:8000.

### Alternative: pip install

If you prefer managing Python environments yourself:

```bash
pip install "mem-mesh[server]"
mem-mesh install           # same interactive installer
mem-mesh serve             # web server + SSE MCP at localhost:8000
```

### Prerequisites (only if NOT using uvx)

mem-mesh loads the `sqlite-vec` extension at runtime, so Python's `sqlite3` module must support **loadable extensions**.

- **uvx users** — uv's managed Python builds already have extension loading enabled. Nothing to do.
- **Linux** — `pysqlite3-binary` wheel installs automatically as a fallback.
- **macOS** — system Python and Homebrew Python both work. Only pyenv's default build is broken.
- **Windows** — system Python works; install `pysqlite3-binary` manually if needed.

**macOS + pyenv users** who hit `Migration failed: no such module: vec0`:

```bash
# Option A: rebuild Python against Homebrew sqlite3
brew install sqlite3
SQLITE_PREFIX="$(brew --prefix sqlite3)"
PYTHON_CONFIGURE_OPTS="--enable-loadable-sqlite-extensions" \
LDFLAGS="-L${SQLITE_PREFIX}/lib" \
CPPFLAGS="-I${SQLITE_PREFIX}/include" \
CFLAGS="-I${SQLITE_PREFIX}/include" \
  pyenv install 3.13 --force
pyenv rehash

# Option B (simplest): just use uvx — it bypasses system Python entirely
```

Linux distro Python, Docker images, and conda Python ship with extension loading enabled — no extra steps needed.

---

## MCP Setup

`mem-mesh install` writes these entries for you automatically. The snippets below are what gets written, for reference.

### uvx (recommended)

Zero Python-env management. The MCP client spawns a cached mem-mesh process per call; the first run downloads it, subsequent runs are instant.

```json
{
  "mcpServers": {
    "mem-mesh": {
      "command": "uvx",
      "args": ["--from", "mem-mesh[server]", "mem-mesh-mcp-stdio"],
      "env": { "MEM_MESH_CLIENT": "cursor" }
    }
  }
}
```

### Stdio (local Python)

Use your own Python install. Good if you need `-e .` dev installs.

```json
{
  "mcpServers": {
    "mem-mesh": {
      "command": "python",
      "args": ["-m", "app.mcp_stdio"],
      "cwd": "/absolute/path/to/mem-mesh",
      "env": { "MCP_LOG_LEVEL": "INFO" }
    }
  }
}
```

### HTTP (streamable, shared running server)

For web clients or when multiple tools share one process. Requires `mem-mesh serve` running. Use `"type": "http"` — `type: "sse"` is legacy and hangs after a server restart.

```json
{
  "mcpServers": {
    "mem-mesh": {
      "url": "http://localhost:8000/mcp/sse",
      "type": "http"
    }
  }
}
```

**Config file locations by tool:**

| Tool | Config file |
|---|---|
| Cursor | `.cursor/mcp.json` |
| Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` |
| Kiro | `~/.kiro/settings/mcp.json` |

### Mode comparison

| | uvx | Stdio | HTTP |
|---|---|---|---|
| Prereq | `uv` only | Python env with `mem-mesh[server]` | Running `mem-mesh serve` |
| First call | ~15s (cache warm) | Instant | Instant |
| Server to manage | None | None | Yes |
| Dashboard | Optional (`uvx … serve`) | Optional | Included |
| Hooks support | Requires separate server | Yes (local mode) | Yes (api mode) |

---

## MCP Tools (15)

| Tool | Description | Key parameters |
|---|---|---|
| `add` | Store a memory | `content`, `project_id`, `category`, `tags` |
| `search` | Hybrid vector + FTS5 search | `query`, `project_id`, `category`, `limit`, `recency_weight`, `response_format` |
| `context` | Retrieve memories surrounding a given memory | `memory_id`, `depth`, `project_id` |
| `update` | Edit a memory | `memory_id`, `content`, `category`, `tags` |
| `delete` | Remove a memory | `memory_id` |
| `stats` | Usage statistics | `project_id`, `start_date`, `end_date` |
| `link` | Create a typed relation between memories | `source_id`, `target_id`, `relation_type` |
| `unlink` | Remove a relation | `source_id`, `target_id` |
| `get_links` | Query relations | `memory_id`, `relation_type`, `direction` |
| `pin_add` | Add a short-lived work-tracking pin | `content`, `project_id`, `importance`, `tags` |
| `pin_complete` | Mark a pin done; optionally promote to permanent memory | `pin_id`, `promote`, `category` |
| `pin_promote` | Promote an already-completed pin to permanent memory | `pin_id`, `category` |
| `session_resume` | Restore context from the previous session | `project_id`, `expand`, `limit` |
| `session_end` | Close a session with a summary | `project_id`, `summary`, `auto_complete_pins` |
| `batch_operations` | Execute multiple ops in one call | `operations` (array of add/search/pin_add/pin_complete) |

**`search` response formats:** `minimal` | `compact` | `standard` | `full`

---

## Search

mem-mesh runs two retrieval engines in parallel and merges results with Reciprocal Rank Fusion:

- **Vector** — `dragonkue/snowflake-arctic-embed-l-v2.0-ko` (1024-dim, Korean retrieval SOTA, MTEB-ko #1) by default; KURE, E5 and MiniLM models supported
- **FTS5** — SQLite full-text search with n-gram tokenization for CJK languages
- **RRF fusion** — balances semantic similarity and keyword precision
- **Quality filters** — noise removal, intent analysis, vector pre-filter overfetch to improve recall

---

## Session & Pins

### Session lifecycle

```
session_resume(project_id, expand="smart")  →  work  →  session_end(project_id, summary)
```

- `session_resume` restores incomplete pins and context from the previous session. Stale pins are auto-closed. `expand="smart"` applies an importance × status matrix that cuts token usage by ~60%.
- `session_end` records a summary and closes the session. If the session terminates abnormally, the next `session_resume` automatically recovers open pins.

### Pin lifecycle

Pins are **the unit of work inside a session**. Track code changes, implementations, and configuration work as pins — not in permanent memory.

```
pin_add(content, project_id)  →  do the work  →  pin_complete(pin_id, promote=True)
                                                   # promote=True completes and promotes in one call
```

**Status flow:** `open` (planned, not started) → `in_progress` (active; **default on pin_add**) → `completed`

Multi-step work can pre-register later steps as `open` pins, then activate them one at a time.

**Auto-stale cleanup** (triggered on `session_resume`):
- `in_progress` pins older than 7 days → auto-completed
- `open` pins older than 30 days → auto-completed

**When to pin:** only when files change. Questions, explanations, and read-only lookups do not need pins. Multi-step tasks get one pin per step.

**Importance levels:**
| Level | Use for |
|---|---|
| `5` | Architecture decisions, core design changes |
| `3–4` | Feature implementations, significant fixes |
| `1–2` | Minor edits, typo fixes |
| omit | Auto-inferred from content |

**Promote:** `pin_complete(pin_id, promote=True)` completes and promotes to permanent memory in one call. To promote after the fact: `pin_promote(pin_id)`.

**Client detection:** In HTTP mode, the calling client is identified from the MCP initialize handshake or User-Agent header (25+ IDE/AI platforms supported). In stdio mode, set `MEM_MESH_CLIENT` in the environment.

### AI agent checklist

```
1. Session start   → session_resume(project_id, expand="smart")
2. Past context    → search() before coding if referencing previous decisions
3. Track work      → pin_add → pin_complete (promote=True to merge into memory)
4. Permanent store → decision / bug / incident / idea / code_snippet only
5. Session end     → session_end(project_id, summary, auto_complete_pins=True)
6. Never store     → API keys / tokens / passwords / PII
```

> **Principle:** Hooks are read-only signals. All pin creation, completion, and promotion decisions are made by the LLM with full context.

---

## Memory Relations

Seven relation types: `related` | `parent` | `child` | `supersedes` | `references` | `depends_on` | `similar`

`get_links` direction: `outgoing` | `incoming` | `both`

---

## Configuration

| Variable | Description | Default |
|---|---|---|
| `MEM_MESH_DATABASE_PATH` | SQLite database path | XDG per-user path (see `app/core/config.py` `_default_db_path`) |
| `MEM_MESH_EMBEDDING_MODEL` | Embedding model name | `dragonkue/snowflake-arctic-embed-l-v2.0-ko` |
| `MEM_MESH_EMBEDDING_DIM` | Vector dimensions | `1024` |
| `MEM_MESH_SERVER_PORT` | Web server port | `8000` |
| `MEM_MESH_SEARCH_THRESHOLD` | Minimum similarity score | `0.5` |
| `MEM_MESH_USE_UNIFIED_SEARCH` | Enable hybrid search | `true` |
| `MEM_MESH_ENABLE_KOREAN_OPTIMIZATION` | Korean n-gram FTS | `true` |
| `MEM_MESH_LOG_LEVEL` | Server log level | `INFO` |
| `MEM_MESH_LOG_FILE` | Log output file | (none) |

See `.env.example` for the full list.

### Pointing hooks at a remote mem-mesh server

Bash hooks (Stop, SessionStart, SubagentStop, …) resolve the API URL in this order:

1. `MEM_MESH_API_URL` environment variable
2. `API_URL` environment variable
3. **`~/.mem-mesh/api_url`** — single-line file with the server URL
4. The URL baked into the hook at install time
5. `http://localhost:8000`

Use the config file when you want one installed hook bundle to talk to a remote server (e.g. `https://mem.example.com`) without editing `settings.json` and without relying on env-var inheritance — Claude Code does not export `settings.json.env` retroactively to already-running sessions, so an env var added mid-session won't reach hooks until you restart.

```bash
mkdir -p ~/.mem-mesh
echo 'https://mem.example.com' > ~/.mem-mesh/api_url
mem-mesh doctor   # confirms the resolved URL and its source
```

Per machine — the file is not synced. Delete it (or set `MEM_MESH_API_URL`) to fall back to the baked default.

---

## Web Dashboard

- **Dashboard:** http://localhost:8000
- **API docs (Swagger):** http://localhost:8000/docs
- **Health check:** http://localhost:8000/health

---

## Architecture

```mermaid
flowchart LR
    subgraph Clients
        Cursor[Cursor]
        Claude[Claude Desktop]
        Kiro[Kiro]
        Web[Web Client]
    end

    subgraph Transport
        Stdio[Stdio MCP]
        SSE[SSE / Streamable HTTP]
    end

    subgraph Core
        MCP[mcp_common]
        Storage[Storage Service]
    end

    subgraph Data
        SQLite[(SQLite + sqlite-vec + FTS5)]
    end

    Cursor --> Stdio
    Claude --> Stdio
    Kiro --> Stdio
    Web --> SSE
    Stdio --> MCP
    SSE --> MCP
    MCP --> Storage
    Storage --> SQLite
```

### Directory structure

```
mem-mesh/
├── app/
│   ├── core/              # DB, embeddings, services, schemas
│   ├── mcp_common/        # Shared MCP tools, dispatcher, batch
│   ├── mcp_stdio/         # FastMCP stdio server
│   ├── mcp_stdio_pure/    # Pure MCP stdio server
│   └── web/               # FastAPI (dashboard, SSE MCP, OAuth, WebSocket)
├── static/                # Frontend (Vanilla JS, Web Components)
├── tests/                 # pytest
├── scripts/               # Migration and benchmark scripts
├── docs/rules/            # AI agent rule modules
├── data/                  # memories.db
└── logs/
```

---

## Docker

```bash
# Build and start
make quickstart
# or step by step:
make docker-build && make docker-up

# Open http://localhost:8000
```

---

## First-run setup (dashboard auth)

When the server starts with **no dashboard auth configured**, anyone who can reach the port can read/write/delete all memories. To let you close this from the browser (no shell-only config), mem-mesh mints a **one-time setup token** on first boot and prints it to the server console:

```
============================================================
  FIRST-RUN SETUP  —  dashboard auth is NOT configured
============================================================
  Open : /setup
  Token: <one-time-token>
  (one-time — consumed the moment you finish setup)
============================================================
```

`Open` shows the bare path `/setup` by default — open it on whatever host:port you bound the server to. Set `MEM_MESH_PUBLIC_URL` to have the banner print a full URL (e.g. `https://your-host/setup`).

The token is also written next to the DB (`/app/data/setup_token`), so it survives a mid-onboarding restart. Retrieve it any time:

```bash
docker exec mem-mesh-prod cat /app/data/setup_token
# or scan the logs
docker compose logs mem-mesh | grep -A1 "Token:"
```

Open `/setup`, enter the token plus an admin **username** (default `admin`) and **password** (≥ 8 chars). On submit mem-mesh saves the credential, enables Basic Auth, **consumes the token** (single-use), and logs you straight into the dashboard. On a fresh server the first page load auto-redirects to `/setup`.

Once auth is configured the token is deleted on every startup, so a leftover token can never reconfigure an already-secured server.

**Reset the token** (lost it, and auth is not configured yet) — delete the file and restart; `ensure_setup_token()` is idempotent, so a plain restart keeps the same value:

```bash
docker exec mem-mesh-prod rm -f /app/data/setup_token
docker restart mem-mesh-prod
docker logs mem-mesh-prod 2>&1 | grep -A1 "Token:"
```

---

## Development

```bash
# Install dev dependencies
pip install -e ".[dev]"

# Run tests
python -m pytest tests/ -v

# Format and lint
black app/ tests/
ruff check app/ tests/

# Check embedding migration status
python scripts/migrate_embeddings.py --check-only
```

---

## Documentation

- [CLAUDE.md](./CLAUDE.md) — AI tool checklist (MUST/SHOULD/MAY rules, security policy)
- [AGENTS.md](./AGENTS.md) — Project context, Golden Rules, Context Map, session management details

### AI agent rule modules (`docs/rules/`)

| File | Purpose |
|---|---|
| [DEFAULT_PROMPT.md](./docs/rules/DEFAULT_PROMPT.md) | **Default behavior rules** — copy into your project's CLAUDE.md or Cursor rules |
| [all-tools-full.md](./docs/rules/all-tools-full.md) | Full rules for all 15 tools |
| [mem-mesh-ide-prompt.md](./docs/rules/mem-mesh-ide-prompt.md) | Compact IDE prompt (~300 tokens) |
| [modules/quick-start.md](./docs/rules/modules/quick-start.md) | 5-minute quick start |
| [modules/](./docs/rules/modules/) | Feature modules: core, search, pins, relations, batch |

### Architecture docs

- [app/core/AGENTS.md](./app/core/AGENTS.md) — Core service internals
- [app/mcp_common/AGENTS.md](./app/mcp_common/AGENTS.md) — MCP common layer

---

## Contributing

1. Open an issue or pull request
2. Follow `black` and `ruff` formatting
3. Add tests for any new behavior

See [CONTRIBUTING.md](./CONTRIBUTING.md) for details and [CHANGELOG.md](./CHANGELOG.md) for release history.

---

## License

[MIT](./LICENSE)
