people-memory-mcp
This server is an MCP interface to a persistent people-memory graph, letting agents retrieve, record, relate, and analyze people data with safety guards.
search_people — find people by name, organization, role, city, identifier, summary, or fact.
get_person — fetch a complete person record, with candidate disambiguation when the name is ambiguous.
remember_person — create or update a person, refusing silent duplicate merges or conflicting overwrites unless confirmed.
add_fact — attach durable typed or free-form facts with source confidence and date.
record_interaction — log dated calls, meetings, messages, meals, or other interactions.
connect_people — record relationships between two people with kind, note, and strength.
find_intro_path — find warm introduction paths from a person to anyone at a target organization.
stale_contacts — list strong relationships with no recent interaction.
set_call_cadence / calls_due — set desired call frequency and get who is due for a live conversation.
graph_status — check graph connection and return record counts.
read_query — run one guarded SELECT for advanced analysis (read-only transaction, bounded results).
write_query — run one guarded INSERT/UPDATE/DELETE with WHERE required and DDL blocked.
Safety features — uncertain merges and overwrites are refused until the user decides; raw SQL is denylist-parsed and runs under read-only transactions/RLS.
Used as part of the local development stack with the Supabase CLI for running the PostgreSQL database locally.
Repository is distributed via GitHub, and Codex plugin packaging supports marketplace or team distribution.
Optional connector to enrich people records using Gmail data such as participants, signatures, organizations, and last interaction dates.
Imports Google Contacts exports to build the people graph with email/phone resolution and deduplication.
Stores the people graph in PostgreSQL, providing durable storage of people, organizations, relationships, facts, and interactions.
Uses Supabase for local/cloud PostgreSQL hosting, Auth, REST API, and browser UI, with row-level security protecting data access.
Imports WhatsApp chat exports to build the people graph, with optional self-hosted MCP bridge for ongoing enrichment.
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., "@people-memory-mcp search for Sarah Chen in my people graph and show her full profile"
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.
People Memory
How do you give an AI agent persistent memory about people? Point it at a real database instead of a context window. People Memory is that database: an MCP server, a PostgreSQL schema, an optional REST API, a browser UI, importers, and agent skills, all built around one graph of people, orgs, and facts that survives every session. Add it once and Claude Code, Codex, or any other MCP client stops forgetting who your mother-in-law is between conversations.
The server retrieves someone the moment you mention them, records durable facts as they come up, and asks before it merges two people who might not be the same person. The graph, not the chat log, is what remembers.
Your data stays in your own PostgreSQL database. The repository contains no hosted service, no telemetry, no contact data, and no credentials.
What it does
Maps people, organizations, relationships, affiliations, identifiers, facts, and interactions.
Gives Codex, Claude Code, and other MCP clients semantic tools plus guarded SQL.
Teaches the agent to look up names before answering and save durable facts during every chat.
Imports LinkedIn Connections, Google Contacts, WhatsApp chat exports, and GEDCOM family trees.
Can enrich records through connectors the user approves, including Gmail, Calendar, and Drive.
Runs fully local with Supabase CLI or in the cloud with any PostgreSQL database.
Recommends a free Supabase project because it includes Postgres, Auth, REST, and a local stack.
Includes a full-screen relationship graph and searchable directory.
Flags what an import got wrong — duplicate people and organizations, contradictory links.
Refuses uncertain merges and conflicting overwrites until the user decides.
Related MCP server: hippocampus-mcp
How it fits together
flowchart LR
Agent["Your existing agent"] --> MCP["People Memory MCP"]
MCP --> PG[("PostgreSQL")]
Agent --> Connectors["Optional Gmail / Calendar / Drive / WhatsApp connectors"]
Connectors --> MCP
UI["Private graph UI"] --> Supabase["Supabase Auth + REST"]
Supabase --> PG
API["Optional REST API"] --> PGThe MCP server provides live reads and controlled writes. The bundled skills provide the behavior: when to retrieve, what to remember, how to import, when to ask, and how to upgrade safely.
Quick start
Requirements: Python 3.11+, uv, Docker, and the
Supabase CLI.
git clone https://github.com/michelgrolet/people-memory-mcp.git
cd people-memory-mcp
uv sync --extra api --extra dev
supabase start
supabase db resetsupabase status prints the local database URL, API URL, and browser publishable key. Save the
database URL in ~/.config/people-memory/.env:
PEOPLE_MEMORY_DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:54322/postgres
PEOPLE_MEMORY_ENABLE_RAW_SQL=false
PEOPLE_MEMORY_DEFAULT_SOURCE=agentThe file is read automatically and should have mode 0600.
Set your owner email before using the UI:
update app_settings set value = 'you@example.com' where key = 'owner_email';Then configure the browser app:
cp web/config.example.js web/config.js
# add the API URL and publishable key printed by `supabase status`
python3 -m http.server 4173 --directory webOpen http://127.0.0.1:4173. Local signup emails appear in Supabase
Mailpit, whose URL is also printed by supabase status.
Connect an agent
The interactive wizard detects Codex and Claude Code:
uv run people-memory setupOr add the server directly.
Codex:
codex mcp add people-memory \
--env PEOPLE_MEMORY_ENV_FILE="$HOME/.config/people-memory/.env" \
-- uvx --from git+https://github.com/michelgrolet/people-memory-mcp people-memory-mcpClaude Code:
claude mcp add -s user people-memory \
-e PEOPLE_MEMORY_ENV_FILE="$HOME/.config/people-memory/.env" \
-- uvx --from git+https://github.com/michelgrolet/people-memory-mcp people-memory-mcpStart a new agent session after adding an MCP server. Install or invoke setup-people-memory from
the plugin to add the durable memory rule, choose imports, connect optional data sources, and verify
the complete flow.
With TARS, optionally
People Memory is standalone and stays standalone: nothing above requires a particular harness, and the agent you already use is the one it wires into. TARS is a harness for a personal agent that lists People Memory in its extension registry, so if you happen to run it, one command does the clone and the wiring:
claude plugin marketplace add michelgrolet/tars
claude plugin install people-memory@tarsWhat that adds over the plain MCP server is when the tools fire: TARS puts the trigger in the one
file it loads on every session, so the agent looks a person up before answering instead of waiting
to be told to. Without it, the remember-people skill does the same job once you install it.
Install the agent skills from a clone
Codex discovers skills in ~/.agents/skills; Claude Code uses ~/.claude/skills. Symlink every
People Memory skill so updates to the clone are picked up automatically:
mkdir -p "$HOME/.agents/skills"
for skill in "$PWD"/skills/*; do
ln -s "$skill" "$HOME/.agents/skills/$(basename "$skill")"
doneUse ~/.claude/skills instead for Claude Code. Restart the agent, then ask it to run
setup-people-memory. The repository is also packaged as a Codex plugin for marketplace or team
distribution.
MCP tools
Tool | Purpose |
| Search names, organizations, roles, cities, identifiers, summaries, and facts |
| Return a complete person record, or candidates when a name is ambiguous |
| Create or update a person without silent duplicate merges or overwrites |
| Save typed or free-form facts with source and confidence |
| Record a call, message, meeting, meal, or other dated interaction |
| Record how two people know each other |
| Find warm introduction paths into an organization |
| Find strong relationships that have gone quiet |
| Say how often you want a live conversation with someone, in days |
| Who to call: people whose last call, video call or time in person is older than their cadence |
| Run one guarded |
| Run one guarded |
Raw SQL is disabled by default. If enabled, the parser rejects multiple statements, DDL, a short
list of dangerous functions, SELECT ... INTO, more than one data-changing operation, and
UPDATE/DELETE with no WHERE clause.
That parser is a denylist, so treat it as the first of two layers rather than the guarantee. The
guarantee is underneath it: read_query runs in a transaction with default_transaction_read_only
on, a 30-second statement timeout and a bounded result set, so a SELECT that finds a way past the
parser still cannot write. Both tools stay behind PEOPLE_MEMORY_ENABLE_RAW_SQL, and RLS applies to
them exactly as it does to everything else.
Imports
Official exports are the safest default. They are inspectable and do not require sharing account passwords.
uv run people-memory import linkedin ~/Downloads/Connections.csv
uv run people-memory import google ~/Downloads/contacts.csv
uv run people-memory import whatsapp ~/Downloads/chat.txt --self-name "Your Name" --date-order dmyImports resolve email or phone first, then name. They fill missing values and never overwrite a
conflict silently. The import-contacts skill can inspect unfamiliar CSV columns and guide the user
through ambiguous matches.
Family trees (GEDCOM)
A genealogy export carries something no contact export has: who is whose parent, who married whom,
and who the siblings are. Open the web app, click the upload button in the header, and drop a .ged
file from Geneanet, Ancestry, MyHeritage or Gramps.
A GEDCOM has no email and no phone, so the dedup key the other importers rely on does not exist: identity is resolved on name and birth date, and a woman recorded under her maiden name is also matched against her spouses' surnames. Anything those cannot settle — the same name on two records, a birth date that contradicts a name match, two entries in the file that may be one person — is listed on a review screen and nothing is written until every one of them is answered. Rerunning the same file writes nothing the second time.
What lands: a person per individual (full birth dates in the column, partial or hedged ones as a
born fact), parent / partner / sibling links, and birth_place, died, death_place,
occupation facts. A link that already exists is left untouched, notes included.
LinkedIn, live
LinkedIn also exposes 1st-degree connections live through its Member Data Portability API (DMA).
Once you hold an OAuth token with the r_dma_portability_self_serve scope (see
LinkedIn's docs),
you can sync without the manual export. Set the token as an environment variable — never pass it
as a CLI flag, which would leak it through ps and shell history:
export LINKEDIN_OAUTH_TOKEN="..."
uv run people-memory sync-linkedinRight after consenting, LinkedIn may not have collated the CONNECTIONS domain yet — it raises a
readable error in that case; retry later or fall back to the CSV export above.
Optional connectors
People Memory does not collect Gmail, Calendar, Drive, or WhatsApp credentials. The enrich-people
skill uses connectors already authorized in the user's agent. It asks which sources may be read,
limits the time range, shows what it plans to save, and marks every fact with its source.
Supported patterns include:
Gmail: participants, signatures, organizations, and last interaction dates.
Calendar: meetings, attendees, locations, and interaction dates.
Drive: user-selected documents that mention known people.
WhatsApp: official exports by default; a self-hosted MCP bridge is optional.
Browser agents: Codex in Chrome or Claude in Chrome can help configure Supabase and inspect the UI.
See docs/connectors.md for the permission and identity rules.
Cleanup
Every import writes records nobody reads again, and they collide. The shield button in the top bar opens a screen that reads them for you: two records sharing an email or a phone, the same name spelled two ways, an organization entered twice under a legal suffix, an organization nobody belongs to, links that cannot all be true (someone their own ancestor, a parent younger than their child, a pair filed as both parent and sibling), and family links still too vague for the tree. The count on the button is what is waiting.
It never decides anything, but every finding is repairable from the panel. Duplicate-looking records
are routinely two real people — a son named after his father, three sisters given the same name — so
each finding shows the records side by side with what separates them, and a pair already linked to
each other is left out entirely: the link is somebody having decided they differ, and a merge across
a parent, sibling or partner link is refused by the database itself.
Merge opens a window with both records on the outside and the one they are about to become in the middle. The middle follows the same rules the server does — the survivor keeps its own values, inherits whatever it lacks, takes the closer of the two ratings, keeps both notes — and every field in it is editable, because the right answer is often one record's name and the other's job, which picking a winner cannot express. Clicking a value on either side takes it. Which record survives is a toggle, and switching it leaves whatever you typed alone. Nothing is written until you confirm.
An empty organization is deleted in a click; a family link nobody classified is filed as parent, child, sibling or partner in a click, keeping the note that was on it; a parent link pointing at someone born before their own child is flipped in a click. Not a problem hides a finding for good, stored with the owner's own settings so the list drains instead of nagging.
A merge moves rows across five tables, so it runs as one Postgres function rather than a series of
calls from the browser: it cannot stop halfway and leave a record whose facts moved but whose links
did not. The losing record is archived into deleted_records in full — with the id it was merged
into — before anything moves, so every merge is recoverable.
Shared days, if you also keep a location archive
Off by default. Set locationCompanions: true in web/config.js when the same database also
carries the schema from tars-location-mcp.
The record card then gets a With me block: give a person a date range, times optional, and
the location archive answers where the two of you were — cities, countries, and the trip it
falls in. Nothing is typed twice, and nothing about a place is stored on the person.
People Memory owns none of those tables. With the flag off, or with the tables absent, the block does not render and the app loads exactly as before.
Hosted Supabase
Create a free Supabase project, then apply the checked-in migrations:
supabase login
supabase link --project-ref YOUR_PROJECT_REF
supabase db pushSet owner_email, enable the desired Auth providers, and deploy web/ to a static host. The browser
uses only the publishable key. Row-level security rejects every account except the configured owner.
The MCP server uses a direct PostgreSQL URL stored outside Git.
For an existing non-Supabase PostgreSQL database, apply every file in supabase/migrations/ in
filename order and use the optional API for browser access:
for f in supabase/migrations/*.sql; do psql "$PEOPLE_MEMORY_DATABASE_URL" -v ON_ERROR_STOP=1 -f "$f"; done20260803000100_security.sql expects the Supabase anon/authenticated roles and an auth.jwt()
function; outside Supabase, create them first or skip that file and keep the database private.
Upgrading an existing install means applying the migrations it has not seen yet, in the same order.
Optional REST API
uv run --extra api people-memory api --host 127.0.0.1 --port 8765Set PEOPLE_MEMORY_API_TOKEN to require Authorization: Bearer .... Keep the API on loopback unless
you put it behind TLS, authentication, rate limits, and backups.
Skills
setup-people-memory: choose local/cloud deployment, configure an existing agent, select imports, offer approved connectors, start the UI, and verify the install.remember-people: retrieve on name mention and save durable human facts during conversation.import-contacts: import and deduplicate official exports.enrich-people: use approved Gmail, Calendar, Drive, browser, or WhatsApp connectors.maintain-people-memory: audit duplicates, conflicts, stale ties, and data quality.upgrade-people-memory: back up, migrate, upgrade the MCP/UI/skills, and verify data.
If the user does not already have an agent, the setup skill can create a minimal Codex or Claude Code
workspace from templates/ and connect People Memory to it.
Privacy and safety
This database contains third-party personal data. Treat it accordingly.
One database per person is the default design.
Hosted UI access is restricted with Supabase Auth and forced row-level security.
The browser publishable key is safe to expose only because RLS is enabled.
The service-role key and PostgreSQL password must never enter browser code or Git.
Connector access is opt-in per source. Imported and inferred facts keep provenance.
Delete archives a JSON snapshot in
deleted_recordsbefore cascading dependent rows.Back up before schema upgrades or bulk merges.
Read docs/privacy.md and SECURITY.md before exposing any component to the internet.
Development
uv sync --extra api --extra dev
uv run ruff check .
uv run pytest --cov=people_memory
supabase db resetThe project uses the current stable v2 line of the official MCP Python SDK and the latest Supabase migration workflow.
License
Available Tools
11 toolsadd_factC
Attach a durable fact to a person. Ask the user if the name is ambiguous.
Inputs: key (required), person_name (required), value, number, +2 more. Returns: object
connect_peopleB
Create a relationship between two existing people. Never guesses ambiguous names.
Inputs: kind (required), person_a (required), person_b (required), note, +1 more. Returns: object
find_intro_pathC
Find short relationship paths from a person to anyone at a target organization.
Inputs: from_person (required), target_organization (required), max_depth. Returns: result
get_personA
Get one person's full record. Returns candidates instead of guessing when ambiguous.
Inputs: name (required). Returns: object
graph_statusB
Check the graph connection and return record counts.
Inputs: no named parameters. Returns: object
read_queryB
Run one guarded SELECT for advanced graph analysis. No DDL or data changes.
Inputs: query (required), max_rows. Returns: result
record_interactionB
Record a dated call, meeting, message, meal, or other interaction.
Inputs: channel (required), happened_on (required), person_name (required), summary. Returns: object
remember_personA
Create or update a person from conversation.
By default this refuses similar-name duplicates and conflicting overwrites. Ask the user, then retry with confirmed_new or overwrite only after they decide.
Inputs: full_name (required), city, email, phone, +9 more. Returns: object
search_peopleC
Search people by name, organization, role, city, identifier, summary, or fact.
Inputs: query (required), limit. Returns: result
stale_contactsC
List strong relationships with no recent interaction.
Inputs: limit, min_days. Returns: result
write_queryA
Run one guarded INSERT, UPDATE, or DELETE. UPDATE/DELETE require WHERE; DDL is blocked.
Inputs: query (required). Returns: object
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 11 tools
Most tools are clearly distinct (graph_status, search_people, get_person, add_fact, record_interaction, find_intro_path, stale_contacts). There is minor overlap between read_query/write_query and the higher-level person/interaction tools, since an agent could use write_query to bypass the intended workflows, but descriptions clarify their guarded, analysis-focused roles.
Tool names follow a consistent verb_noun pattern throughout: graph_status, search_people, get_person, remember_person, add_fact, record_interaction, connect_people, find_intro_path, stale_contacts, read_query, write_query. The naming is clean and predictable.
11 tools is reasonable for a people-memory graph server. Each tool covers a distinct purpose (querying, creating, interactions, relations, analysis). It's slightly on the higher end but not excessive, and no tool feels redundant.
The surface covers search, get, create/update (remember_person), add facts, record interactions, connect people, path finding, and staleness analysis. Minor gaps include no delete/forget person operation and no direct relationship update/removal tool, but core lifecycle and analysis workflows are well covered.
Maintenance
Related MCP Connectors
Persistent, portable memory for AI assistants — your private memory graph, from any MCP client.
ppl personal CRM: contacts, relationship notes, birthdays and follow-ups for AI agents. Hosted MCP.
Persistent memory, knowledge graph, task management and more for AI agents via MCP.
Persistent, inspectable memory for AI agents with lineage, correction, and a hosted MCP endpoint.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceOpen-source MCP memory server providing persistent, cross-platform context for AI tools via a knowledge graph with encrypted storage.2 npm13AGPL 3.0
- AlicenseAqualityCmaintenanceA personal memory MCP server that ingests AI agent conversation logs from multiple platforms into a searchable PostgreSQL+pgvector database, enabling cross-session recall of past reasoning and decisions.6MIT
- AlicenseNot gradedqualityBmaintenanceMCP server providing persistent AI memory with four-tier retrieval (SQLite FTS5, graph, vector, LLM agent) to give AI assistants structured, long-term memory without RAG.1Apache 2.0
- AlicenseNot gradedqualityBmaintenanceA self-hosted MCP server that provides any LLM with a graph-backed memory layer of your life—tasks, email, finance, contacts, calendar—plus autonomous agent offices that act on your behalf.3Apache 2.0