people-network-memory
v0.1.10
Published
Local-first personal network memory MCP for OpenClaw and other MCP harnesses.
Readme
People Network Memory MCP
Local-first V1 scaffold for a personal social-memory MCP. The implementation keeps the product logic independent from Graphiti/MCP/OpenClaw so the core can be tested without external services.
Quick Commands
python -m pip install -e .
python -m unittest discover -s tests -v
people-memory start --test-mode --once
people-memory doctor --test-mode
people-memory load-fixtures --summary
people-memory eval-fixtures
people-memory eval-fixtures --failures-only --output .people-network-memory/eval-report.json
people-memory spike-graphiti
people-memory check-embedding
people-memory tool-schemas
people-memory smoke-graphiti --isolated
people-memory graphiti-gate --isolated --output .people-network-memory/graphiti-gate.json
people-memory index-graphiti --resume --limit 10 --output .people-network-memory/index-graphiti.json
people-memory graphiti-ladder --steps 1,3,10 --cumulative --output-dir .people-network-memory/test-artifacts/graphiti-ladder
people-memory smoke-openclaw
people-memory eval-harness-integration
people-memory release-check
people-memory eval-graphiti --isolated --max-interactions 3 --max-queries 3
people-memory install-openclaw --dry-run
people-memory list-reviews
people-memory web
people-memory backup --output .people-network-memory/backup.json
people-memory backup-archive --output .people-network-memory/backup.zip
people-memory restore --input .people-network-memory/backup.jsonThe base test suite uses unittest and does not require Graphiti, Docker, or a graph
database:
python -m unittest discover -s tests -v
python -m people_network_memory.cli release-check
powershell -ExecutionPolicy Bypass -File .\scripts\run_tests_with_artifacts.ps1The artifact runner writes command output, JSON reports, and a manifest with
exit codes to .people-network-memory/test-artifacts/latest/ by default. That
directory is ignored by git so local verification reports do not leak into the
package.
Local Install
npm Install
The recommended end-user distribution path is the npm wrapper. It keeps the product code in Python, but gives OpenClaw users a familiar install/update surface:
npm install -g people-network-memory
people-memory doctor --test-mode
people-memory install-openclaw --backend local_json
people-memory doctor --agent openclawThe npm people-memory command lazily creates a private Python 3.11+ venv and
installs this package into it. The default venv locations are:
- Windows:
%LOCALAPPDATA%\people-network-memory\npm-venv - macOS:
~/Library/Application Support/people-network-memory/npm-venv - Linux:
${XDG_DATA_HOME:-~/.local/share}/people-network-memory/npm-venv
Set PEOPLE_MEMORY_NPM_VENV to override that location, or
PEOPLE_MEMORY_PYTHON to choose a specific Python command. For Graphiti extras,
either run with --backend graphiti or set
PEOPLE_MEMORY_NPM_EXTRAS=graphiti.
When install-openclaw is run through the npm command, the installer writes
OpenClaw to launch the absolute Node executable plus the npm wrapper script.
That keeps the MCP server independent of shell PATH differences between visible
terminals and hidden OpenClaw gateway sessions on Windows and macOS.
The installer also creates a local env template when it is missing and points
OpenClaw at it with PEOPLE_MEMORY_LOCAL_ENV_FILE. Existing files are never
overwritten, so user-filled API keys and model settings survive future installs.
Default template paths:
- Windows:
%USERPROFILE%\.people-network-memory\local.env.json - macOS/Linux:
~/.people-network-memory/local.env.json
Update flow:
npm update -g people-network-memory
people-memory install-openclaw --backend local_jsonSource Checkout Install
For a clean Windows checkout, use the bootstrap script. It creates .venv,
installs the package, runs the test suite, runs doctor, and starts the MCP
server once in test mode:
powershell -ExecutionPolicy Bypass -File .\scripts\install_windows.ps1Install optional Graphiti/Kuzu dependencies as part of the same flow:
powershell -ExecutionPolicy Bypass -File .\scripts\install_windows.ps1 -GraphitiTo also wire OpenClaw to a managed bootstrap command:
powershell -ExecutionPolicy Bypass -File .\scripts\install_windows.ps1 -OpenClawThe managed OpenClaw entry starts scripts/people_memory_bootstrap.py. On each
server start, that bootstrapper checks for a local .venv, creates it if
missing, installs or updates dependencies when pyproject.toml changes, then
launches the MCP server from the venv. This lets an AI harness repair missing
Python package dependencies without the user manually running pip.
For a trusted git checkout, add -AutoUpdate to let the bootstrapper run
git pull --ff-only before dependency checks:
powershell -ExecutionPolicy Bypass -File .\scripts\install_windows.ps1 -OpenClaw -AutoUpdateThis still requires a system Python 3.11+ command to exist. On Windows the
managed entry uses py -3.11; on macOS/Linux it uses python3.
For Graphiti-backed OpenClaw, first configure the embedding and LLM environment variables in OpenClaw or the launching shell, then run:
powershell -ExecutionPolicy Bypass -File .\scripts\install_windows.ps1 -Graphiti -OpenClaw -Backend graphitiThe bootstrap script does not write API keys or model credentials.
For the current Windows development setup, the live Graphiti artifact run can reuse Volcengine/Doubao embeddings and the LLM values from the Liepin screening config:
$env:VOLCENGINE_API_KEY="<secret>"
powershell -ExecutionPolicy Bypass -File .\scripts\run_graphiti_live_from_liepin.ps1That wrapper maps the embedding endpoint/model to Volcengine, reads
baseUrl/model/credential from ~/.liepin-recommend-mcp/screening-config.json
for the LLM provider, disables Graphiti telemetry, and stores verification
artifacts under .people-network-memory/test-artifacts/graphiti-live/.
spike-graphiti is the prerequisite gate. graphiti-gate is the promotion
gate: it keeps local_json as the default, runs live embedding plus a full
Graphiti/Kuzu fixture eval when providers are configured, and reports whether
Graphiti/Kuzu is ready to become the recommended recall backend.
Backends
local_jsonis the default non-test backend and persists to~/.people-network-memory/people-memory.json.inmemoryis used for tests and quick smoke checks.graphitiis gated behindpeople-memory graphiti-gate; do not treat it as the recommended recall backend until that command passes with live providers.- For local Graphiti, prefer embedded Kuzu first. It avoids Docker and server setup.
Python 3.11 venv setup for Graphiti + Kuzu:
py -3.11 -m venv .venv
.\.venv\Scripts\python.exe -m pip install --upgrade pip
.\.venv\Scripts\python.exe -m pip install -e ".[graphiti]"
.\.venv\Scripts\python.exe -m people_network_memory.cli spike-graphiti
.\.venv\Scripts\python.exe -m people_network_memory.cli smoke-graphiti --isolated
.\.venv\Scripts\python.exe -m people_network_memory.cli eval-graphiti --isolated --max-interactions 3 --max-queries 3 --output .people-network-memory/graphiti-eval.json
.\.venv\Scripts\python.exe -m people_network_memory.cli graphiti-gate --isolated --output .people-network-memory/graphiti-gate.jsonEval reports include Recall@3, Recall@5, per-category breakdowns, evidence
coverage, no-result counts, sensitive-leak counts, and optional case-level
diagnostics. When --max-interactions is used, evals default to queries whose
source fixture interactions were ingested. Add --all-queries only when you
intentionally want to test missing/unanswerable targets.
Case-level diagnostics include the query, expected people/terms, matched and
missed expected values, and the actual top retrieval results with evidence
source text/date. The artifact runner saves these full case diagnostics by
default in eval-fixtures.json; Graphiti live artifacts save them in
graphiti-gate.json and graphiti-eval.json.
eval-graphiti without --max-* runs the full fixture set. The small bounded
form above is only a quick smoke check. graphiti-gate uses the full fixture
set by default and requires Recall@3 >= 70%, Recall@5 >= 85%, complete returned
evidence, zero sensitive leaks, at least 40 checked queries, and at least 60
ingested interactions unless you intentionally bound the run.
Graphiti indexing should be treated as resumable background work. Local JSON is
the canonical capture store; index-graphiti reads saved local interactions and
adds compact text episodes into Graphiti/Kuzu one by one, saving progress after
each successful episode:
people-memory index-graphiti --resume --limit 10 --output .people-network-memory/index-graphiti.json
people-memory index-graphiti --resume --limit 10Use the ingestion ladder to find the point where the live Graphiti/LLM path slows down or fails. Each step writes a separate JSON artifact:
people-memory graphiti-ladder --steps 1,3,10,20,40,90 --output-dir .people-network-memory/test-artifacts/graphiti-ladderAdd --cumulative when testing larger rungs. It reuses one artifact-local
Graphiti/Kuzu store plus one state file, so a 20,40,90 run indexes only the
additional interactions at each step and leaves the test database under the
artifact directory:
people-memory graphiti-ladder --steps 20,40,90 --cumulative --output-dir .people-network-memory/test-artifacts/graphiti-ladder-cumulativeIf that command is interrupted, rerun it with --resume-cumulative against the
same output directory to keep the existing state file:
people-memory graphiti-ladder --steps 20,40,90 --cumulative --resume-cumulative --output-dir .people-network-memory/test-artifacts/graphiti-ladder-cumulativeFor slow live providers, raise the per-episode timeout without changing code:
$env:PEOPLE_MEMORY_GRAPHITI_ADD_TIMEOUT_SECONDS="600"
$env:PEOPLE_MEMORY_GRAPHITI_RETRY_ATTEMPTS="4"
people-memory graphiti-ladder --steps 20,40,90 --cumulative --resume-cumulative --output-dir .people-network-memory/test-artifacts/graphiti-ladder-cumulativeBounded ladder runs are diagnostics only; they cannot promote Graphiti/Kuzu to the recommended backend.
After an existing Graphiti/Kuzu fixture graph has been indexed, evaluate search quality without re-ingesting episodes:
people-memory eval-graphiti-search --data-path .people-network-memory/test-artifacts/graphiti-ladder-cumulative/cumulative-store/projection --graphiti-kuzu-path .people-network-memory/test-artifacts/graphiti-ladder-cumulative/cumulative-store/graphiti.kuzu --indexed-interactions 90 --output .people-network-memory/test-artifacts/graphiti-search/eval.jsonIf the graph was populated by the fixture ladder before the projection cache was hydrated, rebuild that local cache without calling Graphiti again:
people-memory hydrate-graphiti-cache --data-path .people-network-memory/test-artifacts/graphiti-ladder-cumulative/cumulative-store/projection --fixture-seed 42 --limit 90 --output .people-network-memory/test-artifacts/graphiti-search/cache-hydration.jsonBuild the optional local semantic sidecar over the hydrated projection cache:
people-memory build-semantic-cache --data-path .people-network-memory/test-artifacts/graphiti-ladder-cumulative/cumulative-store/projection --limit 90 --reset --output .people-network-memory/test-artifacts/graphiti-search/semantic-cache.jsonWith that sidecar present, Graphiti retrieval merges three sources: Graphiti
graph search, local semantic interaction search, and local token/event search.
The strict eval report checks intent-aware matches, not just loose token
overlap: who mentioned X must return the speaker, promise queries must return
follow-up items, and profile queries must combine the requested terms in one
returned result. For intentionally broad fixture queries, the report also keeps
expected_person_rank as a diagnostic because several mock people may satisfy
the same place/topic or company/topic query.
Required LLM Runtime
Production MCP startup requires an OpenAI-compatible LLM for extraction, identity advice, and retrieval reranking:
$env:PEOPLE_MEMORY_INGESTION_EXTRACTOR="llm"
$env:PEOPLE_MEMORY_IDENTITY_ADVISOR="llm"
$env:PEOPLE_MEMORY_RETRIEVAL_JUDGE="llm"
$env:PEOPLE_MEMORY_INGESTION_EXTRACTOR_TIMEOUT_SECONDS="30"
$env:PEOPLE_MEMORY_IDENTITY_ADVISOR_TIMEOUT_SECONDS="30"
$env:PEOPLE_MEMORY_RETRIEVAL_JUDGE_TIMEOUT_SECONDS="30"
$env:PEOPLE_MEMORY_LLM_BASE_URL="<base-url>"
$env:PEOPLE_MEMORY_LLM_MODEL="<model>"
$env:PEOPLE_MEMORY_LLM_API_KEY="<secret>"record_interaction asks the extractor to propose structured capture data, then
the MCP validates it, strips model-invented person IDs, preserves the original
note as evidence, and applies review-first identity policy. If a single exact
name or alias matches an existing card, capture creates a provisional card plus
needs_review; it does not silently update the existing person. Stable
person_id, email, or phone can still link directly.
retrieve_network_context fetches a broader candidate set, applies deterministic
ambiguity and privacy constraints, then requires the LLM judge to rank the
evidence pack. If the judge is unavailable or returns invalid JSON, retrieval
fails with an actionable configuration/provider error instead of returning
unreranked results. Tests and fixture evals use fake LLM adapters, so CI does
not need live provider credentials.
The intended harness split is hybrid:
- This tool owns social-memory retrieval quality: query expansion, graph/semantic search, deterministic social-intent checks, required LLM reranking, evidence, sensitivity policy, and stable person IDs.
- The AI harness owns user-aware synthesis: applying its memory of the user's goals, style, preferences, and current constraints to the retrieved social evidence.
- The OpenClaw skill instructs the harness to retrieve from this MCP first, treat returned people/network facts as source-backed evidence, then combine them with the harness's own user memory for final recommendations, briefs, drafts, or plans.
Run the deterministic harness integration eval to check that contract without needing a live AI harness:
people-memory eval-harness-integration --output .people-network-memory/harness-integration-eval.jsonThe eval seeds a small social graph and verifies planning, intro drafting, conflict resolution, missing-person evidence, and follow-up cases. Each case records the prompt, simulated harness memory, MCP tool calls, actual retrieval results, hydrated person cards when needed, and pass/fail checks for source boundaries.
LLM Ingestion Extractor
The extractor proposes participants, aliases, topics, claims, relationships,
and follow-ups from source_text. The application layer still preserves the
original note as evidence, strips model-invented person_id values, applies the
deterministic normalizer, and sends identity ambiguity to review instead of
silently merging cards. If the extractor fails or returns invalid JSON in
production, capture fails without writing a partial record. Deterministic rule
parsing remains available inside explicit test/eval paths via fake LLM adapters.
OpenClaw installs default to PEOPLE_MEMORY_INGESTION_EXTRACTOR=llm,
PEOPLE_MEMORY_IDENTITY_ADVISOR=llm, and PEOPLE_MEMORY_RETRIEVAL_JUDGE=llm.
The installer writes a fill-in local.env.json once; production MCP startup
will fail until PEOPLE_MEMORY_LLM_BASE_URL, PEOPLE_MEMORY_LLM_MODEL, and
PEOPLE_MEMORY_LLM_API_KEY are configured.
Embeddings
Local-first default recommendation:
$env:PEOPLE_MEMORY_EMBEDDING_PROVIDER="ollama"
$env:PEOPLE_MEMORY_EMBEDDING_BASE_URL="http://localhost:11434/v1"
$env:PEOPLE_MEMORY_EMBEDDING_MODEL="nomic-embed-text"
people-memory check-embeddingOpenAI-compatible cloud endpoints use the same command with:
$env:PEOPLE_MEMORY_EMBEDDING_PROVIDER="openai_compatible"
$env:PEOPLE_MEMORY_EMBEDDING_BASE_URL="https://ark.cn-beijing.volces.com/api/coding/v3"
$env:PEOPLE_MEMORY_EMBEDDING_MODEL="doubao-embedding-vision-251215"
$env:PEOPLE_MEMORY_EMBEDDING_API_KEY="<secret>"
people-memory check-embeddingDo not commit API keys or write them into config files.
Sensitivity Policy
Sensitivity labels such as sensitive and do_not_surface_unprompted are kept
as metadata, but V1 defaults to personal surfacing because this is a private
single-user memory tool.
$env:PEOPLE_MEMORY_SENSITIVITY_POLICY="personal" # private recall includes sensitive context
$env:PEOPLE_MEMORY_SENSITIVITY_POLICY="strict" # hide sensitive context unless explicitly requested
$env:PEOPLE_MEMORY_SENSITIVITY_POLICY="task_aware" # include for private recall, hide for shareable outputThe MCP retrieval tool also accepts sensitivity_policy, output_context, and
the explicit include_sensitive override per request.
Graphiti also needs an LLM provider for episode extraction. Embeddings alone are
enough for vector smoke checks, but not enough for live Graphiti ingestion.
Some OpenAI-compatible gateways do not support response_format; leave
PEOPLE_MEMORY_LLM_RESPONSE_FORMAT unset or set it to none for those
providers. Use json_object or json_schema only when the provider explicitly
supports that mode.
Thinking/reasoning controls are provider-dependent. Leave
PEOPLE_MEMORY_LLM_THINKING_LEVEL unset or set it to default to send no
reasoning option. Set it to values such as low, medium, or high only when
your OpenAI-compatible gateway accepts reasoning_effort. For provider-specific
fields such as nested thinking settings, set PEOPLE_MEMORY_LLM_EXTRA_BODY to
a JSON object. The MCP protects core fields like model, messages,
temperature, response_format, and stream from being overridden by
EXTRA_BODY.
$env:PEOPLE_MEMORY_BACKEND="graphiti"
$env:PEOPLE_MEMORY_GRAPH_BACKEND="kuzu"
$env:PEOPLE_MEMORY_LLM_PROVIDER="openai_compatible"
$env:PEOPLE_MEMORY_LLM_BASE_URL="<base-url>"
$env:PEOPLE_MEMORY_LLM_MODEL="<model>"
$env:PEOPLE_MEMORY_LLM_API_KEY="<secret>"
$env:PEOPLE_MEMORY_LLM_RESPONSE_FORMAT="none"
$env:PEOPLE_MEMORY_LLM_THINKING_LEVEL="default"
$env:PEOPLE_MEMORY_LLM_EXTRA_BODY='{}'
.\.venv\Scripts\python.exe -m people_network_memory.cli smoke-graphiti --isolatedreset is intentionally guarded:
people-memory reset --confirm DELETEIt deletes only the configured local JSON data file for local_json.
Backup
Use JSON backup for portable person-card projection data:
people-memory backup --output .people-network-memory/backup.json
people-memory restore --input .people-network-memory/backup.jsonUse archive backup when the backend has local storage beyond the projection
cache. For graphiti + Kuzu, the archive includes projection.json and the
configured Kuzu database directory:
people-memory backup-archive --backend graphiti --output .people-network-memory/graphiti-backup.zip
people-memory restore-archive --backend graphiti --input .people-network-memory/graphiti-backup.zip --confirm OVERWRITErestore-archive requires explicit confirmation and rejects unsafe archive
member paths.
Web Network Graph
Run the local visual browser for saved person cards:
people-memory webIt serves a read-only interactive network graph at http://127.0.0.1:8765/.
Each saved person card is a dot, relationship and shared-interaction evidence
becomes a line, search filters/highlights the graph, and the selected card opens
in the side panel on the same page.
Useful options:
people-memory web --data-path ~/.people-network-memory --port 8765
people-memory web --backend graphiti --data-path ~/.people-network-memory
people-memory web --no-openLow-Friction Capture
The MCP tool accepts raw source_text first. If the harness can confidently
extract structure, it should include it. If not, it can still send the original
note and the server will conservatively normalize obvious people, places,
topics, mentions, work/school facts, contacts, interests, and follow-ups.
Review Queue
Ambiguous identity matches are captured without blocking the original note. Use the review commands to clean them up later:
people-memory list-reviews
people-memory resolve-review --review-id review_0001 --source-person-id person_0003 --target-person-id alice
people-memory dismiss-review --review-id review_0002 --note "not enough information"Resolving an identity review merges the source person into the target person, preserves aliases, interactions, facts, claims, follow-ups, relationships, and evidence, then marks the review item as resolved.
OpenClaw
Install or preview the OpenClaw adapter:
people-memory install-openclaw --dry-run
people-memory install-openclaw
people-memory install-openclaw --managed-bootstrap
people-memory doctor --agent openclaw
people-memory smoke-openclaw
people-memory eval-harness-integrationThe installer updates only the people-network-memory MCP server entry in the
active OpenClaw config. Current OpenClaw builds use ~/.openclaw/openclaw.json
under mcp.servers; when that file exists the installer updates it, and also
maintains the legacy/generic ~/.openclaw/mcp.json shape for older MCP
harnesses. It preserves unrelated MCP servers and writes the mirrored skill to
~/.openclaw/skills/ppl/SKILL.md. The MCP server namespace remains
people-network-memory for tool compatibility, but the user-facing OpenClaw
slash trigger is /ppl; /people_network_memory and /people-network-memory
remain accepted aliases for existing prompts.
It also writes ~/.people-network-memory/local.env.json once as a fill-in
template and adds PEOPLE_MEMORY_LOCAL_ENV_FILE to the MCP server env. If that
file already exists, the installer leaves every value untouched. Use
--local-env-file <path> to choose a different template path. The template uses
llm for extraction, identity advice, and retrieval reranking but leaves the
provider keys blank on purpose; fill those keys before starting the production
MCP server. It also includes PEOPLE_MEMORY_LLM_THINKING_LEVEL set to
default and an empty PEOPLE_MEMORY_LLM_EXTRA_BODY object for
OpenAI-compatible provider tuning.
LLM switches accept off/llm; on, true, yes, and 1 are treated as
llm compatibility aliases, but production startup requires the three LLM
features to be llm.
By default it uses the active Python executable with
-m people_network_memory.cli start, so it does not require the people-memory
console script to be on PATH. For end-user harness installs, prefer
--managed-bootstrap: that stores a tiny stdlib launcher in the MCP command so
the harness can recreate the venv and refresh Python dependencies automatically.
smoke-openclaw installs into a temporary OpenClaw home by default, checks the
adapter config and skill text, verifies prompt-routing examples, and exercises
the three public MCP workflows with the test backend.
