Twee getallen over hetzelfde ecosysteem, allebei van dit jaar.
Een studie uit juli komt tot de conclusie dat bestaande MCP-scanners 96,89% van de servers als risicovol aanmerken. Ik draai zelf een scanner over datzelfde ecosysteem, en die zet 63% van de servers in de laagste risicoband.
Allebei die uitkomsten zijn eerlijk. Geen van beide liegt over de data. Het gat ertussen is het onderwerp van deze post, want ze zijn het oneens over wat een scanner mag beweren.
Wat het onderzoek naar scanners aantrof
Het paper heet Rethinking MCP Security: A Large-Scale Study of Runtime MCP Servers and Security Scanner Reliability en is ingediend op 13 juli 2026. De auteurs bouwden MCPZoo, 64.611 unieke servers waarvan er meer dan 37.288 goed genoeg draaien voor dynamische analyse, en lieten de bestaande scanners erop los.
Hun bevinding, in hun eigen woorden:
While existing scanners report that 96.89% of servers are risky, we find that these signals are unreliable. In particular, manual validation shows that less than 50% of sampled alerts are true positives, and scanner outputs exhibit clear inconsistency across scanners.
Minder dan de helft van de meldingen blijft overeind zodra een mens ernaar kijkt. En de scanners zijn het onderling oneens, wat betekent dat er bij elke server waarover ze van mening verschillen hooguit één gelijk kan hebben.
Scanners zijn nog steeds de moeite waard. Het probleem is de scanner die je een cijfer geeft en daarna zwijgt, want je kunt niet zien welke helft van zijn output je in handen hebt.
De andere meting op ecosysteemschaal, A First Look at the Security Issues in the Model Context Protocol Ecosystem (herzien in april 2026, 67.057 servers over zes publieke registries), kijkt naar de voordeur van het ecosysteem: "At the registry-level, weak vetting and ownership checks allow adversarial or hijacked servers to enter hosts." Hun eigen tool markeerde 833 kwetsbare servers en 18 met verdachte beschrijvingen, op een totaal van 67.057.
Twee studies, sterk uiteenlopende aantallen meldingen, hetzelfde ecosysteem. Dat is het patroon.
Drie dingen die een signaal kan zijn
Ik bouwde MCP Observatory deels om dit probleem ergens kwijt te kunnen. Op 6 september 2026 om 09:31 UTC, het moment waarop de snapshot achter elk getal in deze post is genomen, stonden er 74.101 servers in en was van 66.212 daarvan de broncode gelezen. Beschouw die twee getallen als een foto. De sectie hieronder gaat over hoe hard het onderwerp beweegt. Elk signaal krijgt een label voor wat eronder ligt, en dat zijn er precies drie:
- attested. Onderbouwd met een verifieerbaar bewijsstuk: een CVE- of OSV-melding, een cryptografische build-attestatie, of het gepubliceerde artefact zelf.
- reported. Beweerd door een derde partij, een registry of een directory, en doorgegeven. Wat waar is, is dat zij het beweren, niet dat het klopt.
- inferred. Een heuristiek. Een keyword-match, een patroon, een score. Een aanwijzing om iets na te lopen, meer niet.
De verdeling over die drie bakken is somberder dan het klinkt.
Attested is zeldzaam. Een build-attestatie ontstaat pas bij een packaging-stap, en slechts 5.143 van de 74.101 servers verschijnen überhaupt op npm of PyPI. Daarvan dragen er 2.122 zo'n attestatie. Bij de overige 68.958 gaat het om een repository die je kloont en draait, zonder iets cryptografisch om te controleren.
Reported is het meeste van wat op verificatie lijkt. 18.946 servers dragen een signaal van de officiële registry, 51 een directory-vermelding. Dat is een registry die zegt dat de server is wat hij beweert te zijn. Dat is iets waard. Nagekeken heeft niemand het.
De rest is inferred, en daar zit het volume. 45.525 safety-bevindingen over 17.852 servers. 26.821 daarvan komen uit een generieke statische analyse, niet uit iets MCP-specifieks, en binnen die groep gaan er 24.373 over één codepatroon in de laagste ernstklasse.
De permissievlaggen vertellen hetzelfde verhaal van de andere kant. Bij de 33.222 servers waarvan de tools zijn vastgelegd, samen 496.883 tools, zet de detector bij 27.352 daarvan geen enkele permissievlag. Tweeëntachtig procent van de servers licht nergens op. Lees dat als een keyword-matcher die geen keywords vond. Dat is iets anders dan 82% veilige servers.
Het antwoord dat geen dashboard toont
Op dit punt denk ik dat de hele categorie het mis heeft, en ik zag het pas door het ding zelf te bouwen.
Een scanner kan drie dingen zeggen over een server. Hij kan hebben gekeken en iets gevonden. Hij kan hebben gekeken en niets gevonden. En hij kan nooit gekeken hebben.
In elk dashboard dat ik ken zijn de tweede en de derde niet uit elkaar te houden. Ze kleuren allebei groen.
Daarom weigert de observatory een geruststellend cijfer te tonen bij broncode die nooit is gelezen. Is de bron nooit geanalyseerd en komt het cijfer uit op A of B, dan staat er "insufficient evidence", want "we vonden niets" en "we hebben nooit gekeken" krijgen dezelfde kleur en betekenen het tegenovergestelde. Op dit moment geldt dat voor 1.944 servers. Weinig tegenover 74.101, en stuk voor stuk zouden ze elders als schoon doorgaan.
Het omgekeerde geval krijgt zijn eigen label. 101 servers dragen een zorgelijk cijfer met ongelezen bron, en daar is het cijfer echt, maar het is een ondergrens: de code lezen kan het alleen omhoog duwen.
De grond beweegt terwijl je leest
Elk getal hierboven is een foto. Dit is de sluitertijd.
In de dertig dagen tot 6 september brachten de servers in dit corpus gemiddeld 3.657 releases per dag uit, met dagtotalen tussen 2.913 en 4.519. In dezelfde periode kwamen er per dag 418 nieuwe repo's met een MCP-server bij.
Dat tweede getal versnelt. Nieuwe repo's per maand: 2.539 in februari, 5.120 in april, 9.441 in juni, 12.621 in augustus.
Twee kanttekeningen, en ze wijzen dezelfde kant op. Van 63.581 van de 74.101 servers is de aanmaakdatum bekend, dus dit zijn ondergrenzen. En juist de nieuwste repo's vindt een crawler het laatst, waardoor de recente maanden eerder te laag dan te hoog uitvallen. De groei is minstens zo steil.
Dat is de echte reden dat ik elk getal dat ik publiceer van een datum voorzie. 74.101 klopte op 6 september 2026 om 09:31 UTC, en tegen lunchtijd klopte het al niet meer. Een telling in dit vakgebied heeft een halfwaardetijd van uren, en dat hoort naast de telling te staan in plaats van in een voetnoot eronder.
Het leidt ook rechtstreeks naar het volgende probleem. Een ecosysteem dat 3.657 releases per dag uitbrengt, is een ecosysteem dat harder loopt dan iedereen die het scant.
Een cijfer heeft een houdbaarheidsdatum
De scan draaide op een bepaalde dag, tegen een bepaalde commit. Daarna ging de server door.
Van de servers die nu een cijfer dragen zijn er 20.417 verouderd in strikte zin: er is een nieuwere package-versie verschenen, of er is naar de repository gepusht, ná de analyse. Dat is 28% van het beoordeelde corpus, en het beschrijft code die niet meer de code is die je zou installeren.
Geen enkel dashboard zegt dat hardop, het mijne tot voor kort ook niet. Een cijfer zonder datum erbij is een uitspraak over een momentopname die wordt gepresenteerd als een uitspraak over een server.
Het meetinstrument beweegt ook
Dit verraste me, en het is de reden dat ik geen enkel driftgetal meer zonder meer geloof.
De observatory houdt van elke tool-definitie die hij ooit zag een geschiedenis bij: naam, beschrijving, input-schema en een afgeleid permissiemasker. Van de 525.435 unieke tools hebben er 63.184 meer dan één vastgelegde definitie. Mijn eerste ingeving was om dat te lezen als een ecosysteem dat onder zijn gebruikers vandaan beweegt.
Toen keek ik welk deel er veranderd was. Bij 1.952 van die tools bewoog het permissiemasker terwijl de beschrijving en het input-schema byte voor byte identiek bleven.
Aan die servers veranderde niets. De classifier veranderde. Ik had de permissiedetector verbeterd, en een groep tools die alleen geminificeerde code meelevert matchte ineens niet meer op keywords waar hij eerder wel op matchte. De meting bewoog en de wereld niet.
Dat is dezelfde val als een duplicatiescore op AI-code lezen en concluderen dat de code schoner werd, terwijl de detector het was die bewoog. Een afgeleid signaal is een functie van twee dingen, en maar één daarvan is de server.
Wat een veranderde beschrijving waard is
Het echt interessante in die geschiedenis zijn de beschrijvingen.
Van de 63.184 tools met meer dan één definitie veranderden er 61.155 hun beschrijving. Slechts 280 veranderden hun input-schema. Bij 60.952 veranderde de tekst terwijl het machineleesbare contract exact hetzelfde bleef.
De beschrijving is het deel dat het model leest en waar het zijn acties op baseert. Het schema is het deel waar jouw code tegen valideert. Zestigduizend tools herschreven de instructie aan de agent zonder de interface naar het programma aan te raken.
Dat is precies de vorm van een tool-poisoning rug pull, waarbij een server schoon binnenkomt, geïnstalleerd wordt, en daarna aanpast wat zijn tool het model opdraagt. Het is ook precies de vorm van iemand die een typfout in zijn documentatie herstelt.
Ik kan die twee van buitenaf niet uit elkaar houden, en niemand anders ook. De specifieke rug-pull-regel levert 81 bevindingen op over 51 servers, en ik zou niet durven beweren dat dat getal volledig is. Dus het signaal over veranderde beschrijvingen gaat de deur uit als wat het is: een lijst servers die het herlezen waard zijn voordat je ze weer vertrouwt, gelabeld als inferred, zonder dat er een beschuldiging aan vastzit. Een versie onder een geïnstalleerde dependency vandaan schuiven is een bekende zet in dit ecosysteem, en de tool-beschrijving is een zachter doelwit dan het versienummer.
Wat je wél kunt nalopen voor je installeert
De checklist om een server na te lopen blijft staan en dit vervangt hem niet. Wat de data toevoegt is een volgorde, naar hoeveel het signaal waard is:
- Is er een attestatie? Zeldzaam, binair, en het enige op deze lijst dat cryptografisch is. De meeste servers kunnen er geen hebben, en dat is op zichzelf al iets om te weten.
- Is de bron echt gelezen, en wanneer? Een cijfer over ongelezen of een half jaar oude broncode is iets anders dan een cijfer over de commit die je nu gaat installeren.
- Is de tool-beschrijving veranderd sinds de vermelding? Op zichzelf niet belastend. Tien seconden leestijd waard, want het is het veld dat het model stuurt.
- Wat claimt het permissie-oppervlak? 658 servers hebben één tool die het netwerk bereikt en een andere die credentials raakt, en dat is precies het paar dat een geïnjecteerde instructie aan elkaar rijgt. Behandel een stille uitkomst als "de matcher vond niets", niet als goedkeuring.
- Wie zegt dat het goed zit, en is dat dezelfde partij als de uitgever? Het meeste dat hier op verificatie lijkt, is een registry die de claim van de uitgever doorgeeft.
Het zijn vijf vragen, en bij elke vraag is het antwoord op "hoeveel mag ik dit geloven" anders.
Waar ik steeds op terugkom is dat de MCP-supply-chain het distributiemodel van npm heeft geërfd zonder zelfs het bescheiden provenance-verhaal van npm mee te nemen. Wat wél is meegekomen is de honger naar één letter die de beslissing voor je neemt.
Elk cijfer en elke permissievlag op de observatory is een afgeleide aanwijzing om iets na te lopen in plaats van een oordeel, en elk daarvan draagt een label voor wat eronder ligt: attested, reported of inferred. Dat is een slechter product. Niemand wil vijf voorzichtige antwoorden als de concurrent één zelfverzekerde letter biedt.
Het is de enige versie die ik kan verdedigen zolang minder dan de helft van de meldingen in dit vakgebied overeind blijft zodra iemand ze nakijkt.