mem-port
About
A local MCP (Model Context Protocol) server for portable, long-term agentic memory, a thumb drive for your AI context.
Details
- Author
- rsl-innovation
- Categories
- AI
Jump to
Setup
Install mem-port in your MCP client (Claude Desktop, Cursor, Windsurf, and others).
Repository: https://github.com/rsl-innovation/mem-port
Follow the installation instructions in the repository README, then restart your MCP client.
A local MCP (Model Context Protocol) server for portable, long-term agentic memory — a thumb drive for your AI context.
Every AI copilot (Claude Code, Cursor, Windsurf, ...) keeps its own memory, siloed to that tool. The usual workaround — copy-pasting context, summaries, or exported notes from one agent into another — only captures a snapshot frozen at the moment you made it. From there the copies drift: each agent keeps learning on its own, nothing keeps the copies in sync, and the longer you go the more your copilots disagree about what's actually true. mem-port runs as a single local daemon that any number of copilots can connect to, backed by an embedded knowledge graph (entities, episodes, memories, skills, architectural decision records, and the relations between them) that survives restarts and can be exported to a portable file and moved anywhere. Every connected copilot reads and writes the same graph, so there's nothing to paste and nothing to drift.
Unlike other memory-for-agents projects, mem-port needsno external services— no Postgres, no Qdrant, no Neo4j. It's one process, one embeddedSurrealDBinstance combining graph storage and vector search, and zero-config local semantic search (no API key required).
Connecting a client (below) gives it theabilityto use mem-port; for more detailed, tunable instructions on what it should actually save and when — including keeping personal/team/project memory in separate scopes — seeMEMORY_GUIDE.md.
This starts a daemon onhttp://127.0.0.1:8787/mcp. Point any MCP client at it over Streamable HTTP, with alibrary-idheader identifying your workspace. Every copilot that connects with the samelibrary-idshares the same memory; differentlibrary-ids are fully isolated from each other (each maps to its own SurrealDB namespace/database) — there's no cross-tenant leakage.
npxre-checks the registry on every invocation. If you'll be running mem-port commands often, install it globally instead somem-portis a plain command on your PATH:
npm install -g @rsl-innovation/mem-port mem-port serve
The rest of this README usesmem-port <command>for brevity. If you didn't install globally, substitutenpx @rsl-innovation/mem-port <command>wherever you see that — it works identically, just slower to start.
Easiest: use the CLI (--headeraccepts any number ofKey: Valuepairs). Add--scope userso the server is available in every project on this machine, not just the one you happen to be in when you run the command — the defaultlocalscope ties it to a single project directory:
claude mcp add --transport http mem-port http://127.0.0.1:8787/mcp \ --header "library-id: my-personal-workspace" \ --scope user
Or add it directly to~/.claude.json(user scope, applies everywhere) or.mcp.json(project scope, shareable via version control with that repo's team). Thetypefield is required — an entry with aurlbut notypeis treated as a misconfigured stdio server:
{ "mcpServers": { "mem-port": { "type": "http", "url": "http://127.0.0.1:8787/mcp", "headers": { "library-id": "my-personal-workspace" } } } }
Run/mcpinside Claude Code to confirm it showsmem-portas connected.
Connecting from the Claude Code VS Code extension
The extension shares the exact same MCP configuration as the CLI (.mcp.json/~/.claude.json) — there's no separate settings UI to add a server from. Open the integrated terminal (Ctrl+/Cmd+`) and run the same command as above:
claude mcp add --transport http mem-port http://127.0.0.1:8787/mcp \ --header "library-id: my-personal-workspace" \ --scope user
Once added, type/mcpin the chat panel to confirm mem-port shows as connected, or to enable/disable/reconnect it.
Any client that supports Streamable HTTP with custom headers can connect the same way. Clients that only support stdio-based servers (some Claude Desktop configurations, for example) need a stdio-to-HTTP bridge such asmcp-remote:
{ "mcpServers": { "mem-port": { "command": "npx", "args": ["-y", "mcp-remote@latest", "http://127.0.0.1:8787/mcp", "--header", "library-id:my-personal-workspace"] } } }
Getting your copilot to use it proactively
Connecting the server gives your copilot theabilityto save/recall memory — the server's MCPinstructionsand tool descriptions already nudge any client toward using it proactively. For more explicit control (and for keeping personal/organizational/project memory in separatelibrary-idscopes instead of one bucket), seeMEMORY_GUIDE.mdfor instructions to paste into your copilot's own custom-instructions file.
Memoriesare the core unit — one durable, self-contained statement worth recalling in a later session that starts from zero context ("User prefers dark mode in all editors"). Each carries amemory_type(fact,preference,decision,task, orreference) thatsearch_memorycan filter on, and animportancefrom 0 to 1. The type is worth picking deliberately: it's the difference between a searchable library and a flat pile of text — seeMEMORY_GUIDE.mdfor how to choose, and for what doesn't belong in memory at all.
Episodesare the raw material memories get derived from — a conversation, a debugging session, a meeting — recorded with atitle,content, asource(which copilot recorded it) andoccurred_at. Where a memory is a distilled claim, an episode is an unedited record of something that happened.save_memorytakes asource_episode_id, so a memory can point back at the episode it came from and keep its provenance.
The two answer different questions, which is why both exist:"what's true about this project?"is a semantic search over memories, while"what happened last Tuesday?"is a chronological read of episodes vialist_episodes(filterable by time range and source). Memories are what you search; episodes are what you replay.
Entities— people, projects, tools — are the connective tissue. Passingentity_refswhen saving anything links it to those entities, creating them on first mention.get_entitythen returns every memory, episode, skill, and ADR that mentions the entity plus its related entities, which makes "tell me everything relevant to checkout-service" one lookup instead of several searches.relate_entitiesadds typed edges between entities themselves (Alice—leads→mem-port).
Alongside episodes and memories, mem-port storesskills— reusable procedures for recurring tasks (e.g. "how to debug a flaky test in this repo," "the deploy steps for checkout-service"). A skill has aname, adescription(the trigger condition — when a copilot should reach for it, matched bysearch_skills), andcontent(the actual instructions).
Skills are what makes "porting common skills across AI" work with no extra machinery: since they live in the same shared knowledge graph as everything else, a skill saved by Claude Code is immediately visible to Cursor or Windsurf the moment they connect with the samelibrary-id— no file format conversion needed.export_library/import_librarycarry skills between machines exactly like entities, episodes, and memories.
mem-port also keeps anADR log— architectural decision records, the consequential technical choices whose reasoning matters months later. Each ADR gets a sequential number within its library (ADR-0001,ADR-0002, ...) and holds the four things a decision record needs: thecontextthat forced the decision, thedecisionitself, itsconsequences, and thealternativesthat lost.
This is deliberately not the same assave_memory(memory_type: "decision"). A memory recordsthatsomething was decided; an ADR keeps the problem framing and the rejected options, which is what you actually need when someone proposes the rejected option again a year later.search_adrsmatches against title + context + decision, so "why aren't we using Postgres?" finds the record even when it shares no words with it.
Decisions get reversed, so ADRs have a lifecycle (proposed→accepted, thensupersededordeprecated) and a supersede chain. Passingsupersedeswhen recording a newer decision — as a record id, a number, or its display form likeADR-0003— automatically marks the older onesupersededand links the two, so the log stays readable from either end rather than accumulating contradictory records.
Prefer superseding an ADR overforget_adr— a decision that was reversed is usually worth keeping on the record.
Same-machine sharing across copilots needs no extra step — they just connect to the same daemon with the samelibrary-id.export_library/import_librarysolve a different problem: moving to a new machine, backing up, versioning (the bundle is plain JSON — commit it to a private git repo if you like), or handing a curated slice of memory to someone else.
# on the old machine mem-port export --library-id my-personal-workspace # -> writes <data-dir>/exports/my-personal-workspace-<timestamp>.memport.json # on the new machine, after copying the file over mem-port import --library-id my-personal-workspace --in ./my-personal-workspace-....memport.json
importdefaults to--mode merge(dedupes entities by name+type, memories/episodes/skills/ADRs by content hash — importing the same bundle twice is a no-op). Imported ADRs are renumbered onto the end of the target library's sequence rather than colliding with its existing numbers; supersede links are carried across by record reference, so a chain survives renumbering intact. Pass--mode overwriteto wipe the target library first, or--dry-runto see what would happen without writing anything.
mem-port serveruns in the foreground — it's a long-lived daemon, not a one-shot command, so it blocks whatever terminal started it and dies when that terminal closes. If your MCP client can't connect (ECONNREFUSED 127.0.0.1:8787), that's almost always the reason: nothing is actually listening. Check withlsof -i :8787.
For a quick session, background it:mem-port serve &(ornohup mem-port serve > ~/.mem-port.log 2>&1 &to survive closing the terminal). For something that survives reboots and restarts itself if it ever crashes, set it up as a proper background service.
which node # note this path which mem-port # note this path too, then resolve the symlink: readlink -f "$(which mem-port)" # -> .../lib/node_modules/@rsl-innovation/mem-port/bin/mem-port.js
Write~/Library/LaunchAgents/com.rsl-innovation.mem-port.plist, substituting the two paths above.Invokenodedirectly with the resolved script path — don't pointProgramArgumentsat themem-portshim itself.launchddoesn't inherit your shell's PATH, so the shim's#!/usr/bin/env nodeshebang fails withenv: node: No such file or directorywhen launchd runs it:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.rsl-innovation.mem-port</string> <key>ProgramArguments</key> <array> <string>/usr/local/bin/node</string> <string>/usr/local/lib/node_modules/@rsl-innovation/mem-port/bin/mem-port.js</string> <string>serve</string> </array> <key>RunAtLoad</key> <true/> <key>KeepAlive</key> <true/> <key>StandardOutPath</key> <string>/Users/YOUR_USERNAME/Library/Logs/mem-port.log</string> <key>StandardErrorPath</key> <string>/Users/YOUR_USERNAME/Library/Logs/mem-port.error.log</string> </dict> </plist>
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.rsl-innovation.mem-port.plist # start now + on every login launchctl bootout gui/$(id -u)/com.rsl-innovation.mem-port # stop and unregister tail -f ~/Library/Logs/mem-port.log ~/Library/Logs/mem-port.error.log # logs
To pick up a new version afternpm install -g @rsl-innovation/mem-port, restart the running job in place — no need to unload/reload the plist:
launchctl kickstart -k gui/$(id -u)/com.rsl-innovation.mem-port
To stop it and start it again later, usebootout/bootstrap(above) rather thanlaunchctl stop— this plist'sKeepAliveis unconditionallytrue, so a plainstopgets immediately relaunched by launchd.bootoutactually unregisters the job, andbootstrapregisters and starts it again.
# ~/.config/systemd/user/mem-port.service [Unit] Description=mem-port [Service] ExecStart=/usr/bin/node /path/to/lib/node_modules/@rsl-innovation/mem-port/bin/mem-port.js serve Restart=on-failure [Install] WantedBy=default.target
systemctl --user enable --now mem-port journalctl --user -u mem-port -f
All state lives under one data directory — the SurrealDB store (surrealkv://, persistent across restarts) and the cached local embedding model. Delete the data dir to fully reset.
npm install npm run dev # start the daemon with tsx, no build step npm test # vitest: tenancy isolation + export/import round-trip npm run typecheck npm run build # tsup -> dist/, what npx @rsl-innovation/mem-port actually runs
scripts/smoke.shis a plain-curl smoke test against a already-running daemon (no Node/Inspector dependency, usable in CI):
Releases are automated. Bumping the version creates the commit and thevX.Y.Ztag; pushing the tag is what triggers everything else:
npm version patch # or minor / major — runs typecheck + tests first git push --follow-tags
TheReleaseworkflow then verifies the tag matchespackage.json, re-runs typecheck and tests, publishes to npm (with provenance, via OIDC trusted publishing — no token stored anywhere), and creates the GitHub Release with notes generated from the commits since the previous tag.
Do not runnpm publishby hand; a tag push is the only supported path.
- mem-port is a localhost server — it only works with clients running on the same machine.Web/cloud-hosted chat sessions (e.g. chatgpt.com or claude.ai in a browser tab) run server-side and have no route to127.0.0.1on your computer, so they can't reach mem-port no matter how it's configured. To connect ChatGPT, Claude, or similar tools, install theirdesktop appand add mem-port there — the desktop app runs locally and can reach the daemon, whereas the same account's web session cannot.
- Claude Code's CLI and VS Code extension both run locally already, so they work out of the box (see the connection instructions above) — this gotcha mainly matters for tools you might otherwise only use through a browser.
- Vector search is brute-force (no HNSW/DISKANN index yet) — fine at personal-memory-store scale, revisit if a library grows very large.
- export_library's scope filtering supportsmemory_typesandsince; filtering byentity_idsisn't implemented yet.127.0.0.1
- No authentication — the daemon binds toonly and trusts anything running locally on your machine.@huggingface/transformers
- ' bundledonnxruntime-node/sharpcarry known transitive advisories (ZIP/image parsing libs) with no upstream fix yet. mem-port never feeds them untrusted input, butnpm audit`will flag them.
Local-first agent memory: a plain-Markdown Obsidian vault is the source of truth, with a rebuildable DuckDB index for hybrid BM25 + vector + graph recall.
Persistent memory and semantic search for AI coding assistants across sessions
Give your agent a memory: shared, cited, tenant-isolated knowledge-graph memory for any MCP host. Grounded answers from a local-first June endpoint — abstains rather than guesses.
Decentralized persistent memory for AI agents — encrypted vault storage built on Walrus and Sui.
Persistent memory for AI assistants and coding agents across ChatGPT, Claude, Cursor, and other MCP-compatible tools.
Your portable AI memory vault — memories, skills & configs, shared across every AI tool.
Local Work Model for AI agents that learns from real outcomes.
Adaptive MCP memory system for AI applications. Learns which retrieval strategies work for your data, scores results using cognitive science models, builds a knowledge graph automatically, and validates every parameter change against real query history before adopting it. Patent pending.
Auditable, self-improving knowledge & memory for AI agents over MCP — citation-enforced answers and a replayable why-trace, self-hosted on Postgres.
Turns your task manager into agent memory: hybrid (RRF) retrieval over TickTick or an Obsidian vault via an adapter contract. MCP server + CLI, no vector DB to maintain.
Sign in to leave a review
Use Google, GitHub, or an email account so ratings stay tied to real people.
No reviews posted yet.
