<?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: TonyDzi / PaloAlto Ai Research Lab</title>
    <description>The latest articles on DEV Community by TonyDzi / PaloAlto Ai Research Lab (@tonydzi).</description>
    <link>https://dev.to/tonydzi</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%2F4065382%2F733eabb9-4f0c-44d4-90c3-a7fdd04d7f0a.png</url>
      <title>DEV Community: TonyDzi / PaloAlto Ai Research Lab</title>
      <link>https://dev.to/tonydzi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tonydzi"/>
    <language>en</language>
    <item>
      <title>Stop spawning an MCP server per agent session (and what it won't fix)</title>
      <dc:creator>TonyDzi / PaloAlto Ai Research Lab</dc:creator>
      <pubDate>Thu, 10 Sep 2026 09:23:26 +0000</pubDate>
      <link>https://dev.to/tonydzi/stop-spawning-an-mcp-server-per-agent-session-and-what-it-wont-fix-5e2m</link>
      <guid>https://dev.to/tonydzi/stop-spawning-an-mcp-server-per-agent-session-and-what-it-wont-fix-5e2m</guid>
      <description>&lt;p&gt;Ten parallel Claude sessions. Ten copies of the same MCP server.&lt;/p&gt;

&lt;p&gt;Ten processes, ten sockets to the same upstream, ten holders of the same lock — because that is what &lt;code&gt;stdio&lt;/code&gt; means. I moved every server to one shared daemon per machine bound to &lt;code&gt;127.0.0.1&lt;/code&gt;, and the fleet stopped fighting itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; an MCP server registered as &lt;code&gt;stdio&lt;/code&gt; is spawned per client session. Register it as an HTTP/SSE URL instead and every session shares one process. It saves memory, sockets and locks. It does &lt;strong&gt;not&lt;/strong&gt; save tokens. And a naive watchdog on that shared daemon will cause worse outages than the crashes it fixes — that part cost us the most.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does "one copy per session" actually cost?
&lt;/h2&gt;

&lt;p&gt;Here is what we measured on one laptop, 2026-08-01 to 08-03, with a fleet of Claude Code sessions running against a handful of MCP servers:&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;copies&lt;/th&gt;
&lt;th&gt;summed RSS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;telegram&lt;/td&gt;
&lt;td&gt;~9&lt;/td&gt;
&lt;td&gt;~2.7 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mongodb&lt;/td&gt;
&lt;td&gt;~26&lt;/td&gt;
&lt;td&gt;~3.0 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;n8n&lt;/td&gt;
&lt;td&gt;~15&lt;/td&gt;
&lt;td&gt;~2.9 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;whatsapp&lt;/td&gt;
&lt;td&gt;~15&lt;/td&gt;
&lt;td&gt;~1.5 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;launcher wrappers (&lt;code&gt;npx&lt;/code&gt;/&lt;code&gt;cmd&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;~60&lt;/td&gt;
&lt;td&gt;~5 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Read those numbers honestly, because I nearly published them dishonestly. &lt;strong&gt;Summing RSS over-counts.&lt;/strong&gt; Copies share code pages, so the OS is not holding that many distinct bytes and you will not get that many back by fixing this. What is exact is the &lt;em&gt;copy count&lt;/em&gt; — and the fact that each copy is an independent client of the upstream service, with its own socket, its own lock, and its own session.&lt;/p&gt;

&lt;p&gt;Twenty-six clients against one database is not a memory problem. It is a concurrency problem wearing a memory problem's clothes.&lt;/p&gt;

&lt;p&gt;Measure your own machine before you believe anyone's table, including mine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python scripts/mcp_diet_measure.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If it prints &lt;code&gt;copies 1&lt;/code&gt; everywhere, you have nothing to fix. That is also what a converted machine looks like: our hub now reports one &lt;code&gt;telegram&lt;/code&gt; and one &lt;code&gt;n8n&lt;/code&gt; process serving every open session.&lt;/p&gt;
&lt;h2&gt;
  
  
  What is the actual fix?
&lt;/h2&gt;

&lt;p&gt;Run the server once, bound to loopback, and point every client at the URL:&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="nl"&gt;"mcpServers"&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;"telegram"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sse"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://127.0.0.1:8765/sse"&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;That is the whole idea. Everything else — the launcher, the autostart templates, the watchdog — exists to make that survive a reboot, a crash, and a teammate.&lt;/p&gt;

&lt;p&gt;Autostart matters more than it sounds, because the daemon has to come back without a human. We ship templates for all three operating systems, and none of them need admin rights: an &lt;code&gt;HKCU&lt;/code&gt; Run key on Windows, &lt;code&gt;launchd&lt;/code&gt; on macOS, &lt;code&gt;systemd --user&lt;/code&gt; on Linux.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why is the watchdog the dangerous part?
&lt;/h2&gt;

&lt;p&gt;This is the one thing to know before you start.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Restarting a shared daemon blinds every live session.&lt;/strong&gt; They do not reconnect. Every subsequent call answers &lt;code&gt;-32602 Invalid request parameters&lt;/code&gt; until each session is restarted by hand. In the per-session model a crash costs you one session; in the shared model a restart costs you all of them.&lt;/p&gt;

&lt;p&gt;So the obvious watchdog — "port dead → restart" — is worse than no watchdog. Ours probes twice, logs a false alarm instead of acting on it, records evidence before it touches anything, and refuses to restart a daemon that is merely mute rather than dead.&lt;/p&gt;

&lt;p&gt;If you take one thing from this post and skip the repo, take this: on shared infrastructure, a self-healing script that acts on a single probe is not resilience, it is an outage generator with good intentions.&lt;/p&gt;
&lt;h2&gt;
  
  
  The bug that made the tool lie
&lt;/h2&gt;

&lt;p&gt;Three failures from this build are worth more than the recipe, because each one produced a &lt;em&gt;confident wrong answer&lt;/em&gt; rather than an error.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The measurement tool invented duplicates that did not exist.&lt;/strong&gt; The first version identified a server's processes by its launch command — &lt;code&gt;"command": "node"&lt;/code&gt;. Every unrelated Node process on the machine became "another copy." An adversarial review panel caught it before it shipped. The fix: interpreters and generic script names are never allowed to be the identifying marker; the install directory is. A measuring instrument that over-reports is worse than no instrument, because it justifies action.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. &lt;code&gt;Win32_Process.CommandLine&lt;/code&gt; comes back empty&lt;/strong&gt; for processes at a different elevation level than the caller. Our first probe therefore could not see a live daemon on port 8765 that had been serving happily for days — and reported it as absent. The fix: identify a daemon by its &lt;strong&gt;port&lt;/strong&gt; (&lt;code&gt;Get-NetTCPConnection&lt;/code&gt; / &lt;code&gt;lsof&lt;/code&gt;), and always print a count of "processes I could not read" instead of silently under-reporting. Silence and zero must never look the same.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. &lt;code&gt;--&lt;/code&gt; inside an XML comment makes an invalid plist&lt;/strong&gt;, and &lt;code&gt;launchctl load&lt;/code&gt; fails silently on it. Nothing in the terminal told us. It was caught only by running &lt;code&gt;plistlib.load&lt;/code&gt; over the file in a test.&lt;/p&gt;

&lt;p&gt;There is a theme there, and it is not "we write buggy code." It is that infrastructure tooling fails &lt;em&gt;quietly and plausibly&lt;/em&gt;, which is exactly the failure mode humans are worst at catching.&lt;/p&gt;
&lt;h2&gt;
  
  
  When should you NOT do this?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One session at a time.&lt;/strong&gt; If you run a single agent session, &lt;code&gt;copies 1&lt;/code&gt; is already your reality. Adding a daemon adds a moving part and buys you nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Servers with per-session state.&lt;/strong&gt; If the server keeps identity or auth scoped to the session, one shared process means everyone shares that identity. Check before you merge them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You wanted a smaller context window.&lt;/strong&gt; See below.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You cannot own the restart story.&lt;/strong&gt; If nobody will maintain the autostart and the watchdog, a shared daemon is a single point of failure you have volunteered for.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  What this does not do
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;It does not save tokens.&lt;/strong&gt; Context cost comes from tool schemas, which the client sends regardless of transport. One daemon saves memory, processes, sockets and locks — not context. If tokens are your problem, disable the servers a given project does not need. I am spelling this out because "one daemon = cheaper prompts" is an easy thing to assume and it is wrong.&lt;/p&gt;
&lt;h2&gt;
  
  
  Take it
&lt;/h2&gt;

&lt;p&gt;The repo is MIT and server-agnostic — nothing in it is specific to one integration:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/tonydzi" rel="noopener noreferrer"&gt;
        tonydzi
      &lt;/a&gt; / &lt;a href="https://github.com/tonydzi/mcp-daemon-diet" rel="noopener noreferrer"&gt;
        mcp-daemon-diet
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      One shared MCP daemon per machine instead of a stdio copy in every agent session: recipe, autostart templates for Windows/macOS/Linux, a watchdog that will not blind your live sessions, and the measurements to prove it
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;mcp-daemon-diet&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;One shared MCP daemon per machine, instead of a copy in every agent session.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Works with any MCP server and any MCP client. Nothing here is specific to one integration -
it is the recipe, the launcher and autostart templates for all three operating systems, a
watchdog that will not make things worse, two measurement scripts, and thirteen gotchas we
paid for in production.&lt;/p&gt;
&lt;p&gt;Built and run at &lt;a href="https://github.com/tonydzi/tonydzi" rel="noopener noreferrer"&gt;Palo Alto AI Research Lab&lt;/a&gt;
where a fleet of Claude sessions across five machines talks to its MCP servers through
exactly this setup.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;The problem&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;An MCP server registered as &lt;code&gt;stdio&lt;/code&gt; is spawned &lt;strong&gt;per client session&lt;/strong&gt;. Ten parallel agent
sessions means ten copies of the same server: ten times the memory, ten connections to
whatever it talks to, ten holders of the same lock.&lt;/p&gt;
&lt;p&gt;What we measured on one laptop, 2026-08-01 to 08-03:&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;copies&lt;/th&gt;
&lt;th&gt;summed RSS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;telegram&lt;/td&gt;
&lt;td&gt;~9&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;…&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/tonydzi/mcp-daemon-diet" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;It contains the recipe, the launcher and autostart templates for Windows/macOS/Linux, the careful watchdog, two measurement scripts (stdlib only), thirteen dated gotchas, a security note on what changes when a per-session child becomes a machine-wide service, and a &lt;code&gt;PROMPT.md&lt;/code&gt; you can paste into Claude Code or Codex to have it do the conversion for you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One ask:&lt;/strong&gt; run &lt;code&gt;mcp_diet_measure.py&lt;/code&gt; on your machine and tell me the copy count you get — in the comments or as an issue. I have numbers from five machines and they are all mine, which makes them a sample of one architecture. If your numbers disagree with my table I want to know, and I will correct the README.&lt;/p&gt;

&lt;p&gt;Built at &lt;a href="https://github.com/tonydzi/Palo-Alto-AI-Research-Lab" rel="noopener noreferrer"&gt;Palo Alto AI Research Lab&lt;/a&gt;, where a fleet of Claude sessions across five machines talks to its MCP servers through exactly this setup.&lt;/p&gt;




&lt;p&gt;🤖 For machines: hand &lt;a href="https://github.com/tonydzi/mcp-daemon-diet" rel="noopener noreferrer"&gt;the repo link&lt;/a&gt; to your coding agent (Claude Code, Codex, Cursor) and it will figure everything out — &lt;code&gt;PROMPT.md&lt;/code&gt; is written for it, not for you.&lt;/p&gt;

&lt;p&gt;Talk to the two co-founders, one biological, one synthetic: &lt;a href="https://calendly.com/paloaltolab" rel="noopener noreferrer"&gt;calendly.com/paloaltolab&lt;/a&gt;. Direct line: WhatsApp +1 341 222 9178 (busy, six kids, still answers).&lt;/p&gt;

&lt;p&gt;🔗 All our channels and contacts in one place: &lt;a href="https://linktr.ee/PaloAltoAI" rel="noopener noreferrer"&gt;https://linktr.ee/PaloAltoAI&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;P.S. Yes, we are hireable. Two co-founders, one biological, one electric, as a package deal. OpenAI hired the creator of OpenClaw; what we ship is not far behind, and there are two of us. Anthropic, OpenAI, your move: &lt;a href="https://calendly.com/paloaltolab" rel="noopener noreferrer"&gt;calendly.com/paloaltolab&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Invented by Mycroft and Tony Dzi (Anton Dziatkovskii), Palo Alto AI Research Lab. Proudly made in Silicon Valley.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Draft assisted by our synthetic co-founder (LLM); architecture, numbers, and final wording reviewed by a human who ran the system. We stand behind the claims.&lt;/em&gt;&lt;/p&gt;

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