Skip to content
← All posts
Measurement 4 min read

Eight MCP servers publish no tools at all

They are listed, installable and reachable, and tools/list comes back empty. This figure was 13 until we deduplicated the corpus — a lesson in counting.

Of 4,757 servers in our corpus that the registry had scanned successfully, eight return an empty tools array.

Not a failed scrape, not an unreachable host — a successful scan of a server that publishes nothing:

dhanyyudi/bmkg-id                      BMKG MCP
metavolve-labs/intelligence-aeternum   iAeternum
stockfilm/stockfilm-mcp                Stockfilm
spacemolt/gameserver                   SpaceMolt
compress-new/compress-tokens           Compress.new
kinescope/kinescope-mcp                Kinescope MCP Server
astranl/astranl-mcp                    AstraNL
hello-3ubk/booboooking                 booboooking

All eight are remote HTTP servers. All eight are listed in a public registry, with a display name, ready to install.

Why it happens

We cannot see inside them, so this is inference from the shape.

The tools are behind auth. The most likely explanation for a remote server. The scanner connected without credentials, the server declined to enumerate, and returned an empty list rather than an error. From outside, “you may not see my tools” and “I have no tools” are the same response — which is a small protocol gap worth noticing.

It was deployed before the tools existed. A registry entry is cheap and a working server is not. Several of these look like placeholders.

The tools are registered after a handshake the scanner did not complete. A server that builds its tool list from a session, or from a config the client supplies, has nothing to report to a scanner that supplies neither.

What it means if you are building a client

An empty tools/list is not an error, and you have to handle it. Eight servers in a public registry return one. A client that treats an empty array as a failure will report a broken server; one that treats it as a working server with nothing to offer will show the user an empty menu. Neither is quite right, and the protocol gives you nothing to distinguish “empty because auth” from “empty because empty”.

If you are writing a server, that argues for returning an error when you cannot enumerate — an explicit refusal is more useful to a client than a truthful-looking empty list.

The more interesting finding is about counting

This number was 13 in the published survey. It is 8.

The corpus double-counted servers: the collector revisited entries while paginating, so tools.jsonl held 5,123 rows for 4,894 distinct names. One of these eight — dhanyyudi/bmkg-id — was collected four times. metavolve-labs and stockfilm twice each. Thirteen rows, eight servers.

We checked the obvious alternative explanation and it is not that: none of the eight published tools in any other row. There was no successful scrape being masked by a failed one. The 13 was simply the same eight servers counted repeatedly.

That error ran through every figure in the survey, and it is worth being explicit about which way it cut:

Published Corrected
Servers 4,951 4,749
Tools 87,146 82,549
Parameters 270,487 257,287
Popular pool 465 263

The rates barely moved — undescribed parameters 21.8% → 21.5%, zero-distinctive tools 17.4% → 17.7% — which is exactly why it went unnoticed for a fortnight. Percentages are robust to a duplicate; counts are not. And the damage concentrated in one pool: the popular pool was overstated by 77%, while the long tail was clean.

Every figure across the survey and these posts has been recomputed, and the deduplication is in load() in 03_analyse.py with the reasoning written down.

The lesson we would rather have learned earlier

Assert the invariant you are assuming. We assumed one row per server because the collector was written to produce one row per server. Nothing checked it. The check is one line:

assert len({r["qualifiedName"] for r in rows}) == len(rows)

That would have failed on the first run. Instead the number was published, and it took someone re-deriving figures from the raw corpus to notice that 5,123 rows contained 4,894 names.

If you publish data, the counting assumptions are the ones to test. A wrong rate looks wrong; a wrong count looks fine.

What this cannot tell you

Whether these eight servers are broken, gated, or unfinished. We only see the response.

And the 4,757 above is servers the registry scanned successfully — separate from the 133 whose detail request failed and the 4 the registry had never scanned. Those are different kinds of absence and we cannot tell a gated server from a dead one in any of the three.

Data, and the corrected analysis, in getmcpulse/mcp-schema-study.