# cpersona-memory

Adopt this skill: https://proskills.md/skills/other/cloto-dev-cpersona-plugin-skills-cpersona-memory

Page: https://proskills.md/skills/other/cloto-dev-cpersona-plugin-skills-cpersona-memory
This document is the public skill bundle for that page. It contains SKILL.md and every helper file the catalog carries. No login is required.

## File: plugin/skills/cpersona-memory/SKILL.md
size: 34116
sha: 77fed364b80963453ad747eda4bba63910e6634f
upstream: https://github.com/cloto-dev/cpersona/blob/master/plugin/skills/cpersona-memory/SKILL.md
truncated: true

````text
---
name: cpersona-memory
description: >-
  Give Claude persistent, searchable memory across sessions using the CPersona
  MCP server. Use this skill whenever the user wants Claude to remember things
  between conversations, asks to install or set up CPersona / a memory server,
  or when CPersona tools are available and the conversation contains decisions,
  rules, preferences, or a session boundary worth recording, or when a recall
  comes back degraded or the user says memory search stopped working. Covers
  install, MCP-client configuration, the embedding server, the day-to-day
  store / recall / archive workflow, diagnosing and repairing a degraded
  embedding backend, and persisting the memory policy into the user's
  always-loaded instructions file (CLAUDE.md, AGENTS.md, …) so the triggers
  survive without this skill loaded.
---

# CPersona — persistent memory for Claude

CPersona is an [MCP](https://modelcontextprotocol.io/) server that gives Claude
persistent memory across sessions. It stores memories in a single local SQLite
file and retrieves them with a 3-layer hybrid search (vector + FTS5 + keyword,
merged by Reciprocal Rank Fusion). It has **zero LLM dependency** — the server
never calls a model, so memory adds no generative API cost and stays out of
your token budget; the calling agent (you) does all summarization. (Embedding
is separate: `EMBEDDING_MODE=api` bills per store and per recall against
`CPERSONA_EMBEDDING_API_URL`. The local `http` mode and `none` cost nothing.)

- Single SQLite file, MIT licensed.
- Works with Claude Code, Claude Desktop, Codex CLI, Cursor, VS Code, and any
  other MCP host.
- Repo: <https://github.com/Cloto-dev/cpersona>

This skill has two jobs: **(1) help the user install and configure CPersona**,
and **(2) use it correctly** once it is connected.

---

## When to use this skill

Activate this skill when any of the following is true:

- The user asks Claude to **remember** something across sessions, or complains
  that Claude forgets context between conversations.
- The user asks to **install / set up / configure** CPersona or "a memory
  server".
- CPersona MCP tools (`store`, `recall`, `archive_episode`, …) are connected
  **and** the current turn contains a decision, a standing rule/preference, a
  bug finding, or a session boundary (start/end).
- A `recall` response carries an `advisory` field, a `store` response comes back
  `embedded: false`, or the user reports that memory search has got worse.

If CPersona tools are **not** connected and the user wants memory, go to
**Setup**. If they are connected, go to **Usage** — or to **When recall is
degraded** if a signal in the last bullet is what brought you here.

---

## Setup

CPersona is a Python MCP server. Installing it has two parts: the **memory
server** and a strongly recommended **embedding server** (it powers the
vector-search layer). Running without an embedding backend is supported as a
fallback — CPersona then serves FTS5 + keyword only — but it is not
recommended for normal operation.

**Prerequisites:** Python 3.11+ (Git only for from-source installs). No Python
on the machine? Install [uv](https://docs.astral.sh/uv/) first — `uvx` and
`uv tool install` fetch a suitable interpreter on their own, without touching
the system.

### 1. Install CPersona

The one-command path (needs [uv](https://docs.astral.sh/uv/)):

```bash
uvx cpersona          # run directly
# or: pip install cpersona  (then the `cpersona` command is on PATH)
```

From source (development): `git clone https://github.com/Cloto-dev/cpersona.git`,
`cd cpersona`, `python -m venv .venv && source .venv/bin/activate`,
`pip install .`, run with `python -m cpersona`.

### 2. Install the embedding server (recommended)

CPersona is embedding-server-agnostic — it talks to any HTTP endpoint that
implements `POST /embed` → `{ "embeddings": [[float,…],…], "dimensions": int }`.
The reference server is [CEmbedding](https://github.com/Cloto-dev/CEmbedding)
(MIT), which runs `bge-m3` on-device (CPU), MIT-licensed and multilingual — the
model CPersona recommends (the earlier recommendation, `jina-v5-nano`, is licensed
for non-commercial use only):

```bash
# one-time model fetch into ./data/models (run both commands from the same directory)
uvx --from "cembedding[onnx]" cembedding-download-model --model bge-m3
EMBEDDING_PROVIDER=onnx_bge_m3 uvx --from "cembedding[onnx]" cembedding   # serves http://127.0.0.1:8401/embed
```

Or install it onto your PATH with `pip install "cembedding[onnx]"`, then run
`cembedding-download-model --model bge-m3` and
`EMBEDDING_PROVIDER=onnx_bge_m3 cembedding`. From source: `git clone
https://github.com/Cloto-dev/CEmbedding.git`, `cd CEmbedding`,
`pip install ".[onnx]"`, then `cembedding-download-model --model bge-m3`
and `EMBEDDING_PROVIDER=onnx_bge_m3 python -m cembedding` — the download
and the provider are not optional here any more than in the recipe above; a
server started without them serves a different backend than the one just
downloaded.

**Keep its stdin open.** The reference server runs an MCP session on stdio and
serves `/embed` from a background task, so its lifetime is bound to stdin.
Started with stdin closed — `nohup … </dev/null`, a service manager, or your
own background shell — it binds the port and exits within the same second with
status 0, leaving CPersona pointed at a URL nothing answers. Hold the pipe:
`sleep infinity | cembedding` (with the same `EMBEDDING_PROVIDER` as above).
In an interactive terminal the terminal already does this. `uvx` runs the
package out of uv's cache; for a server that has to come back after a reboot,
`uv tool install "cembedding[onnx]"` gives a persistent install with
`cembedding` on PATH.

> CEmbedding is the reference and recommended backend; any other server
> satisfying the contract above is equally supported and equally recommended.
>
> Without an embedding server, set `EMBEDDING_MODE=none`. That configuration is
> supported as a fallback but is not recommended for normal operation: vector
> search (the strongest retrieval layer) is disabled and recall falls back to
> FTS5 + keyword. CPersona v2.4.33+ will *tell* you when it is running degraded
> (see Troubleshooting) instead of silently serving reduced recall.

### 3. Register with the MCP client

Pick an absolute `CPERSONA_DB_PATH` (e.g. `~/.claude/cpersona.db`).

**Claude Code:**

```bash
claude mcp add-json cpersona '{"type":"stdio","command":"uvx","args":["cpersona"],"env":{"CPERSONA_DB_PATH":"/absolute/path/cpersona.db","EMBEDDING_MODE":"http","EMBEDDING_HTTP_URL":"http://127.0.0.1:8401/embed"}}' -s user
```

**Every other client** (Claude Desktop, Codex CLI, Cursor, VS Code, any MCP
host) — the per-client entries live in one place,
[Getting Started §3](https://cloto-dev.github.io/CPersona/getting-started/#3-register-cpersona-with-your-mcp-client),
and this skill does not repeat them. The shape is always the same triple:
command `uvx`, args `["cpersona"]`, and the `env` above; only the file and the
key names differ.

> **No embedding server yet?** Drop the `EMBEDDING_*` lines (or set
> `EMBEDDING_MODE=none`) — recall runs on FTS5 + keyword and reports when degraded.
> **From source:** use `"command": "python", "args": ["-m", "cpersona"]` with the
> venv's python. **ClotoCore users:** install from the in-app marketplace
> ([ClotoHub](https://hub.cloto.dev)) instead.

**Choose the `agent_id` now** — one stable id for this user and client (e.g.
`"claude-code"`, `"claude-desktop"`), reused on every call from here on. Recall
is scoped to an exact `agent_id`: a later session that guesses a different one
gets an empty result, not an error. With the cpersona plugin installed, its
session-start note already names one: the plugin's `agent_id` option, else
`CPERSONA_AGENT_ID`, else `default`. Keep that one, or, if the user wants
another, set it there as well (`/config` in Claude Code, `CPERSONA_AGENT_ID`
for Codex) so the notes and the policy block below name t
````

## File: plugin/skills/cpersona-memory/references/always-loaded-index.md
size: 6364
sha: 4ce942c3c023853b575ee2200b3bc3c2e0e201e1
upstream: https://github.com/cloto-dev/cpersona/blob/master/plugin/skills/cpersona-memory/references/always-loaded-index.md

````text
# Using an always-loaded index over this store

Some MCP clients keep a small file that is injected into **every** session —
Claude Code's `MEMORY.md` is the one this reference is written against. This
page is about pairing such a file with CPersona. It is loaded on demand; the
one-line rule that belongs in every session lives in the policy block in
`SKILL.md`.

Read this before moving any existing memory. **The migration is not a setup
step and this skill will not perform it for you** — see "Moving existing
memories" below for why.

## Why pair them at all

The two channels fail in opposite directions, and that is the whole value:

| | always-loaded index | CPersona |
| --- | --- | --- |
| arrives | **deterministically**, every session | when a `recall` happens to retrieve it |
| holds | one line per memory | the full text, vector-searchable |
| bounded by | a hard size cap | corpus size |
| readable by | one client | every client sharing the `agent_id` |

An index alone cannot hold bodies; it runs out of room. A store alone cannot
guarantee that an agent ever learns a memory exists, because retrieval is
ranked and a query that does not resemble the memory will not surface it. Put
the pointers where arrival is certain and the bodies where search works, and
each channel does the thing the other cannot.

## The shape

One line per memory in the index:

```
- <slug> — <one sentence that changes behaviour without opening anything>
```

The line is not a title. It is the part that has to work when nothing else
loads, so write the conclusion, not the topic: `never delete the -wal file` is
a line, `about WAL files` is not. Keep it short enough that the index stays
well under its cap — a line that costs 180 bytes buys nothing a 90-byte line
does not.

The body goes to CPersona with the slug as the join key, so that reading the
line tells the agent exactly what to ask for:

```
store(
  agent_id="<AGENT_ID>",
  message={
    "id": "memory-index:<slug>",          # dedup key: re-storing edits, never duplicates
    "content": "[<slug>] <the full memory>",
  },
)
```

Retrieve it with `recall(agent_id="<AGENT_ID>", query="<slug>")`. The prefix is
what makes the join checkable in both directions (below); `message.id` is what
makes a re-store idempotent, because CPersona deduplicates on it.

### Not everything should move

Split by whether the memory is **fired** or **queried**:

- **Fired** — behavioural rules that must act without anyone asking for them
  ("always do X", "never do Y"). The index line is the working copy; the body
  in CPersona is the evidence and the reasoning.
- **Queried** — lookups you open on purpose (a runbook, a table of endpoints,
  a design decision you cite). These want a real file at a real path, because
  citing one means naming where it is. Keep the body wherever your client
  keeps files, and give it an index line too.

Splitting this way also keeps the index honest about its own cost: the bodies
that grow without bound are exactly the ones that left.

## Keeping the two in step

The index and the store can disagree in two directions, and **both are silent**:

- a line whose row was never written, or was deleted — it costs its bytes in
  every session and points at nothing;
- a row with no line — it is in the store, and no session is told it exists.

Nothing detects either one for you. `check_health` audits the store's internal
consistency, not its agreement with a file it has never heard of. So check the
join yourself, on the two sets of slugs:

```bash
# every slug named by an index line
grep -oE '^- [a-z0-9-]+' MEMORY.md | sed 's/^- //' | sort > /tmp/index-slugs

# every slug stored under the prefix, via list_memories on the agent
#   -> extract the text between "[" and "]" at the start of each content
#   -> sort into /tmp/store-slugs

comm -23 /tmp/index-slugs /tmp/store-slugs   # lines with nothing behind them
comm -13 /tmp/index-slugs /tmp/store-slugs   # rows no session will hear about
```

Both outputs empty is the invariant. Run it after any bulk change, and treat a
non-empty result as a defect rather than as drift to be tidied later — a
half-migrated corpus is worse than either whole one, because neither channel
can be trusted on its own.

## The cap is the binding constraint

The index has a hard limit (Claude Code's is 25,000 bytes / 200 lines), it is
paid in every session, and **exceeding it does not raise an error** — the
content is simply not injected, which from inside the session is
indistinguishable from having no memories at all.

So decide the rule before you need it:

- Set a working threshold well below the cap (80% is a reasonable warning
  line) and act at the threshold, not at the limit.
- At the threshold, **consolidate rather than compress**. Merging several
  related lines into one durable document — one you can cite, and reference by
  a single line — reduces the growth rate. Shortening existing lines is a
  one-off saving that buys a few sessions and changes nothing about the slope.
- Never solve a full index by deleting entries you have not read. A line you
  cannot justify is a line whose body you should read first; the body is in
  the store, which is the point of putting it there.

## Moving existing memories

**Ask before moving anything, and do the move yourself rather than having the
agent do it unattended.** This skill deliberately does not automate it:

- It is destructive in a way that is hard to see. If the bodies are deleted
  from their old location after being stored, the old location is the only
  copy that existed; if they are not deleted, you now have two copies that
  will drift.
- It is not idempotent until the join key exists. Re-running a half-finished
  move without `message.id` set as above writes second copies of everything it
  already wrote.
- It is unbounded. A corpus of any size is a long sequence of writes, and
  nothing reports that it stopped halfway.

If you do move a corpus, the order that survives an interruption is: store the
body (with `message.id`), verify the row, then rewrite the index line, then
remove the old body — one memory at a time, so an interruption leaves a
consistent prefix rather than a scattered mixture. Run the join check above
afterwards, in both directions, and read the result rather than the exit code
of the loop.
````

## File: plugin/skills/cpersona-memory/references/embedding-backend-repair.md
size: 12907
sha: f67db63b59116664c83e44b291a38a1e22f50474
upstream: https://github.com/cloto-dev/cpersona/blob/master/plugin/skills/cpersona-memory/references/embedding-backend-repair.md

````text
# Repairing a degraded embedding backend

CPersona answers `recall` from three retrieval layers. Two of them — FTS5 and
keyword — live inside the SQLite file and cannot fail on their own. The third,
vector search, reaches an embedding server over HTTP, and it is the one that
degrades silently at the network boundary.

This file is the walkthrough for that single failure: how to tell which
condition you are in, what to say about it, what to propose, and how to prove
the repair worked. It is a separate file because it is only needed when
something is actually wrong — the day-to-day store / recall / archive workflow
in `SKILL.md` never reaches it.

---

## 0. What this file authorizes

Diagnosing and explaining: freely. Everything else: a proposal.

Starting a process, editing an MCP client's configuration file, changing an
environment variable, restarting a server — each is a change to the user's
machine, and the approval rules you already operate under govern them here
exactly as they do anywhere else. Show the exact command or the exact edit, say
what it will do, ask, and wait for an answer.

Nothing about a degraded backend makes that urgent. The memories are intact,
writes keep succeeding, and one of three search layers is off. There is no
state that decays while you wait for the user to reply.

---

## 1. Name the condition before you explain anything

Two conditions degrade recall and they need different conversations. Telling
them apart is the first step, not a detail.

| | **HINT** | **FAULT** |
|---|---|---|
| Configuration | no embedding backend configured (`mode=none`) | a backend is configured (`mode=http` or `mode=api`) |
| What it means | a standing, deliberate configuration | an outage: the configured endpoint did not answer |
| `advisory.severity` | `hint` | `fault` |
| `advisory.evidence` | `mode=none / no embedding backend configured` | names the mode, the endpoint, and the failure |
| Raised after | the first `recall` | two consecutive failed embed calls (one blip is debounced) |
| Right response | offer to connect a backend | find out why it stopped answering |
| Wrong response | treating it as a fault the user must fix now | treating it as a configuration choice |

A `hint` is not a smaller `fault`. The user chose a supported configuration and
may keep it; your job is to make sure they chose it knowingly. A `fault` is
something that used to work and stopped.

---

## 2. The signals, and what each one can and cannot tell you

- **`advisory` on a `recall` response** — the primary surface, and the only one
  that fires on its own. Shape:
  `{degraded, severity, reason, evidence, runbook, advisory_scope}`. The first
  time in a degraded episode it carries the full runbook; afterwards a short
  reminder. `advisory_scope` says whose "already told" state you are seeing:
  `session` when the caller declared a session key, `process` when the state is
  the whole server's and the transport is shared.
- **Running a `recall` is the liveness test — with two caveats.** It embeds the
  query through the real client, so a success clears the degraded state and
  re-arms the full runbook, and a failure feeds the fault counter. There is no
  separate probe to run. The first caveat is that it is not always read-only:
  with `CPERSONA_CONFIDENCE_ENABLED=true` a successful, non-deep recall that is
  not a gate fallback updates `recall_count` and `last_recalled_at` on the rows
  it returns. The flag ships off, so a default install is unaffected — but this
  page tells you to run the test repeatedly, and an install that turned
  confidence on is ranking on state those runs move. It writes nothing while the
  session is paused, which is what keeps the test safe during a no-persist
  window. The second caveat: a single-text embed is cached for five minutes, and a cache hit
  returns the stored vector without a request leaving the process. It no longer
  clears the degraded state — an advisory therefore survives a repeated query —
  but it cannot confirm a repair either. **Test with a query you have not just
  used**, or the answer describes the cache rather than the backend.
- **`embedded` on a `store` response** — `false` means the write landed without
  a vector. The memory is saved and searchable by keyword; the vector is
  repairable later (§6).
- **`check_health(agent_id, fix=true)`** — probes the backend and reports
  `embedding_backend_unreachable` (`warn`) with the failing call's own evidence
  when a configured backend does not answer. No finding means it answered; an
  answer to this run's own probe outranks a fault latched earlier.
- **`check_health(agent_id, fix=false)`** — does not judge liveness. It may
  still contact the backend (the dimension check's probe embed, the backend's
  identity, token counts for the node scan) but reads none of those calls as
  liveness, and says so: `embedding_backend_not_probed` (`info`). A fault
  a recall already latched is still reported. Do not read a quiet `fix=false`
  run as evidence that the backend is up. A `fix=true` run reports the same
  finding when the probe hit the embed cache instead of the endpoint; its
  `reason` field distinguishes `served_from_cache` from `no_probe_on_this_run`.
- **No backend configured is deliberately not a `check_health` finding.** It is
  permanent, it was never confusable with "the server is up", and a finding
  that can never be resolved teaches an operator to skim past the check. That
  state is raised on the recall advisory instead, which is the surface a user
  reads.
- **`get_session_findings()`** — `_meta.server_version` is the version of the
  code answering the request, not of whatever distribution sits beside it. Quote
  that when a version matters.

---

## 3. HINT — no backend is configured

### Say this

- CPersona is working. Memories are being stored and recalled right now.
- No embedding backend is configured, so recall runs on FTS5 + keyword only.
- That configuration is **supported** — it is not a broken install.
- It is **not recommended for normal operation**: recall matches on shared
  words, so a memory phrased differently from the question can be missed, and
  so can older ones.
- Connecting a compatible embedding server is strongly recommended, and it is a
  one-time setup.
- CEmbedding is the reference implementation and the first recommendation; any
  server satisfying the embedding contract is equally supported.

### Then propose one of two paths

**The reference backend.** Both commands run from the same directory; the first
downloads the model once, into `./data/models`:

```bash
uvx --from "cembedding[onnx]" cembedding-download-model --model bge-m3
EMBEDDING_PROVIDER=onnx_bge_m3 uvx --from "cembedding[onnx]" cembedding
```

That serves `http://127.0.0.1:8401/embed`. On a PATH install
(`pip install "cembedding[onnx]"`) the same two steps are
`cembedding-download-model --model bge-m3` and
`EMBEDDING_PROVIDER=onnx_bge_m3 cembedding`; from a source checkout,
`python -m cembedding.download_model --model bge-m3` and
`EMBEDDING_PROVIDER=onnx_bge_m3 python -m cembedding`.

**Any conforming backend.** CPersona is embedding-server-agnostic. The server
must implement `POST /embed`, taking `{"texts": ["…"]}` and returning
`{"embeddings": [[float, …], …]}`. Three requirements are easy to miss and each
degrades ranking quietly: embeddings **must be L2-normalized** (similarity is a
raw dot product, so unnormalized vectors bias ranking by magnitude); the
contract is **role-less**, with queries and documents embedded through the same
call and no instruction prefix, which is why prompt-prefix model families
underperform behind it; and CPersona sends **at most 32 texts per request**.

### Point CPersona at it

Both variables belong in the MCP client's `env` block for the `cpersona` server
— the same block that carries the database path — and the client has to be
restarted to pick them up:

- `CPERSONA_EMBEDDING_MODE=http`
- `CPERSONA_EMBEDDING_URL=http://127.0.0.1:8401/embed`

`EMBEDDING_MODE` and `EMBEDDING_HTTP_URL` are accepted as aliases, and the
`CPERSONA_`-prefixed names win when both are set. Check for an existing pair
before editing: setting the alias while the prefixed one is already present
changes nothing, and it looks exactly like a repair that did not take.

### If the user declines

That is a legitimate answer, and `CPERSONA_DEGRADED_ADVISORY=false` records it —
it stops the report, not the degradation. Offer it only after they have heard
what the configuration costs, and say plainly that it does not make running
without a backend the recommended setup.

---

## 4. FAULT — the configured backend is not answering

### Say this

- A backend is configured and is currently unavailable.
- Only the vector layer is affected. Storing and recalling both still work; the
  fallback in force is FTS5 + keyword.
- Writes made while it is down come back `embedded: false`. They are saved, and
  their vectors are repairable once the backend returns (§6).
- Quote `advisory.evidence` verbatim. It is built from the client's own
  configuration and the exception, never from request headers, and the endpoint
  is stripped of any userinfo and query string — so it names the mode, the
  endpoint and the failure without carrying a credential.

### Work through the causes in this order

1. **Read the endpoint in the evidence.** If it is not the endpoint the user
   expects, the configuration is the fault and no amount of restarting the
   server will help.
2. **Is the backend process alive?** It may have exited, or never been started
   in this session.
3. **Is the port reachable from this host?** A backend on another machine, in a
   container, or behind a firewall answers locally and not here.
4. **Did the model ever get downloaded on this machine?** A freshly provisioned
   host — or a database copied to a new one — has the configured URL and no
   model cache behind it.
5. **Did the client's configuration change?** A port moved, or a second
   `CPERSONA_`-prefixed variable was introduced and now takes precedence.

A direct probe separates "the backend is down" from "CPersona cannot reach it",
and it is read-only:

```bash
curl -s -X POST http://127.0.0.1:8401/embed \
  -H 'Content-Type: application/json' -d '{"texts":["ping"]}'
```

An `embeddings` array back means the backend is healthy and the problem is
between it and CPersona.

### Then propose the matching repair

Restart the backend; or correct the URL and restart the MCP client; or download
the model on this machine and start it. One cause, one proposal — and say which
of the five checks led you there, so the user can disagree with the diagnosis
rather than only with the command.

---

## 5. Verify the repair — do not stop at "it started"

Run these in order. The first three prove liveness; only the fourth repairs
what the outage left behind.

1. **`curl` the endpoint** (command above) — the backend answers at all.
2. **Run a `recall` with a query you have not just used** — no `advisory` on the
   response. An embed that reaches the backend clears the degraded state, so this
   is the check that says CPersona itself is satisfied. A repeated query can be
   answered from the five-minute embed cache; that hit clears nothing, so a
   still-present advisory after one would tell you about the cache, not the
   backend.
3. **`store` a fact** — the response carries `embedded: true`.
4. **`check_health(agent_id, fix=true)`** — no `embedding_backend` finding, and
   the rows written during the outage are re-embedded. A `not_probed` finding
   with `reason: served_from_cache` means step 4 asked the cache, not the
   backend; re-run it once the entry expires.

A green step 2 with a skipped step 4 leaves the user with working recall over a
corpus that still has holes in it.

---

## 6. Backfill what was written while it was down

`check_health(agent_id, fix=true)` re-embeds rows whose embedding is NULL —
exactly the rows that came back `embedded: false`. One run re-embeds up to 500
of them, so on a long outage run it until the finding count reaches zero rather
than assuming a single pass caught everything.

---

## 7. What repair cannot reach

Swapping a **different model of the same dimension** behind the same URL is
undetectable and unrepairable by these tools. CPersona fingerprints a backend by
embedding dimension only, so nothing is flagged; the dimension check NULLs blobs
of the wrong length, so nothing is NULLed; and `fix=true` re-embeds only NULL
blobs, so nothing is re-embedded. The corpus is then scored against a model that
did not produce it, and every layer above reports healthy. The remedy is to
treat a model swap as a corpus rebuild, not as a configuration change — see the
Getting Started guide at <https://cloto-dev.github.io/CPersona/getting-started/>.
````

## File: README.md
upstream: https://github.com/cloto-dev/cpersona/blob/master/README.md

````text
<!-- mcp-name: io.github.Cloto-dev/cpersona -->

<div align="center">

# CPersona

**Long-term memory for AI agents, in one SQLite file you own.**

CPersona is an [MCP](https://modelcontextprotocol.io/) server that lets Claude Code, Codex, Claude Desktop, Cursor, VS Code
or any other MCP client remember what you decided, why, and what happened last time — across sessions.

[![PyPI](https://img.shields.io/pypi/v/cpersona)](https://pypi.org/project/cpersona/) [![CI](https://github.com/Cloto-dev/cpersona/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/Cloto-dev/cpersona/actions/workflows/ci.yml) [![Python](https://img.shields.io/badge/python-3.11+-blue.svg)](https://github.com/Cloto-dev/cpersona/blob/master/pyproject.toml) [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/Cloto-dev/cpersona/blob/master/LICENSE)

[Documentation](https://cloto-dev.github.io/CPersona/) · [Getting Started](https://cloto-dev.github.io/CPersona/getting-started/) · [Tools](https://cloto-dev.github.io/CPersona/tools/) · [Discussions](https://github.com/Cloto-dev/cpersona/discussions)

<img src="https://raw.githubusercontent.com/Cloto-dev/cpersona/51261bf/docs/assets/dashboard/network.png" alt="The CPersona dashboard's memory network: each point is a memory, lines join the most similar ones, grouped by agent" width="860">

<sub>The built-in dashboard, showing a made-up store of 72 memories.</sub>

</div>

## Why CPersona

Every new session with a coding agent starts from zero. CPersona gives the agent a memory it
can write to as it works and read from before it acts.

- **Yours, on your machine.** Everything lives in a single SQLite file. No account, no cloud,
  no external database; `sqlite3 .backup` copies it.
- **No language model inside.** CPersona never calls a generative model to store or recall.
  Your agent writes the summary; CPersona keeps it and finds it again.
- **Answers you can trace.** `reconstruct`, the recommended way to answer from memory, quotes the
  stored records behind each item, so you can see where an answer came from.
- **Search that catches what vectors miss.** Vector search runs beside full-text and keyword
  search, so exact names, identifiers and error strings are found too, in Japanese and other
  scripts without spaces as well.
- **See what it remembers.** `cpersona dashboard` opens a read-only page of the store: what was
  stored last, what is recalled most, every memory in full, and the network above.
- **Honest when it degrades.** Without an embedding server it still runs on keyword search, and
  says so on every recall instead of quietly returning less.

## Quick start

**With Claude Code or Codex**, install the agent plugin and let the agent do the rest:

```text
/plugin install cpersona --marketplace Cloto-dev/cpersona
```

Then say *"Set up CPersona."* The plugin carries the setup skill and the hooks that remind the
agent to recall before it acts and to store as it goes. (Claude Code 2.1.275 or later. In Codex:
`codex plugin marketplace add Cloto-dev/cpersona`, then `codex plugin add cpersona@cpersona`.)

**By hand**, in three steps:

```bash
# 1. Run an embedding server (recommended; it powers vector search)
uvx --from "cembedding[onnx]" cembedding-download-model --model bge-m3
EMBEDDING_PROVIDER=onnx_bge_m3 uvx --from "cembedding[onnx]" cembedding

# 2. Register CPersona with your client (Claude Code shown)
claude mcp add-json cpersona '{"type":"stdio","command":"uvx","args":["cpersona"],"env":{"CPERSONA_DB_PATH":"/home/you/.claude/cpersona.db","EMBEDDING_MODE":"http","EMBEDDING_HTTP_URL":"http://127.0.0.1:8401/embed"}}' -s user

# 3. In a new session, ask the agent to store something, then recall it in another new session

# 4. See what it remembers, in the browser
cpersona dashboard
```

[Getting Started](https://cloto-dev.github.io/CPersona/getting-started/) covers the other clients,
Windows, containers, and the short policy block that makes every future session use the memory.

Upgrading an existing install? Read the *When you upgrade* section of the
[release notes](https://github.com/Cloto-dev/cpersona/releases) and
[SUPPORT.md](https://github.com/Cloto-dev/cpersona/blob/master/SUPPORT.md) first.

## A look inside

| Overview | A memory, opened |
| --- | --- |
| ![The dashboard overview: the memories stored last and recalled most](https://raw.githubusercontent.com/Cloto-dev/cpersona/51261bf/docs/assets/dashboard/overview-light.png) | ![A memory opened in the dashboard, its text first](https://raw.githubusercontent.com/Cloto-dev/cpersona/51261bf/docs/assets/dashboard/memory-dark.png) |

Memories are kept per agent, and `project_id` and `channel` let several agents and projects share
one database without mixing.

## Benchmarks

On [OmniMemEval](https://github.com/MemTensor/OmniMemEval)'s LongMemEval-S pipeline (500 questions,
answered by `gpt-4.1-mini`, judged by `gpt-4o-mini`), in this project's own run. Overall is the
share of questions judged correct; Context Tokens is the average sent to the answer model per question:

| Backend | Deployment | SS-User | SS-Asst | SS-Pref | Temp. Reas | Multi-S | Know. Upd | Overall | Context Tokens |
| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| CPersona 2.6.11 (pro mode) | local | 97.14 | 94.64 | 96.67 | 86.47 | 85.71 | 88.46 | 89.60 | 4,262.5 |
| CPersona 2.6.11 (lite mode) | local | 95.71 | 83.93 | 90.00 | 80.45 | 74.44 | 82.05 | 82.20 | 993.3 |
| MemOS | cloud | 100.00 | 100.00 | 100.00 | 89.47 | 78.95 | 84.62 | 89.20 | 4,151 |

Pro mode is level with MemOS within sampling error, and on LoCoMo it answers 84.03%. Setups, the
registered rules and every departure from a clean run are in the
[results](https://github.com/Cloto-dev/cpersona/blob/master/benchmarks/measurements/results-omnimemeval-2_6_11.md).

## Documentation

[cloto-dev.github.io/CPersona](https://cloto-dev.github.io/CPersona/) is the full reference, also in Japanese:

| Guide | What it covers |
| --- | --- |
| [Getting Started](https://cloto-dev.github.io/CPersona/getting-started/) | Install, embedding server, client registration, verification |
| [Behavior Contracts](https://cloto-dev.github.io/CPersona/behavior-contracts/) | What you may rely on: recall ordering, dedup, scan window, response shapes |
| [Tools](https://cloto-dev.github.io/CPersona/tools/) | Every tool, grouped by what you reach for it for |
| [Architecture](https://cloto-dev.github.io/CPersona/architecture/) | Storage, the retrieval pipeline, isolation axes |
| [Roadmap](https://cloto-dev.github.io/CPersona/roadmap/) | What each release line is for and may break; planned retrieval features and the scale ladder |
| [Operations Runbook](https://cloto-dev.github.io/CPersona/operations/) | Backup, degradation detection, tuning, CJK guidance, corpus sync |
| [Configuration](https://cloto-dev.github.io/CPersona/configuration/) | Every environment variable and its default |
| [Quality Assurance](https://cloto-dev.github.io/CPersona/quality-assurance/) | How a release is gated: audits, the bug ledger, structural and mutation gates |
| [FAQ](https://cloto-dev.github.io/CPersona/faq/) | Short answers to the questions operators actually ask |

## Contributing

Bug fixes, tests and documentation can be sent as a pull request straight away. For changes to the
database schema, the tool contracts or the tool list, open an issue first.
[CONTRIBUTING.md](https://github.com/Cloto-dev/cpersona/blob/master/CONTRIBUTING.md) has the setup —
no embedding server is needed to run the
**~4,150 test functions** — and the three checks CI runs.

Questions and ideas are welcome in [Discussions](https://github.com/Cloto-dev/cpersona/discussions).
A sentence in the docs that confused you is worth reporting too: it is a defect of its own.

## Support

- **Bugs and requests**: [open an issue](https://github.com/Cloto-dev/cpersona/issues/new/choose).
- **Security**: report privately, as [SECURITY.md](https://git
````
