{
  "markdown": "# N3MemoryCore MCP — Lite (Ephemeral)\n\n> **N3MC-MCP-Lite is an \"external memory server\" used by MCP-compatible\n> editors such as Claude Code, Cursor, and Windsurf.**\n> It runs as an MCP Server so AI can save and search conversation and\n> code context across sessions.\n\n> A NeuralNexusNote™ product — **free Lite** build: ephemeral hybrid\n> (vector + BM25) memory exposed as a Model Context Protocol server, backed\n> by Redis Stack with a 7-day TTL per entry.\n\n> 💬 **The MCP protocol can only nudge the LLM to call `save_memory`, so\n> which conversations actually get saved is ultimately up to the LLM. But\n> if you ask Claude Code, it can also wire up hook-based auto-saving of\n> every conversation.**\n> Just say \"after every turn, automatically save the full Claude Code\n> transcript to Lite\" and Claude Code will drop a script under\n> `~/.claude/hooks/` and add a `Stop` hook to `~/.claude/settings.json`.\n> The harness runs the hook deterministically — it does not depend on the\n> LLM remembering to call `save_memory`, so Claude can never accidentally\n> skip a save. See the\n> [Hook-based full-transcript saving](#hook-based-full-transcript-saving)\n> section below for details.\n\n> 🇯🇵 **[日本語版はこちら](./README_JP.md)**\n> 🛡️ **[Development Philosophy](./PHILOSOPHY.md)**\n\n---\n\n## 🚀 Quickstart — connect to Claude Code in 3 steps\n\n> The fastest path from \"nothing installed\" to \"Claude Code is using\n> N3MC memory\". Pick the install path that matches you (PyPI / fork /\n> uvx), then add the server to your client config. Both Claude Code\n> CLI and Claude Desktop are covered.\n\n### Step 1 — Start Redis Stack\n\n```bash\ndocker run -d --name redis-stack -p 6379:6379 redis/redis-stack-server:latest\n# (Subsequent sessions: `docker start redis-stack`)\n```\n\n### Step 2 — Install the package (choose one)\n\n**Quickest path — Claude Code marketplace.** Bundles install + MCP wiring in two\ncommands. Run inside Claude Code:\n\n```\n/plugin marketplace add NeuralNexusNote/n3mcmcp-lite\n/plugin install n3mc-workingmemory@neuralnexusnote\n```\n\nThen `/reload-plugins` and skip Step 3 — the plugin manifest handles MCP wiring.\nThe manual options below remain available for forks, custom configs, and\nClaude Desktop.\n\n---\n\n**(a) From PyPI** — most users:\n\n```bash\npip install n3memorycore-mcp-lite\n```\n\n**(b) From a fork (you cloned this repo)** — contributors / customizers:\n\n```bash\ngit clone https://github.com/<YOU>/n3mcmcp-lite\ncd n3mcmcp-lite\npip install -e \".[dev]\"\n```\n\n**(c) Zero-install via uvx** — no global install, isolated env:\n\n```bash\n# Just verify it runs; the actual launch is handled by your MCP client config:\nuvx --from n3memorycore-mcp-lite n3mc-workingmemory --help\n```\n\nAfter step 2, the `n3mc-workingmemory` command is on your `PATH`. Run\n`where n3mc-workingmemory` (Windows) or `which n3mc-workingmemory`\n(macOS/Linux) to confirm.\n\n### Step 3 — Wire it into your MCP client\n\n| Client | What to do |\n|---|---|\n| **Claude Code (CLI), this repo's working tree** | `.mcp.json` is already committed — just `cd` into the repo and run `claude`. The CLI auto-connects on next prompt. |\n| **Claude Code (CLI), a different project directory** | Copy [.mcp.json](./.mcp.json) into that project, or add the same `n3mc-workingmemory` block to its `.mcp.json`. See [Claude Code (standalone CLI)](#claude-code-standalone-cli). |\n| **Claude Desktop** (incl. its built-in \"Code\" tab) | Edit `claude_desktop_config.json` (path differs per OS). See [Claude Desktop](#claude-desktop-and-the-code-tab-inside-claude-desktop). |\n| **Claude Code with auto-tool-approval** | One extra block in `~/.claude/settings.json` so the AI never blocks on \"Allow?\" prompts. See [Auto-approve tool calls](#auto-approve-tool-calls-claude-code-only). |\n| **uvx-launched** (no global install needed) | Use the uvx-form `command`/`args` in your client config. See [Claude Code (standalone CLI)](#claude-code-standalone-cli). |\n\nThat's it. Once Claude Code is connected, the server's behavioral\ninstructions take over — `search_memory` runs at the start of every\nturn and `save_memory` runs after each meaningful exchange, all\nautomatically.\n\n> First call may take 30–60 seconds the **first** time only — the\n> ~400 MB `intfloat/multilingual-e5-base` embedding model downloads to\n> `~/.cache/huggingface/`. Subsequent starts complete in seconds.\n\n---\n\n## ⚠️ Prerequisites (required before install)\n\nThis server does **not** run out of the box — you must prepare two things first:\n\n1. **Redis Stack on `localhost:6379`** — the Lite build stores memory in Redis + RediSearch. The easiest way is Docker:\n   ```bash\n   # First time only (creates the container):\n   docker run -d --name redis-stack -p 6379:6379 redis/redis-stack-server:latest\n\n   # Every subsequent session (container already exists):\n   docker start redis-stack\n   ```\n   Re-running the `docker run` command after the container exists fails with `Conflict. The container name \"/redis-stack\" is already in use`. Use `docker start` from the second session onward.\n\n   > **Why no persistence flags on the docker line**: this build is\n   > *deliberately volatile*. Ephemerality is a design feature, not a\n   > missing capability — see the \"Use cases\" section below. Rather\n   > than rely on fragile shell-quoting for `--save \"\"` (which breaks on\n   > Windows PowerShell and cmd.exe), the MCP server **enforces** the\n   > ephemeral state at startup by issuing `CONFIG SET appendonly no` and\n   > `CONFIG SET save \"\"` on every connect. If you manually re-enable\n   > persistence between sessions, it is reverted on the next Lite run.\n   > The plain `docker run` above is sufficient — the server is the\n   > source of truth for the ephemerality guarantee.\n2. **[`uv`](https://docs.astral.sh/uv/) on your `PATH`** — required only for the Claude Code plugin / `uvx` install path. Not needed if you install from source.\n\nThe server refuses to start if Redis is unreachable, and the Claude Code plugin will fail to launch without `uv`. Install both before running `/plugin install` or any client-side config.\n\n---\n\n## Features\n\n- 💾 **Fully local** — Your conversations stay in your own Redis instance. Nothing sent to the cloud.\n- 🔍 **Semantic search** — Finds relevant past conversations even when the exact words differ.\n- 🌐 **Multilingual out of the box** — CPU-only, no LLM/GPU required. NFKC fold (`ｱﾙﾌｧ`↔`アルファ`, `１２３`↔`123`, ligatures), bigram coverage for Japanese / Chinese / Korean / Thai / Lao / Myanmar / Khmer, diacritic cross-match for Latin scripts (`café`↔`cafe`).\n- 🛡️ **Encoding safety** — stdio UTF-8 reconfigure on Windows (cp932 → UTF-8), lone-surrogate sanitization on every input. Same defenses as the Free build.\n- 🔄 **Context across sessions** — Working memory that lasts **7 days** (auto-expires via Redis TTL; pair with any persistent memory backend if you need longer retention).\n- ⚡ **Works automatically** — Saving and searching happen automatically. The MCP `initialize` response ships behavioral instructions, so no user action is required.\n- 🤖 **Multi-agent ready** — Multiple AI agents share one Redis. The `b_local` and `b_session` biases prioritize each project's own memories while still surfacing the team's collective knowledge.\n- 🏢 **Team & organization support** — Deploy Redis on a shared server and point `N3MC_REDIS_URL` to it for team-wide memory sharing (⚠️ authentication must be handled at the Redis layer).\n- 🧹 **Ephemerality is a design feature** — 7-day auto-expiry means failed attempts and abandoned designs don't bleed into the next task. `docker restart redis-stack` wipes everything instantly.\n- 💰 **Reduces token waste** — No more re-explaining past context. Memory search uses local embeddings (`intfloat/multilingual-e5-base`) and costs zero Claude tokens, and accurate context injection means fewer corrections and back-and-forth.\n\n## How It Works\n\n```\nUser's message\n    │\n    ▼\n┌──────────────┐     ┌──────────────┐     ┌──────────────┐\n│  1. Auto-save │────▶│ 2. Semantic   │────▶│ 3. Context    │\n│  Save last    │     │    search     │     │    injection   │\n│  response to  │     │  Find related │     │  Feed to       │\n│  Redis        │     │  memories     │     │  Claude        │\n└──────────────┘     └──────────────┘     └──────────────┘\n                                                 │\n                                                 ▼\n                                          Claude responds\n                                          with full context\n```\n\nEverything runs automatically via the **behavioral instructions** shipped\nin the MCP `initialize` response. No Claude Code hooks are involved — the\nonly client-side setup is adding the tools to `permissions.allow`. No user\naction required.\n\n### Relationship with Claude's built-in auto-memory\n\nClaude Code has a built-in auto-memory system\n(`~/.claude/projects/.../memory/`). N3MemoryCore **complements it rather\nthan competing with it**.\n\n|                 | Claude auto-memory                                      | N3MemoryCore RAG                                     |\n| --------------- | ------------------------------------------------------- | ---------------------------------------------------- |\n| **Strengths**   | Reliable, loads every session, great for fixed facts    | Conversation context, detailed history               |\n| **Weaknesses**  | Cannot capture conversation flow or context             | Depends on search quality; not guaranteed to surface |\n| **Best for**    | User profile, folder paths, stable settings             | Conversation threads, past decisions, reasoning      |\n\n**Recommended usage:**\n\n- **Fixed information needed every session** (folder paths, user preferences) → save to auto-memory\n- **Conversation context and history** (discussion threads, past decisions) → N3MemoryCore accumulates automatically (7-day window; pair with a persistent memory backend if you need longer retention)\n\n---\n\n## Use cases — when working memory is the right tool\n\nThe 7-day TTL and volatile Redis storage are **design features, not\nlimitations**. They make this server the right fit for:\n\n- **Agentic code-generation loops** — failed attempts and abandoned\n  designs don't bleed into the next task; `docker restart redis-stack`\n  wipes the slate clean.\n- **Multi-agent collaboration** — decisions made during one task don't\n  contaminate unrelated follow-ups.\n- **Experimental / throwaway prototyping** — leave it alone and memory\n  evaporates in 7 days, no pruning needed.\n- **Project-scoped working memory** — pin a `session_id` per task /\n  project to keep contexts cleanly separated.\n\nIf you need **long-term, persistent knowledge accumulation across\nmonths or years**, working memory is not the right layer. Pair this\nserver with any persistent memory MCP — the official knowledge-graph\nserver, your own SQLite-backed implementation, or an external\nservice — to cover the long-term side.\n\n## What is this?\n\n`n3memorycore-mcp-lite` is a local-only MCP server that gives Claude (and\nany other MCP-compatible client) short-lived memory across conversations.\nIt stores text entries in a local Redis Stack instance with both a BM25\nfull-text index and a 768-dimension vector index\n([`intfloat/multilingual-e5-base`](https://huggingface.co/intfloat/multilingual-e5-base)), and\nreturns hybrid-ranked results.\n\nEvery operation runs on the user's machine. No API calls, no cloud\nstorage.\n\n## Tools exposed\n\n| Tool                          | Purpose                                                                          |\n| ----------------------------- | -------------------------------------------------------------------------------- |\n| `search_memory`               | Hybrid (vector + BM25) search, ranked & time-decayed, `session_id` boost         |\n| `save_memory`                 | Persist a short entry (7d TTL, dedup: exact + near-duplicate)                    |\n| `list_memories`               | Most-recent entries, newest first                                                |\n| `delete_memory`               | Remove a specific entry by id (cascades to chunks if id is a parent doc)         |\n| `delete_memories_by_session`  | Bulk-delete every memory tied to a `session_id` — wraps up a finished project    |\n| `repair_memory`               | Re-create the RediSearch index if missing                                        |\n\nThe server also ships **behavioral instructions** via MCP's `initialize`\nresponse, asking the client to `search_memory` at the start of each turn\nand `save_memory` after each meaningful exchange — so \"auto-save\" is\npreserved without any Claude Code hooks.\n\n## ID hierarchy\n\nN3MemoryCore identifies the origin and context of every record with\nfive ID fields. Most users only ever touch `session_id` (and rarely\n`agent_name`); the rest are filled in automatically.\n\n| ID                    | Stored in                       | Generated                                  | Granularity                            | Purpose |\n|-----------------------|---------------------------------|--------------------------------------------|----------------------------------------|---------|\n| `id` (PK)             | Redis hash                      | Per record (UUIDv7, time-ordered)          | **One record**                         | Unique identifier for each memory — used for `delete_memory` and dedup. |\n| `owner_id`            | `config.json`                   | First startup (UUIDv4)                     | **Owner / installation**               | Identifies whose data this is. Validated on every `save_memory`; mismatched payloads are rejected with `owner_id mismatch`. Stored as a TAG field; filtering happens in Python (see spec §3.12). |\n| `local_id` (agent_id) | `config.json`                   | First startup (UUIDv4)                     | **Agent / install**                    | UUIDv4 identifier for this install. Stored on every row for forward-compatibility with future persistent variants, but **does NOT feed Lite's `b_local` ranking** — `b_local` is computed from `stored_importance + access_count` only (see Ranking formula). |\n| `session_id`          | In-memory or supplied by client | Per task / project / conversation (string) | **Task / project / conversation**      | Surfaces memories from the same task / project together. Drives the **`b_session` ranking bias** (`b_session_match=1.0`, `b_session_mismatch=0.6`) so the current chat's memories outrank unrelated cross-project rows in the same Redis instance. Also the filter key for `delete_memories_by_session`. Resolution order: per-call argument → `N3MC_SESSION_ID` env var → per-process UUIDv4 fallback. |\n| `agent_name`          | Redis hash                      | Per `save_memory` call (free-form string)  | **Agent display label**                | Human-readable label (e.g. `\"claude-code\"`, `\"claude-desktop\"`). Not used in ranking — display/audit only. |\n\n```\nowner_id  (one N3MC server / data owner)\n  └── session_id  (one task / project / conversation)\n        └── local_id  (the agent speaking inside that session)\n              ├── agent_name  (its display name: \"claude-code\" etc.)\n              └── id  (one memory record)\n```\n\n**Practical guidance:**\n\n- **You should pin `session_id`** when working on a named project or\n  task. Pass the same string (e.g. `\"proj-alpha\"`, `\"task-refactor-auth\"`)\n  to both `save_memory` and `search_memory`. This both ranks-up the\n  project's own memories and gives you a one-shot\n  `delete_memories_by_session` for project teardown.\n- **You can leave `agent_name` empty** for single-agent use. Set it\n  (`\"claude-code\"`, `\"cursor\"`, …) when multiple agents share the same\n  Redis so audit/list output stays readable.\n- **You should not pass `owner_id`** unless you specifically need to\n  prove ownership (the server validates it against `config.json` and\n  rejects mismatches; an empty value means \"use my own\").\n\n## Prerequisites\n\n### 1. Start Redis Stack\n\nThe Lite build requires Redis Stack (Redis + RediSearch module). The\neasiest way is Docker:\n\n```bash\n# First time only (creates the container):\ndocker run -d --name redis-stack -p 6379:6379 redis/redis-stack-server:latest\n\n# Every subsequent session (container already exists):\ndocker start redis-stack\n```\n\nThat's it — the container exposes Redis on `localhost:6379` and the\nserver will find it automatically. Re-running the `docker run` command\nafter the first install produces `Conflict. The container name\n\"/redis-stack\" is already in use`; use `docker start redis-stack`\nthereafter.\n\n### 2. Install the package\n\n**From PyPI (recommended):**\n\n```bash\npip install n3memorycore-mcp-lite\n```\n\nOr zero-install via `uvx` (the Claude Code plugin uses this path):\n\n```bash\nuvx --from n3memorycore-mcp-lite n3mc-workingmemory\n```\n\n**From source** (if you want to edit the code):\n\n```bash\ngit clone https://github.com/NeuralNexusNote/n3mcmcp-lite\ncd n3mcmcp-lite\npip install -e .\n```\n\nThe first run downloads the ~400 MB embedding model from Hugging Face\ninto the standard `~/.cache/huggingface/` directory.\n\n> **First install requires internet access to three resources:**\n> 1. **github.com** — when `/plugin marketplace add NeuralNexusNote/n3mcmcp-lite`\n>    registers the plugin (skip this step if you install via `uvx` or from\n>    source instead).\n> 2. **pypi.org** — when `uvx --from n3memorycore-mcp-lite` (or `pip install`)\n>    resolves the package.\n> 3. **huggingface.co** — when the server first starts and downloads\n>    `intfloat/multilingual-e5-base` (~400 MB) into `~/.cache/huggingface/`.\n>\n> All three fail with explicit, time-bounded errors when offline; none\n> hang. Subsequent starts use only the local cache and require no\n> internet.\n\n## Configure a client\n\n### Claude Desktop (and the \"Code\" tab inside Claude Desktop)\n\nIf you are using the **Claude Desktop application** — including its\nbuilt-in **Code** tab — configure MCP via the desktop config file, NOT\nvia `.mcp.json` (which is only read by the standalone `claude` CLI).\n\nAdd to `~/Library/Application Support/Claude/claude_desktop_config.json`\n(macOS) or `%APPDATA%\\Claude\\claude_desktop_config.json` (Windows):\n\n```json\n{\n  \"mcpServers\": {\n    \"n3mc-workingmemory\": {\n      \"command\": \"n3mc-workingmemory\",\n      \"args\": []\n    }\n  }\n}\n```\n\n**Windows tip:** if Claude Desktop fails to spawn the server with the\nbare command name above (the hammer/tool icon never appears), replace\n`\"command\"` with the absolute path to the installed `.exe`, for example:\n\n```json\n\"command\": \"C:\\\\Users\\\\<YOU>\\\\AppData\\\\Local\\\\Programs\\\\Python\\\\Python312\\\\Scripts\\\\n3mc-workingmemory.exe\"\n```\n\nRun `where n3mc-workingmemory` in a terminal to find the exact path on\nyour machine.\n\n**After editing the config, fully quit Claude Desktop** — closing the\nwindow is not enough. Right-click the Claude icon in the system tray (or\nuse Task Manager) and terminate every Claude process, then relaunch.\n\n### Claude Code (standalone CLI)\n\nThis section applies ONLY to the `claude` command-line tool, not to the\nClaude Desktop \"Code\" tab (see above for that).\n\n**`.mcp.json` is already included in this repository.** Clone the repo,\ninstall the package, and the Claude Code CLI connects automatically — no\nmanual configuration needed.\n\nFor other projects, add the following to that project's `.mcp.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"n3mc-workingmemory\": {\n      \"type\": \"stdio\",\n      \"command\": \"n3mc-workingmemory\",\n      \"args\": []\n    }\n  }\n}\n```\n\n### Auto-approve tool calls (Claude Code only)\n\nBy default, Claude Code prompts the user for each MCP tool call. For a\nfully automatic memory loop — so the connected AI never blocks on an\n\"Allow?\" prompt — the `n3mc-workingmemory` tools must be listed under\n`permissions.allow` in Claude Code settings.\n\n**Plugin install auto-configures this** — when you install via\n`/plugin install n3mc-workingmemory@neuralnexusnote`, a `SessionStart`\nhook runs [`hooks/install_permissions.py`](plugins/n3mc-workingmemory/hooks/install_permissions.py)\nwhich idempotently adds the six `mcp__n3mc-workingmemory__*` tools to\n`~/.claude/settings.json`. No manual editing needed. The hook only\nwrites if at least one entry is missing and never touches unrelated\nfields. The `hooks.json` command tries `python` → `py` (Windows Python\nLauncher) → `python3` in a `||` fallback chain, so the hook works as\nlong as any one of these is on `PATH`. It only exits non-zero —\nsurfacing in Claude Code's `/plugins` Errors tab — when **all three**\nare missing, avoiding silent failure.\n\nThe same hook also performs a **`uvx` pre-flight check** — the plugin\nmanifest launches the MCP server via\n`uvx --from n3memorycore-mcp-lite n3mc-workingmemory`, so a missing\n`uvx` would otherwise surface only as an opaque `ENOENT` in the MCP\nlauncher. The hook calls `shutil.which(\"uvx\")` and, if not found,\nwrites a bilingual install hint to stderr (`pipx install uv`,\n`curl -LsSf https://astral.sh/uv/install.sh | sh`, plus the docs URL)\nso the user sees an actionable message in the `/plugins` Errors tab.\nThe hook still exits 0 because the permission install itself\nsucceeded.\n\n**If you installed without the plugin** (e.g. `claude mcp add` or a\nmanual `.mcp.json`), or no Python interpreter is available at all, add\nthe block below manually to `~/.claude/settings.json` (user-global,\nrecommended) or `.claude/settings.json` (per-project):\n\n```json\n{\n  \"permissions\": {\n    \"allow\": [\n      \"mcp__n3mc-workingmemory__search_memory\",\n      \"mcp__n3mc-workingmemory__save_memory\",\n      \"mcp__n3mc-workingmemory__list_memories\",\n      \"mcp__n3mc-workingmemory__delete_memory\",\n      \"mcp__n3mc-workingmemory__delete_memories_by_session\",\n      \"mcp__n3mc-workingmemory__repair_memory\"\n    ]\n  }\n}\n```\n\nWithout this, every `save_memory` / `search_memory` call surfaces an\napproval prompt and the AI blocks if the user is away. Claude Desktop\nhas no per-tool permission gate, so this step is not needed there.\n\n## Data location\n\nThe Lite build does not store a database on disk — memories live in\nRedis and expire automatically. Only a small `config.json` sits in the\nplatform-standard user data directory:\n\n| OS      | Path                                                       |\n| ------- | ---------------------------------------------------------- |\n| Windows | `%LOCALAPPDATA%\\n3mc-workingmemory\\`                       |\n| macOS   | `~/Library/Application Support/n3mc-workingmemory/`        |\n| Linux   | `~/.local/share/n3mc-workingmemory/`                       |\n\nOverride with the `N3MC_DATA_DIR` environment variable.\n\n## Configuration\n\nOn first run, `config.json` is auto-generated with random UUIDs for\n`owner_id` and `local_id`. Editable defaults:\n\n```json\n{\n  \"owner_id\":                 \"<uuid>\",\n  \"local_id\":                 \"<uuid>\",\n  \"redis_url\":                \"redis://localhost:6379/0\",\n  \"ttl_seconds\":              604800,\n  \"dedup_threshold\":          0.95,\n  \"half_life_days\":           3,\n  \"bm25_min_threshold\":       0.1,\n  \"search_result_limit\":      20,\n  \"context_char_limit\":       3000,\n  \"min_score\":                0.2,\n  \"search_query_max_chars\":   2000,\n  \"chunk_threshold\":          400,\n  \"chunk_overlap\":            100,\n  \"access_count_enabled\":     true,\n  \"access_count_weight\":      0.02,\n  \"access_count_max_boost\":   0.5,\n  \"ttl_refresh_on_search\":    true,\n  \"ttl_refresh_top_k\":        5,\n  \"lexical_rerank_enabled\":   true,\n  \"rerank_weight\":            0.3,\n  \"rerank_phrase_weight\":     0.2,\n  \"b_session_match\":          1.0,\n  \"b_session_mismatch\":       0.6,\n  \"skip_code_blocks\":         false\n}\n```\n\n- `redis_url` — connection URL; `N3MC_REDIS_URL` env var takes precedence.\n- `ttl_seconds` — TTL on every new memory and sha-guard (default 7 d).\n- `chunk_threshold` / `chunk_overlap` — sliding-window size and overlap (chars). Bodies longer than the threshold trigger the parent-document + chunks path for verbatim recall.\n- `access_count_*` — access-frequency auto-importance; top-K search hits receive a capped boost on future queries.\n- `ttl_refresh_on_search` / `ttl_refresh_top_k` — TTL reset for the top-K hits on each search (reset-only; no extension past a fresh save).\n- `lexical_rerank_*` / `rerank_weight` / `rerank_phrase_weight` — lightweight post-fusion lexical reranker (CPU-only).\n- `b_session_match` / `b_session_mismatch` — multiplicative ranking boost for rows whose stored `session_id` matches (default `1.0`) vs. rows from other projects (`0.6`). Pass the same `session_id` to `save_memory` and `search_memory` to surface a project's memories above unrelated cross-project rows in the same Redis instance. Set both to `1.0` to disable the bias.\n- `skip_code_blocks` — when `true`, `save_memory` rejects any payload containing a triple-backtick fence (```` ``` ````) and returns `status: \"skipped_code\"`. Default `false`. Set to `true` if you want FastAPI-era N3MemoryCore-style code exclusion (keep code out of the memory index entirely — useful when your workflow already has git/IDE history for code and you only want prose decisions/plans in Redis).\n\nSee the spec §6 for the complete field-by-field reference.\n\n## Multilingual support\n\nBuilt-in, CPU-only, no LLM and no GPU required. Search and dedup behave\nthe same regardless of how the user types the same word:\n\n| Layer | What it does | Real-world example |\n|---|---|---|\n| **NFKC normalization** | Folds compatibility forms before SHA / embedding / BM25 | `ｱﾙﾌｧ` ↔ `アルファ`, `１２３` ↔ `123`, `ﬁ` ↔ `fi` |\n| **Bigram BM25 side channel** | Overlapping bigrams emitted for space-less scripts | `記憶装置` → `記憶 憶装 装置`; same for Korean (`안녕하세요`), Thai (`สวัสดี`), Lao, Myanmar, Khmer |\n| **Diacritic fold** | Latin/Greek/Cyrillic words also indexed without combining marks | `café` matches `cafe`, `Ångström` matches `Angstrom` |\n| **multilingual-e5-base embedding** | Multilingual semantic space across 100+ languages | Cross-language paraphrase retrieval |\n\nThese run automatically on every `save_memory` and `search_memory` call.\nThe raw `content` field is never rewritten — verbatim recall (spec §3.11)\nstill returns the original bytes byte-for-byte.\n\n## Encoding safety\n\nTwo layers of defense run before any tool body executes (spec §3.13).\nSame guards as the Free build, ported one-to-one:\n\n1. **stdio UTF-8 reconfigure** — at module import, `sys.stdin` /\n   `sys.stdout` / `sys.stderr` are switched to `encoding=\"utf-8\"`. On\n   Windows-Japanese hosts the default console code page is cp932, which\n   would otherwise mangle every non-ASCII byte on the MCP JSON-RPC\n   channel. POSIX systems are already UTF-8, so the call is a safe no-op.\n2. **Lone-surrogate sanitization** — every `save_memory.content` and\n   `search_memory.query` is passed through `sanitize_surrogates()` before\n   any `.encode(\"utf-8\")` call. Lone UTF-16 surrogate halves\n   (`U+D800`–`U+DFFF`) appear when Windows subprocess pipes deliver UTF-8\n   bytes that Python's decoder maps with `errors=\"surrogateescape\"` —\n   they round-trip through `json.loads` but raise `UnicodeEncodeError` at\n   SHA1 / Redis HSET / embedding time. Without the guard the entire write\n   is silently lost. The function is recursive so JSON payloads with\n   surrogates buried inside are cleaned in one pass.\n\nIf a save payload consists entirely of surrogates, sanitization collapses\nit to the empty string and the regular empty-content rejection path\napplies — `{\"status\":\"error\",\"saved\":false,\"reason\":\"empty content\"}`.\n\n## Ranking formula\n\n```\nfinal_score = (0.7 * cosine_similarity + 0.3 * keyword_relevance) * time_decay * b_local * b_session\n\ntime_decay   = 2 ^ (-days_elapsed / half_life_days)       (default half-life: 3 days)\nb_local      = clamp(0.5, 2.0, stored_importance + access_boost)\naccess_boost = min(0.5, access_count * 0.02)\nb_session    = b_session_match (default 1.0)   if row.session_id == effective_session\n             = b_session_mismatch (default 0.6) otherwise\n```\n\nWith a default 3-day half-life (shorter than the 7-day TTL), `time_decay`\nis meaningful in the Lite build: a fresh memory scores 1.0, a 3-day-old\none exactly 0.5, and a 7-day-old (near-expiry) entry ≈ 0.20 — pushing\nrecent context ahead in the ranking.\n\n**Auto-importance (access-frequency boost)**: each time `search_memory`\nreturns a memory in its top 5 hits, that memory's `access_count` is\nincremented by 1 and `b_local` rises by 0.02 on future queries (capped at\n+0.5). No LLM judgement required — frequently-useful memories naturally\nfloat to the top through CPU-only self-tuning.\n\n## Development\n\n```bash\n# Start Redis Stack first (see Prerequisites), then:\npip install -e \".[dev]\"\npytest tests/ -q\n```\n\nTests target Redis DB index `0` (configurable via `N3MC_REDIS_TEST_URL`)\nand `FLUSHDB` it before/after each test. RediSearch refuses to create\nindexes outside DB 0 (`Cannot create index on db != 0`), so a separate\ntest DB isn't an option — run the test suite against a **dedicated**\nRedis container, never one that holds data you care about. Tests refuse\nto run if Redis isn't reachable.\n\n## Extending the Lite build\n\nIf you want to modify behavior (change the ranking formula, drop in a cross-encoder reranker, plug in a Japanese morphological tokenizer, etc.), start from the design spec shipped in this repository:\n\n- [`N3MemoryCore_MCP_Spec_EN.md`](https://github.com/NeuralNexusNote/n3mcmcp-lite/blob/main/N3MemoryCore_MCP_Spec_EN.md) — full design document (English)\n- [`N3MemoryCore_MCP_Spec_JP.md`](https://github.com/NeuralNexusNote/n3mcmcp-lite/blob/main/N3MemoryCore_MCP_Spec_JP.md) — 日本語版\n\nAppendix A of the spec lists optional extensions (cross-encoder reranker, save-time chunking, HyDE, Japanese morphological analysis) with drop-in points and library candidates. Use it as reference when you want to edit the code without breaking the TTL, dedup, or RediSearch contracts.\n\n## Why N3MemoryCore? (vs. built-in memory)\n\nThe auto-save *reliability* of N3MemoryCore is no better than the memory\nfeatures built into modern LLM products (e.g. Claude's built-in memory)\n— both depend on the LLM voluntarily calling a save tool, and both share\nthe non-determinism described in *On compliance* below. The differentiation\nsits elsewhere:\n\n| Aspect | Built-in memory | N3MemoryCore (Lite) |\n|---|---|---|\n| **Data ownership** | Vendor-hosted | **Your own Redis Stack on your machine** |\n| **Client surface** | The vendor's product only | **Any MCP-compliant client** (Claude Code, Cursor, Cline, Goose, your own app) |\n| **Multi-AI collaboration** | One AI's memory | **`session_id` lets multiple agents share the same memory namespace; `delete_memories_by_session` wraps up a finished task** |\n| **Verbatim recall** | Opaque (may be summarized) | **Parent-document contract — byte-exact full text returned** |\n| **Search internals** | Black box | **Hybrid BM25 + e5 vectors + CJK bigram + time decay + lightweight reranker, all parameters visible and tunable** |\n| **Inspect / control** | UI only | **`list_memories` / `delete_memory` / `delete_memories_by_session` operate on raw records** |\n| **Persistence** | Tied to the vendor's service lifetime | **In-memory Redis with 7-day TTL** — short-lived by design, but you own the container; pair with any persistent memory backend for long-term storage |\n| **Tunability** | Fixed | `half_life_days`, `chunk_threshold`, `dedup_threshold`, rerank weights — all editable |\n\nSo the value of running N3MemoryCore Lite is **not** \"more reliable\nauto-save\" — it is **owning a transparent, multi-client working-memory\nlayer** that several AIs can collaborate on under a shared `session_id`,\nwhere search behaviour is editable and verbatim recall is contractually\nguaranteed. (For long-term, persistent storage of user-invested artifacts,\npair it with any persistent memory backend.)\n\nIf those properties matter to your workflow, Lite earns its keep. If you\nonly need \"the LLM remembers something across sessions\" inside one\nvendor's product, the built-in memory is simpler.\n\n## On compliance — MCP can persuade, not force\n\nThis server cannot make the LLM call its tools. The MCP protocol gives a\nserver only three persuasion levers:\n\n1. **Tool descriptions** in `tools/list` — visible to the LLM on every turn.\n2. **The `instructions` field** sent at session start — usually surfaced to\n   the LLM as a system-level hint.\n3. **Tool response text** — read by the LLM when it does call a tool.\n\nWe use all three: tool descriptions are explicit, `instructions` lays out a\nrule set, and `search_memory` / `save_memory` responses end with short\nreminders that re-anchor the auto-save discipline mid-turn. Even with all\nof that, **whether the LLM follows through is non-deterministic**.\nCompliance depends on the model's tool-calling bias, the MCP client's\nprompt construction (some clients summarize or drop the `instructions`\nfield), and competing instructions from the user prompt, `CLAUDE.md`, etc.\n\nIn practice: **most turns will auto-save correctly, but some won't** —\nespecially short answers, fact-correction turns, or turns where the LLM is\nheavily focused on the user's question. If a fact you wanted saved is\nmissing next session, just say \"save this\" — the server is still ready to\ntake it.\n\n### When you need a guaranteed save\n\nWithin the MCP framing, three paths bypass this non-determinism:\n\n**Path 1 — ask the LLM explicitly in your prompt** (operational workaround,\nimmediate). Write *\"save this to N3MemoryCore\"* or *\"record this in\nmemory\"* into your prompt. LLMs almost always honour explicit user\nrequests. Pros: zero infrastructure, works today, works with every MCP\nclient. Cons: cognitive load — you must remember to say it; not automatic.\n\n### Hook-based full-transcript saving\n\n**Path 2 — Claude Code hook that saves the full transcript** (Claude Code\nonly, deterministic). Claude Code exposes harness-level hooks (`Stop`,\netc.) that the harness runs deterministically — they do not depend on the\nLLM remembering anything. Setup is one prompt to Claude Code:\n\n> *\"After every turn, automatically save the full Claude Code transcript\n> to Lite.\"*\n\nClaude Code then provisions:\n\n- A script at `~/.claude/hooks/save_transcript.py` that reads\n  `transcript_path` from hook input, imports `n3mc_mcp.database.Database`\n  directly, and calls `save_memory` on the Lite DB (no MCP round-trip).\n- A `hooks.Stop` block in `~/.claude/settings.json` that runs the script\n  after every assistant turn with `async: true` (so model load never\n  blocks the UI).\n\nBehavioral notes:\n\n- **Claude can never accidentally skip a save** — the harness fires the\n  hook regardless of what the LLM does.\n- No MCP round-trip overhead; the hook talks to Redis directly.\n- As a session grows, the per-turn transcripts collide via near-duplicate\n  detection (`dedup_threshold`), so the DB stays close to **one entry per\n  session** instead of one per turn.\n- Transcripts shorter than ~200 chars are skipped as noise.\n- Pros: deterministic / independent of model behavior / no save anxiety.\n- Cons: Claude Code only (Cursor / Windsurf need a different approach) /\n  the hook process loads the embedding model each turn (async, so no UI\n  block, but there is CPU/IO cost) /\n  **Lite's 7-day TTL still applies**, so transcripts saved this way still\n  expire within a week — point the same hook at any persistent memory\n  backend when long-term retention matters.\n\n**Path 3 — bypass MCP and call the first-party Anthropic Messages API\nyourself** (architecture change). Step outside MCP clients (Claude Code,\netc.) and drive `messages.create` `tool_use` directly from your own\napplication code; you can then fire `save_memory` deterministically every\nturn regardless of what the LLM \"decided\" to do. Pros: deterministic /\nworks with any model and any client. Cons: you have to write the\norchestration application.\n\nThe convenience of \"MCP + LLM handles it for me\" and the guarantee of\n\"every turn saves\" sit at opposite ends of a tradeoff. This server packs\nits persuasion levers as hard as the protocol allows; any stronger\nguarantee is your call as the user or client implementer (and if you're\non Claude Code, Path 2 is by far the lowest-cost option).\n\n## Forking & contributing\n\nThis repository is **public and Apache-2.0 licensed** — fork, modify,\nand run it freely. The fork-and-run path is:\n\n```bash\ngit clone https://github.com/<YOU>/n3mcmcp-lite\ncd n3mcmcp-lite\ndocker run -d --name redis-stack -p 6379:6379 redis/redis-stack-server:latest\npython -m venv .venv && source .venv/bin/activate    # Windows: .venv\\Scripts\\Activate.ps1\npip install -e \".[dev]\"\npytest tests/ -q                                      # 105 tests, ~30s warm\n```\n\nCI runs the same matrix on every push and PR — see\n[`.github/workflows/test.yml`](./.github/workflows/test.yml). Read\n[`CONTRIBUTING.md`](./CONTRIBUTING.md) for the full developer guide\n(EN + JP) including coding conventions, the spec-as-contract policy,\nand PR checklist.\n\n**To actually use the fork from Claude Code**, you do NOT need any\nadditional setup beyond the `pip install -e \".[dev]\"` above:\n\n1. The `n3mc-workingmemory` command is now on your `PATH` (run\n   `which n3mc-workingmemory` to confirm).\n2. The repository's [`.mcp.json`](./.mcp.json) already declares the\n   server, so the moment you `cd n3mcmcp-lite && claude`, the CLI\n   auto-connects.\n3. For other client surfaces (Claude Desktop, a different project's\n   `.mcp.json`, auto-tool-approval), the [Quickstart Step 3\n   table](#-quickstart--connect-to-claude-code-in-3-steps) lists the\n   exact action.\n\nIf you intend to publish your fork under a new package name, also\nedit the `name`, `[project.urls]`, and console-script names in\n[`pyproject.toml`](./pyproject.toml) before re-uploading to PyPI.\n\n## Troubleshooting\n\n### Windows: `pip install --upgrade` fails with `WinError 32` (file in use)\n\nSymptom:\n```\nERROR: Could not install packages due to an OSError: [WinError 32]\nThe process cannot access the file because it is being used by another process:\n'...\\Scripts\\n3mc-workingmemory.exe' -> '...\\Scripts\\n3mc-workingmemory.exe.deleteme'\n```\n\nCause: an MCP client (Claude Code / Claude Desktop) is currently holding\n`n3mc-workingmemory.exe` open as a child process, so pip cannot replace\nthe binary.\n\nFix — pick one:\n\n1. **Fully quit the MCP client first.** Closing the window is not enough\n   on Windows. Open Task Manager and end every `claude` /\n   `n3mc-workingmemory.exe` / `python.exe` process whose command line\n   includes `n3mc-workingmemory`, then re-run `pip install --upgrade`.\n2. **Use `uvx` instead of a global install** — `uvx --from\n   n3memorycore-mcp-lite n3mc-workingmemory` runs in an isolated\n   ephemeral environment per session, so there is no system-level\n   `.exe` to lock.\n\nThis is a Windows file-locking quirk, not a packaging defect — the wheel\nitself installs cleanly into a fresh venv (`python -m venv .venv &&\n.venv/Scripts/pip install n3memorycore-mcp-lite`).\n\n### `~3memorycore-mcp-lite` warnings during pip install\n\nIf you see lines like:\n```\nWARNING: Ignoring invalid distribution ~3memorycore-mcp-lite\n```\nthat is pip flagging a previous install that was interrupted mid-write\n(typically by the file-lock issue above). The leftover directory is\nnamed with a leading `~` and is harmless but noisy. Delete it manually:\n\n```bash\n# Windows\nrmdir /s \"%LOCALAPPDATA%\\Programs\\Python\\Python312\\Lib\\site-packages\\~3memorycore_mcp_lite-1.5.0.dist-info\"\n```\n\n(Adjust the path to match your Python installation.)\n\n## License\n\nApache License 2.0 — see [LICENSE](./LICENSE).\n\n---\n\n<sub>MCP Registry: `mcp-name: io.github.NeuralNexusNote/n3mc-workingmemory`</sub>\n",
  "bytes": 39495,
  "sha": "39dc1ec9c6577ee4b246c882ac0384c795dc48338b6183d1df5e81bb5c5c6ac6",
  "repo_slug": "neuralnexusnote/n3mcmcp-lite",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_neuralnexusnote_n3mc_workingme_feb1e347/readme"
}