MemorySync

Shared Memory for Multi-Agent Systems

Agents that keep private state drift apart. How to share one memory store, track which agent wrote what, decide who may write, and keep concurrent writes safe.

Published on 5 min read

Shared Memory for Multi-Agent Systems
On this page

A planner agent learns that the customer wants the report in British English. The researcher never hears about it, and the writer produces American spelling. Each agent kept what it learned in its own state, and by the end of the run the three disagreed about the customer.

The same drift happens across tools. A developer tells Claude Code that the team uses pnpm, and the next morning Cursor suggests npm. In both cases the fix is one memory store that every agent reads, with clear rules about which agents write to it.

One store, scoped deliberately

Shared memory starts with the scope every agent uses. In MemorySync, each API-key call names an end user, and that id is the unit of sharing: agents that send the same id read and write the same memories.

  • Per end user. Every agent working for one customer uses that customer's id, such as customer-7, so the planner, the researcher and the writer see the same facts.
  • Per team. Conventions everyone should share live under a workspace-level id such as team-engineering, the pattern the docs give for organization standards.

An agent can use both. It reads conventions with the team id and facts about the customer with the customer id, as two separate calls, so a customer's details never end up stored under the team id where every agent would see them.

The API enforces the boundary. Reads, searches and deletes are filtered to the end user, project and environment, and deduplication never crosses them, so a crew working for one customer cannot read another customer's memories by mistake.

Record which agent wrote what

With several writers, every memory needs provenance. Pass source on each write, one value per agent or tool. Use short, lowercase names defined in your code, such as planner and writer, rather than a new label for each write, so you can later group and filter by them:

Python
from memorysync import MemorySyncClient
ms = MemorySyncClient(
api_key="...",
base_url="https://api.memorysync.io",
end_user_id="customer-7",
)
ms.add("Customer wants reports in British English.", source="planner")

Over MCP, the list_entities tool groups a user's memories by source, extracted category or extracted type, with a count and the time of the latest memory for each group. When one agent has been writing bad facts, delete_entity removes every memory with that source. It runs as a dry run by default and reports how many memories would go; the real delete needs dry_run=false.

Preview first. Run delete_entity with the default dry run, compare the count with what list_entities reported, and only then delete for real.

Decide who may write

Shared memory gets noisy when every agent writes everything. Give each agent a role:

  • Writers talk to the user or confirm outcomes. They store facts the user stated and decisions the user confirmed.
  • Readers, such as a researcher or a summarizer, only need context. They get read-only access.
  • Nobody writes drafts, intermediate reasoning or tool output that will be stale tomorrow.

State the rules in two places. Each agent's instructions say what it should save, and its credentials stop it from writing when the instructions are ignored.

Read-only access exists at each layer. The LangChain tools take read_only=True and then return only search_memory and list_memories. An MCP connection without write scope is not shown the write tools at all, so its agent cannot try to call them.

Python
from langchain_memorysync import create_memorysync_tools
writer_tools = create_memorysync_tools(end_user_id="customer-7")
researcher_tools = create_memorysync_tools(end_user_id="customer-7", read_only=True)

Facts-only capture keeps writes small. Every conversation-capture integration stores curated facts rather than transcripts, so an agent that saves a turn adds what the turn established instead of the whole exchange.

Concurrent writes

Agents run in parallel, retry after timeouts and repeat each other. These mechanisms keep that from corrupting shared memory:

  • Idempotent writes. Send your own id for a record as client_ref, up to 128 characters. Sending it again returns a skipped response with reason already_ingested and the ids the first write produced, so a retry never stores a second copy.
  • Deduplication. A new fact is compared with existing memories of the same user, project, environment, type and key, and skipped at a cosine similarity of 0.9 or more. It compares meaning, so treat it as a filter rather than a guarantee.
  • Replacement of one-value facts. When a new value arrives for a one-value slot such as a city, the older memory is superseded and search stops returning it. Multi-value keys keep every member, and other changes are not replaced automatically.
Python
import hashlib
from memorysync import AddSkippedResponse
text = "Customer prefers tables over charts in reports."
ref = "crew:" + hashlib.sha256(text.encode()).hexdigest()[:32]
result = ms.add(text, source="writer", client_ref=ref)
if isinstance(result, AddSkippedResponse):
print(result.reason) # "already_ingested" if this client_ref was sent before

The add_memory tools in the framework packages derive client_ref from a hash of the content, and the same pattern works for your own writes. A retried save of the same text is recognised, while a changed fact gets a new reference instead of being skipped as already ingested.

One memory across interfaces

Every interface reaches the same store. The REST API, the Python and TypeScript SDKs, the framework packages and the hosted MCP server read and write the same memories, so a fact a Python crew saved can show up for an MCP client working in the same scope.

In CrewAI, the crewai-memorysync package plugs into CrewAI's own memory as its storage backend. One user_id per crew gives the crew a shared knowledge base, and one per end user keeps each customer's crew memory separate:

Python
from crewai import Crew
from crewai.memory import Memory
from crewai_memorysync import MemorySyncStorage
crew = Crew(
agents=[researcher, writer],
tasks=[research_task, write_task],
memory=Memory(storage=MemorySyncStorage(user_id="support-crew")),
)
crew.kickoff()

CrewAI's records are kept as framework state, and the facts in them are extracted into memories that the SDKs, MCP and the other integrations can find. Saving an identical record again stores and extracts nothing twice.

Coding agents share memory the same way. Claude Code, Cursor, Codex and VS Code connected to the hosted MCP server under one account read and write one memory, and One Memory Across Claude Code, Cursor, Codex and VS Code is the guide for that setup.

Next step

To run the CrewAI example above against your own crew, follow A Durable Memory Backend for CrewAI.

Share article

Blog

Try it on your own workload

The free tier stores real memories and returns real rankings. One write and one retrieval to start.

Start free. No credit card required.