Someone ran npx -y against 922 npm-published MCP servers, sent them JSON-RPC initialize and tools/list calls, and documented what happened. The results are rough: only 359 responded cleanly. The other 563 failed in 15 distinct ways—and most of those failures reveal a packaging problem, not an MCP problem.
The Startup Hang Problem
Two hundred sixty-one servers—28% of everything published to npm as an MCP server—never made it past their own startup. The signature is distinctive: "[stderr] connecting to upstream..." followed by silence for 120 seconds until the introspection runner gives up and kills the process. These servers spawn fine, load their packages, run constructors, and then try to phone home to their upstream API before answering the protocol handshake. Lazy-connect your external dependencies or you're in this bucket.
How the Introspection Works
The MCP protocol has a built-in discovery mechanism: send tools/list after initialize and you get back full JSON Schema for every tool the server exposes. No README scraping, no LLM interpretation—just the actual protocol doing what it's designed to do. The runner spawned npx -y per package over stdio, sent the proper JSON-RPC handshake sequence (initialize → notifications/initialized → tools/list), then captured results with a 120-second timeout and SIGTERM cleanup. Eight concurrent processes, about 25 minutes wall time for all 922 packages.
Failure Mode Breakdown
The failures fell into predictable buckets. Beyond the 261 init_timeouts: 172 had generic npm_install failures (npx itself couldn't run), 54 exited with usage errors before listing tools, and 42 needed environment variables without clear error messages. Eleven servers had broken package.json—bad main fields or missing bin targets that crash on require. These don't need credentials; they needed a smoke test before publish.
The Credential Wall
The needs_* buckets (needs_slack_token, needs_google_creds, needs_openai_key, needs_stripe_key, etc.) add up to roughly 109 servers—almost 12% of the published set. If you're populating an agent's tool list by parsing READMEs or hitting /tools without credentials, those show as zero-tool servers in your index. They're not zero-tool servers. They expose 5, 12, maybe 40 tools and they're waiting for a key you didn't supply.
Windows-Specific Gotchas
The runner ran on Windows, which introduced two fun edge cases. First: npx on Windows resolves to npx.cmd, a batch script. Node's child_process.spawn without shell:true won't invoke .cmd files—you get ENOENT even though where npx shows it on PATH. The fix is spawn('cmd', ['/c', 'npx', '-y', pkg], ...). Second: default Windows code page isn't UTF-8, so any tool description with a German umlaut, em-dash, or curly quote causes JSON parse errors unless you force encoding='utf-8' on the pipe. Three servers in this run had non-ASCII characters; without proper encoding they'd all be miscounted as crashes.
What MCP Server Authors Should Fix
The data points to concrete patterns: defer upstream connections until the first tool call instead of running them during initialize, don't require CLI arguments before you'll list your own tools (accept a config path from an env var or default gracefully), document required environment variables clearly in stderr messages so users get actionable errors rather than silent hangs, and ship package.json that actually points at files that exist. Eleven broken_install servers had main fields pointing at non-existent files—zero credentials needed, just a publish-time sanity check.
The Dataset
The full results—9,922 tool schemas across 922 server status rows—are published on HuggingFace as automatelab/mcp-servers-tool-catalog under CC-BY-4.0. The pipeline runs monthly via GitHub Actions and re-introspects everything on the first of each month. If your server landed in a failure bucket it shouldn't be in, open a PR against the AutomateLab-tech/mcp-tool-catalog repo.
The Bottom Line
The npm MCP ecosystem is publishing a lot of broken packages. Most failures aren't protocol issues—they're startup sequencing bugs, missing dependency deferral, and zero-effort smoke testing before publish. If you're building an MCP client or agent framework, the introspection approach here beats README parsing every time: just use the damn protocol for discovery like it was designed to do.