PMI
Allows importing GitHub pull requests as original sources, enabling PMI to distill and cite memories from PR content.
Allows importing Linear tickets as source material, so ticket details can be stored and turned into sourced memories.
Allows importing Notion pages into PMI as original sources for distillation into cited memories.
Allows importing Slack discussions into PMI as original sources for distillation into cited memories.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@PMI What did we decide about Project A's launch date?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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
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 mcpRestart 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 thedata/folder and runpmi init.
2. Installation
What scripts/install.sh does
Step | What happens |
1 |
|
2 |
|
3 | Links |
4 | Links the 3 skills under |
Optional |
|
Options:
--no-skills: do not install the skills.--no-semantic: keyword search only, fewer dependencies. Afterwards addexport PMI_NO_SEMANTIC=1to your shell profile, and pass-e PMI_NO_SEMANTIC=1toclaude mcp add; otherwise the firstpmirun 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 ( | Install the GitHub CLI and run |
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 / denyNever 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 connectedinstall.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 |
|
Claude Desktop | macOS: |
Gemini CLI |
|
{
"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 |
| Start here. Given a question, returns the current memories (with quotes), history, conflicts and relevant source passages; says so when the evidence is insufficient |
| Low-level search over memories or source passages |
| Read by id: memories (with version history), sources (paged), entities, pending changes |
| Store an original text or file. Passing |
| The only write path: create / supersede / update / link / archive / restore |
| Browse memories, entities, slots, pending changes and recent activity. |
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 externalOn 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 external4.3 Let an agent distill it (recommended)
In Claude Code, say: "Store this PRD in PMI and distill the knowledge." The agent will:
Call
memory_add_sourceto store the original text.Call
memory_proposeto submit memories, each with a verbatimquote.
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 |
| 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 |
| 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 |
| 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#123orslack:<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 reviewExample 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:
kindis one ofepisodic(event),semantic(fact),project,person,decision,procedure,preference.evidence.quotemust appear verbatim in the source named bysource_idand 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 |
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 projectYou 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 # browseFields 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 | 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 againpmi 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 vendorMoving to another computer:
Run
pmi backupfirst, or make sure no agent is writing.Copy the whole folder to the new computer.
.venv/anddata/index/can be left out.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 |
| Initialize / check the environment / check integrity |
| Import files or folders |
| Store a piece of text; |
| Assemble context for a question |
| Search |
| Show / browse |
| Write memories |
| Confirm / review |
| Archive / restore / forget |
| Entities and aliases |
| Maintenance |
| Download the semantic search model |
| Print a PR as Markdown via |
| Parse the channel ID, thread timestamp and source identity |
| 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, andpmi github-pr, which calls your localgh. The package stores no secrets.External content is labeled. Content with
trust=externalis 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 |
| Add |
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 |
The notes in a result say | Run |
You suspect data corruption | Run |
verify reports | An original file is not referenced by any source (usually left by an interrupted delete). Run |
The schema is newer than the code | The database comes from a newer PMI. Run |
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 firstMain 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 toolsmemory_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_getARead-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_contextBRead-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_listBRead-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
memory_searchCRead-only
Low-level search over memories and/or source passages (scope: all|memories|sources).
Inputs: query (required), kind, limit, scope, +2 more. Returns: object
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 6 tools
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.
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.
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.
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
Related MCP Connectors
Persistent, inspectable memory for AI agents with lineage, correction, and a hosted MCP endpoint.
Provenance-carrying memory for AI agents: corrections supersede, never overwrite. Free, API + MCP.
Your memory, everywhere AI goes. Build knowledge once, access it via MCP anywhere.
Cross-session memory for AI agents with a Source Receipt for every memory, over MCP.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceLocal-first, source-grounded memory for AI agents, with citations, bitemporal history, review-gated corrections, and MCP tools for search and recall.4Apache 2.0
- AlicenseNot gradedqualityCmaintenanceDurable, inspectable memory for MCP agents. Preserves decisions, preferences, and project knowledge across sessions with full provenance and version history.3Apache 2.0
- AlicenseNot gradedqualityBmaintenanceProvides a local-first, source-cited memory layer for AI agents, with MCP tools to search, read, explain sources, and propose/apply memory updates.75 npm13Apache 2.0
- AlicenseNot gradedqualityCmaintenanceEnables 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.1AGPL 3.0