# Stop spawning an MCP server per agent session (and what it won't fix)

> Source: <https://dev.to/tonydzi/stop-spawning-an-mcp-server-per-agent-session-and-what-it-wont-fix-5e2m>
> Published: 2026-09-10 09:23:26+00:00

Ten parallel Claude sessions. Ten copies of the same MCP server.

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

**TL;DR:** an MCP server registered as `stdio` 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 **not** 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.

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:

| server | copies | summed RSS | 
|---|---|---|
| telegram | ~9 | ~2.7 GB | 
| mongodb | ~26 | ~3.0 GB | 
| n8n | ~15 | ~2.9 GB | 
|  | ~15 | ~1.5 GB | 
| launcher wrappers ( `npx` /`cmd` ) | ~60 | ~5 GB | 

Read those numbers honestly, because I nearly published them dishonestly. **Summing RSS over-counts.** 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 *copy count* — 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.

Twenty-six clients against one database is not a memory problem. It is a concurrency problem wearing a memory problem's clothes.

Measure your own machine before you believe anyone's table, including mine:

```
python scripts/mcp_diet_measure.py
```

If it prints `copies 1` everywhere, you have nothing to fix. That is also what a converted machine looks like: our hub now reports one `telegram` and one `n8n` process serving every open session.

Run the server once, bound to loopback, and point every client at the URL:

```
"mcpServers": {
  "telegram": { "type": "sse", "url": "http://127.0.0.1:8765/sse" }
}
```

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.

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 `HKCU` Run key on Windows, `launchd` on macOS, `systemd --user` on Linux.

This is the one thing to know before you start.

**Restarting a shared daemon blinds every live session.** They do not reconnect. Every subsequent call answers `-32602 Invalid request parameters` 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.

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.

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.

Three failures from this build are worth more than the recipe, because each one produced a *confident wrong answer* rather than an error.

**1. The measurement tool invented duplicates that did not exist.** The first version identified a server's processes by its launch command — `"command": "node"`. 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.

**2. `Win32_Process.CommandLine` comes back empty** 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 **port** (`Get-NetTCPConnection` / `lsof`), and always print a count of "processes I could not read" instead of silently under-reporting. Silence and zero must never look the same.

**3. `--` inside an XML comment makes an invalid plist**, and `launchctl load` fails silently on it. Nothing in the terminal told us. It was caught only by running `plistlib.load` over the file in a test.

There is a theme there, and it is not "we write buggy code." It is that infrastructure tooling fails *quietly and plausibly*, which is exactly the failure mode humans are worst at catching.

`copies 1` is already your reality. Adding a daemon adds a moving part and buys you nothing.
**It does not save tokens.** 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.

The repo is MIT and server-agnostic — nothing in it is specific to one integration:

**One shared MCP daemon per machine, instead of a copy in every agent session.**

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.

Built and run at [Palo Alto AI Research Lab](https://github.com/tonydzi/tonydzi)
where a fleet of Claude sessions across five machines talks to its MCP servers through
exactly this setup.

An MCP server registered as `stdio` is spawned **per client session**. 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.

What we measured on one laptop, 2026-08-01 to 08-03:

| server | copies | summed RSS | 
|---|---|---|
| telegram | ~9 |  | 

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 `PROMPT.md` you can paste into Claude Code or Codex to have it do the conversion for you.

**One ask:** run `mcp_diet_measure.py` 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.

Built at [Palo Alto AI Research Lab](https://github.com/tonydzi/Palo-Alto-AI-Research-Lab), where a fleet of Claude sessions across five machines talks to its MCP servers through exactly this setup.

🤖 For machines: hand [the repo link](https://github.com/tonydzi/mcp-daemon-diet) to your coding agent (Claude Code, Codex, Cursor) and it will figure everything out — `PROMPT.md` is written for it, not for you.

Talk to the two co-founders, one biological, one synthetic: [calendly.com/paloaltolab](https://calendly.com/paloaltolab). Direct line: WhatsApp +1 341 222 9178 (busy, six kids, still answers).

🔗 All our channels and contacts in one place: [https://linktr.ee/PaloAltoAI](https://linktr.ee/PaloAltoAI)

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: [calendly.com/paloaltolab](https://calendly.com/paloaltolab).

Invented by Mycroft and Tony Dzi (Anton Dziatkovskii), Palo Alto AI Research Lab. Proudly made in Silicon Valley.

*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.*
