A field test by the Anclum team, November 12, 2026.
An install line on an MCP directory is a promise: paste this, and a server starts. We wanted to know how often that promise holds for the servers that speak stdio, so we took the command exactly as extracted from each README and ran it — no corrections, no added flags, no "you obviously meant".
This page covers seven servers. A single JSON-RPC initialize request was sent to each, and we recorded what came back within 45 seconds. That is the entire test: if the process can complete a handshake, it started; if it cannot, the reason it cannot is the interesting part.
Three of the seven completed the handshake. The other four failed for three different reasons, and none of them failed because the package was broken.
What we counted
| Verdict | Count | What it means |
|---|---|---|
STARTED | 3 | A JSON-RPC reply with id=1 and serverInfo.name / serverInfo.version |
NEEDS_ARGS | 1 | Exited with a usage line or a list of required arguments |
NEEDS_DATA | 1 | Exited non-zero; stderr points at a missing external data file |
NEEDS_AUTH | 2 | Exited, or is waiting, for a credential we did not supply |
CRASHED | 0 | Exited non-zero with an error in its own code or dependencies |
Handshake rate for this group: 3 of 7 = 42.9%.
That percentage describes these seven stdio servers and nothing else. The wider catalog list mixes transports: 17 entries document an HTTP transport, 2 are registration commands rather than launch commands, and 12 carry no transport signal at all. HTTP and registration entries are not applicable to a stdio probe, no-transport entries are not assumed to be stdio, and none of them sit in the denominator above.
The seven
| Server | Command, run verbatim | Verdict | First decisive line | Time (+08:00) |
|---|---|---|---|---|
atomno-mcp-mcp-egrul | uvx atomno-mcp-egrul | STARTED | serverInfo {"name":"mcp-egrul","version":"3.4.11"} | 02:34:21 |
archerkattri-mathlas | uvx mathlas-mcp | STARTED | serverInfo {"name":"mathlas","version":"1.5.0"}, protocol 2025-06-18 | 02:36:41 |
ihuzaifashoukat-x-use | uvx x-use-mcp==3.2.0 | STARTED | serverInfo {"name":"x-use","version":"3.2.0"} | 02:37:29 |
getpoststack-mcp | npx @poststack.dev/mcp | NEEDS_AUTH | POSTSTACK_API_KEY environment variable is required | 02:23:41 |
gitmotion-ntfy-me-mcp | npx ntfy-me-mcp | NEEDS_AUTH | [NTFYME-LOG-ERROR]: NTFY_TOPIC environment variable is required. | 02:24:28 |
jeanfrancoisgagne-crapkit | uvx crapkit | NEEDS_ARGS | crapkit: error: the following arguments are required: command | 02:35:54 |
lizhuojunx86-chinese-history-mcp | uvx chinese-history-mcp | NEEDS_DATA | FileNotFoundError: 找不到语料库: /root/data/corpus.db | 02:35:07 |
Two notes on that table, because both are easy to misread:
- The
crapkitcommand in the catalog is incomplete. Its own usage line listsmcpas a subcommand, so the working shape isuvx crapkit mcp. Run verbatim, it exits 2 and asks for the subcommand. - The
chinese-history-mcpstderr starts withTraceback (most recent call last):. The decisive line is theFileNotFoundErrorabove: the code and its dependencies loaded fine, and what is missing is a corpus file at/root/data/corpus.db(the package documentsSTORYEXTRACTOR_DBfor pointing elsewhere).
How we judged
Each verdict needs the stderr (or stdout, for a handshake) first line quoted — a label alone is not evidence. Three boundary rules decided the close calls:
- An exception class is not the criterion; the object it points at is.
FileNotFoundErrorraised from a package's own entry point is stillNEEDS_DATAwhen the missing object is an external data file.ImportErrornaming a package module isCRASHED. - When two labels both explain the exit, follow what the reader has to do next. Go fetch data →
NEEDS_DATA. Go change the command →NEEDS_ARGS. STARTEDrequires a parsed JSON-RPC result, not merely output that looks like JSON:id=1plusserverInfo.nameandserverInfo.version. Staying alive without a handshake is not a start.
One gap we are keeping visible: the two NEEDS_AUTH rows never exited with a recorded rc — the process was still waiting when the probe stopped, so rc is null for both. Their verdict rests on the stderr line above plus the absence of a handshake. A rerun should record rc for those two.
What we did not do
Each command was executed once, verbatim, and nothing else was tried for it: no retry with added arguments, no credential injection, no second attempt after a failure. The working shape we note for crapkit is a reading of its own usage text, not a run we counted. The 45-second limit is a probe deadline, not a verdict — a stdio server that stays alive is expected to be killed by it.
This page adds no executions beyond those seven. The HTTP, registration and no-transport entries in the catalog are outside this count; they are neither passes nor failures here, and no combined rate is implied.
Probe environment: Linux host, Node and Python available through npx / uvx; uv 0.13.7; a throwaway cache under /tmp with UV_PYTHON_DOWNLOADS=never. Servers that need credentials were not given any.
Also in the lab: We told Google not to index 10,538 of our 24,667 pages. Two batches of those were our mistake.