I kept hitting import errors installing MCP servers. Not obscure ones — reference servers from companies whose names you know. After the third, I stopped debugging and started measuring.
The headline: roughly 43% of Python MCP servers on PyPI don't run on a fresh machine. The cause is one character.
Here is the method, because the number means nothing without it.
The trigger
The MCP Python SDK shipped 2.0 and removed several APIs. Any package that declares the SDK with a lower bound and no ceiling —
mcp>=1.0.0
— now resolves to 2.x on a clean install and dies on import. The maintainer's own machine has a working 1.x cached, so pip install never upgrades anything and the bug is invisible to the one person able to fix it. It only breaks for new users.
Stage 1: how many packages are exposed?
PyPI carries about 18,000 packages with "mcp" in the name. I took a seeded random sample of 300 (seed 20260806, so anyone can reproduce the exact draw). Of those, 212 were genuine Python MCP servers rather than clients, docs or forks.
183 of the 212 — 86.3% — declare the MCP SDK with no upper bound.
That is exposure, not breakage. A package can declare an unbounded dependency and still work fine, because it migrated its code and never tightened the pin. Metadata alone cannot tell you. So stage 1 is necessary and not sufficient, and reporting 86% as a failure rate would have been wrong.
Stage 2: install them and see
I drew 30 of those 183 at random and installed each in a genuinely fresh virtualenv — never reused, because a cached SDK is the entire reason this bug hides. Then I found each package's console entry points and ran them with stdin closed.
immediate traceback → broken
exits quietly on EOF, or blocks waiting for input → starts fine
Two failed to install at all. Of the 28 that installed, 14 failed to start.
Combined with stage 1, that puts roughly 43% of Python MCP servers on PyPI in a broken state on a fresh machine, 95% CI 26–61%. The interval is wide because 30 installs is a small sample, and I would rather publish the interval than a tidy number.
Three errors, one cause
ModuleNotFoundError: No module named 'mcp.server.fastmcp'
ImportError: cannot import name 'McpError'
AttributeError: 'Server' object has no attribute 'list_tools'
All three are the same event: code written against 1.x, running on an SDK that removed those APIs.
The part I didn't expect
I assumed the failures would be abandoned weekend projects. They weren't.
Separately from the random sample, I tested 23 established servers — widely used ones, including reference implementations from major companies. 18 of 21 that installed failed to start: 86%, against 50% for randomly drawn packages.
Better-known packages were worse. The likely reason is age. They were written early, against 1.x, and nobody has installed them from scratch since. Popularity doesn't protect you here; it correlates with having been written before the breaking change.
The trap I fell into myself
Having found that unbounded pins predict breakage, the obvious next move is to skip the installs and just read everyone's metadata. It's free and instant.
It doesn't work. I took four organisation-owned repos whose pins flagged them as broken and verified them properly in clean virtualenvs. Only one actually failed. Two started fine despite unbounded pins, and one failed to install for an unrelated reason.
The metadata heuristic over-predicts about 4:1. That is why the tool does real installs, and why I'm publishing this against my own headline number rather than quietly keeping the tidier version.
The fix
- mcp>=1.0.0
- mcp>=1.0.0,<2 I verified this on every server that failed: pinning below 2.0 restored startup in all of them.
If you maintain a Python package that depends on the MCP SDK, the check takes about four seconds and cannot be done on your dev machine:
python -m venv /tmp/checkme
/tmp/checkme/bin/pip install your-package-name
/tmp/checkme/bin/your-entry-point
A brand new virtualenv, not the one you already have.
Limitations
The install stage is n=30. Hence the 26–61% interval. The metadata stage is n=212 and much tighter.
Tested on Windows with Python 3.13. Resolution can differ across platforms.
"Starts without a traceback" is a low bar. It does not mean the server is correct — only that it runs.
Packages under a separate coordinated-disclosure process were excluded from published results.
Tools and data
Everything is open: the sampler, the verifier, the raw JSON results, and the seed.
https://github.com/junaidshahid-dev/mcp-probe
That repo also lists five false positives I found in my own tool and fixed, because a scanner that fails well-built packages is worse than no scanner at all.
If you maintain an MCP server, go and run the clean-venv check. It takes four seconds and it is not something you can see from where you're standing.
Top comments (0)