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.
PMM format (version 1)
Section titled “PMM format (version 1)”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.
Implementation (orchestrator + adapters)
Section titled “Implementation (orchestrator + adapters)”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.
Unified MCP tools
Section titled “Unified MCP tools”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):
memory_exportwithformat: "json"and optionalbanknpx @bcmyguest/openmem switch <other> --yesmemory_importwith the exported JSON andmerge_strategy: "skip_duplicates"
Restart Cursor Agent after npx @bcmyguest/openmem install-mcp --yes.
Provider behavior
Section titled “Provider behavior”| 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 |