One of our Macs runs AI coding agents unattended at night. On 2026-10-03 it stopped mid-job with ENOSPC — no space left on device — with more than 417 GB used on a 460 GB disk. Nobody had installed anything on purpose. Nobody had downloaded anything on purpose. The machine had been doing its normal work.
By the time we cleaned it, 50 GiB of the disk was ~/.cache/uv: 3,286,436 files, largely written by MCP servers. Another 14 GB was ~/.npm/_npx, for the same reason.
If you run any AI assistant with MCP tools — a desktop app, an IDE extension, a CLI — there is a fair chance the same thing is growing on your own machine right now. This is what it is, why the routine cleanup does not just work, and the exact commands that fixed it, with the numbers we measured.
Why MCP servers fill a disk nobody is watching
Most Python MCP servers are launched with uvx <package>. uvx is a fine tool: it resolves the package, builds an isolated environment and runs it, in seconds, with nothing to install by hand. The cost is where it keeps that environment — in uv's global cache, which uv never cleans up on its own. Everything it downloads and unpacks stays there until something deletes it.
uv does deduplicate: relaunching the same pinned server reuses what is already cached, so a restart costs nothing. The cache grows when new versions arrive — an unpinned uvx <package> picks up every new release, and three different tools pinning three different versions of the same server keep three environments. Add a few servers, a few clients and a few months, and the old versions are still all there.
The npm side is the same story: npx-launched MCP servers land in ~/.npm/_npx, and nothing prunes that either.
Why the obvious cleanup does not just work
The documented fix is uv cache clean. On a machine with MCP servers running, it blocks: the cache is lock-held by the live uvx processes. The thing filling the disk is also the thing holding the lock. That is why this ends up as a chore someone has to be present for, rather than something a nightly job does — you have to stop the servers first, which means every AI client on that machine loses its MCP tools until it is restarted.
Two more traps we hit on the way, so you do not have to:
uv cache sizeis experimental and walks the whole tree. On 3.29 million files it takes minutes and looks hung. So doesdu -sh. Measure once, not in every step.uv cache pruneis safe to run while servers are live — it only removes unreachable objects. We did not test it on this cache; it is the one that belongs in a cron.
The playbook, with the real numbers
We freed the npm caches first (that bought the headroom to keep working), then ran the block below for uv. The host is the Mac above; the shell is zsh. The output is reproduced with paths shortened, uv's "experimental" warning lines dropped, and one annotation added.
Before pasting it: pkill -f uvx matches the full command line of every process. Pasted into an interactive shell it is fine; inside a script or a zsh -c '…' wrapper whose own command line contains the string, it kills the wrapper. Run pgrep -fl uvx first and look at what you are about to stop.
echo "== BEFORE =="; uv cache size; df -h / | tail -1; du -sh ~/.cache/uv
pkill -f uvx; sleep 2
uv cache clean
echo "== AFTER =="; uv cache size; df -h / | tail -1; du -sh ~/.cache/uv
== BEFORE ==
53771051008 # uv cache size, bytes = 50.1 GiB
/dev/disk3s3s1 460Gi 12Gi 54Gi 18% /
50G /Users/…/.cache/uv
Clearing cache at: .cache/uv
Removed 3286436 files (42.6GiB)
== AFTER ==
0
/dev/disk3s3s1 460Gi 12Gi 67Gi 15% /
du: /Users/…/.cache/uv: No such file or directory
3,286,436 files gone, 42.6 GiB according to uv. df recovered 13 Gi of that (54 → 67 Gi free); we did not chase the gap — local APFS snapshots or files shared with existing environments would both explain it, and neither was measured.
One side effect to plan for: pkill -f uvx stops the MCP servers for every AI client on the machine. Restart those clients after the clean.
What we changed so it does not come back
Clearing a cache is not a fix; it is a reset. The fix is treating disk like any other budget an unattended job can blow:
- Pre-flight in every long-running job. Before a batch starts, read
dfand refuse to run below a floor — ours is 30 GiB free. A job that dies atENOSPChalfway through corrupts its own results; a job that refuses to start costs nothing. uv cache prunebefore each batch. Safe while servers run, free, and it stops unreachable objects from accumulating.- A scheduled, attended
uv cache clean— monthly is enough — with the servers stopped. Same for~/.npm/_npx. - Measure it on a card, not in your head. The before/after block above now lives on the ticket that tracked the incident, with the host name on it. The next person who hits
ENOSPCon that machine finds the recipe in one search instead of re-deriving it at 2am.
If you are running AI agents unattended on real machines and want the guard rails built in rather than discovered, talk to us — this is what we do.