<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Edward Izgorodin</title>
    <description>The latest articles on DEV Community by Edward Izgorodin (@izgorodin).</description>
    <link>https://dev.to/izgorodin</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3989589%2F15cb8fb8-9785-496a-be83-65c3061529d2.jpeg</url>
      <title>DEV Community: Edward Izgorodin</title>
      <link>https://dev.to/izgorodin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/izgorodin"/>
    <language>en</language>
    <item>
      <title>How can I automatically rank past successful code suggestions for an autonomous bot?</title>
      <dc:creator>Edward Izgorodin</dc:creator>
      <pubDate>Tue, 29 Sep 2026 10:48:00 +0000</pubDate>
      <link>https://dev.to/izgorodin/how-can-i-automatically-rank-past-successful-code-suggestions-for-an-autonomous-bot-4mj5</link>
      <guid>https://dev.to/izgorodin/how-can-i-automatically-rank-past-successful-code-suggestions-for-an-autonomous-bot-4mj5</guid>
      <description>&lt;p&gt;Your bot fixes failing tests on its own. Before each attempt it looks up how similar failures were fixed before, and the lookup is ranked by similarity. So the fix it tried last Tuesday, the one that turned the suite red, keeps coming back first, because it is still the closest text to today's error. The fix that actually worked sits at position four.&lt;/p&gt;

&lt;p&gt;You want the ones that passed to rise and the ones that failed to sink, without a person rating anything.&lt;/p&gt;

&lt;p&gt;The short answer: a code bot already has the verdict a chat bot lacks. The test run exits zero or it does not. Send that result back to the memory store as a rating on the memories the attempt actually followed, and a store that reads ratings at ranking time will weigh what worked into the order of the next lookup. The rest of this article is the three decisions that make that loop honest, and a check with a control.&lt;/p&gt;

&lt;p&gt;Disclosure: I work on Mnemoverse, the memory layer the code below uses.&lt;/p&gt;

&lt;h2&gt;
  
  
  The outcome is already there
&lt;/h2&gt;

&lt;p&gt;A bot that answers people has to infer an outcome from the conversation, and most conversations say nothing. A bot that changes code has a build, a test run, a linter, a command that exits zero or does not. That is a verdict produced by something other than the bot's own opinion of its work, and it arrives at a known moment.&lt;/p&gt;

&lt;p&gt;pytest, for example, documents its exit codes: 0 is "All tests were collected and passed successfully", and 1 is "Tests were collected and run but some of the tests failed". Codes 2 to 5 mean the run was interrupted, broke internally, was called wrongly, or collected nothing, and current pytest adds 6 for too many warnings. None of those is a verdict on your fix, with one trap for a bot that edits code: by default a test file that fails to import stops the run with code 2, so a patch with a syntax error would look like no verdict. &lt;code&gt;--continue-on-collection-errors&lt;/code&gt; turns that case into code 1. An import error inside &lt;code&gt;conftest.py&lt;/code&gt; still exits 4, so if the bot can touch code that conftest imports, compile the changed files first and count a failure there as red.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three decisions, in the order they come up
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;When the call goes.&lt;/strong&gt; After the test run, not inside the lookup. At lookup time you only know what was similar. The outcome exists once the suite has finished, so the rating goes out then. Rating in the same breath as the lookup rates the retrieval, not the fix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which ids.&lt;/strong&gt; Keep the ids of the items the patch actually followed with the attempt that used them: not with the session, and not every item the read returned. Items that are always rated together always move together, so a fix that failed and a fix that worked, returned for the same failure, would never come apart. If the bot makes three attempts in one run, rating whatever was read last hands the second attempt's red run to the third attempt's memories.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to send when there is no verdict.&lt;/strong&gt; Nothing. A run killed by a job time limit, an interrupted run, a suite that collected no tests: none of these says whether the fix was right. Our library page puts the rule in one sentence: "&lt;code&gt;outcome&lt;/code&gt; is a required argument with no default, and nothing calls &lt;code&gt;feedback()&lt;/code&gt; for you". An attempt you do not rate leaves those memories' valence where it was. A small positive on every run with no verdict is not the same as silence, because it moves every memory it touches and tells the ranking nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The loop in Python
&lt;/h2&gt;

&lt;p&gt;This uses our Python SDK, &lt;code&gt;mnemoverse&lt;/code&gt; 0.3.1. The bot's own calls are placeholders.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;mnemoverse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MnemoClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MnemoClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mk_live_YOUR_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;failure&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;payments integration test times out after the retry change&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;recall&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;failure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top_k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# the bot returns its patch and the recalled items it actually followed
&lt;/span&gt;&lt;span class="n"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;followed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;propose_fix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;failure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;recall&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;used&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;atom_id&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;followed&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;   &lt;span class="c1"&gt;# the ids this attempt is built on
&lt;/span&gt;&lt;span class="n"&gt;bot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pytest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-q&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--continue-on-collection-errors&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tests/integration&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;returncode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;outcome&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;      &lt;span class="c1"&gt;# collected and passed
&lt;/span&gt;&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;returncode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;outcome&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;     &lt;span class="c1"&gt;# ran and failed, including a test file the patch broke on import
&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;outcome&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;     &lt;span class="c1"&gt;# no verdict: interrupted, internal error, usage error, nothing collected, killed
&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;outcome&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;used&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;feedback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;atom_ids&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;used&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;outcome&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;outcome&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query_concepts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;recall&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query_concepts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The loop above only reads and rates. What it reads are notes the bot wrote earlier with &lt;code&gt;client.write()&lt;/code&gt;, one short note per fix it applied, and the ratings attach to those notes, not to the failure text. A write can be refused by the write gate, so check &lt;code&gt;stored&lt;/code&gt; on the response.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the rating changes
&lt;/h2&gt;

&lt;p&gt;Two things, both documented on our library page. The memory's &lt;code&gt;valence&lt;/code&gt;, an outcome polarity from -1.0 to +1.0 that every returned item carries, moves with the rating. And the links between the query's concepts and the memory's concepts are updated, which is why the call takes &lt;code&gt;query_concepts&lt;/code&gt; as well as ids.&lt;/p&gt;

&lt;p&gt;At ranking time the valence modulates relevance, so a fix with a history of green runs gains ground and one with a history of red runs loses it, and a fix that worked can out-rank a closer match that kept failing. Nothing is deleted. The record of what failed stays in the store and stays readable, which is useful the day someone asks why the bot stopped trying the obvious fix. The coefficient is not published, so this article makes no promise about how many positions one rating moves.&lt;/p&gt;

&lt;h2&gt;
  
  
  If your bot talks MCP
&lt;/h2&gt;

&lt;p&gt;A bot built on an MCP client, such as a coding agent running headless, gets the rating half of the same loop through the &lt;code&gt;memory_feedback&lt;/code&gt; tool, which takes &lt;code&gt;memory_ids&lt;/code&gt; and an &lt;code&gt;outcome&lt;/code&gt; from -1.0 to 1.0. It does not send &lt;code&gt;query_concepts&lt;/code&gt;, so it moves the memories' valence but not the links between the question and the memories; those are taught from the Python path. Its description tells the agent when to call it: "right after you act on (or reject) recalled memories". Put the test-result rule in the agent's standing instruction: rate the memories you followed after the suite finishes, send +1 on a green run and -1 on a red one, and send nothing when the run gave no verdict.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check it with a control
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Write three notes&lt;/strong&gt; for the same failure: fix A, which you know passes; fix B, which you know fails; and fix C, similar wording, which you will never rate. Check &lt;code&gt;stored&lt;/code&gt; on each write, then read once and note each item's &lt;code&gt;valence&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate each fix by its own run.&lt;/strong&gt; Apply A, run the suite, and send the verdict for A's id only. Do the same for B.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read the failure again.&lt;/strong&gt; A's valence should be above where it started and B's below. The update can land after the call returns, so if nothing has moved yet, wait and read again before concluding anything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Control.&lt;/strong&gt; C's valence should be where it started. If it moved, a rating reached an id the attempt did not use. C's position may still shift, because the ratings also updated the links between the query's concepts and the memories' concepts. That is not a leak, which is why the control reads valence, not position.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There is a 12-minute walkthrough of the same loop for a bot that answers people, where the outcome has to come from the conversation instead of a test run:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/DLKJnqBGe84" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://mnemoverse.com/docs/library/rescorla-wagner-agent-memory" rel="noopener noreferrer"&gt;library page&lt;/a&gt; covers where the update rule comes from and the chat-bot case in full. What does your bot currently do with a red test run, apart from retrying?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Python SDK is on PyPI as &lt;code&gt;mnemoverse&lt;/code&gt;, and the MCP server package on npm is open source (MIT): &lt;a href="https://github.com/mnemoverse/mcp-memory-server" rel="noopener noreferrer"&gt;github.com/mnemoverse/mcp-memory-server&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>python</category>
      <category>mcp</category>
    </item>
    <item>
      <title>How do I share developer preferences between Claude Desktop and Cursor without retyping prompts?</title>
      <dc:creator>Edward Izgorodin</dc:creator>
      <pubDate>Mon, 28 Sep 2026 13:03:00 +0000</pubDate>
      <link>https://dev.to/izgorodin/how-do-i-share-developer-preferences-between-claude-desktop-and-cursor-without-retyping-prompts-560a</link>
      <guid>https://dev.to/izgorodin/how-do-i-share-developer-preferences-between-claude-desktop-and-cursor-without-retyping-prompts-560a</guid>
      <description>&lt;p&gt;You told Claude Desktop that you use pnpm, that tests go in Vitest, and that you want short answers without a summary at the end. Then you opened Cursor and typed the same three lines again. A week later you changed your mind about the summary and had to change it in two places.&lt;/p&gt;

&lt;p&gt;The short answer: keep them in a place both tools can read. Each tool's own settings apply only inside that tool, but both speak MCP, so one memory server connected to each as the same account, plus a one-time instruction in each to check it before answering, leaves one copy to change. Below is where each tool keeps preferences today, why a one-time copy goes stale, and a two-minute check with a control.&lt;/p&gt;

&lt;p&gt;Disclosure: I work on Mnemoverse, one of the memory layers that can sit between these two tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Claude Desktop keeps preferences for the Claude apps
&lt;/h2&gt;

&lt;p&gt;Anthropic documents several places; these three are where a preference like yours ends up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Instructions for Claude.&lt;/strong&gt; "Instructions are account-wide settings that help Claude understand your general instructions that Claude should consider in responses." You set them once under Settings, and they follow your account across the Claude apps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Project instructions.&lt;/strong&gt; These "only apply to chats within that project", which is what you want for one codebase's conventions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memory.&lt;/strong&gt; Claude can "remember context from your chats and carry it into new conversations". It is "on by default for Free, Pro, and Max plans on the web, Claude Desktop, and Claude Mobile", and "Each project has its own separate memory space".&lt;/p&gt;

&lt;p&gt;All three apply inside Claude's own apps, and none of them is among the rule types Cursor's documentation says it reads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cursor keeps preferences for Cursor
&lt;/h2&gt;

&lt;p&gt;Cursor's rules page gives the reason rules exist, under "How rules work": "Large language models don't retain memory between completions. Rules provide persistent, reusable context at the prompt level."&lt;/p&gt;

&lt;p&gt;The kind that matches a personal preference is the User Rule. "User Rules are global preferences defined in Customize → Rules that apply across all projects. They are used by Agent (Chat) and are perfect for setting preferred communication style or coding conventions". The same page calls them "Global to your Cursor environment."&lt;/p&gt;

&lt;p&gt;Project Rules in &lt;code&gt;.cursor/rules&lt;/code&gt; and an &lt;code&gt;AGENTS.md&lt;/code&gt; at the repository root travel with the code, so a teammate who clones the repo gets them. That solves the codebase half. It does not reach a Claude Desktop chat: none of the Anthropic pages above says a chat reads &lt;code&gt;.cursor/rules&lt;/code&gt; or &lt;code&gt;AGENTS.md&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Anthropic offers for moving it
&lt;/h2&gt;

&lt;p&gt;Anthropic documents a transfer and frames it as a move: it lets you "import memories from other AI providers into Claude, or export your Claude memory for backup or migration", and the prompt it recommends begins "I'm moving to another service and need to export my data". The flow is a prompt you run in the other assistant, asking it to list every memory it holds, including "Instructions I've given you about how to respond (tone, format, style, 'always do X', 'never do Y')", and then a text box in Claude where you paste the result and click "Add to memory".&lt;/p&gt;

&lt;p&gt;The other direction has the same shape: Anthropic suggests asking Claude to "Write out your memories of me verbatim, exactly as they appear in your memory" and pasting the result, which for Cursor means into a User Rule.&lt;/p&gt;

&lt;p&gt;Either way it is a one-time copy. The next preference you change is back to two places. The retyping has been given a better interface, not removed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one store both tools can reach
&lt;/h2&gt;

&lt;p&gt;Both Claude Desktop and Cursor speak MCP. So there is a place neither of them owns and both can call: a memory server connected to each as the same account. A preference written from Cursor is a record in that store, and a new Claude Desktop chat can read it, because the store is the same one.&lt;/p&gt;

&lt;p&gt;Two things have to be true, and they are worth checking rather than assuming.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Both tools are connected as the same account.&lt;/strong&gt; When the memory belongs to an account on the server, two connections under two accounts are two memories that happen to share a name.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Each tool is told to read before it answers.&lt;/strong&gt; A connected server does nothing until the model calls it. That instruction is typed once per tool: in Claude Desktop it goes into Instructions for Claude, in Cursor into a rule, either a User Rule for every project or a rule file in the repository. After that, a preference is written once and both tools read it; changing it is one more write, and both tools see that too.&lt;/p&gt;

&lt;h2&gt;
  
  
  How that looks with Mnemoverse
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Claude Desktop.&lt;/strong&gt; In Anthropic's current steps it is Customize &amp;gt; Connectors, then "+" and "Add custom connector": paste &lt;code&gt;https://mcp.mnemoverse.com/mcp&lt;/code&gt; and complete the browser sign-in on first use. There is no config file and no key to paste. Anthropic lists custom connectors on "Free, Pro, Max, Team, and Enterprise plans", with one custom connector on Free; on Team and Enterprise an owner adds it first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cursor.&lt;/strong&gt; The Add to Cursor button on &lt;a href="https://mnemoverse.com/docs/api/cursor#add-to-cursor-no-api-key" rel="noopener noreferrer"&gt;our Cursor page&lt;/a&gt; adds the same hosted server to &lt;code&gt;~/.cursor/mcp.json&lt;/code&gt;. The link carries only the server address, no key. Then Cursor Settings → Tools &amp;amp; MCPs → Connect, and you approve in the browser.&lt;/p&gt;

&lt;p&gt;Signed in as the same account, the two tools share one memory. Our Cursor page puts it in one sentence: "The memory belongs to the account, not to a tool or a key, so a rule you store in Cursor can be recalled in Claude Code or ChatGPT without re-explaining." Claude Desktop is one more client on the same account.&lt;/p&gt;

&lt;p&gt;For that instruction, &lt;a href="https://mnemoverse.com/docs/api/agent-memory#the-fix-make-it-a-standing-instruction" rel="noopener noreferrer"&gt;our agent memory page&lt;/a&gt; has a block you paste once into Instructions for Claude and once into Cursor, where our docs show it as &lt;code&gt;.cursor/rules/mnemoverse.mdc&lt;/code&gt; with &lt;code&gt;alwaysApply: true&lt;/code&gt;. The Claude setting is per account, so each person on a team sets it once for themselves; the Cursor rule file can be committed with the repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check it in two minutes, with a control
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Write one preference in Cursor.&lt;/strong&gt; Ask the agent to remember that you use pnpm and never npm.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open a new chat in Claude Desktop&lt;/strong&gt; and ask which package manager you use. The answer should come from a Mnemoverse &lt;code&gt;memory_read&lt;/code&gt; call you can see in the chat. Claude's own memory and its past-chat search can answer too, the second one also as a visible tool call, so check the tool name.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Control.&lt;/strong&gt; Open one more new chat, switch Mnemoverse off for it under + &amp;gt; Connectors, and ask the same question. It should not know. If it still says pnpm, Claude's own memory or instructions hold it, and step 2 proved nothing: repeat steps 1 and 2 with a preference you have never given Claude.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Change it once.&lt;/strong&gt; Tell Cursor you switched from pnpm to Bun. Ask Claude Desktop again: Bun, again from &lt;code&gt;memory_read&lt;/code&gt;. One edit, both tools.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If step 2 comes back empty, look at the tool call. No &lt;code&gt;memory_read&lt;/code&gt; at all: check that Mnemoverse is on for that chat under + &amp;gt; Connectors and that the instruction is saved. A &lt;code&gt;memory_read&lt;/code&gt; that returns nothing: the two tools are signed in as different accounts (&lt;a href="https://mnemoverse.com/docs/api/agent-memory#first-confirm-the-tools-work" rel="noopener noreferrer"&gt;troubleshooting&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://mnemoverse.com/docs/library/mcp-servers-sharing-state-across-ides" rel="noopener noreferrer"&gt;full review&lt;/a&gt; checks six memory services against the same mechanism and names the ways sharing quietly stops. Which of your preferences do you currently keep in more than one tool?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Setup for &lt;a href="https://mnemoverse.com/docs/api/claude-apps" rel="noopener noreferrer"&gt;Claude Desktop&lt;/a&gt; and &lt;a href="https://mnemoverse.com/docs/api/cursor" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt; is in our docs, and the MCP server package on npm is open source (MIT): &lt;a href="https://github.com/mnemoverse/mcp-memory-server" rel="noopener noreferrer"&gt;github.com/mnemoverse/mcp-memory-server&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>productivity</category>
      <category>llm</category>
    </item>
    <item>
      <title>I Told My Agent's Memory a Result Was Wrong. Did Anything Read It?</title>
      <dc:creator>Edward Izgorodin</dc:creator>
      <pubDate>Sat, 26 Sep 2026 13:33:00 +0000</pubDate>
      <link>https://dev.to/izgorodin/i-told-my-agents-memory-a-result-was-wrong-did-anything-read-it-jld</link>
      <guid>https://dev.to/izgorodin/i-told-my-agents-memory-a-result-was-wrong-did-anything-read-it-jld</guid>
      <description>&lt;p&gt;Yesterday your agent pulled four memories into context and acted on one that had been out of date for two sprints. You sent the rating the tool exposes, a negative one, against that memory id. The call came back clean. This morning the same memory is in the same recall, in the same position.&lt;/p&gt;

&lt;p&gt;The question is not whether the rating was sent. It is whether anything reads it. One rule runs through this article: &lt;strong&gt;is there a documented call that takes the result of a recall that already happened and changes the order of the next recall.&lt;/strong&gt; The word feedback does not settle it: several vendors use it for several different things.&lt;/p&gt;

&lt;p&gt;Disclosure: I work on Mnemoverse, one of the tools in the table below. It is measured by the same rule as the others.&lt;/p&gt;

&lt;p&gt;There are no performance numbers here, ours or anyone's. And one correction first. Re-ranking a result set from a reported judgement of it is relevance feedback; the classic algorithm for that was published in 1971, so the technique is 55 years old. Learning a ranking function from clicks was published in 2002, so removing the human is not new either. What is new is how rarely the client your agent is connected through can reach it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The report that gets counted, and the report someone has to volunteer
&lt;/h2&gt;

&lt;p&gt;Two things get filed under one word. The first says an item came back. The second says it helped, or that it misled. A system generates the first by itself; the second has to be volunteered by whoever used the result, which in an agent stack is the agent, after the answer is already written.&lt;/p&gt;

&lt;p&gt;Mem0 has a clean example of the first, and its page is precise about the input (&lt;code&gt;docs.mem0.ai/platform/features/memory-decay&lt;/code&gt;, read 25 September 2026):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Records a fire-and-forget reinforcement against each returned memory: its access history grows by one, capped at the most recent 20 touches.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That history becomes "a &lt;em&gt;scaling factor&lt;/em&gt; in the range &lt;code&gt;0.3×&lt;/code&gt; to &lt;code&gt;1.5×&lt;/code&gt;" multiplied into the ranking score at search time. Read the input again: "each returned memory", not each memory that helped. The one that came back wrong gets the same increment as the one that saved the session, because nobody has said yet which happened. It is a real loop with a real effect on rank, and it runs on attention rather than outcome. It is also "&lt;strong&gt;opt-in per project&lt;/strong&gt; and &lt;strong&gt;off by default&lt;/strong&gt;".&lt;/p&gt;

&lt;h2&gt;
  
  
  Three names, and an adjective is not one of them
&lt;/h2&gt;

&lt;p&gt;The rule breaks into three questions, and each answer is a name.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Where does the verdict land?&lt;/strong&gt; A field name, not "it is used to improve results".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What reads that field when ranking happens?&lt;/strong&gt; A term in the score, or a call parameter. If a vendor documents its ranking layers and your field is in none of them, that is an answer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What is the default?&lt;/strong&gt; A loop shipped switched off is not a running loop.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Four rows, and the third column is the one that decides
&lt;/h2&gt;

&lt;p&gt;Every quotation below is from a page or file the vendor published, read on 21 September 2026 and re-read on 25 September 2026.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;system&lt;/th&gt;
&lt;th&gt;where the verdict lands&lt;/th&gt;
&lt;th&gt;what reads it when ranking happens&lt;/th&gt;
&lt;th&gt;default&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cognee&lt;/td&gt;
&lt;td&gt;"specific entries" in a session, 1 to 5 points, via &lt;code&gt;cognee.session.add_feedback&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;"&lt;code&gt;feedback_weight&lt;/code&gt; on graph nodes and edges that were used during retrieval", blended by &lt;code&gt;feedback_influence&lt;/code&gt; on &lt;code&gt;recall&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;"&lt;code&gt;0.0&lt;/code&gt; (off) by default"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mem0&lt;/td&gt;
&lt;td&gt;a memory identifier, with &lt;code&gt;POSITIVE&lt;/code&gt;, &lt;code&gt;NEGATIVE&lt;/code&gt; or &lt;code&gt;VERY_NEGATIVE&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;not documented on the surfaces read: the ranking layers are written out, and the report is an input to none of them&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Letta&lt;/td&gt;
&lt;td&gt;an agent step, not a returned memory: &lt;code&gt;feedback: optional "positive" or "negative"&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;no page on the surfaces read names a consumer&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mnemoverse&lt;/td&gt;
&lt;td&gt;the memories a recall returned, by &lt;code&gt;memory_ids&lt;/code&gt;, with an &lt;code&gt;outcome&lt;/code&gt; from -1.0 to 1.0&lt;/td&gt;
&lt;td&gt;"Final score after valence modulation: &lt;code&gt;score * (1 + alpha * v)&lt;/code&gt;", in the public engine schema&lt;/td&gt;
&lt;td&gt;on: the default relevance order carries the valence term&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;An empty cell is no claim rather than a negative: that surface did not answer that column. Every absence is an absence on a named surface on a named date. The full table runs to eight systems: &lt;a href="https://mnemoverse.com/docs/library/outcome-reranking-agent-memory" rel="noopener noreferrer"&gt;Outcome feedback in agent memory: who reads the rating?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cognee is the one system there, ours aside, that publishes all three names, and its own example names the value where behaviour changes: "&lt;strong&gt;From &lt;code&gt;0.4&lt;/code&gt; on the up-rated context owns the answer.&lt;/strong&gt;"&lt;/p&gt;

&lt;p&gt;Mem0 is the opposite shape, and the more common one. Their guidance names the exact moment this article is about, send it "Immediately after memory retrieval when you can assess relevance". The effect is then stated as a result rather than a mechanism, "This feedback is used to improve the accuracy of the memories and search results". Their own ranking page says "&lt;code&gt;rerank&lt;/code&gt; is the only lever here that changes result &lt;em&gt;order&lt;/em&gt;". The bound: 47 paths in their machine-readable specification, 1 feedback route, a 3-field response, no route that reads feedback back. So Mem0 accepts the report and does not document a mechanism that reads it when ranking. Not that the report does nothing.&lt;/p&gt;

&lt;p&gt;One more input answers another question: Supermemory judges whether a fact its graph derived by itself is true, holding it "&lt;strong&gt;down-weighted in search&lt;/strong&gt; until confirmed". That is a verdict on a guess the engine made, not on how a recall turned out. So when a vendor answers "yes, we have feedback", the follow-up is which of four things they mean: reinforcement for having been returned, a verdict on a guess the engine made, an outcome with no named reader, on a memory or on a step, or an outcome that re-orders the next recall.&lt;/p&gt;

&lt;h2&gt;
  
  
  From an MCP client, the loop mostly does not close
&lt;/h2&gt;

&lt;p&gt;Your agent is handed a list of tools, and the loop closes only if one of them takes an outcome. An SDK page is not an answer.&lt;/p&gt;

&lt;p&gt;On the hosted Mem0 MCP page the tool table carries 11 tools and feedback is not among them. Cognee's default list is 5 tools, three for memory and two for reaching the rest by name, and its own page records &lt;code&gt;improve&lt;/code&gt; and &lt;code&gt;save_interaction&lt;/code&gt; among the tools removed "in every mode". Graphiti's built-in server carries 13 and none reports an outcome.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The caveat, without which that paragraph is false.&lt;/strong&gt; Cognee's same page says any registered tool can be invoked by name, and that the default list does not show all of them. So the claim is narrow: no tool in the list moves ranking. Not that an outcome cannot be reported.&lt;/p&gt;

&lt;p&gt;One row breaks the pattern. The Mnemoverse tool list carries the rating tool on both MCP paths, the npm package and the hosted connector. For memories in a shared room (Beta), the rating goes through the npm package, which takes the room's address as &lt;code&gt;domain&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our row, asked the same three questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Where the verdict lands.&lt;/strong&gt; The tool is &lt;code&gt;memory_feedback&lt;/code&gt;. It takes &lt;code&gt;memory_ids&lt;/code&gt;, the identifiers of the memories a &lt;code&gt;memory_read&lt;/code&gt; just returned, and an &lt;code&gt;outcome&lt;/code&gt; from -1.0 to 1.0. Its description tells the agent when to call it, "right after you act on (or reject) recalled memories", and what the call is for: "positive feedback raises a memory's ranking so it surfaces faster next time (across all of the user's tools), negative feedback lowers it so other memories out-rank it". The rating lands in each memory's &lt;code&gt;valence&lt;/code&gt;, which the public engine schema describes as "Outcome polarity [-1, +1]: positive = successful, negative = failed." Nothing is erased. An unhelpful memory is out-ranked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What reads it.&lt;/strong&gt; The public engine schema describes the relevance of a returned item as "Final score after valence modulation: &lt;code&gt;score * (1 + alpha * v)&lt;/code&gt;". The shape of the term is public and the value of &lt;code&gt;alpha&lt;/code&gt; is not, so this article claims no magnitude. The adjustment is applied before the cut, so the rating counts when the engine chooses what comes back, not only in the score printed beside it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The default.&lt;/strong&gt; Relevance order, so a rating affects the default read. Valence is one term among several: a recency boost and expansion along learned associations also move the order, and under a recency sort the date sets it.&lt;/p&gt;

&lt;p&gt;The loop is explicit. A rating is a call the agent makes after acting on a recall, and nothing runs on a background clock, which is why the call belongs in a standing instruction rather than in the hope that the agent remembers.&lt;/p&gt;

&lt;p&gt;One claim this article does not make about anyone, us included: that an outcome loop improves retrieval accuracy. This is about whether a documented mechanism exists and is reachable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four steps, and the control on each one
&lt;/h2&gt;

&lt;p&gt;Four steps, each with a control, because a zero without a control proves nothing.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Ask the server for its tool list&lt;/strong&gt; and look for one that takes the identifiers of the items just returned plus an outcome. &lt;em&gt;Control:&lt;/em&gt; ask for a package version that cannot exist. It must fail to start.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Find the field name the rating lands in&lt;/strong&gt;, a name rather than a sentence about improving results. &lt;em&gt;Control:&lt;/em&gt; request a page that cannot exist on that host. If it answers 200 with a not-found body, compare bodies, not status codes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Find the term in the ranking score that reads that field.&lt;/strong&gt; &lt;em&gt;Control:&lt;/em&gt; search the same corpus for a word that must be found and one that must not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read the default together with the capability, and check the path is reachable from your client.&lt;/strong&gt; &lt;em&gt;Control:&lt;/em&gt; call the tool and read what came back. A silent success that changes nothing is how you find out the call went elsewhere.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Run it against us first. Step 1 finds &lt;code&gt;memory_feedback&lt;/code&gt; taking &lt;code&gt;memory_ids&lt;/code&gt; and &lt;code&gt;outcome&lt;/code&gt;. Step 2 finds the field: &lt;code&gt;valence&lt;/code&gt;, "Outcome polarity [-1, +1]" in the public engine schema. Step 3 finds the term in the public engine schema. Step 4: the default order is relevance, and the tool is in the list on both MCP paths.&lt;/p&gt;

&lt;p&gt;Then run step 2 on the memory tool you have connected right now and tell me what you got: a field name, or a sentence about improving results.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I work on &lt;a href="https://mnemoverse.com" rel="noopener noreferrer"&gt;Mnemoverse&lt;/a&gt;, one of the memory layers this question is about. The &lt;a href="https://mnemoverse.com/docs/library/outcome-reranking-agent-memory" rel="noopener noreferrer"&gt;full review&lt;/a&gt;, read on named surfaces on a named date, with every source, is on our library, and the MCP server is open source (MIT): &lt;a href="https://github.com/mnemoverse/mcp-memory-server" rel="noopener noreferrer"&gt;github.com/mnemoverse/mcp-memory-server&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>mcp</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Do I need A2A if my agents already use MCP?</title>
      <dc:creator>Edward Izgorodin</dc:creator>
      <pubDate>Fri, 25 Sep 2026 04:48:00 +0000</pubDate>
      <link>https://dev.to/izgorodin/do-i-need-a2a-if-my-agents-already-use-mcp-37im</link>
      <guid>https://dev.to/izgorodin/do-i-need-a2a-if-my-agents-already-use-mcp-37im</guid>
      <description>&lt;p&gt;Your agents already call tools over MCP. A search tool, a database, a file system, maybe a memory server. Then someone on the team reads about A2A and asks whether you should be moving to it, and the conversation turns into which protocol is winning.&lt;/p&gt;

&lt;p&gt;It is the wrong race to watch. MCP and A2A answer different questions, and both projects say so in their own words. The question worth asking is narrower: is the thing on the other end of this connection a tool you call, or an agent that runs on its own and hands you back a result?&lt;/p&gt;

&lt;p&gt;One line to carry: MCP connects an agent to things it uses, A2A connects an agent to another agent that owns its own process, both can now keep a long piece of work open and pause it for input, and neither protocol carries what was learned into the next piece of work.&lt;/p&gt;

&lt;h2&gt;
  
  
  What each one connects
&lt;/h2&gt;

&lt;p&gt;MCP is the vertical one. An agent, through its client, discovers the tools, resources and prompts a server offers and calls them. The unit of work is a tool call or a resource read. The current revision of the specification lists "Stateless, self-contained requests" among its design points, and its &lt;a href="https://modelcontextprotocol.io/specification/2026-07-28/server/tools" rel="noopener noreferrer"&gt;tools page&lt;/a&gt; is blunt about it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;MCP has no protocol-level session, so a server cannot rely on implicit per-connection state to relate one tool call to the next.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A2A is the horizontal one. It is a protocol for agents to hand work to other agents without seeing inside them, and the &lt;a href="https://a2a-protocol.org/latest/specification/" rel="noopener noreferrer"&gt;A2A specification&lt;/a&gt; states that directly: agents coordinate "without needing access to each other's internal state, memory, or tools". Its building blocks are an Agent Card, a manifest of what an agent can do and where to reach it, and Tasks, which carry Messages and produce Artifacts. The specification defines JSON-RPC, gRPC and HTTP+JSON bindings. And a Task has a lifecycle, with states such as working, input-required and completed, at the core of the protocol.&lt;/p&gt;

&lt;p&gt;The lifecycle alone no longer separates them. Since the July revision an MCP tool call can come back asking for more input, and MCP's official tasks extension, opt-in on both client and server, gives long-running operations the same kind of states: working, input_required, completed, failed, cancelled. What separates the two is what sits on the other side. An MCP task is a long operation of a tool you called and understand. An A2A task is work handed to an agent you cannot see into, which decides for itself how to do it and hands you back a result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Both projects say they compose
&lt;/h2&gt;

&lt;p&gt;The A2A documentation has a page on exactly this question, &lt;a href="https://a2a-protocol.org/latest/topics/a2a-and-mcp/" rel="noopener noreferrer"&gt;A2A and MCP&lt;/a&gt;, and describes the two as addressing "distinct but highly complementary needs". It uses an auto repair shop to make the point: mechanics use their tools to do the work, and talk to each other to coordinate it.&lt;/p&gt;

&lt;p&gt;Google's launch post for A2A put it the same way from the start, describing A2A as a protocol that "complements Anthropic's Model Context Protocol (MCP), which provides helpful tools and context to agents" (&lt;a href="https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability/" rel="noopener noreferrer"&gt;Google Developers Blog&lt;/a&gt;). A 2025 analysis of integrating the two, &lt;a href="https://arxiv.org/abs/2505.03864" rel="noopener noreferrer"&gt;arXiv 2505.03864&lt;/a&gt;, frames them as horizontal and vertical integration standards respectively.&lt;/p&gt;

&lt;p&gt;In practice a system that uses both looks like this. A planner agent receives a request. It delegates part of it over A2A to a specialist agent that another team runs. The specialist calls its own tools over MCP, finishes the task, and returns an Artifact over A2A. The planner never sees the specialist's tools, and does not need to.&lt;/p&gt;

&lt;h2&gt;
  
  
  The test for any given connection
&lt;/h2&gt;

&lt;p&gt;Look at the other end of the connection you are about to build.&lt;/p&gt;

&lt;p&gt;If it is a tool, an API, a data source, a file system or a context provider, something that does what it is told and returns, MCP is the protocol. That covers most of what a single agent needs.&lt;/p&gt;

&lt;p&gt;If it is another agent, something with its own model, its own tools and its own judgement, that you want to hand a goal rather than a function call, A2A is the protocol. That matters most when the other agent is opaque to you: owned by another team or another company, running somewhere you do not control, and not something you want to import into your own process.&lt;/p&gt;

&lt;p&gt;And if all your agents live in one process inside one framework, you may not need A2A at all yet. Handing work between them is a function call in your own code. A2A earns its place at the boundary where you stop owning the other side.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where composing them costs something
&lt;/h2&gt;

&lt;p&gt;The same 2025 analysis lists the costs at the seam: matching what a task means to what a tool can do, security exposure that compounds when discovery and execution are chained across agents, and debugging tools that do not span both protocols.&lt;/p&gt;

&lt;p&gt;Security is the least settled of the three. A February 2026 threat-modelling study of MCP, A2A, Agora and the Agent Network Protocol, &lt;a href="https://arxiv.org/abs/2602.11327" rel="noopener noreferrer"&gt;arXiv 2602.11327&lt;/a&gt;, says in its abstract that standardised threat modelling is limited and that "no protocol-centric risk assessment framework has been established yet". When an A2A task on one agent triggers MCP tool calls on another, each protocol's security model covers its own hop, and a model for the chain is exactly what that study is trying to start.&lt;/p&gt;

&lt;p&gt;A2A is also not the only protocol on the horizontal axis. The same study covers Agora and the Agent Network Protocol, and the field has not settled on one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What neither of them does
&lt;/h2&gt;

&lt;p&gt;Both protocols move information. Neither keeps it.&lt;/p&gt;

&lt;p&gt;MCP can connect an agent to a memory tool, and the agent can read and write through it like any other tool, but the protocol defines nothing about memory itself, and two agents with their own MCP connections do not share anything unless the server behind them does. A2A deliberately keeps agents opaque to each other: a task carries messages and artifacts, and nothing in the protocol carries the context the two agents built into the next task. It survives only if one side, or something outside the protocol, decides to keep it.&lt;/p&gt;

&lt;p&gt;Research names the same gap. &lt;a href="https://arxiv.org/abs/2507.10562" rel="noopener noreferrer"&gt;SAMEP&lt;/a&gt; opens with the claim that current agent architectures "suffer from ephemeral memory limitations" and proposes a memory exchange layer built to work alongside MCP and A2A. A 2026 position paper on &lt;a href="https://arxiv.org/abs/2603.10062" rel="noopener noreferrer"&gt;multi-agent memory from a computer architecture perspective&lt;/a&gt; names two protocol gaps, "cache sharing across agents and structured memory access control", and calls multi-agent memory consistency the most pressing open challenge.&lt;/p&gt;

&lt;p&gt;That is not a defect in either protocol. It is a layer neither was designed to fill, and it is worth knowing before you expect A2A to give your agents a shared history. It gives them a shared task. The history is a separate decision.&lt;/p&gt;

&lt;p&gt;So the answer to the question in the title is usually no, not yet, and not instead. Keep MCP for everything an agent uses. Add A2A at the point where you delegate to an agent you do not own. Then decide separately what should outlive the task.&lt;/p&gt;

&lt;p&gt;Where in your system is the first agent you hand work to without owning it?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I work on &lt;a href="https://mnemoverse.com" rel="noopener noreferrer"&gt;Mnemoverse&lt;/a&gt;, a memory service for agents that connects over MCP, which is one way to fill the gap in the last section, so weigh that section accordingly. The longer version with every source is on our library, and the MCP server is open source (MIT): &lt;a href="https://github.com/mnemoverse/mcp-memory-server" rel="noopener noreferrer"&gt;github.com/mnemoverse/mcp-memory-server&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>agents</category>
      <category>architecture</category>
    </item>
    <item>
      <title>What are the best persistent memory APIs for AI agents?</title>
      <dc:creator>Edward Izgorodin</dc:creator>
      <pubDate>Thu, 24 Sep 2026 06:02:00 +0000</pubDate>
      <link>https://dev.to/izgorodin/what-are-the-best-persistent-memory-apis-for-ai-agents-4lln</link>
      <guid>https://dev.to/izgorodin/what-are-the-best-persistent-memory-apis-for-ai-agents-4lln</guid>
      <description>&lt;p&gt;You need your agent to remember things between sessions, you search for the best persistent memory API, and every list you find is a feature grid where every row has a tick in every column. Everyone has memory. Everyone has a graph. Everyone learns. Then you pick one, and three weeks later the question that actually mattered for your project turns out to be the one the grid never asked.&lt;/p&gt;

&lt;p&gt;So I stopped asking which system is best and asked the same six questions of six systems instead: Mem0, Letta, Zep with its open-source engine Graphiti, Cognee, Supermemory and Mnemoverse. The answers come from each vendor's own pages, read on 18 September 2026. I work on the last one, which is reason to weigh my reading of it harder.&lt;/p&gt;

&lt;p&gt;One line to carry: every one of these six says somewhere that it learns or improves, two of them name the step that turns a rating into ranking, five of them can be run with no vendor account, and the right choice depends on which of those you actually need.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. How is memory stored and found?
&lt;/h2&gt;

&lt;p&gt;Six different mechanisms, and the differences are real.&lt;/p&gt;

&lt;p&gt;Mem0 fuses several signals at read time, semantic similarity, keyword matching and an entity layer, with dates reordering results on its hosted platform. Letta's agent memory is Markdown files in a git repository, and its FAQ says plainly that "MemFS does not include a semantic or vector index by default". Zep and Graphiti build a temporal knowledge graph and retrieve through a hybrid of semantic search, keyword matching and graph traversal. Cognee runs a relational store, a vector store and an LLM-built graph together, and defines itself against plain retrieval: "Classic RAG embeds text chunks and retrieves by similarity." Supermemory describes a learning model on top of a graph of typed connections between facts.&lt;/p&gt;

&lt;p&gt;Ours stores memories written with the concepts you give them and retrieves by meaning. What makes it different is in the ranking: the reference documents the relevance of a read as "Final score (similarity * valence modulation)", and valence is the thing outcome feedback moves. More on that in question 3.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Does the same memory follow you between tools?
&lt;/h2&gt;

&lt;p&gt;All six document a route. Mem0 says its memory "persists across sessions, tools, and runs". Supermemory's MCP documentation says "Supermemory MCP gives every MCP-compatible assistant a shared memory layer". Cognee's MCP overview describes an API mode that can "Connect multiple clients to a shared knowledge graph". Zep's hosted product and Graphiti's MCP server both document editor setups. Ours is one account behind one key or OAuth, with Claude Code, Cursor, VS Code, Windsurf and Claude Desktop each documented in its own configuration shape.&lt;/p&gt;

&lt;p&gt;Letta answers differently. Its route into editors is its own Agent Client Protocol adapter, set up for Zed, JetBrains IDEs and Obsidian, plus an OpenAI-compatible API on its App Server. If your editor is Cursor or VS Code, check that route before assuming it.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Does anything change when a memory turns out to be wrong?
&lt;/h2&gt;

&lt;p&gt;This is the question the grids never ask, and it is where the six separate most.&lt;/p&gt;

&lt;p&gt;Mem0 has a feedback endpoint, and its page describes the effect this way: "Over time, Mem0 continuously learns from this feedback, refining its memory generation and search capabilities for better performance." That page names no step from the rating to the ranking. The same product documents a separate feature, memory decay, step by step and precisely: "Decay never zeroes a candidate out: at worst it scales its score by 0.3×". Both are on the hosted platform.&lt;/p&gt;

&lt;p&gt;Zep had fact ratings, a rule written up front and scored against every fact. Its February 2026 deprecation notice says: "Fact ratings are being deprecated entirely." Letta records a positive or negative signal per agent step, and the pages I read document no effect of it on memory. Supermemory keeps inferred memories low until they are approved, expires temporary facts and strengthens preferences with repetition, and on the pages I read documents nothing tied to whether a recalled memory actually helped.&lt;/p&gt;

&lt;p&gt;Two name the step. Cognee stores ratings and can apply them to ranking, and its release notes say the default influence "remains &lt;code&gt;0.0&lt;/code&gt;", so it is off until you turn it on. Ours takes a rating through a &lt;code&gt;memory_feedback&lt;/code&gt; call, and the valence it moves is part of the documented ranking formula above. The call is one of the tools in the same MCP connection the agent reads through, so the agent that saw the outcome can report it in the same session. In both, what moves the ranking is a rating actually being sent.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Can several agents or people share one pool?
&lt;/h2&gt;

&lt;p&gt;Six shapes. Mem0 has group chat with every write scoped by the caller. Letta has shared memory blocks. Zep has standalone graphs, and Graphiti gives each team its own graph namespace. Cognee treats an agent as its own permission-holding principal, and warns that its default embedded store is not for agents writing at once. Supermemory shares through spaces, where "Teammates collaborate within the spaces they are allowed to read or write." Ours calls them Rooms: a shared space separate from anyone's personal memory, with membership checked on every request.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. What does it cost to start, and can you run it yourself?
&lt;/h2&gt;

&lt;p&gt;Five of the six can run with no vendor account on your own machine, on a model you bring: Mem0's open-source library, Letta's local runtime, where "no Letta account is required", Cognee's core, Graphiti, and Supermemory's local binary. Zep's own local edition is gone, in its FAQ's words: "Zep Community Edition, which allows you to host Zep locally, is deprecated and no longer supported." Graphiti is the open path there.&lt;/p&gt;

&lt;p&gt;Letta puts a number on its hosted free tier: "Free plans are limited to 3 stateful agents."&lt;/p&gt;

&lt;p&gt;Ours has a free tier and is managed by default: the service you connect to is the product, and we run the engine for you. Enterprise adds dedicated instances that we provide and operate, a choice of data residency, and self-hosting by agreement when security or compliance requirements call for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. What does each one tell you about its own limits?
&lt;/h2&gt;

&lt;p&gt;The useful ones say it themselves. Mem0's graph documentation says its entity layer does not assign typed relationships between entities. Letta's FAQ says there is no vector index by default. Cognee says its embedded store is not for concurrent agents. Zep says fact ratings are going away. Ours puts its terms in the same places: the pricing page says who runs the engine and when you can run it yourself, and the reference gives the ranking formula that a rating moves.&lt;/p&gt;

&lt;h2&gt;
  
  
  The six answers in one table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;memory&lt;/th&gt;
&lt;th&gt;across tools&lt;/th&gt;
&lt;th&gt;outcome feedback&lt;/th&gt;
&lt;th&gt;sharing&lt;/th&gt;
&lt;th&gt;run it yourself&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mem0&lt;/td&gt;
&lt;td&gt;semantic, keyword and entity signals fused&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;endpoint, no step named; decay specified&lt;/td&gt;
&lt;td&gt;group chat, caller-scoped&lt;/td&gt;
&lt;td&gt;yes, open-source library&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Letta&lt;/td&gt;
&lt;td&gt;Markdown files in git, no vector index by default&lt;/td&gt;
&lt;td&gt;via its ACP adapter and an OpenAI-compatible API&lt;/td&gt;
&lt;td&gt;per-step signal, no memory effect documented&lt;/td&gt;
&lt;td&gt;shared memory blocks&lt;/td&gt;
&lt;td&gt;yes, local runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zep / Graphiti&lt;/td&gt;
&lt;td&gt;temporal knowledge graph&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;fact ratings, being deprecated&lt;/td&gt;
&lt;td&gt;standalone graphs; team namespaces&lt;/td&gt;
&lt;td&gt;Graphiti only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cognee&lt;/td&gt;
&lt;td&gt;relational, vector and LLM-built graph&lt;/td&gt;
&lt;td&gt;yes, API mode&lt;/td&gt;
&lt;td&gt;step named, off by default&lt;/td&gt;
&lt;td&gt;principals and permissions&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Supermemory&lt;/td&gt;
&lt;td&gt;learning model over a typed graph&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;review, expiry, repetition&lt;/td&gt;
&lt;td&gt;spaces&lt;/td&gt;
&lt;td&gt;yes, local binary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mnemoverse&lt;/td&gt;
&lt;td&gt;concepts plus a ranking that feedback moves&lt;/td&gt;
&lt;td&gt;yes, one account&lt;/td&gt;
&lt;td&gt;step named, an MCP tool the agent calls&lt;/td&gt;
&lt;td&gt;Rooms&lt;/td&gt;
&lt;td&gt;Enterprise, by agreement&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  So which is best?
&lt;/h2&gt;

&lt;p&gt;The one whose answer to your hardest question is the one you need. If you must run it yourself, five document a local path with no vendor account, and ours offers it on Enterprise by agreement. If you need wrong memories to lose ground over time, two name how that happens, and in both it depends on a rating actually being sent. If your agents live in Letta or your editor speaks ACP, Letta's route is built for you and the others are not. Picking by the number of ticks in a grid is how you end up re-asking this question in three weeks.&lt;/p&gt;

&lt;p&gt;Which of the six questions turned out to matter most after you had already chosen?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I work on &lt;a href="https://mnemoverse.com" rel="noopener noreferrer"&gt;Mnemoverse&lt;/a&gt;, one of the six systems above, so weigh the argument accordingly. The full comparison with every source page is on our library, and the MCP server is open source (MIT): &lt;a href="https://github.com/mnemoverse/mcp-memory-server" rel="noopener noreferrer"&gt;github.com/mnemoverse/mcp-memory-server&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>llm</category>
      <category>mcp</category>
    </item>
    <item>
      <title>Which MCP memory servers can I set up with one npx command?</title>
      <dc:creator>Edward Izgorodin</dc:creator>
      <pubDate>Wed, 23 Sep 2026 05:11:00 +0000</pubDate>
      <link>https://dev.to/izgorodin/which-mcp-memory-servers-can-i-set-up-with-one-npx-command-163</link>
      <guid>https://dev.to/izgorodin/which-mcp-memory-servers-can-i-set-up-with-one-npx-command-163</guid>
      <description>&lt;p&gt;You want memory in your coding agent from the terminal, today, without standing up a database. So you search for a memory MCP server you can start with npx, find vendor pages that mention npx, paste the line, and something happens. Sometimes a server starts. Sometimes a config file changes and nothing starts at all. Once, a different protocol comes up.&lt;/p&gt;

&lt;p&gt;That is the practical version of a question people ask about this category: which MCP memory servers can I set up with one npx command? I read six vendors' own setup documentation to answer it, Mem0, Letta, Zep with Graphiti, Cognee, Supermemory and Mnemoverse. I work on the last one, and on this particular question it is the one that answers yes, so its limits go in first rather than last.&lt;/p&gt;

&lt;p&gt;One line to carry: across six memory vendors npx does five different jobs, and only one of them is a client starting that vendor's own MCP server, so read the sentence around npx, never the word alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  What npx starts, vendor by vendor
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;It starts the vendor's MCP server as a local process.&lt;/strong&gt; This is the case people assume, and in this set it is ours. The line each client runs is &lt;code&gt;npx -y @mnemoverse/mcp-memory-server@latest&lt;/code&gt;, with one environment variable carrying an API key, followed by a client restart and a test write and read. Three limits are worth stating plainly. The key comes from a free signup, so there is an account before there is a command. Node has to be present every session, because the client spawns that line each time rather than once. And the process on your machine is the MCP server, not the memory: the memories are stored on our service and reached over HTTPS. There is also a path with no npx at all, a remote URL with an OAuth sign-in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It writes configuration for a server that runs somewhere else.&lt;/strong&gt; Mem0 has an npx command, and its &lt;a href="https://docs.mem0.ai/platform/mem0-mcp" rel="noopener noreferrer"&gt;MCP documentation&lt;/a&gt; is exact about what it does:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Nothing runs on your machine: the server is hosted by Mem0, and your client connects to it over HTTPS.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The same page adds that "The server is authenticated, so connecting is not enough on its own." Before the command it lists an account, a key, and Node for npx. The npx line is a configuration step, and the useful thing is that the vendor says so in one sentence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It stands up a self-hosted server, which is a different product from the hosted MCP.&lt;/strong&gt; Supermemory's &lt;a href="https://supermemory.ai/docs/self-hosting" rel="noopener noreferrer"&gt;self-hosting page&lt;/a&gt; runs &lt;code&gt;npx supermemory local&lt;/code&gt; and describes it as "One binary, zero config", adding "The only thing you bring is a model". That is a real server on your machine. It is not the hosted memory MCP, whose &lt;a href="https://supermemory.ai/docs/supermemory-mcp/setup" rel="noopener noreferrer"&gt;setup page&lt;/a&gt; has a URL and no command:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Supermemory MCP uses OAuth. Your client opens the authorization page so you can sign in and approve access. No API key or custom header is required.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Supermemory's other npx commands are first-party too, a CLI listed for "Setup, smoke tests, agent-driven integration" and a skill installer, and neither of them launches the hosted memory MCP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It starts an agent over a different protocol.&lt;/strong&gt; Letta's &lt;a href="https://docs.letta.com/self-hosting" rel="noopener noreferrer"&gt;self-hosting guide&lt;/a&gt; installs its server with &lt;code&gt;npm install -g @letta-ai/letta-code&lt;/code&gt; and starts it with &lt;code&gt;letta server&lt;/code&gt;, a global install and a named command rather than npx. Where npx does appear in Letta's docs, on its &lt;a href="https://docs.letta.com/platform/acp" rel="noopener noreferrer"&gt;ACP page&lt;/a&gt;, it launches &lt;code&gt;@letta-ai/letta-acp&lt;/code&gt;, an adapter that lets an editor drive a Letta agent over the Agent Client Protocol. A useful command, and not MCP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It runs a third-party bridge to a server that is already running.&lt;/strong&gt; The README of &lt;a href="https://github.com/getzep/graphiti/blob/main/mcp_server/README.md" rel="noopener noreferrer"&gt;Graphiti's MCP server&lt;/a&gt; mentions npx in exactly one context:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Graphiti MCP Server uses HTTP transport (at endpoint &lt;code&gt;/mcp/&lt;/code&gt;). Claude Desktop does not natively support HTTP transport, so you'll need to use a gateway like &lt;code&gt;mcp-remote&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The npx line there launches &lt;code&gt;mcp-remote&lt;/code&gt;, a proxy, after you have started Graphiti's server some other way. Cognee's MCP pages use the same bridge in the same place, on the Claude Desktop page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And for some, npx is not part of setup at all.&lt;/strong&gt; Cognee's three MCP setup pages, the overview, the quickstart and the local setup guide, contain the string npx zero times on 18 September, while the same pages name both Docker and uvx, so the search itself works on them. Its server starts from a container, uvx, pip or source. Zep's hosted memory server, per its &lt;a href="https://help.getzep.com/memory-mcp-server/connect" rel="noopener noreferrer"&gt;connect page&lt;/a&gt;, is a URL behind an organisation sign-in, with no npx on that page.&lt;/p&gt;

&lt;h2&gt;
  
  
  The table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;vendor&lt;/th&gt;
&lt;th&gt;what the npx line does&lt;/th&gt;
&lt;th&gt;what starts their memory server&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mnemoverse&lt;/td&gt;
&lt;td&gt;starts the MCP server process, key required&lt;/td&gt;
&lt;td&gt;the npx line itself; or a remote URL with OAuth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mem0&lt;/td&gt;
&lt;td&gt;writes client config for the hosted server&lt;/td&gt;
&lt;td&gt;nothing local for the hosted MCP; a self-run REST server uses make and Docker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Supermemory&lt;/td&gt;
&lt;td&gt;stands up a self-hosted server without the hosted MCP&lt;/td&gt;
&lt;td&gt;hosted MCP: a URL and an OAuth sign-in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Letta&lt;/td&gt;
&lt;td&gt;launches &lt;code&gt;letta-acp&lt;/code&gt;, an agent over ACP&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;npm install -g&lt;/code&gt; then &lt;code&gt;letta server&lt;/code&gt;, the desktop app, or a container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Graphiti&lt;/td&gt;
&lt;td&gt;launches the &lt;code&gt;mcp-remote&lt;/code&gt; bridge for Claude Desktop&lt;/td&gt;
&lt;td&gt;Docker Compose or a Python toolchain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cognee&lt;/td&gt;
&lt;td&gt;none on its three MCP setup pages; the &lt;code&gt;mcp-remote&lt;/code&gt; bridge on its Claude Desktop page&lt;/td&gt;
&lt;td&gt;Docker, uvx, pip or source&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Zep's hosted memory server is left out of the table because the npx question does not arise for it: it is a URL behind a sign-in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the same word covers five jobs
&lt;/h2&gt;

&lt;p&gt;npx promises one thing: fetch a package and run its binary, now, with no separate install. What that binary is, is entirely up to the package. A config writer, a self-hosted server, an ACP adapter, a proxy and an MCP server are all binaries in npm packages, and all of them start with the same three letters. The word carries no information about which of these you are about to run. The vendor's sentence next to it does.&lt;/p&gt;

&lt;p&gt;There is also a trade hidden in the case that does start a server locally. A process your client spawns every session needs Node every session, and it goes wherever your client goes. A hosted URL needs nothing installed and goes wherever the network does. Neither is simply easier. It depends on whether the machine running your agent can run Node, and on whether your security team prefers a local process or an outbound HTTPS call.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check your own setup in two minutes
&lt;/h2&gt;

&lt;p&gt;Open the vendor's own setup page, not a blog post about it, and search the page for npx. If it is there, read the sentence around it and answer three things before you paste anything: what process starts, on which machine, and where the memories end up. If npx is not there, the real path is usually a container command, a Python toolchain or a hosted URL one section away, and often a second path sits on another page, so search the whole documentation before you decide a vendor has no local option.&lt;/p&gt;

&lt;p&gt;Which of these five did your first npx line turn out to be?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I work on &lt;a href="https://mnemoverse.com" rel="noopener noreferrer"&gt;Mnemoverse&lt;/a&gt;, one of the six vendors above and the one whose npx line starts its MCP server, so weigh the argument accordingly. The full comparison with every source is on our library, and the MCP server is open source (MIT): &lt;a href="https://github.com/mnemoverse/mcp-memory-server" rel="noopener noreferrer"&gt;github.com/mnemoverse/mcp-memory-server&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>node</category>
      <category>productivity</category>
    </item>
    <item>
      <title>What are the standard tools exposed by an MCP memory server?</title>
      <dc:creator>Edward Izgorodin</dc:creator>
      <pubDate>Tue, 22 Sep 2026 04:45:00 +0000</pubDate>
      <link>https://dev.to/izgorodin/what-are-the-standard-tools-exposed-by-an-mcp-memory-server-pk7</link>
      <guid>https://dev.to/izgorodin/what-are-the-standard-tools-exposed-by-an-mcp-memory-server-pk7</guid>
      <description>&lt;p&gt;You wire a memory server into your editor, it works, and you write the obvious instruction into your agent's setup: call &lt;code&gt;add_memory&lt;/code&gt; when the user states a preference, call the search tool before answering anything about the project. A month later you try a different memory server, because the first one was slow or expensive or went down, and the instruction stops working. The tool names are different. Worse, one of them has a tool with the same name as before, and it does something else.&lt;/p&gt;

&lt;p&gt;So the question people ask sounds reasonable: what are the standard tools an MCP memory server exposes? The answer is that there are none. Not few, none. The protocol standardises how a client finds a tool and calls it, and every tool name after that is the server's own invention.&lt;/p&gt;

&lt;p&gt;One line to carry: MCP defines &lt;code&gt;tools/list&lt;/code&gt; and &lt;code&gt;tools/call&lt;/code&gt;, the word memory appears once in its schema, in a comment, and three popular memory servers each ship a tool called &lt;code&gt;add_memory&lt;/code&gt; that does a different thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the protocol actually pins down
&lt;/h2&gt;

&lt;p&gt;A server can offer three kinds of thing, per the &lt;a href="https://modelcontextprotocol.io/specification/2026-07-28/architecture" rel="noopener noreferrer"&gt;architecture page&lt;/a&gt;: resources, prompts and tools. There is no memory primitive among them.&lt;/p&gt;

&lt;p&gt;You can check the rest yourself in under a minute. The &lt;a href="https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/2026-07-28/schema.ts" rel="noopener noreferrer"&gt;TypeScript schema&lt;/a&gt; is the protocol's source of truth. Search it for &lt;code&gt;memory&lt;/code&gt; and you find it once, in the comment on a boolean hint, where a memory tool is the example of a closed world:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For example, the world of a web search tool is open, whereas that of a memory tool is not.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Search it for &lt;code&gt;persistence&lt;/code&gt;, &lt;code&gt;storage&lt;/code&gt;, &lt;code&gt;retrieval&lt;/code&gt;, &lt;code&gt;vector&lt;/code&gt; or &lt;code&gt;session&lt;/code&gt; and you get nothing. The session part is recent. The current revision lists "Stateless, self-contained requests" among its design points, and the &lt;a href="https://modelcontextprotocol.io/specification/2026-07-28/server/tools" rel="noopener noreferrer"&gt;tools page&lt;/a&gt; says it without qualification:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;MCP has no protocol-level session.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What the protocol does standardise is the plumbing. A client asks &lt;code&gt;tools/list&lt;/code&gt; and gets back definitions. The model picks one, the client sends &lt;code&gt;tools/call&lt;/code&gt;, the result comes back. That is the whole contract as far as memory is concerned: two methods, and a shape for the answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Five servers, five vocabularies
&lt;/h2&gt;

&lt;p&gt;Here is what that looks like in practice. Tool names as registered in each server's source or listed in its own documentation, read on 18 September 2026. The last row is ours, since I work on it.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;server&lt;/th&gt;
&lt;th&gt;write&lt;/th&gt;
&lt;th&gt;search&lt;/th&gt;
&lt;th&gt;tools in total&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MCP reference memory server&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;create_entities&lt;/code&gt;, &lt;code&gt;add_observations&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;search_nodes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mem0, hosted MCP&lt;/td&gt;
&lt;td&gt;&lt;code&gt;add_memory&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;search_memories&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Graphiti MCP server&lt;/td&gt;
&lt;td&gt;&lt;code&gt;add_memory&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;search_nodes&lt;/code&gt;, &lt;code&gt;search_memory_facts&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Supermemory MCP&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;add_memory&lt;/code&gt;, &lt;code&gt;save-memory&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;search_memory&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mnemoverse, 0.10.1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;memory_write&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;memory_read&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Five servers, five different names for writing, and not even one naming convention: Supermemory registers &lt;code&gt;add_memory&lt;/code&gt; with an underscore and &lt;code&gt;save-memory&lt;/code&gt; with a hyphen in the same server.&lt;/p&gt;

&lt;p&gt;The collisions are more instructive than the differences. Three of the five have a tool called &lt;code&gt;add_memory&lt;/code&gt;, and the name is the only thing they share. Mem0's documentation describes its &lt;code&gt;add_memory&lt;/code&gt; as "Save text or conversation history for a user/agent". Graphiti's docstring opens with "Add an episode to memory" and says the function "returns immediately and processes the episode addition in the background". Supermemory's description begins "Add (save) or forget a memory in the user's ACTIVE space", and its input takes an &lt;code&gt;action&lt;/code&gt; of &lt;code&gt;save&lt;/code&gt; or &lt;code&gt;forget&lt;/code&gt;, so the tool named add can also remove.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;search_nodes&lt;/code&gt; repeats the pattern. The reference server's version is a substring test over names, types and observations. Graphiti's is described as "Search for nodes (entities) in the graph memory" and takes group filters, a node cap with a default of 10, and entity type filters. Same name, unrelated mechanisms.&lt;/p&gt;

&lt;p&gt;If your agent instructions name a tool, they are written for one server. That is the practical cost of there being no standard, and it is invisible until you switch.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the model actually sees when it chooses
&lt;/h2&gt;

&lt;p&gt;A tool definition has a name, an input schema, and a description. The schema's own comment on that description field is worth reading in full:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This can be used by clients to improve the LLM's understanding of available tools. It can be thought of like a "hint" to the model.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The quotes around hint are theirs. The field is declared &lt;code&gt;description?: string&lt;/code&gt;, so it is optional: a server can register a tool with a name and an input schema and nothing that says when to use it. Whatever the model knows about when to call your memory tool, it learned from a sentence the server's author wrote, sitting next to every other tool's sentence.&lt;/p&gt;

&lt;h2&gt;
  
  
  How a server decides what comes back
&lt;/h2&gt;

&lt;p&gt;The protocol does not say. It defines no relevance model, no ordering, no opinion about a good answer, so each server invents one, and this is where they genuinely differ.&lt;/p&gt;

&lt;p&gt;The reference server is honest about its choice. In &lt;a href="https://github.com/modelcontextprotocol/servers/tree/main/src/memory" rel="noopener noreferrer"&gt;&lt;code&gt;src/memory/index.ts&lt;/code&gt;&lt;/a&gt; the search lowercases the query and checks whether it appears in an entity's name, type or observations, and the comment above the function reads:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;// Very basic search function&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On the current main branch the file has no ranking, no scoring, no embeddings and no timestamps, so nothing in a stored record says when a fact was written. It keeps everything in one newline-delimited JSON file on disk. And it registers no prompts: the instructions for when to recall and what to save live in its README as text you paste into your client, opening with "Follow these steps for each interaction:". The server stores and returns. The remembering is done by the model, following text a person pasted.&lt;/p&gt;

&lt;p&gt;That is not a criticism. It is a reference implementation and it says so. It is worth knowing before you read "MCP memory server" as though the phrase implied a retrieval system.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the answer reaches the model
&lt;/h2&gt;

&lt;p&gt;As text. A tool result is a list of content blocks, and the schema describes a text block in one line:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Text provided to or from an LLM.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The result then goes back into the conversation as an ordinary message. Anthropic's &lt;a href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls" rel="noopener noreferrer"&gt;tool-call documentation&lt;/a&gt; states there is no special channel for it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Unlike APIs that separate tool use or use special roles like &lt;code&gt;tool&lt;/code&gt; or &lt;code&gt;function&lt;/code&gt;, the Claude API integrates tools directly into the &lt;code&gt;user&lt;/code&gt; and &lt;code&gt;assistant&lt;/code&gt; message structure.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;However clever a memory server's ranking is, what arrives is a string in the same window as everything else, competing for the same attention and subject to the same limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to ask instead of which tools
&lt;/h2&gt;

&lt;p&gt;Since the names carry no guarantee, the questions that separate one memory server from another are the ones the protocol leaves open. What gets admitted, and who decides a thing is worth storing. What comes back for a given question, and in what order. What happens when two clients write at once. Whether you can get your data out. And one more that follows from the table above: does the server's &lt;code&gt;tools/list&lt;/code&gt; stay the same between versions, or will the names your instructions depend on move under you.&lt;/p&gt;

&lt;p&gt;The cheapest check is to read the list yourself. Connect the server, ask for &lt;code&gt;tools/list&lt;/code&gt;, and read every description the way the model will: as the only thing it knows about when to call that tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  When you do not need one
&lt;/h2&gt;

&lt;p&gt;If nothing needs to survive the conversation, you do not need a memory server, because surviving the conversation is the entire function. If the facts already fit in the window, putting them there is more reliable, because a pasted fact is always present and a tool call is a decision the model makes from a description string. And if what you want is a JSON file with nine tools on it, the reference server is exactly that, and it is worth reading before you buy anything, including from us.&lt;/p&gt;

&lt;p&gt;What does your agent setup name by tool, and has a server switch ever broken it?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I work on &lt;a href="https://mnemoverse.com" rel="noopener noreferrer"&gt;Mnemoverse&lt;/a&gt;, one of the five servers in the table, so weigh the argument accordingly. The longer walk-through of the protocol, with every source, is on our library, and the MCP server is open source (MIT): &lt;a href="https://github.com/mnemoverse/mcp-memory-server" rel="noopener noreferrer"&gt;github.com/mnemoverse/mcp-memory-server&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>llm</category>
      <category>agents</category>
    </item>
    <item>
      <title>Why does my AI coding assistant ignore my CLAUDE.md, AGENTS.md and Cursor rules?</title>
      <dc:creator>Edward Izgorodin</dc:creator>
      <pubDate>Mon, 21 Sep 2026 03:56:00 +0000</pubDate>
      <link>https://dev.to/izgorodin/why-does-my-ai-coding-assistant-ignore-my-claudemd-agentsmd-and-cursor-rules-1h3o</link>
      <guid>https://dev.to/izgorodin/why-does-my-ai-coding-assistant-ignore-my-claudemd-agentsmd-and-cursor-rules-1h3o</guid>
      <description>&lt;p&gt;You put one line in &lt;code&gt;CLAUDE.md&lt;/code&gt;: never push directly to main. You ask the assistant what the project rules are, and it reads that line back to you word for word. Twenty minutes into a refactor it runs &lt;code&gt;git push origin main&lt;/code&gt;. So you write the rule in capitals, add IMPORTANT in front of it, copy it into &lt;code&gt;AGENTS.md&lt;/code&gt; for good measure, and a week later it happens again.&lt;/p&gt;

&lt;p&gt;The question people type after that is some version of how to stop an AI coding assistant from forgetting project rules across sessions. It did not forget. The rule arrived, the model read it, and nothing in a rules file was ever going to stop the push. That is not my reading of the situation. All three vendors say it on their own pages, and one of them tells you what to use instead.&lt;/p&gt;

&lt;p&gt;One line to carry: a rules file asks, a permission rule or a hook refuses, and even the refusing layer has a form it does not catch and a way to fail open, so the rule that must hold every time belongs in the last layer, the one that never reads your prose at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three files, three vendors, the same sentence
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;, from Anthropic's &lt;a href="https://code.claude.com/docs/en/memory" rel="noopener noreferrer"&gt;memory documentation&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Claude treats them as context, not enforced configuration. To block an action regardless of what Claude decides, use a PreToolUse hook instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;, from &lt;a href="https://agents.md" rel="noopener noreferrer"&gt;its own page&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A simple, open format for guiding coding agents&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and a line below that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Think of AGENTS.md as a README for agents: a dedicated, predictable place to provide the context and instructions to help AI coding agents work on your project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cursor rules, from &lt;a href="https://cursor.com/docs/context/rules" rel="noopener noreferrer"&gt;Cursor's documentation&lt;/a&gt;, which is the most mechanical of the three:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Large language models don't retain memory between completions. Rules provide persistent, reusable context at the prompt level. When applied, rule contents are included at the start of the model context.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The same Cursor page treats the formats as interchangeable: "If you prefer plain markdown, use AGENTS.md instead."&lt;/p&gt;

&lt;p&gt;Context. Guiding. A README. Included at the start of the model context. None of the three claims the file binds anything, and none of them is hiding it. The idea that a rules file enforces a rule is something readers bring to it, because the file is called rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the rule actually reaches the model
&lt;/h2&gt;

&lt;p&gt;Anthropic's memory page answers this directly:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;CLAUDE.md content is delivered as a user message after the system prompt, not as part of the system prompt itself. Claude reads it and tries to follow it, but there's no guarantee of strict compliance&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A user message. Not a configuration value, not a system instruction. Your rule sits in the conversation next to your actual task, next to whatever the last tool call returned, next to the file somebody else committed, and it competes with all of it on the same terms.&lt;/p&gt;

&lt;p&gt;One person who put mitmproxy between the client and the model, while trying to understand why adherence to their &lt;code&gt;CLAUDE.md&lt;/code&gt; had slipped, &lt;a href="https://news.ycombinator.com/item?id=49055504" rel="noopener noreferrer"&gt;reported on Hacker News&lt;/a&gt; what the file was wrapped in at the time:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;IMPORTANT: this context may or may not be relevant to your tasks. You should not respond to this context unless it is highly relevant to your task.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The comment is from July 2026 and describes what they saw about six months earlier, so it is their observation at that time, not a claim about today's build. It does not need to be current to make the point, because the vendor's own sentence above already says the same thing in plainer words: the model reads the file and tries.&lt;/p&gt;

&lt;p&gt;The people who meet this in practice describe it the same way. From &lt;a href="https://github.com/anthropics/claude-code/issues/87825" rel="noopener noreferrer"&gt;claude-code#87825&lt;/a&gt;, open at the time of writing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The rule was in memory. Claude had read it. Claude pushed directly to main three times.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And from &lt;a href="https://github.com/anthropics/claude-code/issues/75334" rel="noopener noreferrer"&gt;claude-code#75334&lt;/a&gt;, a different person: "Memories are written but not acted on."&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading and obeying are two different failures
&lt;/h2&gt;

&lt;p&gt;Before changing anything, find out which one you have, because they have unrelated fixes.&lt;/p&gt;

&lt;p&gt;First, ask the assistant to state your rule back. If it cannot, the rule is not arriving, and the fix is about delivery: wrong file name, wrong directory, wrong scope, a setting that excludes it. Nothing further matters until this passes.&lt;/p&gt;

&lt;p&gt;Second, only if the first passed, put the assistant in the situation the rule governs and watch. If it states the rule correctly and then does the thing anyway, delivery works and nothing is enforcing. That outcome is not a malfunction. There is no obedience layer to malfunction, only a sentence in a context window and a model deciding.&lt;/p&gt;

&lt;p&gt;If the decision has to go your way every time, the decision cannot be the mechanism.&lt;/p&gt;

&lt;h2&gt;
  
  
  What refuses, in the vendors' own words
&lt;/h2&gt;

&lt;p&gt;Anthropic's &lt;a href="https://code.claude.com/docs/en/permissions" rel="noopener noreferrer"&gt;permissions page&lt;/a&gt; draws the line in one note:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Permission rules are enforced by Claude Code, not by the model. Instructions in your prompt or &lt;code&gt;CLAUDE.md&lt;/code&gt; shape what Claude tries to do, but they don't change what Claude Code allows.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The rule that stops the push in the opening scene is a deny rule in &lt;code&gt;.claude/settings.json&lt;/code&gt;. This is the shape the same page uses for its own example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deny"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(git push *)"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two details on that page change how you should write these. A deny rule wins over everything else: "An allow rule can't carve an exception out of a deny rule." And a deny rule behaves differently depending on its shape. A bare tool name "removes the tool from Claude's context entirely, so Claude never sees it", while a scoped one like &lt;code&gt;Bash(rm *)&lt;/code&gt; "leaves the tool available and blocks matching calls when Claude attempts them". The first is not a refusal at all. The model is never shown the tool, so there is nothing to refuse.&lt;/p&gt;

&lt;h2&gt;
  
  
  The refusing layer has its own gaps
&lt;/h2&gt;

&lt;p&gt;This is the part most advice stops before, and the vendors document it themselves.&lt;/p&gt;

&lt;p&gt;A Bash deny rule matches the command text, not the program. The permissions page says a deny or ask rule "covers the invocation Claude usually produces and isn't a security boundary around the program", and its own table lists what &lt;code&gt;Bash(git push *)&lt;/code&gt; does not stop: &lt;code&gt;git -C . push origin main&lt;/code&gt;, &lt;code&gt;git -c push.default=current push origin main&lt;/code&gt;, &lt;code&gt;git 'push' origin main&lt;/code&gt;. For enforcement that does not depend on the command text, the page points elsewhere: "For filesystem and network enforcement that doesn't depend on the command text, use sandboxing."&lt;/p&gt;

&lt;p&gt;A hook sees the full command and runs your own logic on it. In Claude Code that is a &lt;code&gt;PreToolUse&lt;/code&gt; hook, written as the &lt;a href="https://code.claude.com/docs/en/hooks-guide" rel="noopener noreferrer"&gt;hooks guide&lt;/a&gt; shows, and the permissions page states its precedence: "A hook that exits with code 2 stops the tool call before permission rules are evaluated". In Cursor it is a &lt;code&gt;beforeShellExecution&lt;/code&gt; hook in &lt;code&gt;hooks.json&lt;/code&gt;, and &lt;a href="https://cursor.com/docs/agent/hooks" rel="noopener noreferrer"&gt;Cursor's hooks page&lt;/a&gt; lists exit code 2 as "Block the action".&lt;/p&gt;

&lt;p&gt;And a hook can fail open. Cursor states it plainly: "Crashes, timeouts, and non-zero exit codes other than 2 fail open by default: Cursor logs the failure and allows the action through." It offers &lt;code&gt;failClosed: true&lt;/code&gt; to change that. Claude Code's guide describes the same default in more steps: for any exit code other than 0 and 2, with plain text or nothing on stdout, "the action proceeds as a non-blocking error." The quietest failure does not even reach that branch. The guide's example scripts read the command with &lt;code&gt;jq&lt;/code&gt;. On a machine where &lt;code&gt;jq&lt;/code&gt; is not installed that line yields an empty string, nothing matches, the script reaches &lt;code&gt;exit 0&lt;/code&gt;, and the command goes on as if the hook were not there.&lt;/p&gt;

&lt;p&gt;None of this makes hooks or deny rules weak. It means each layer has a known gap, and it is worth knowing which gap you are standing in.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;layer&lt;/th&gt;
&lt;th&gt;what it does&lt;/th&gt;
&lt;th&gt;what gets past it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;rules file: &lt;code&gt;CLAUDE.md&lt;/code&gt;, &lt;code&gt;AGENTS.md&lt;/code&gt;, Cursor rules&lt;/td&gt;
&lt;td&gt;puts your sentence in the context window&lt;/td&gt;
&lt;td&gt;the model deciding otherwise&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;permission deny rule&lt;/td&gt;
&lt;td&gt;refuses the command text it matches&lt;/td&gt;
&lt;td&gt;the same program invoked another way&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hook: &lt;code&gt;PreToolUse&lt;/code&gt;, &lt;code&gt;beforeShellExecution&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;runs your code on the full command first&lt;/td&gt;
&lt;td&gt;a hook that crashes or times out, by default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;the system the action lands on: a protected branch, file permissions, a sandbox&lt;/td&gt;
&lt;td&gt;refuses whatever sent the request&lt;/td&gt;
&lt;td&gt;nothing the assistant writes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For never push to main, the last row is the one that holds every time: a protected branch on the hosting side, set so that nobody can bypass it, refuses the push whether it came from the assistant, from a teammate, or from you at two in the morning. The rules file is still worth keeping, because it makes the assistant try the right thing first and saves you the refused attempt. It just cannot be the thing you rely on.&lt;/p&gt;

&lt;h2&gt;
  
  
  When a rules file is exactly the right tool
&lt;/h2&gt;

&lt;p&gt;If what you need is the same instructions in front of the model every session, a file in your repository does that. It costs nothing, you can read it, your team reviews it in a pull request, and it is already installed. For style, conventions, which test command to run and where things live, that is the whole answer and there is nothing to buy.&lt;/p&gt;

&lt;p&gt;If what you need is for something to not happen, no rules file and no memory layer will do it, whoever sells it. Storage is not enforcement. Use the layer that runs outside the model.&lt;/p&gt;

&lt;p&gt;A memory layer is for a narrower job than either: facts that accumulate over time, that nobody wrote down by hand, and that need to be present in a session you have not started yet, sometimes in a tool you were not using when they were learned. That is a real problem, and it is a different one from the push in the opening scene.&lt;/p&gt;

&lt;p&gt;Which rule in your &lt;code&gt;CLAUDE.md&lt;/code&gt; or &lt;code&gt;AGENTS.md&lt;/code&gt; have you watched get read back correctly and broken anyway, and which of the four layers finally stopped it?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I work on &lt;a href="https://mnemoverse.com" rel="noopener noreferrer"&gt;Mnemoverse&lt;/a&gt;, a memory layer for AI agents that connects over MCP. Nothing of ours appears in this article, and its conclusion is partly an argument against buying a memory layer for this problem. The longer version with every source is on our library, and the MCP server is open source (MIT): &lt;a href="https://github.com/mnemoverse/mcp-memory-server" rel="noopener noreferrer"&gt;github.com/mnemoverse/mcp-memory-server&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>cloud</category>
      <category>agents</category>
    </item>
    <item>
      <title>Does my AI agent memory graph change when it reads, or only when it writes?</title>
      <dc:creator>Edward Izgorodin</dc:creator>
      <pubDate>Sat, 19 Sep 2026 06:15:00 +0000</pubDate>
      <link>https://dev.to/izgorodin/does-my-ai-agent-memory-graph-change-when-it-reads-or-only-when-it-writes-20mj</link>
      <guid>https://dev.to/izgorodin/does-my-ai-agent-memory-graph-change-when-it-reads-or-only-when-it-writes-20mj</guid>
      <description>&lt;p&gt;You ask the agent about the payments retry table, and it comes back with the retry table plus the queue migration you argued about last month, because both mention the same service. Useful. Next week you ask about the queue and the retry table comes back with it again, and the week after that too. At some point you wonder whether that pairing is now something your memory layer knows, or whether you are watching the same shared entity match fire every time and nothing has been learned at all. The vendor page says knowledge graph, which is true, and answers nothing.&lt;/p&gt;

&lt;p&gt;That is the buyer's question about graph memory once you strip the feature list off it. Every product in this category builds a graph, so the feature list separates nobody. What separates them is what a node is, when an edge is made, and whether anything in that graph changes because of what a search returned. Not because you wrote something new. Because you read. I put that question to six systems, Mem0, Zep with Graphiti, Cognee, Letta, Supermemory and Mnemoverse, and read each vendor's own pages and published code. I work on the last one, and on this exact question it holds the weakest row, so it goes first.&lt;/p&gt;

&lt;p&gt;One line to carry: in all six the edges are made when you write, two of them walk further when you read, one of them writes a reinforcement when you read, and the only place where a read demonstrably strengthens an edge is a research repository, not a product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our row first, because it is the exposed one
&lt;/h2&gt;

&lt;p&gt;Mnemoverse describes the mechanism this article goes looking for, in public, in code you can open. The memory server we publish prints it in a line the user sees: "concept-to-concept links learned from concepts that occur together as memories are stored and used". The comment above that line names the operation without a metaphor, &lt;code&gt;co_activate&lt;/code&gt;, linking query concepts "to result concepts on use". Our library page says the same in prose: "Its association layer links concepts through weighted edges strengthened by co-activation, with feedback tuning the weights over time". On use. That is the loop, written down by us.&lt;/p&gt;

&lt;p&gt;And our engine is closed, so you can read those sentences and you cannot check one of them. Five of the six say nothing about this mechanism in public, and silence promises nothing. We have said more and shown less. If you are choosing on evidence rather than on ambition, that runs against us, and it should.&lt;/p&gt;

&lt;h2&gt;
  
  
  Six graphs, four kinds of node
&lt;/h2&gt;

&lt;p&gt;Zep: "Zep's temporal Context Graph is the unit of agent memory. Nodes are entities, and edges are facts or relationships. The graph updates as new data arrives." Cognee extracts entities and relationships into a graph store, Mem0 ships a feature named Graph Memory, Supermemory sells a memory graph on its product page, Letta resolves links between memory files, and Mnemoverse ships a learned association graph. Six products, six graphs, and the word does different work in each sentence.&lt;/p&gt;

&lt;p&gt;Entities are what Zep and Cognee mean, and Cognee's retrieval guide is exact about the order: nodes are selected first and "An edge is only carried over if both of the nodes it connects were selected".&lt;/p&gt;

&lt;p&gt;Memories are what Mem0 means, and its graph is bipartite. Entities are stored once, and "Over time this forms a graph: a web of entities, each connecting all the memories that mention it." Two memories are connected when they share an entity, with no learned weight on that connection, and the vendor rules out typed relations in the same breath: "It does not assign typed, labeled relationships between entities". Connected through a shared thing, not to each other.&lt;/p&gt;

&lt;p&gt;Documents are what Letta means. The nodes are Markdown files the model itself wrote, the edges are file links a resolver parses, nothing weights them, and Letta's own published memory showcase says what a link is for: "A double-bracket path is a discovery link, not an automatic include or semantic retrieval instruction."&lt;/p&gt;

&lt;p&gt;Facts on facts are what Supermemory means: "When content is processed, new facts connect to existing ones through three relationship types." Its published package types those three as strings and carries no weight, strength or access count on the edge, though its generated SDK leaves the field an open enum.&lt;/p&gt;

&lt;p&gt;Concept labels are what we mean. Our nodes are neither entities nor memories, and our edges carry no relation types, no direction and no entity resolution, so the association layer never asserts that two records denote the same thing. Four kinds of object under one word.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the edges get made
&lt;/h2&gt;

&lt;p&gt;In all six, the edges are created when something is written. Extraction, linking, and then the work is finished before you ask anything. Same in every row, ours included.&lt;/p&gt;

&lt;p&gt;Two of the six do something extra at query time. Cognee ships a neighbourhood depth parameter that triggers real multi-hop expansion, which the shipped code declares None, so it is off unless you set it. Zep documents the same shape under a different name: "You can enable breadth-first search to expand results around specified graph nodes." Its parameter table gives the seed no default and marks it not required.&lt;/p&gt;

&lt;p&gt;Both walk the graph and find nodes. Walking is not changing, and for Cognee that is the vendor's own statement about its retrieval object: the memory fragment built for a query "is never read directly from, or written back to, the full persisted graph". For Zep the conclusion is narrower, because the served engine is closed: every documentation page I read uses the vocabulary of returning results, and none of them documents a write on the search path. That walk was not completed, so this is a statement about the pages read, not about the engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one place a read writes, and where the write lands
&lt;/h2&gt;

&lt;p&gt;Mem0 is the real counter-example: a read does write. Its memory decay page says so twice, once as a principle, "every time a memory is returned in a search it gets a small reinforcement", and once as a numbered step in the search pipeline, "Records a fire-and-forget reinforcement against each returned memory".&lt;/p&gt;

&lt;p&gt;On that entire page the words edge, graph, connection and link occur zero times, checked on 8 September 2026. The reinforcement lands on the memory's own access history. It is not a connection that got stronger because two things came back together. And the whole feature is "opt-in per project" and "off by default". A read writes, to the memory, not to the graph, and only if you asked for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Twenty searches, with controls
&lt;/h2&gt;

&lt;p&gt;To reach a surface documentation never touches, I searched each vendor's GitHub organisation for the vocabulary this mechanism has: hebbian, co-activation, spreading activation, co-retrieval. Four terms, five organisations, twenty code searches, all printed on the full write-up so you can run them. Re-run on 8 September 2026, all twenty return zero files.&lt;/p&gt;

&lt;p&gt;A zero from an instrument is a statement about the instrument first, so it needs controls, and it has two. The positive control is the same search with the term memory, which returns files in every one of the five organisations. The stronger control does not depend on the index: I downloaded the default branch of each vendor's main repositories, seven trees, and grepped them directly, hyphenated and unhyphenated spellings both. Zero for all four terms in every tree, and zero in the single-file documentation corpora of Cognee, Supermemory and Mem0. One trap: letta-ai/letta no longer holds the source, so a grep there gives a zero that looks like evidence and is not; the source is in letta-code.&lt;/p&gt;

&lt;p&gt;A search of what is published is not a search of what runs, and four of these six do not publish an engine you can read, ours among them. Two vendors also run chat communities unreadable from outside, and this sweep does not cover them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The vocabulary is already taken
&lt;/h2&gt;

&lt;p&gt;What makes this hard to see from outside is that the words are in use, for other mechanisms. Supermemory's preferences memory type "Strengthens with repetition", in a table whose neighbouring rows persist until updated and decay unless significant, and nothing in that vendor's documentation corpus says what does the strengthening. Letta's shipped system prompt tells the model that its references should strengthen with use, an instruction to a model, not a description of an engine.&lt;/p&gt;

&lt;p&gt;An earlier cut of this argument, in my own drafts and video, said those values were computed once and frozen. That was wrong and catchable in one click: Zep regenerates observations when new evidence lands, and Supermemory's row sits in a table about things that move. What none of them says is where the change comes from. New evidence, a rating, a repetition, a score. Every one is a real mechanism. Not one is a connection that got stronger because two things came back together.&lt;/p&gt;

&lt;p&gt;The one public implementation where that happens is the code attached to an ACL 2026 paper, ReinerBRO/HeLa-Mem. The last thing its retrieve method does before returning results is call a routine whose docstring reads "Strengthen connections between simultaneously retrieved memories." It walks every pair of the retrieved identifiers and adds weight to the edge between them. That is the whole mechanism, and what none of the five products has published is those few statements between having the results and returning them. It is a research artifact with no releases, no tags and no licence, and the claim is exactly that narrow: one public implementation where retrieving changes the connection.&lt;/p&gt;

&lt;h2&gt;
  
  
  The table
&lt;/h2&gt;

&lt;p&gt;Every quotation above is a contiguous substring of a page its author published, re-checked on 2026-09-13, and each source is named on the &lt;a href="https://mnemoverse.com/docs/library/agent-memory-knowledge-graphs-compared" rel="noopener noreferrer"&gt;full write-up&lt;/a&gt;. The table is a reading of published pages and code, not a benchmark.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;system&lt;/th&gt;
&lt;th&gt;a node is&lt;/th&gt;
&lt;th&gt;edges made&lt;/th&gt;
&lt;th&gt;extra at read time&lt;/th&gt;
&lt;th&gt;does a read write anything&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mnemoverse&lt;/td&gt;
&lt;td&gt;concept labels&lt;/td&gt;
&lt;td&gt;on write, and on use per our public source&lt;/td&gt;
&lt;td&gt;cannot be checked, engine closed&lt;/td&gt;
&lt;td&gt;our source says yes, to the edge; unverifiable from outside&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mem0&lt;/td&gt;
&lt;td&gt;memories, linked through shared entities&lt;/td&gt;
&lt;td&gt;on write&lt;/td&gt;
&lt;td&gt;entity boost reorders an over-fetched pool&lt;/td&gt;
&lt;td&gt;yes, a reinforcement on the memory's own access history; opt-in, off by default; nothing on an edge&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zep with Graphiti&lt;/td&gt;
&lt;td&gt;entities; edges are facts&lt;/td&gt;
&lt;td&gt;on write&lt;/td&gt;
&lt;td&gt;breadth-first search from named nodes, optional&lt;/td&gt;
&lt;td&gt;none documented on the pages read; hosted engine closed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cognee&lt;/td&gt;
&lt;td&gt;entities&lt;/td&gt;
&lt;td&gt;on write&lt;/td&gt;
&lt;td&gt;neighbourhood depth, off unless set&lt;/td&gt;
&lt;td&gt;no; the query fragment is never written back; an optional last-accessed stamp moves no weight&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Letta&lt;/td&gt;
&lt;td&gt;Markdown files the model wrote&lt;/td&gt;
&lt;td&gt;on write&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Supermemory&lt;/td&gt;
&lt;td&gt;facts on facts, three typed relations, enum left open&lt;/td&gt;
&lt;td&gt;on write&lt;/td&gt;
&lt;td&gt;none documented&lt;/td&gt;
&lt;td&gt;no weight on the edge type; the engine is not in the published tree&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What to ask a vendor
&lt;/h2&gt;

&lt;p&gt;Three questions separate these six, and none of them is on a comparison table. What are the nodes: entities, memories, documents or concepts. When does anything happen: only at write time, or at query time as well, and if so whether it is on by default, because for the two that have it, it is not. And when the graph changes, what moved it: new evidence, a rating, a repetition, a score, or a read. The last one decides whether a memory layer learns from being used or only from being written to, and the category is quietest about it.&lt;/p&gt;

&lt;p&gt;From published material, five of the six never name the last case. One names it in its own public source and cannot show you the engine that would settle it, and that one is us. Ask us that question harder than you ask anybody else.&lt;/p&gt;

&lt;p&gt;Which of the six do you run, and have you ever seen a pairing come back that nothing in your writes explains?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I work on &lt;a href="https://mnemoverse.com" rel="noopener noreferrer"&gt;Mnemoverse&lt;/a&gt;, one of the systems above, so weigh the argument accordingly. The full comparison with every source page is on our library, and the MCP server is open source (MIT): &lt;a href="https://github.com/mnemoverse/mcp-memory-server" rel="noopener noreferrer"&gt;github.com/mnemoverse/mcp-memory-server&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>machinelearning</category>
      <category>mcp</category>
    </item>
    <item>
      <title>Is transformer attention really a Hopfield network?</title>
      <dc:creator>Edward Izgorodin</dc:creator>
      <pubDate>Sat, 19 Sep 2026 06:08:00 +0000</pubDate>
      <link>https://dev.to/izgorodin/is-transformer-attention-really-a-hopfield-network-cdg</link>
      <guid>https://dev.to/izgorodin/is-transformer-attention-really-a-hopfield-network-cdg</guid>
      <description>&lt;p&gt;Someone in a thread says attention is just a Hopfield network, and the next reply runs with it: so the model already has associative memory, so an agent does not need anything else to remember. Both sentences sound like the same claim. Only the first one is true, and only in a narrow sense that is worth stating exactly, because the narrow version is useful and the loose version leads people to wrong architecture decisions.&lt;/p&gt;

&lt;p&gt;One line to carry: one update step of a modern continuous Hopfield network equals scaled dot-product attention under a specific identification of queries, keys and values; that is an identity of operation, not a history of how transformers were built, and it does not turn a context window into memory that outlives the conversation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The identity, stated precisely
&lt;/h2&gt;

&lt;p&gt;The result is from Ramsauer and colleagues, in a 2020 paper titled &lt;a href="https://arxiv.org/abs/2008.02217" rel="noopener noreferrer"&gt;Hopfield Networks is All You Need&lt;/a&gt; that appeared at ICLR 2021. The abstract states it in one sentence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The new update rule is equivalent to the attention mechanism used in transformers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The update rule of their modern Hopfield network is &lt;code&gt;ξ_new = X softmax(β Xᵀξ)&lt;/code&gt;. Read it as: compare the current state with every stored pattern, turn the similarities into softmax weights, return the weighted sum of patterns. Now identify the stored patterns with keys, the state with a query, add the value projection, and set &lt;code&gt;β = 1/√d_k&lt;/code&gt;. The update becomes &lt;code&gt;softmax(QKᵀ/√d_k)V&lt;/code&gt;, which is scaled dot-product attention.&lt;/p&gt;

&lt;p&gt;Under those substitutions the equality is exact, at the level of that one operation. A query attends over keys and returns a weighted blend of values. A Hopfield state updates by attending over stored patterns and returning their weighted blend.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the identity does not say
&lt;/h2&gt;

&lt;p&gt;It is one update step. The paper's own text says that "retrieval with one update is compatible with activating the layers of deep networks", and its theorems bound how close a single update gets to the stored pattern when patterns are well separated. It is a statement about a step, not about a network iterating to convergence.&lt;/p&gt;

&lt;p&gt;It is not a lineage. Attention was introduced in &lt;a href="https://arxiv.org/abs/1706.03762" rel="noopener noreferrer"&gt;Attention Is All You Need&lt;/a&gt; in 2017, three years before the equivalence was shown. The paper's first figure uses an equality sign and explains it in the caption: the sign means "keeps the properties". The two were found to be the same operation after the fact.&lt;/p&gt;

&lt;p&gt;It covers the operation and nothing around it. Residual connections, layer normalisation, the feed-forward blocks, causal masking and training dynamics are outside it. Later work narrows the conditions under which the identity holds rather than overturning it.&lt;/p&gt;

&lt;p&gt;The title itself is a nod to the 2017 paper, and it belongs to a genre large enough to have been measured: a December 2025 preprint, &lt;a href="https://arxiv.org/abs/2512.19700" rel="noopener noreferrer"&gt;All You Need is Not All You Need for a Paper Title&lt;/a&gt;, counts 717 arXiv titles containing the phrase between 2009 and 2025, 200 of them in 2025 alone, and argues the format favours "memorability over precision". This is one of the rare cases where the memorable title is also the theorem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three capacity numbers that get merged into one
&lt;/h2&gt;

&lt;p&gt;Ask how many patterns a Hopfield network can store and you will see three different answers quoted as if they were the same. They answer three different questions.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;source&lt;/th&gt;
&lt;th&gt;capacity&lt;/th&gt;
&lt;th&gt;what it tolerates&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Hopfield, 1982, simulations at N = 30 and 100&lt;/td&gt;
&lt;td&gt;about 0.15N&lt;/td&gt;
&lt;td&gt;until recall errors become severe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amit, Gutfreund and Sompolinsky, 1985 and 1987&lt;/td&gt;
&lt;td&gt;αc ≈ 0.138, often rounded to 0.14, so about 0.138N&lt;/td&gt;
&lt;td&gt;a small fraction of bit errors, under 1.5 % at zero temperature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;McEliece, Posner, Rodemich and Venkatesh, 1987&lt;/td&gt;
&lt;td&gt;n/(2 ln n)&lt;/td&gt;
&lt;td&gt;none: exact recall of most memories&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There is no contradiction between them. The first is an empirical rule of thumb from small simulations. The second is a statistical mechanics result that allows a little noise in what comes back. The third demands exact recovery, so it is smaller and grows more slowly. The modern variants changed the question altogether: dense associative memories raised capacity to polynomial and then exponential scaling, and Ramsauer and colleagues prove exponential capacity in the dimension of the space for continuous states. If a post quotes 0.14N as the capacity of the network inside a transformer, it has taken the number from the wrong row of a table that stopped at 1987.&lt;/p&gt;

&lt;h2&gt;
  
  
  A vocabulary for what retrieval returns
&lt;/h2&gt;

&lt;p&gt;The most practical part of the paper is not the equivalence but the taxonomy of fixed points it gives for the update. A query can settle on a single stored pattern, the clean case, when one pattern is close and well separated. It can settle on a metastable state that averages a subset of similar patterns. Or, when nothing is well separated, it can settle on a global average of everything stored.&lt;/p&gt;

&lt;p&gt;The authors then used that lens on trained models. The abstract reports that attention heads "perform in the first layers preferably global averaging and in higher layers partial averaging via metastable states". A blend, in this reading, is an operating regime of the retrieval step and not automatically a malfunction: when the stored patterns are not separated enough, the step returns an average of a subset rather than one of them. That is a statement about one retrieval step. It is useful vocabulary, and it is not an explanation of every wrong answer a model gives.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for an agent's memory
&lt;/h2&gt;

&lt;p&gt;It gives a clean boundary. Attention retrieves associatively over what is in the context right now. It does that very well, and the Hopfield reading explains why. It does not keep anything once the context is gone.&lt;/p&gt;

&lt;p&gt;Google Research's &lt;a href="https://arxiv.org/abs/2501.00663" rel="noopener noreferrer"&gt;Titans paper&lt;/a&gt; draws the same line in its abstract:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;From a memory perspective, we argue that attention due to its limited context but accurate dependency modeling performs as a short-term memory, while neural memory due to its ability to memorize the data, acts as a long-term, more persistent, memory.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So the reply in the opening thread has the direction backwards. Attention being a Hopfield update is a statement about how a model retrieves inside a window. What an agent needs to have in the window next week, after a restart, in a different session or a different tool, has to be stored somewhere that survives the session and put back in front of the model when it matters. The identity tells you what the retrieval step does with whatever gets there. It says nothing about getting it there.&lt;/p&gt;

&lt;p&gt;Where did you last see the capacity of a Hopfield network quoted, and which of the three rows was it actually from?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I work on &lt;a href="https://mnemoverse.com" rel="noopener noreferrer"&gt;Mnemoverse&lt;/a&gt;, a memory service for AI agents, which is the kind of system the last section is about, so weigh that section accordingly. The longer version, with every paper and the full capacity table, is on our library, and the MCP server is open source (MIT): &lt;a href="https://github.com/mnemoverse/mcp-memory-server" rel="noopener noreferrer"&gt;github.com/mnemoverse/mcp-memory-server&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>deeplearning</category>
      <category>llm</category>
    </item>
    <item>
      <title>Which Python package actually gives my agent memory after pip install?</title>
      <dc:creator>Edward Izgorodin</dc:creator>
      <pubDate>Fri, 18 Sep 2026 06:05:00 +0000</pubDate>
      <link>https://dev.to/izgorodin/which-python-package-actually-gives-my-agent-memory-after-pip-install-4pmf</link>
      <guid>https://dev.to/izgorodin/which-python-package-actually-gives-my-agent-memory-after-pip-install-4pmf</guid>
      <description>&lt;p&gt;You pick a memory package for your agent the way most people do: the landing page has an install command, you paste it, it installs cleanly, you write the first lines from the quickstart, and the first call fails. The error is not about your code. It wants a key, or a database address, or a token limit, or it wants nothing at all and quietly stores your memory in a dictionary that will be gone when the process exits. The install worked. The memory did not arrive with it.&lt;/p&gt;

&lt;p&gt;So which Python package actually gives an agent memory after pip install? I put that question to eleven packages, including the one I work on, and the answer is none of them, because the install command is the least informative fact any of these vendors publish. Three other things, all published and none advertised, tell you what you bought, and each is faster to read than a landing page: the runtime dependency list on PyPI, the class names the package exports, and what the constructor asks for first when you hand it nothing.&lt;/p&gt;

&lt;p&gt;One line to carry: pip install gives you a client or an engine, never memory, and the dependency list tells you which of the two before you type anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Eleven packages, twelve rows
&lt;/h2&gt;

&lt;p&gt;The row count is one higher than the package count because &lt;code&gt;pip install mem0ai&lt;/code&gt; is two rows: the classes &lt;code&gt;Memory&lt;/code&gt; and &lt;code&gt;MemoryClient&lt;/code&gt; come out of the same install and are two different products. Every version and count below was re-read from &lt;code&gt;pypi.org/pypi/&amp;lt;name&amp;gt;/json&lt;/code&gt; on 8 September 2026; the versions will date, the checks will not.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;package&lt;/th&gt;
&lt;th&gt;version&lt;/th&gt;
&lt;th&gt;runtime dependencies, extras excluded&lt;/th&gt;
&lt;th&gt;where the memory runs by default&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;mnemoverse&lt;/code&gt; (ours)&lt;/td&gt;
&lt;td&gt;0.2.0, and 0.3.0 since 16 September&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;ours, hosted only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;mem0ai&lt;/code&gt;, the &lt;code&gt;Memory&lt;/code&gt; class&lt;/td&gt;
&lt;td&gt;2.0.20&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;yours, in your process or as a self-hosted server, with OpenAI as the default model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;mem0ai&lt;/code&gt;, the &lt;code&gt;MemoryClient&lt;/code&gt; class&lt;/td&gt;
&lt;td&gt;2.0.20, same package&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Mem0's hosted platform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;letta-client&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1.12.1&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Letta's by default, yours if you set &lt;code&gt;base_url&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;zep-cloud&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3.28.0&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Zep's cloud, or your own cloud through Zep's BYOC offer for enterprise customers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;graphiti-core&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0.30.1&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;yours, plus a graph database you run or an embedded one behind a pip extra, with OpenAI as the default model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cognee&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1.5.4&lt;/td&gt;
&lt;td&gt;45 lines, 44 distinct names, 42 with no platform marker&lt;/td&gt;
&lt;td&gt;yours, with the stores embedded and OpenAI as the default model, or Cognee Cloud once you call &lt;code&gt;serve()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;supermemory&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3.61.0&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;their server, or a binary you run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;langgraph&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1.2.11&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;yours&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;langmem&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0.0.30&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;yours, with a model called remotely&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;llama-index-core&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0.14.24&lt;/td&gt;
&lt;td&gt;29&lt;/td&gt;
&lt;td&gt;yours&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deepagents&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0.7.13&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;yours, as files&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The counting rule: every requirement line carrying an &lt;code&gt;extra ==&lt;/code&gt; marker dropped, platform markers such as &lt;code&gt;sys_platform&lt;/code&gt; kept because they are not extras, and that is the number closest to what a bare pip install puts on your disk. Where a line carries a platform marker, an install takes it only on that platform: of Cognee's forty five lines, forty two carry no marker, and an install resolves forty four on Windows and forty three on Linux and macOS. And a count is a structural fact rather than a score: forty five lines is not worse than two, it is more work happening on your machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check one: read the dependency list for contents, not length
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;requires_dist&lt;/code&gt; is published by every project on PyPI, in the JSON the index serves at &lt;code&gt;pypi.org/pypi/&amp;lt;name&amp;gt;/json&lt;/code&gt;, and it settles the largest question here before you install anything. Four of these declare a handful of dependencies and not one of the four lists contains a store, an index or a model. Ours declares &lt;code&gt;httpx&lt;/code&gt; and &lt;code&gt;pydantic&lt;/code&gt;. Zep's cloud package declares five. Letta's client and Supermemory's client declare six each, and the two lists are identical name for name, which tells you both came out of the same code generator. A package with no store, no database driver and no model client in its dependency list is a request builder. It assembles HTTP requests, and the memory runs in a server outside the package. Whose server varies: Supermemory documents a self-hosted server that "speaks the same API as the hosted platform", Letta's client names a local address, &lt;code&gt;http://localhost:8283&lt;/code&gt;, beside its cloud one, Zep offers "BYOC deployments where Zep runs in your own cloud infrastructure" to enterprise customers, and ours is hosted only.&lt;/p&gt;

&lt;p&gt;Three declare something quite different. &lt;code&gt;graphiti-core&lt;/code&gt; lists &lt;code&gt;neo4j&lt;/code&gt; and &lt;code&gt;openai&lt;/code&gt;: a graph database driver and a model client. &lt;code&gt;mem0ai&lt;/code&gt; lists &lt;code&gt;qdrant-client&lt;/code&gt; and &lt;code&gt;sqlalchemy&lt;/code&gt;: a vector store client and a database toolkit. &lt;code&gt;cognee&lt;/code&gt; lists &lt;code&gt;lancedb&lt;/code&gt;, an embedded vector store, &lt;code&gt;ladybug&lt;/code&gt;, a graph database declared twice under mutually exclusive platform markers, and &lt;code&gt;fastapi&lt;/code&gt;, &lt;code&gt;starlette&lt;/code&gt;, &lt;code&gt;uvicorn&lt;/code&gt; and &lt;code&gt;gunicorn&lt;/code&gt;, which is a web server. Those three ship the memory engine in the package, which is not the same as running entirely in your process: each calls a hosted model by default, Graphiti wants a Neo4j or FalkorDB server you run unless you install an embedded graph engine behind a pip extra, and Mem0 documents a self-hosted server mode beside the library, while its default stores and Cognee's sit on local disk. That is a different failure mode, a different bill and a different thing to operate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check two: the import line, when one package is two products
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;mem0/__init__.py&lt;/code&gt; in the shipped 2.0.20 wheel does all of its public routing in two import lines: &lt;code&gt;AsyncMemoryClient&lt;/code&gt; and &lt;code&gt;MemoryClient&lt;/code&gt; from &lt;code&gt;mem0.client.main&lt;/code&gt;, &lt;code&gt;AsyncMemory&lt;/code&gt; and &lt;code&gt;Memory&lt;/code&gt; from &lt;code&gt;mem0.memory.main&lt;/code&gt;. Four names, two products, and the vendor states the routing itself in the index it publishes for answer engines, on two adjacent lines: "Use &lt;code&gt;MemoryClient&lt;/code&gt; (Python) / &lt;code&gt;mem0ai&lt;/code&gt; (npm) when the user has a Mem0 Platform API key." and "Use &lt;code&gt;Memory&lt;/code&gt; (Python) / &lt;code&gt;mem0ai/oss&lt;/code&gt; (npm) when the user self-hosts." Same package, same install line, and the import decides whether your memory goes to Mem0's managed Platform, which has a free tier, or runs the same engine on your own infrastructure; the vendor's own overview says "Mem0 Open Source is the same memory engine as the Platform, running on your own infrastructure."&lt;/p&gt;

&lt;p&gt;The reverse case is the pair that gets quoted as though it were one product: &lt;code&gt;zep-cloud&lt;/code&gt; is the hosted client with a single hosted address in its environment enum, and Graphiti is the engine in a separate package, which Zep's own FAQ calls "The open-source Context Graph framework that powers Zep Cloud". &lt;code&gt;graphiti_core&lt;/code&gt; exposes exactly one class, &lt;code&gt;Graphiti&lt;/code&gt;, with no synchronous twin, and the memory functions of &lt;code&gt;cognee&lt;/code&gt; are async as well, while the Mem0, Letta, Zep, Supermemory and Mnemoverse clients each ship both, as a pair of names differing by the word async.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check three: construct the client with nothing and read what comes back
&lt;/h2&gt;

&lt;p&gt;This check needs a terminal rather than a web page. I ran every constructor on 8 September 2026 in a clean virtual environment on Python 3.11.9, from the wheel downloaded from PyPI, with every relevant environment variable cleared first.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;MnemoClient()&lt;/code&gt;, ours, on 0.2.0: &lt;code&gt;TypeError: MnemoClient.__init__() missing 1 required positional argument: 'api_key'&lt;/code&gt;; on 0.3.0, released on 16 September and re-run on 17 September, the same call reads &lt;code&gt;MNEMOVERSE_API_KEY&lt;/code&gt; and, with it unset, raises &lt;code&gt;ValueError: No API key: pass api_key= or set the MNEMOVERSE_API_KEY environment variable&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MemoryClient()&lt;/code&gt;, Mem0 hosted: &lt;code&gt;ValueError&lt;/code&gt; saying the Mem0 API key was not provided&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Memory()&lt;/code&gt;, Mem0 open source: &lt;code&gt;openai.OpenAIError&lt;/code&gt; about missing credentials, because on its default path Mem0 calls a model during ingestion&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Zep()&lt;/code&gt;: &lt;code&gt;ApiError&lt;/code&gt; asking for &lt;code&gt;api_key&lt;/code&gt; or &lt;code&gt;ZEP_API_KEY&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Supermemory()&lt;/code&gt;: &lt;code&gt;SupermemoryError&lt;/code&gt; asking for &lt;code&gt;api_key&lt;/code&gt; or &lt;code&gt;SUPERMEMORY_API_KEY&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Graphiti()&lt;/code&gt;: &lt;code&gt;ValueError&lt;/code&gt; saying &lt;code&gt;uri&lt;/code&gt; must be provided when no graph driver is given&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Letta()&lt;/code&gt;: constructs, no exception&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;InMemoryStore()&lt;/code&gt;, LangGraph: constructs, no exception&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Memory()&lt;/code&gt;, LlamaIndex: &lt;code&gt;ValidationError&lt;/code&gt; on an unset token limit, no credential involved&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cognee&lt;/code&gt;, &lt;code&gt;langmem&lt;/code&gt;, &lt;code&gt;deepagents&lt;/code&gt;: no client class to construct&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Graphiti's first refusal is not about an account. It asks for a database address or a graph driver, and its default path then wants an OpenAI key, which Graphiti's quick-start lists among its requirements; once a driver is given the address is not needed, because the embedded Kuzu driver's database path defaults to an in-memory one. And the two silences are terms as well. &lt;code&gt;Letta()&lt;/code&gt; constructs with nothing because its environment table names a cloud address first and localhost second, so the silent object is already pointed at a hosted service and the local address is one keyword argument away. &lt;code&gt;InMemoryStore()&lt;/code&gt; constructs with nothing because there is nothing to authenticate against: it is a dictionary.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trade is not a key against no key
&lt;/h2&gt;

&lt;p&gt;The tidy version people repeat is that everything which survives a restart needs a key, and everything which needs no key does not survive a restart. The vendors' own pages refute it. The trade is also not one decision: the model and the index are placed separately. Graphiti's default path runs the graph on your machine and still wants an OpenAI key, while Mem0's local recipe puts both the model and the index on yours. Persisting with no vendor credential is documented by Mem0, Cognee, Graphiti and Letta, and none of it is hidden: Mem0's own comparison page lists "local LLMs (Ollama), self-hosted vector DBs, no usage-based billing" among the reasons to choose open source, and Cognee has a guide titled "Local Setup (No API Key)".&lt;/p&gt;

&lt;p&gt;Mem0 publishes a recipe for exactly this, headed Self-Hosted AI Companion: "Mem0 can be utilized entirely locally by leveraging Ollama for both the embedding model and the language model (LLM)." There is no key anywhere in its configuration. What it costs is two servers on your machine, a Qdrant and an Ollama. Cognee's floor is lower because its stores are already inside the package, and its local setup page is the sharpest statement of the trade in this field, cutting both ways in one paragraph. First: "running it locally with Ollama needs no account, subscription, or paid API key", and "You are not being asked to pay for anything." Then, a few lines later: "The error appears because Ollama is one of the providers Cognee requires a non-empty LLM_API_KEY for, even though Ollama itself ignores the value." The remedy the vendor gives is "The fix is to set any placeholder string". A placeholder is not a purchase. It is not nothing either, and anyone who reads only the first sentence will file a bug.&lt;/p&gt;

&lt;p&gt;The same applies one door over: an MCP server does not mean your computer. Mem0's hosted one says so in the vendor's words: "Nothing runs on your machine: the server is hosted by Mem0, and your client connects to it over HTTPS." Cognee's standalone server runs the whole pipeline locally. Same protocol, opposite answers.&lt;/p&gt;

&lt;p&gt;And the two rows that need no credential out of the box are the two that do not persist, and both vendors say so first. The comment in LangGraph's own code sample reads "InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production use." LlamaIndex's page says "By default, the Memory class is using an in-memory SQLite database. You can plug in any remote database by changing the database URI." Working instantly and forgetting are the same fact about the same default.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our row, and I read it hardest
&lt;/h2&gt;

&lt;p&gt;I work on &lt;code&gt;mnemoverse&lt;/code&gt;, so weigh this section accordingly. It is a hosted client. The dependency list shows that no memory engine ships in the package: two runtime dependencies, &lt;code&gt;httpx&lt;/code&gt; and &lt;code&gt;pydantic&lt;/code&gt;, in a pure Python wheel of 13,468 bytes for 0.2.0, with both clients issuing HTTP requests to our service. That the service behind it is hosted only is my own statement, which no dependency list can show. In 0.2.0, the release this table read on 8 September, the key was required more strictly than anywhere else in the table: &lt;code&gt;api_key&lt;/code&gt; was positional with no default in both clients, a full grep of that wheel finds no &lt;code&gt;getenv&lt;/code&gt;, no &lt;code&gt;environ&lt;/code&gt; and no &lt;code&gt;dotenv&lt;/code&gt;, and what you got was the language's own error rather than a sentence. 0.3.0, uploaded to PyPI on 16 September 2026, changed that, and our changelog says so: &lt;code&gt;api_key&lt;/code&gt; is now optional, the key is read from the &lt;code&gt;MNEMOVERSE_API_KEY&lt;/code&gt; environment variable when it is not passed, and a constructor given neither raises &lt;code&gt;ValueError: No API key: pass api_key= or set the MNEMOVERSE_API_KEY environment variable&lt;/code&gt;. On the current release we behave the way Mem0, Zep and Supermemory do, reading an environment variable before we complain.&lt;/p&gt;

&lt;p&gt;Two things we published did not survive the check this article recommends: one in the package, one about the memory server we also publish. The first we found ourselves and wrote into our changelog: 0.1.0 shipped a default address pointing at a host that answered 401, so a client configured the ordinary way could not reach the service at all, and it was the only published release for four months until 0.2.0 corrected the default. The second was not in this package. It was the one line description of the memory server we also publish, &lt;code&gt;mcp-memory-server&lt;/code&gt;, which GitHub prints in the page title and the meta description: when the full write-up went out on 8 September 2026 it carried two claims our own documentation had withdrawn. The README and the description of the latest npm release no longer carried them; the repository description did, and it is the first line a stranger reads. It was rewritten on 12 September 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four minutes, then decide
&lt;/h2&gt;

&lt;p&gt;What the three checks do not tell you is what a read hands back, and that is where these stop being one category: ranked stored text, extracted fact strings with a score by default (Mem0 says so itself: "By default, Mem0 stores extracted memories, not a verbatim transcript", and documents a flag that stores content exactly as provided), passages, a context block beside a search that returns ranked edges, graph edges where some carry a validity window, ranked memories or document chunks, an answer a model wrote, your own JSON, chat messages, or files. That column decides how much code you write after the install, and it is the one you have to look up.&lt;/p&gt;

&lt;p&gt;Read &lt;code&gt;requires_dist&lt;/code&gt; with the extras dropped, for contents rather than length. Read the exported class names, because where one package ships two products the import line is the switch. Construct the client with nothing and read what comes back, knowing that a first refusal names what the client asks for first and not always everything it needs, and where it constructs silently, ask what address the object is already pointed at. At least one version number above will be wrong by the time you read this, ours included, since 0.3.0 shipped on 16 September, and all three checks will still work. The &lt;code&gt;mnemoverse&lt;/code&gt; row sits in the table on the same terms: hosted only, a key required on every release, and two things we published that did not survive the check. That is a reason to run the checks on it too, and not a reason to buy anything.&lt;/p&gt;

&lt;p&gt;Which of the three checks would have changed the package you already installed?&lt;/p&gt;

&lt;p&gt;Every quotation above is a contiguous substring of a page the vendor published, re-checked on 2026-09-17, and the &lt;a href="https://mnemoverse.com/docs/library/python-sdk-agent-memory-compared" rel="noopener noreferrer"&gt;full write-up&lt;/a&gt; carries the same quotations with the method behind them.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I work on &lt;a href="https://mnemoverse.com" rel="noopener noreferrer"&gt;Mnemoverse&lt;/a&gt;, one of the systems above, so weigh the argument accordingly. The full comparison with every source page is on our library.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>agents</category>
      <category>mcp</category>
    </item>
    <item>
      <title>My LLM agents forget conversation history when I restart them, how do I fix this?</title>
      <dc:creator>Edward Izgorodin</dc:creator>
      <pubDate>Thu, 17 Sep 2026 05:47:00 +0000</pubDate>
      <link>https://dev.to/izgorodin/my-llm-agents-forget-conversation-history-when-i-restart-them-how-do-i-fix-this-2c4c</link>
      <guid>https://dev.to/izgorodin/my-llm-agents-forget-conversation-history-when-i-restart-them-how-do-i-fix-this-2c4c</guid>
      <description>&lt;p&gt;You close the terminal on Friday with the agent halfway through a refactor it understands better than you do by now. On Monday you open a new session, ask it to continue, and it asks which project this is. Nothing in the tool is broken, and that is the first thing worth clearing before any fix. Every one of the five coding tools I checked, Claude Code, Cursor, Codex, VS Code and Windsurf, already ships a working way to reopen the previous conversation. Nothing is broken. The gap people are actually hitting is narrower than "my history is gone", and naming it precisely is the whole point.&lt;/p&gt;

&lt;p&gt;Comparing memory across five products is meaningless until the word is split. Four questions separate cleanly, and the five answer them differently: can you return to the same conversation; what degrades even when you do; what a brand new session still loads from disk regardless; and whether there is built-in memory that the agent writes for itself and reads back later. Every fact below comes from a page the vendor published, re-checked on 2026-09-12, and the page for each is named on the &lt;a href="https://mnemoverse.com/docs/library/what-survives-when-ai-agents-restart" rel="noopener noreferrer"&gt;full write-up&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resume exists everywhere, so the premise does not survive
&lt;/h2&gt;

&lt;p&gt;Claude Code documents five entry points, among them a flag for the most recent session in the current directory and a picker, and states that a resumed session restores the conversation together with the state saved in it. Its documentation also names what does not come back, which is easy to miss: flags for MCP configuration, settings and added directories have to be passed again.&lt;/p&gt;

&lt;p&gt;Cursor resumes only from its CLI and documents three ways, "To resume the most recent conversation, use &lt;code&gt;agent resume&lt;/code&gt;, &lt;code&gt;--continue&lt;/code&gt;, or the &lt;code&gt;/resume&lt;/code&gt; slash command", plus a session list to pick from. Codex resumes the last session or a chosen one, with its resume commands marked stable in its own reference. VS Code needs no flag: its agent sessions view restores previous sessions in the interface. Windsurf, now Devin Desktop, resumes from its CLI and from the history panel in the desktop app.&lt;/p&gt;

&lt;p&gt;The practical consequence is small and useful. If what you lost was the conversation you were just in, you did not lose it. You started a new one instead of reopening that one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real gap is the new session
&lt;/h2&gt;

&lt;p&gt;Claude Code states it outright: each session begins with a fresh context window. A new task, a new terminal, a different day, and none of the five carries the earlier conversation into it. That is the case people actually mean when they say the agent forgets, and it is not a bug in any of the five. It is the unit of persistence being the session.&lt;/p&gt;

&lt;p&gt;What every one of the five does reload in a new session is the static instruction file a person wrote. VS Code calls them always-on instructions, included in every chat request. Cursor reads its own rules format and other tools' files as well. Windsurf's default agent injects rules at the start of every session. That is why a fresh session still knows your conventions while knowing nothing about last week, and it is also not memory: an instruction file is a thing a person decided once, and memory is what the agent accumulated from what actually happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resume is not a perfect rewind either
&lt;/h2&gt;

&lt;p&gt;All five document compaction, and here they stop agreeing. Claude Code is the only one that documents a choice rather than a silent swap: on Pro and Max plans, resuming a session idle for more than about an hour and over a hundred thousand tokens opens a dialog with three options, resume from summary, resume the full session as is, or stop asking. It is also unusually specific about the loss: its documentation says the summary replaces the verbatim conversation, that full tool outputs and intermediate reasoning are gone, and that the instruction file, the auto memory and up to five recently modified files are re-read from disk.&lt;/p&gt;

&lt;p&gt;Codex compacts on command and automatically at a configurable token limit, unset by default. VS Code is explicit about the loss and silent about the trigger: "important details from early in a long conversation might be compressed or lost", and no threshold appears anywhere in its current pages. Cursor documents that every chat shares a fixed context window and that the agent compacts as it fills, but names neither a threshold nor what is dropped. Windsurf documents a background compaction and a forced one, and says nothing about what a compaction keeps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Built-in cross-session memory: three of five, and the differences matter more than the presence
&lt;/h2&gt;

&lt;p&gt;One line to carry: if a new session knows your conventions but not last week, memory is not failing. An instruction file is working, and there is no memory at all.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;tool&lt;/th&gt;
&lt;th&gt;resume&lt;/th&gt;
&lt;th&gt;what compaction drops&lt;/th&gt;
&lt;th&gt;loaded in a new session&lt;/th&gt;
&lt;th&gt;built-in cross-session memory&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude Code&lt;/td&gt;
&lt;td&gt;five documented entry points, transcripts on disk&lt;/td&gt;
&lt;td&gt;tool outputs and intermediate reasoning gone; instruction file, auto memory and up to five recent files re-read&lt;/td&gt;
&lt;td&gt;instruction files, every session&lt;/td&gt;
&lt;td&gt;yes, on by default, four note types, per project&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cursor&lt;/td&gt;
&lt;td&gt;CLI only: resume, continue, or a session list&lt;/td&gt;
&lt;td&gt;mechanism documented, threshold and contents not&lt;/td&gt;
&lt;td&gt;rules, AGENTS.md and other tools' files&lt;/td&gt;
&lt;td&gt;contested: made generally available in July 2025, the interface for managing it removed in the 2.1.x series in November 2025 per the vendor's own forum, with no changelog entry; the same staff account said in January 2026 that only the interface went and the feature still works&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Codex&lt;/td&gt;
&lt;td&gt;resume last or chosen, marked stable&lt;/td&gt;
&lt;td&gt;manual command plus an automatic limit, unset by default&lt;/td&gt;
&lt;td&gt;instruction file, rebuilt every launch&lt;/td&gt;
&lt;td&gt;yes, off by default, in its own words&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VS Code&lt;/td&gt;
&lt;td&gt;sessions restore in the interface&lt;/td&gt;
&lt;td&gt;detail from early in the conversation may be lost, no threshold named&lt;/td&gt;
&lt;td&gt;always-on instruction files, automatically&lt;/td&gt;
&lt;td&gt;yes, experimental and on by default per the settings reference, three local scopes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windsurf (Devin Desktop)&lt;/td&gt;
&lt;td&gt;CLI flags and a history panel&lt;/td&gt;
&lt;td&gt;forced and background compaction, contents not described&lt;/td&gt;
&lt;td&gt;rules injected at the start of every session&lt;/td&gt;
&lt;td&gt;no, for the current default agent, stated in a warning callout&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Claude Code has auto memory, on by default in its own words. It writes four kinds of note for itself, tagged in the file: your role and preferences, corrections you gave it, ongoing project decisions that cannot be derived from the code or git history, and pointers to where information lives outside the project. Each project gets its own memory directory, and it loads at the start of every session alongside the instruction file.&lt;/p&gt;

&lt;p&gt;Codex has memories, and they are off until you turn them on: "Local Codex memories are off by default."&lt;/p&gt;

&lt;p&gt;VS Code shipped a memory tool with three scopes stated in a table: user memory persists across sessions and workspaces, repository memory persists across sessions but is workspace scoped, and session memory is cleared when the chat ends. All three are stored locally. Its settings reference lists the switch as experimental, describes it as enabling the tool "so agents can save and recall notes across conversations", and prints true in the Default column.&lt;/p&gt;

&lt;p&gt;Cursor is the one everybody gets wrong in both directions. There is no reference page for the feature, and the rules page points the persistent layer elsewhere: "Large language models don't retain memory between completions. Rules provide persistent, reusable context at the prompt level." Stopping there yields "Cursor has no memory", which is false: version 1.0 introduced it, "With Memories, Cursor can remember facts from conversations and reference them in the future", and 1.2 made it generally available. Then the interface for managing it went in the 2.1.x series in November 2025, with no changelog entry anywhere; the vendor's own support forum is the only place that states it, and the same staff account has since said both that the feature was removed and that only the interface went while the feature itself still works, managed by chat commands. I am reporting both statements because both are the vendor's.&lt;/p&gt;

&lt;p&gt;Windsurf needs its qualifier every time. Memories belong to the legacy Cascade agent, and the current default agent, Devin Local, is not Cascade. A warning callout at the top of the memories page and a line on the default agent's own page say the same thing: the default agent does not persist memories between sessions. Cascade's feature remains real and documented. What changed is which agent a new tab opens.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three memories, one company name
&lt;/h2&gt;

&lt;p&gt;One separation is worth making, because it is where most of the confusion arrives from. In late August 2026 Anthropic unified memory between its chat product and Cowork. That announcement is about the assistant applications and is precise about defaults: on by default on Free, Pro and Max plans, saving sensitive topics off by default, admin controlled on Team and Enterprise. Its body does not mention the coding tool or the API at all. So enabling memory in the chat app changes nothing about the agent in your terminal, and the auto memory in the coding tool is a different feature with a different scope. Same company, same word, two mechanisms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where a persistent memory layer closes the gap, and where it does not
&lt;/h2&gt;

&lt;p&gt;I work on one, so weigh this section hardest. Resume is a client feature, and a memory layer neither replaces nor competes with it: with or without one connected, the resume command behaves the same. What a layer adds is exactly the gap in the new session. A fact written once comes back in any later session, including a brand new one, on another day, and in a different tool entirely, because it lives outside the session rather than inside it.&lt;/p&gt;

&lt;p&gt;Two limits belong here, stated as plainly as the gaps above. The first is that this depends on the agent actually calling the write and the read; a tool being available is not the same as a model choosing to use it, which is why the standing-instruction technique exists at all. The second is that Claude Code's auto memory is a genuine close relative of the same idea, on by default, and saying otherwise would be false. The difference is scope: that directory is one tool and one project's worth of notes, while a memory layer follows one account across every connected tool. That is a real difference, and it is not a claim that their feature is worse.&lt;/p&gt;

&lt;h2&gt;
  
  
  A five-minute check on your own setup
&lt;/h2&gt;

&lt;p&gt;Open the tool you use most and resume yesterday's session rather than starting a new one. Then start a genuinely new session and ask it something only the earlier conversation could answer. If the new session knows it, cross-session memory is on. If it knows your conventions but not the conversation, what you have is the instruction file doing its job. Then check whether your tool's memory feature is enabled, which is not the default in one of the five and no longer has an interface in another. Finally, if you work across two tools, ask the second one the same question, because that boundary is the one none of the five crosses on its own.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I work on &lt;a href="https://mnemoverse.com" rel="noopener noreferrer"&gt;Mnemoverse&lt;/a&gt;, a persistent memory layer for AI agents connected over MCP, so weigh the last two sections accordingly. The full comparison with every source page is on our library.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>mcp</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
