Originally published on hexisteme notes.
Four of my MCP servers were dead.
All four reported the same useless thing: .
It looked like four incidents.
It was two — and one of them accounted for three.
Server Symptom Actual cause A Corrupted package-runner cache — a partial install left a dependency missing B SDK 2.0.0: renamed to C SDK 2.0.0: the module removed D Same as C Simultaneous death is a signal, not a coincidence B, C, and D were different repos, different authors, different purposes.
They died the same morning.
Three independent failures landing on the same day is possible.
One shared thing moving is overwhelmingly more likely.
And the shared thing was sitting in the launch commands: A dependency with no upper bound is a different program every time you run it.
The SDK cut 2.0.0 and three deterministic imports became undefined simultaneously.
The diagnostic mistake I nearly made was going server by server.
That path reads the same stack trace three times and calls it three bugs.
The moment I saw in the first trace, the right question wasn't "how do I fix this server" — it was "what else shares this SDK?" When several components fail at the same time, stop looking at the components and look at the shared dependency graph.
That's a cheap habit and it collapses an afternoon into ten minutes.
Same-timestamp failures across unrelated systems are usually one upstream event.
Pinning everything is the wrong fix The reflex is "pin all of it." That's worse than the disease.
An upper bound freezes security patches along with breaking changes.
Pinning all 22 of my servers would trade three outages for twenty-two units of staleness debt, forever, most of it on servers that were never going to break.
What I adopted: Pin only what actually broke (three of twenty-two).
Record the pin date in a pin ledger.
Flag any pin older than 90 days for "can this bound come off yet?" Leave the other sixteen unpinned.
Deal with breakage when it happens.
The principle underneath: A pin is a response to an incident, not a prevention.
The prevention is detection.
You cannot pin your way out of upstream churn — you can only choose whether you find out from a health check or from a user.
So I built the health check.
What the health check actually looks for Two things, and the second one is the point: Retrospective — which servers are dead right now, plus the exact command to reproduce each failure, so diagnosis starts at second zero rather than after ten minutes of reconstructing the invocation.
Prospective — which servers could die the same way.
This is the real product.
Scoping that second list took a revision.
My first pass flagged 19 servers and the signal was mush.
The correct population is narrower: servers that re-resolve upstream on every launch through a package runner (, , , ).
An absolute-path binary — a venv Python, a built Node script — has no version to pin; neither does a URL transport.
Including them wasn't cautious, it was noise.
Sixteen, not nineteen.
A watchlist that flags things you can't act on trains you to ignore the watchlist.
The side finding: enabled ≠ used While I was in there, I compared 60 days of measured usage against what was actually switched on.
A language server for the platform accounting for ~13% of my activity (310 prompts): off.
A language server for a language I'd mentioned once in five months, with no session history in any relevant directory: on.
I swapped them.
Nobody chose that configuration.
There was a good reason for each toggle at the moment it was set, the reason expired, and the toggle stayed.
Plugin state rots silently because nothing in the system ever asks whether the original justification still holds.
Worth a periodic diff: what's enabled, against what you actually use.
Both halves are measurable.
Almost nobody measures them together.
Mirroring configs, and the tool I deliberately left out I also mirrored the server set into a second client.
Two things worth stealing: Package-runner servers need an