Skip to content

Portable memory (PMM)

OpenMem’s portability layer moves memories between providers without baking in a vendor dump format. PMM (Personal Memory Manifest) is the first interchange format: export what a backend stores, import into another (or the same) provider, and keep a portable copy for backup or “give me my data” requests.

npx @bcmyguest/openmem switch rewires the stack only — it does not migrate content. Use export → switch → import (or re-index) when you change backends.

Status: memory_export / memory_import on the unified MCP are beta. PMM v1 is stable; adapters live under cli/mcp/export/ (orchestrator: cli/mcp/portability.mjs). MCP install: MCP setup.

JSON object, or JSONL with one header line followed by one normalized record per line:

Field Type Meaning
pmm_version 1 Schema version
exported_at ISO-8601 string When the export was taken
source_provider string hindsight | mem0 | cognee
scope object Provider-specific filter (bank, user_id, datasets, …)
records array Normalized memory records (see below)

Each record:

Field Type Meaning
id string | null Provider id when known
content string Primary text
metadata object Optional tags, timestamps, provider payload
scope_key string Bank name, mem0 user_id, Cognee dataset, etc.

Import accepts PMM with merge_strategy:

Strategy Behavior
skip_duplicates (default) For mem0, skip records whose id or content fingerprint already exists in the same bank
overwrite Re-import all records (may duplicate on backends without idempotent upsert)

Cross-provider import (e.g. PMM from mem0 into Hindsight) is best-effort: we map content + scope_key into the active provider’s add API; embeddings and graph structure are not preserved.

The MCP export currently collects all records in memory before returning them, including with format: "jsonl". Use a bank filter for large stores; the MCP response is not a file stream. Hindsight export reads the memory-list endpoint and does not include knowledge pages, bank configuration, or source documents. Confirm migrated data before switching providers.

openmem stays a meta orchestrator: PMM shape and import live in cli/mcp/portability.mjs; each backend implements list/map in cli/mcp/export/ (hindsight.mjs, mem0.mjs, cognee.mjs). New providers add an adapter and one branch in cli/mcp/export/index.mjs — no changes to the MCP tool names agents call.

On the openmem MCP server (MCP setup, Architecture):

memory_export { format?: "json"|"jsonl", bank?: string, user_id?: string }
memory_import { data: string, merge_strategy?: "skip_duplicates"|"overwrite" }

Example flow (Cursor Agent / any host with the unified server):

  1. memory_export with format: "json" and optional bank
  2. npx @bcmyguest/openmem switch <other> --yes
  3. memory_import with the exported JSON and merge_strategy: "skip_duplicates"

Restart Cursor Agent after npx @bcmyguest/openmem install-mcp --yes.

Provider Export Import
mem0 GET /memories with user_id from bank arg (API key from env file) POST /memories per record (infer: false)
Hindsight GET /v1/default/banks then paginated GET …/memories/list per bank (bank arg = one bank; omit = all banks) POST …/banks/{bank}/memories per record
Cognee Resolve indexer dataset name from bank, GET /api/v1/datasets, paginate GET …/data (label/name as content; not full file bytes) POST /api/v1/add per record when dataset in scope