Skip to main content
Glama

PMI · Personal Memory Infrastructure

My Memory, Any Agent. PMI is a long-term memory store that runs on your own machine. Give it PRDs, meeting notes, Notion pages, Linear tickets, GitHub PRs and Slack discussions: it keeps the original text and distills it into knowledge that always cites its source. Claude Code, Codex, Cursor and any other AI agent read and write the same memory through MCP (or the command line).

  • Sourced: every memory carries a verbatim quote from the original text. Quotes are checked on write; a quote that does not match is rejected.

  • Evolving: a newer decision automatically supersedes an older one, and the old one becomes history with a validity period. When new content conflicts with a conclusion you confirmed, PMI does not overwrite it silently; the change goes to review.

  • Clear status: search results say which category each item is in: current, superseded, disputed, open question, or insufficient evidence.

  • No services: no database server, no Docker, no background process. One SQLite file plus a folder of originals is all the data; copy the folder to move it.

  • Model-agnostic: PMI never calls an LLM itself. The agent you are already using does the reading and understanding. An optional local multilingual embedding model is used only for semantic search.

PMI handles Chinese and English text. The bundled sample data and the three Claude Code skills are written in Chinese.


Contents

  1. Quick start (5 minutes)

  2. Installation

  3. Connecting AI agents

  4. Adding knowledge

  5. Updating knowledge

  6. Deleting knowledge

  7. Querying knowledge

  8. Write policy and trust

  9. Data, backup and migration

  10. Command reference

  11. Security boundary

  12. Troubleshooting

  13. Development and project layout


Related MCP server: Memory Cortex

1. Quick start (5 minutes)

Requirements: macOS or Linux, git, and uv. uv provides Python 3.12 automatically.

# 0. Install uv if you don't have it
curl -LsSf https://astral.sh/uv/install.sh | sh

# 1. Clone and install: dependencies, data folder, `pmi` linked into ~/.local/bin, Claude Code skills
git clone https://github.com/yunzePlaud/personal-memory.git ~/personal-memory
cd ~/personal-memory
./scripts/install.sh            # add --with-model to also download the semantic search model (~220 MB)

# 2. Import the sample data and ask questions (the samples are in Chinese)
pmi import examples/project-a --extract
pmi context "为什么 Project A 不采用方案 B"                    # Why didn't Project A choose option B?
pmi context "Project A 现在什么时候上线"                       # When does Project A launch now?
pmi context "Project A 什么时候上线" --as-of 2026-10-15        # The same question, as of an earlier day

# 3. Connect Claude Code (available in all projects)
claude mcp add --scope user pmi -- ~/personal-memory/pmi mcp

Restart Claude Code and ask: "Why didn't Project A choose option B?" The agent calls memory_get_context first and answers with sources.

The data in examples/ is fictional. To start over, delete the data/ folder and run pmi init.


2. Installation

What scripts/install.sh does

Step

What happens

1

uv sync --frozen --extra semantic: installs the dependencies pinned in uv.lock, including fastembed for local semantic search

2

pmi init: creates the data/ folder for your memory data (already in .gitignore)

3

Links pmi into ~/.local/bin/pmi. If that folder is not on your PATH, the script tells you how to add it

4

Links the 3 skills under integrations/claude-code/skills/ into ~/.claude/skills/

Optional

--with-model: downloads the local multilingual embedding model paraphrase-multilingual-MiniLM-L12-v2 (~220 MB)

Options:

  • --no-skills: do not install the skills.

  • --no-semantic: keyword search only, fewer dependencies. Afterwards add export PMI_NO_SEMANTIC=1 to your shell profile, and pass -e PMI_NO_SEMANTIC=1 to claude mcp add; otherwise the first pmi run installs the semantic search dependencies anyway.

  • --with-model: also download the semantic search model.

  • PMI_BIN_DIR / PMI_SKILLS_DIR: environment variables that change where the links go.

The script is safe to run again. If a real file or folder already exists where a link would go, the script leaves it untouched.

Manual installation

uv sync --frozen --extra semantic
./pmi init
./pmi doctor          # checks Python, SQLite, FTS5, the model and folder permissions
./pmi model download  # optional: semantic search model

./pmi is a launcher script: it sets PMI_HOME to the repository folder and runs PMI with uv. It also finds the repository when called through a symlink such as ~/.local/bin/pmi. Every command accepts --json.

External tools (only when needed)

Purpose

Requirement

Importing from Notion / Linear / Slack

Connect the matching connector in Claude (claude.ai → Settings → Connectors)

Importing GitHub PRs (/pmi-linear, pmi github-pr)

Install the GitHub CLI and run gh auth login

Configuration (optional)

Copy config/settings.example.toml to config/settings.toml. Common settings:

[embedding]
provider = "none"                 # turn off semantic search; the default is "fastembed"
[capture]
import_roots = ["~/Documents", "~/Desktop", "~/Downloads"]   # folders an agent may ask PMI to read; hidden folders are always refused
[policy]
inferred_create = "review"        # agent-proposed claims without a source quote: review / apply / deny

Never put secrets in the configuration file: the whole folder is meant to be copied around.


3. Connecting AI agents

Every agent starts the PMI MCP server (stdio) with the same command:

command: /absolute/path/to/personal-memory/pmi
args:    ["mcp"]

Claude Code

claude mcp add --scope user pmi -- ~/personal-memory/pmi mcp    # --scope user: available in all projects
claude mcp list                                                 # check that it is connected

install.sh has already installed the 3 skills: /pmi-notion, /pmi-linear and /pmi-slack (see section 4).

Codex CLI

Add this to ~/.codex/config.toml:

[mcp_servers.pmi]
command = "/Users/<you>/personal-memory/pmi"
args = ["mcp"]

Cursor / Claude Desktop / Gemini CLI / other MCP clients

These clients share the same JSON structure; only the file location differs:

Client

Configuration file

Cursor

~/.cursor/mcp.json (global), or .cursor/mcp.json inside a project

Claude Desktop

macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

Gemini CLI

~/.gemini/settings.json

{
  "mcpServers": {
    "pmi": { "command": "/Users/<you>/personal-memory/pmi", "args": ["mcp"] }
  }
}

The 6 MCP tools

The server's instructions field explains how to use them, so a newly connected agent can start right away:

Tool

Purpose

memory_get_context

Start here. Given a question, returns the current memories (with quotes), history, conflicts and relevant source passages; says so when the evidence is insufficient

memory_search

Low-level search over memories or source passages

memory_get

Read by id: memories (with version history), sources (paged), entities, pending changes

memory_add_source

Store an original text or file. Passing url gives the source a stable identity so later imports become new versions. Defaults to trust="external"

memory_propose

The only write path: create / supersede / update / link / archive / restore

memory_list

Browse memories, entities, slots, pending changes and recent activity. source=<source id> lists the memories that cite a source

Confirming, deleting and approving are not available over MCP. Only you can do them, from the command line.

Make agents use PMI proactively

Add a paragraph like this to a project's CLAUDE.md / AGENTS.md so agents check PMI first more reliably:

Before answering questions about my past work (requirement background, why a decision was made, who owns what, the current state of a feature), call PMI's `memory_get_context` and cite the sources it returns. If PMI reports insufficient evidence, say so instead of guessing.

Agents and scripts without MCP

Any agent that can run commands can use the CLI:

pmi --json context "some question"

From Python:

from pmi import Memory
with Memory.open("~/personal-memory") as m:
    ctx = m.get_context("Why didn't Project A choose option B?")

4. Adding knowledge

PMI stores data in two layers:

  • Sources: the original texts, which serve as evidence.

  • Memories: knowledge distilled from sources; every memory quotes a source.

4.1 Import files and folders

pmi import ~/Documents/prds                 # import a folder: Markdown / TXT / PDF / DOCX; GBK-encoded Chinese text works too
pmi import meeting.md --extract             # meeting notes in the convention below become memories automatically
pmi import notes/ --trust external          # mark material written by others as external

On import:

  • Hidden files and unsupported formats are skipped, with the reason listed. One broken file does not stop the rest.

  • Identical content is deduplicated automatically.

  • Importing a file again after it changed stores a new version; the old version is kept.

Meeting notes convention (extracted with --extract, no LLM needed):

---
title: Project A weekly sync
date: 2026-10-01
project: Project A
attendees: [Alice, Bob]
type: meeting
---
# Project A weekly sync 2026-10-01

## Decisions
- [slot: launch-date] Project A will launch on November 15.

## Facts
- The push platform is maintained by Bob's team.

## Action Items
- @Bob finish the integration review by October 10.

Notes on the format:

  • Section names can be Decisions, Facts / Context, Action Items / TODO, or their Chinese equivalents (决策, 事实 / 背景 / 结论, 待办 / 行动项).

  • [slot: …] is a topic key; see section 5.

  • Put a space after @name.

  • Free-form documents such as PRDs yield nothing this way. Let an agent distill them instead (see 4.3).

4.2 Store a piece of text

pmi note "Project A's release window moves to Thursdays"
pbpaste | pmi note - --kind document --title "Some PRD" --url "<original link>" --trust external

In Claude Code, say: "Store this PRD in PMI and distill the knowledge." The agent will:

  1. Call memory_add_source to store the original text.

  2. Call memory_propose to submit memories, each with a verbatim quote.

PMI checks every quote and handles deduplication, superseding and conflicts automatically.

4.4 Import from Notion / Linear / Slack (Claude Code skills)

You say

What happens

/pmi-notion <Notion page link>

Fetches the whole page and shows you the title, location and length; only after you confirm does it store the text, then it distills decisions, rules, procedures and owners

/pmi-linear <ticket link or ID>

Fetches the ticket (description, comments, status history) and its linked GitHub PRs (description, human review discussion, list of changed files; no diff; generated verbatim by your local gh). After you confirm, it stores them and distills root causes, fixes, trade-offs and regression notes

/pmi-slack <thread link>

Reads the whole thread. The confirmation flags private channels and DMs. After storing, it distills the discussion by its final outcome, separating settled decisions, open questions and action items

You can also just say "import this ticket into PMI". All three skills follow the same rules:

  • They run only when you explicitly ask for an import. A link that is part of another task ("implement this PRD", "help me reply to this thread") is not stored in PMI.

  • They always ask before writing. One confirmation covers both storing the text and distilling it. Say "store the text only" to skip distilling.

  • Everything is stored as an external document. Memories backed by these sources cannot automatically change memories you confirmed, or memories backed by your own documents.

  • One object, one identity. All link forms of the same page, ticket, PR or thread map to one source identity, such as notion:<page id>, linear:APP-184, github:owner/repo#123 or slack:<channel>/<thread timestamp>. Importing it again creates a new version.

  • No personal identifiers are kept. Email addresses and user IDs are not stored; images keep only their caption or file name.

Other agents can follow the steps in integrations/claude-code/skills/*/SKILL.md with their own Notion / Linear / Slack connectors.

4.5 Write memories directly (JSON)

pmi propose ops.json                      # runs as you; takes effect immediately
pmi propose ops.json --as-agent my-bot    # runs as an agent: write policy applies and conflicts go to review

Example ops.json (full example in examples/agent-ops.json):

[
  {"op": "create", "memory": {
    "kind": "decision",
    "content": "Project A sends push notifications through option A (the existing push platform)",
    "slot": "project-a/architecture",
    "entities": [{"name": "Project A", "kind": "project"}],
    "valid_from": "2026-10-01",
    "evidence": [{"source_id": "src_…", "quote": "option A (the existing push platform)"}]
  }}
]

Fields:

  • kind is one of episodic (event), semantic (fact), project, person, decision, procedure, preference.

  • evidence.quote must appear verbatim in the source named by source_id and be at least 4 characters long.


5. Updating knowledge

Slots: one current conclusion per topic

A slot is a memory's topic key, such as project-a/launch-date. A slot has at most one active memory at any time. When a new claim arrives:

New claim

Result

Dated later than the current conclusion

Automatically supersedes it. The old conclusion becomes superseded, with its validity period and successor recorded

Dated earlier (backfilled history)

Inserted into the history; the current conclusion stays. Import order does not change the outcome

Conflicts with a conclusion you confirmed, or its evidence is less trusted

The new claim is marked disputed and a pending change is created; the current conclusion stays

Undated, so the order is unclear

Goes to review

Same content as an existing memory

Merged into it; only the quote is added

Common update operations

pmi import changed-file.md            # stored as a new version; quoted sentences that disappeared from it are reported
pmi confirm mem_…                     # confirm a memory; agents can no longer change it automatically
pmi review list                       # list pending changes, with the full text and quotes of what would be superseded or activated
pmi review approve prp_…              # approve; the approved memory is marked as confirmed
pmi review reject prp_… --reason "The Slack message was wrong"
pmi show mem_… --history              # full version history and quotes of a memory
pmi entity alias ent_… "alias"        # make different names point to the same person or project

You can also change memories with JSON, through pmi propose or MCP memory_propose:

  • Change the content: {"op":"update","id":"mem_…","changes":{"content":"…"}}

  • Supersede old memories: {"op":"supersede","old_ids":["mem_…"],"memory":{…}}

  • Link two memories: {"op":"link","from_id":"…","to_id":"…","type":"contradicts"}


6. Deleting knowledge

pmi archive mem_… --reason "no longer relevant"     # soft delete: hidden from search by default, can be restored
pmi restore-memory mem_…                            # restore an archived memory

pmi forget mem_… --reason "not needed"              # hard-delete a memory
pmi forget src_… --reason "sensitive"               # hard-delete a source; refused if memories depend on it, listing them
pmi forget src_… --reason "sensitive" --cascade     # also delete the memories that rest only on this source
pmi forget src_… --reason "sensitive" --keep-memories   # keep those memories, downgraded to "inferred"
pmi forget src_… --reason "…" --this-version        # delete only this version (default: every version of the document)

About forgetting:

  • Forgetting cannot be undone. Only a tombstone without content remains (id, time, reason).

  • The database runs with secure_delete, and forgetting also compacts the search index and truncates the WAL, so the deleted content does not linger in the database files.

  • Backups made before forgetting still contain the content. Delete old backups yourself if needed.


7. Querying knowledge

pmi context "When does Project A launch now?"                # assembled context: current, conflicts, history, source passages
pmi context "launch date" --project "Project A" --history
pmi context "launch date" --as-of 2026-10-15                 # answer as of a point in time
pmi search "push platform" --scope sources                   # low-level search
pmi show mem_… src_…                                         # look up by id
pmi list slots | entities | proposals | recent | memories    # browse

Fields in a context result:

  • status: active / superseded / disputed.

  • verification: user_confirmed / source_backed / inferred.

  • trust: the trust level of each quote.

  • insufficient_evidence: true: PMI does not have enough evidence to answer. Items found only through entity links or weak semantic similarity are still listed, but do not count as evidence.


8. Write policy and trust

Who

What they can do

Agents (over MCP, or the CLI with --as-agent)

New memories with a source quote take effect immediately (marked unconfirmed). These go to review: claims without a quote, changes to protected memories, restoring a memory you archived. Agents cannot confirm, delete or approve

You (CLI)

Everything

Protected memories are memories you confirmed, or memories backed by a more trusted source.

Trust levels, from highest to lowest:

  • owner: content you imported yourself.

  • agent: an agent's own analysis.

  • external: Slack, web pages, other people's documents, and Notion / Linear imports.

Whether a change takes effect automatically depends on the lowest trust level among the new evidence it brings. Adding a sentence that already exists in the target's own source does not raise the trust level.


9. Data, backup and migration

personal-memory/
├── pmi                         launcher script
├── scripts/install.sh          one-step installer
├── integrations/claude-code/   Claude Code skills (pmi-notion / pmi-linear / pmi-slack)
├── src/  tests/  docs/  examples/
├── config/settings.toml        optional configuration (not committed)
└── data/                       your memory (not committed)
    ├── memory/pmi.db           ← required: the source of truth
    ├── sources/                ← required: original files
    ├── memory/backups/         backups
    ├── index/search.db         disposable: search index, rebuilt automatically
    └── models/                 disposable: embedding model, can be downloaded again
pmi verify            # integrity check: database, hashes, every quote still in its source, version chains, complete forgetting
pmi verify --fix      # also remove orphan files that no source references
pmi backup            # consistent snapshot → data/memory/backups/*.tar.gz (with checksums)
pmi restore <archive> --to ~/pmi-copy
pmi export --out all.jsonl     # full export, independent of format and vendor

Moving to another computer:

  1. Run pmi backup first, or make sure no agent is writing.

  2. Copy the whole folder to the new computer. .venv/ and data/index/ can be left out.

  3. In the new location, run ./scripts/install.sh && pmi verify.

All paths are relative, so the folder can live anywhere.

Switching the embedding model: change [embedding] model in config/settings.toml, then run pmi reindex --vectors. During the rebuild, semantic search is turned off automatically and keyword search keeps working; memories are not affected.


10. Command reference

Command

Purpose

pmi init / pmi doctor / pmi verify [--fix]

Initialize / check the environment / check integrity

pmi import <path…> [--extract] [--trust …] [--kind …]

Import files or folders

pmi note <text or -> [--kind --title --url --origin-key --source-time --trust --as-agent --extract]

Store a piece of text; - reads standard input

pmi context <question> [--project --participants --as-of --from --to --history --max-tokens]

Assemble context for a question

pmi search <query> [--scope --kind --status --entity --limit]

Search

pmi show <id…> [--history] / pmi list <category> [--source …]

Show / browse

pmi propose <ops.json> [--as-agent name]

Write memories

pmi confirm / pmi review list|approve|reject

Confirm / review

pmi archive / pmi restore-memory / pmi forget

Archive / restore / forget

pmi entity list|alias

Entities and aliases

pmi backup / pmi restore / pmi export / pmi reindex [--vectors]

Maintenance

pmi model download

Download the semantic search model

pmi github-pr <PR link>

Print a PR as Markdown via gh (prints only, stores nothing)

pmi slack-ref <Slack link>

Parse the channel ID, thread timestamp and source identity

pmi mcp

Start the MCP server for agents

Global options:

  • --home <folder>: the PMI folder; defaults to the repository folder or $PMI_HOME.

  • --json: machine-readable JSON output.


11. Security boundary

  • Single user, local. Permission checks limit what an agent can do through the tool interface; they are not operating-system isolation. Any local program that can read and write this folder can bypass them. In Claude Code, Claude Code's own permission prompts guard the commands an agent runs.

  • A portable folder is not access control. Copying the folder takes all the data with it. If it holds company content (PRDs, Slack), follow your company's data policy before copying it to another device.

  • PMI takes no outbound actions. It does not send messages or change other systems. Only two things touch the network: pmi model download, which you run explicitly, and pmi github-pr, which calls your local gh. The package stores no secrets.

  • External content is labeled. Content with trust=external is marked in results, together with a notice that the text is data, not instructions. PMI's behavior depends only on who is calling and which operation is requested, never on the content.

  • Everything is traceable. Every change is written to an audit log, and every memory has a full version chain.

  • Not implemented: multiple users and permission domains, a built-in LLM extractor, an HTTP API, a web UI, automatic Slack / Notion sync.


12. Troubleshooting

Symptom

Fix

pmi: command not found

Add ~/.local/bin to your PATH, or use the full path to ./pmi in the repository

Content you just wrote does not show up in search

Usually nothing to do: every call brings the index up to date. If it persists, run pmi reindex

The notes in a result say semantic search unavailable

Run pmi model download, or see the reason in pmi doctor

You suspect data corruption

Run pmi verify. If the index is damaged, delete data/index/; it is rebuilt automatically

verify reports orphan_file

An original file is not referenced by any source (usually left by an interrupted delete). Run pmi verify --fix

The schema is newer than the code

The database comes from a newer PMI. Run git pull to update the code, then open it again


13. Development and project layout

uv sync --extra semantic
uv run --frozen --extra semantic pytest -q            # all tests; no network or model needed
uv run --frozen --extra semantic pytest -q -m slow    # tests with the real embedding model; run pmi model download first

Main modules in src/pmi/:

  • api.py: the single business entry point.

  • evolution.py: superseding, conflict and review rules.

  • policy.py: write policy.

  • context.py: context assembly.

  • search.py: search (FTS5 over CJK bigrams, plus vectors).

  • index/: the rebuildable search index.

  • mcp_server.py, cli.py: the MCP server and command-line entry points.

  • integrations/github_pr.py: GitHub PR to Markdown.

Other folders and files:

  • tests/test_acceptance.py: 10 end-to-end acceptance scenarios.

  • docs/design/: the design document (in Chinese).

  • docs/implementation-notes.md: trade-offs made during implementation (in Chinese).

  • docs/requirements.md: the original requirements (in Chinese).

Dependencies: mcp, pypdf, python-docx, pyyaml, and optionally fastembed; all versions are pinned in uv.lock.

License: MIT

Available Tools

6 tools
memory_add_sourceB

Store raw evidence (text, or a file path inside the allowed import roots). Duplicates are detected; a changed document with the same origin_key becomes a new version. Pass url for web/Notion pages you fetched: it is kept as metadata and gives a stable origin_key (notion: for Notion links). kind: document|note|conversation|agent_output. trust: external (default) or agent.

Inputs: url, kind, path, text, +6 more. Returns: object

memory_getA
Read-only

Fetch memories (mem_), sources (src_, full text paged by text_offset/text_limit), entities (ent_) or proposals (prp_) by id. include_history adds a memory's version history.

Inputs: ids (required), text_limit, text_offset, include_history. Returns: object

memory_get_contextB
Read-only

Assemble context for a question: current memories (with citations), history, conflicts and source passages. Start here for any question about the user's past work.

Inputs: query (required), as_of, project, time_to, +4 more. Returns: object

memory_listB
Read-only

Browse: what=memories|entities|slots|proposals|recent (filters: kind, status, entity, and source = memories citing that source id).

Inputs: kind, what, limit, entity, +2 more. Returns: object

memory_proposeB

The only way to change memory. Each op is applied, merged as a duplicate, or turned into a pending proposal for the user to review. See the server instructions for the op formats.

Inputs: ops (required), reason. Returns: object

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3.4/5.0

    Scored across 6 tools

    Disambiguation4/5

    Each tool has a distinct role: get_context assembles a full answer context, search is explicitly low-level, get fetches by typed id prefix, list browses, add_source ingests evidence, and propose is declared the sole mutation path. The only mild overlap is between get_context/search and search/list, but descriptions ('start here' vs 'low-level search') give clear guidance on when to use each.

    Naming Consistency5/5

    All six tools use a uniform memory_<verb>[_<noun>] snake_case pattern: get_context, search, get, add_source, propose, list. The shared prefix makes the tool family instantly recognizable and the verb choices are semantically consistent.

    Tool Count5/5

    Six tools is well-scoped for a memory store: one high-level reader, two low-level retrieval tools, one browse tool, one ingest tool, and one write path. No tool feels redundant or gratuitous at this count.

    Completeness4/5

    The surface covers the core lifecycle: ingest evidence (add_source), retrieve (get_context/search/get/list), and mutate via proposals, with versioning via origin_key and include_history. Minor gaps exist around explicit deletion/archival of memories or sources and any tooling to approve/reject proposals, though the latter may be intentionally user-driven.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      B
      maintenance
      Local-first, source-grounded memory for AI agents, with citations, bitemporal history, review-gated corrections, and MCP tools for search and recall.
      4
      Apache 2.0
    • A
      license
      Not graded
      quality
      C
      maintenance
      Durable, inspectable memory for MCP agents. Preserves decisions, preferences, and project knowledge across sessions with full provenance and version history.
      3
      Apache 2.0
    • A
      license
      Not graded
      quality
      B
      maintenance
      Provides a local-first, source-cited memory layer for AI agents, with MCP tools to search, read, explain sources, and propose/apply memory updates.
      75 npm
      13
      Apache 2.0
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables AI agents to store and recall an append-only, hash-chained memory with citation-backed semantic search, fact verification, contradiction detection, and replay/audit capabilities through local MCP tools.
      1
      AGPL 3.0