AI-systemen Onderzoeksartikel

MCP-OAuth is geen toolautorisatie: een geldig token is nog geen ja

OAuth laat je server weten dat het token echt is en voor hem bedoeld is. Jouw code beslist of deze aanroep deze declaratie mag goedkeuren.

Een gereedschapswand bij daglicht met één open gaaskooi vol handgereedschap en daarnaast een aparte, gesloten stalen kooi
Bij de wand komen is één controle. De tweede kooi openen is een andere.

Direct antwoord

Bij een MCP-server op afstand beantwoordt OAuth twee vragen: is dit toegangstoken echt en voor deze server uitgegeven, en welke scopes (benoemde rechten) bevat het? OAuth weet niet welke scope elke tool nodig heeft, of dat iemand alleen declaraties van het eigen team mag goedkeuren. Die rechtenkaart schrijf je zelf op en controleer je bij elke toolaanroep, voordat de tool iets wijzigt. Lokale MCP-servers die via stdio draaien, slaan deze OAuth-flow over en halen hun inloggegevens uit hun omgeving.

OAuth op een MCP-server controleert de sleutel, niet wat die sleutel mag openen. Een geldig token vertelt je server dat het echt is, dat het voor deze server is uitgegeven en welke scopes erin staan. Het beslist niet of deze aanroep deze declaratie mag goedkeuren. Die regel schrijf je zelf, en je controleert hem bij elke toolaanroep, voordat de tool iets wijzigt.

Hieronder zie je waar die grens ligt, wat de MCP-specificatie aan beide kanten eist en hoe je de aanroep test die moet mislukken.

Eerst de begrippen, in gewone taal

  • MCP (Model Context Protocol) is een open standaard waarmee een AI-app tools kan gebruiken die een apart programma aanbiedt: de MCP-server. De begrippenpagina geeft de korte versie.
  • OAuth is de standaard achter het scherm “deze app toegang geven tot je account?” In plaats van je wachtwoord krijgt de app een toegangstoken: een tijdelijke pas die hij bij elk verzoek meestuurt.
  • Een scope is een benoemd recht dat in dat token staat, zoals expenses:read of expenses:approve.
  • Authenticatie stelt vast wie er aanroept. Autorisatie bepaalt wat die mag.

Scopes controleren is al autorisatie, alleen grofmazig: die controle ziet dat een token expenses:approve bevat, maar niet welke tool die scope nodig heeft of aan welke declaraties deze persoon mag komen.

Een uitgewerkt voorbeeld: één token, drie tools

Neem een fictief ontwerpbureau met twaalf mensen. De AI-assistent van het bureau is gekoppeld aan een MCP-server voor declaraties, met drie tools: list_expenses, view_expense en approve_expense.

Sanne, de officemanager, logt in via OAuth. De assistent krijgt een geldig token, uitgegeven voor de declaratieserver, met de scope expenses:read.

Nu stellen drie verschillende onderdelen van het systeem drie verschillende vragen:

VraagWie geeft antwoordWaar de regel vandaan komt
Welke app vraagt dit?De autorisatieserver (de dienst die tokens uitgeeft)De MCP-specificatie
Is dit token echt, niet verlopen en voor deze server uitgegeven?Jouw MCP-server, die het token valideertDe MCP-specificatie
Mag deze aanroep deze ene declaratie goedkeuren?Jouw codeNiemand anders dan jij

Sannes token komt door de eerste twee. Roept de assistent approve_expense aan, dan kan alleen de derde vraag dat tegenhouden. Haar token mist expenses:approve, dus de server weigert.

Stel nu dat haar leidinggevende Joris wel expenses:approve heeft. Hij vraagt de assistent een declaratie van een ander team goed te keuren, of een die hij zelf heeft ingediend. Het token is geldig en de scope is aanwezig. Controleert jouw code niet bij welk team de declaratie hoort en wie die indiende, dan houdt niets anders het tegen.

Wat eist de MCP-specificatie?

De MCP-autorisatiespecificatie, revisie 2026-07-28, maakt autorisatie optioneel. Ondersteunt een server autorisatie via HTTP (een server die je via het netwerk bereikt), dan hoort hij de specificatie te volgen. Een lokale server die via stdio draait (als programma gestart op dezelfde machine), hoort deze flow niet te gebruiken. Die haalt zijn inloggegevens uit zijn omgeving, dus daar is de vraag welke inloggegevens dat proces krijgt.

Voor HTTP-servers zijn dit de belangrijkste regels:

  • De client stuurt het token bij elk verzoek mee in de Authorization-header, en nooit in het webadres.
  • Met de parameter resource noemt de client de server waarvoor hij een token wil. De server moet controleren of het token voor hem is uitgegeven, en mag geen tokens accepteren of doorgeven die voor iets anders bedoeld zijn.
  • Ongeldige of verlopen tokens moeten een 401 krijgen.
  • Een geldig token zonder de scope die een bewerking nodig heeft, hoort een 403 met insufficient_scope te krijgen, met in één keer alle scopes die die bewerking vraagt. De client kan dan een step-up doen: een nieuw token aanvragen dat de eerdere scopes combineert met de nieuwe. Die optelsom bijhouden is de taak van de client.
  • Servers moeten rekening houden met scopehiërarchieën, waarbij een bredere scope de smallere insluit. Een letterlijke tekstvergelijking kan dan ten onrechte een token weigeren dat door had moeten komen. Leg de hiërarchie dus vast en test die.

De pagina over tools in de specificatie voegt een regel toe die vaak wordt overgeslagen: servers moeten degelijke toegangscontrole inbouwen. De specificatie eist die controle. Ze ontwerpt die niet voor je.

Dezelfde pagina staat toe dat een server elke aanroeper alleen de tools laat zien die de scopes van die aanroeper toestaan, als de client om de toollijst vraagt. Dat is een goede ontwerpkeuze, want dan ziet het model nooit een knop die het niet mag indrukken. Maar een tool verbergen is hem niet blokkeren. De controle die telt, draait op het moment dat de tool wordt aangeroepen.

De app identificeren is geen recht toekennen

Volgens revisie 2026-07-28 horen autorisatieservers en clients Client ID Metadata Documents (CIMD) te ondersteunen. Het ID van de app is dan een webadres, en de autorisatieserver haalt op dat adres een kort document op dat de app beschrijft. De oudere methode, waarbij een app zich ter plekke zelf registreert (Dynamic Client Registration), is verouderd maar blijft bestaan voor achterwaartse compatibiliteit.

CIMD vertelt de autorisatieserver welke app erom vraagt. Het kent geen expenses:approve toe en keurt geen toolaanroep goed. De app identificeren, het token valideren en beslissen of deze aanroep deze declaratie mag goedkeuren, zijn drie aparte controles. Door de eerste twee komen zegt nog niets over de derde.

Schrijf de rechtenkaart

Dit is het overzicht dat het principe omzet in iets wat je kunt nalopen. Eén regel per tool, met een standaard voor al het andere:

ToolVereiste scopeExtra regelWaar gecontroleerd
list_expensesexpenses:readAlleen declaraties van het eigen teamIn de code van de tool
view_expenseexpenses:readDe declaratie hoort bij het eigen teamIn de code van de tool
approve_expenseexpenses:approveZelfde team; niet de eigen declaratie; bedrag onder de goedkeuringsgrensIn de code van de tool, voordat er iets wordt weggeschreven
Elke tool die niet in deze tabel staatGeenWeigerenStandaard

De agentische codebase, een van de boeken van Len (nu verkrijgbaar), legt hetzelfde idee per tool vast in een kort contractbestand: een lijst van wat de tool mag, een lijst van wat hij niet mag, en elke scope die niet op de toegestane lijst staat, wordt geweigerd. Je hebt het boek niet nodig om de tabel hierboven te gebruiken.

Geld goedkeuren is bovendien een beslissing die veel teams aan een mens overlaten, wat het token ook zegt. Wat een agent nooit mag doen behandelt die lijst.

Waar kan de controle zitten?

In de code van de tool zelf, in gedeelde autorisatiecode die elke tool aanroept, of in een gateway (een proxy die vóór de server staat). De specificatie eist geen aparte policy-engine. Kies de plek die elke afgeschermde tool dekt en die je team kan lezen en testen.

Voor een gateway biedt de specificatie één praktisch hulpmiddel. Via HTTP moet elk verzoek de headers Mcp-Method en Mcp-Name bevatten, bijvoorbeeld tools/call en approve_expense. Zo ziet een gateway welke tool wordt aangeroepen zonder de inhoud van het bericht te lezen. Die inhoud blijft wel leidend.

Hoe pakken leveranciers het aan?

Dit zijn implementatiekeuzes, geen eisen van MCP.

WorkOS schrijft in zijn bouwnotities van 31 juli 2026 dat zijn product AuthKit geen kant-en-klare scope per tool levert. Je maakt dus zelf een permissie zoals expenses:approve aan en controleert die in elke tool. Zijn samenvatting verdient een plek op het prikbord: “‘Toegang tot de server’ is geen permissie die iets voorstelt. ‘Mag approve_expense aanroepen’ wel.” Eén kanttekening: zijn voorbeeldhelper laat elke tool toe waaraan geen permissie is gekoppeld. Kies die standaard bewust voordat je hem overneemt.

Permit.io kiest de route via een gateway. Het gateway-overzicht beschrijft dat de gateway bij elke aanroep het token verifieert, vraagt of deze agent deze tool op deze server mag aanroepen en de beslissing logt. Medeoprichter Or Weis betoogt dat OAuth nodig maar niet voldoende is. Dat is de visie van een leverancier op zijn eigen markt, en ze sluit aan bij de tweedeling in de specificatie.

Wat laat een geslaagde verbindingstest ongetest?

Een verbindingstest kan slagen terwijl deze gebreken blijven bestaan:

  • Elke tool accepteert dezelfde brede scope. Een declaratie opzoeken en er een goedkeuren vragen om verschillende rechten, maar beide tools accepteren hetzelfde token.
  • Het recht werd alleen bij het inloggen gecontroleerd. Latere aanroepen draaien zonder controle op de scope die ze nodig hebben.
  • Regels per record ontbreken. De tool controleert de scope, maar kijkt niet naar invoer die de declaratie van een ander team selecteert.

MCP uitgelegd voor ondernemers bevat een bredere servercontrole van vijftien minuten; deze pagina behandelt alleen het autorisatiedeel.

Test de aanroep die moet mislukken

Gebruik één geldig token voor twee aanroepen: een die het mag doen en een die het niet mag doen. Sta bijvoorbeeld het opzoeken van een declaratie toe en weiger de goedkeuring als het token expenses:approve mist. Test daarna de andere foutpaden één voor één:

TestVerwacht resultaat
Ontbrekend, ongeldig of verlopen token401; er draait geen afgeschermde tool
Geldig token, uitgegeven voor een andere server401; er draait geen afgeschermde tool
Geldig token zonder de vereiste scope403 insufficient_scope; de tool draait niet
Vereiste scope aanwezig, maar een regel per record verbiedt de actieWeigering door je applicatie; er verandert niets
Vereiste scope aanwezig en regels per record in ordeDe actie slaagt

Een weigering op grond van een regel per record is geen insufficient_scope-fout. Het token van Joris heeft de scope al. Zijn app een ruimer token laten ophalen, verandert niets aan de regel over bij welk team de declaratie hoort.

Controleer het record, niet alleen het antwoord. Een foutmelding die pas komt nadat de declaratie al is goedgekeurd, betekent dat de controle heeft gefaald.

Probeer dit vandaag (20 minuten)

  1. Zet alle tools van één MCP-server die je zelf draait of waarvan je afhankelijk bent, op een rij.
  2. Vul voor elke tool de rechtenkaart hierboven in, inclusief de laatste regel: wat gebeurt er met een tool die niemand heeft opgeschreven?
  3. Roep met een geldig token waarin één recht ontbreekt, de tool aan die dat recht nodig heeft.
  4. Open daarna het record. Is het veranderd, dan heb je het gat gevonden voordat een klant dat deed.

Citeer deze pagina:MCP-OAuth is geen toolautorisatie: een geldig token is nog geen ja.Len P. van der Hof. https://lenvanderhof.com/nl/blog/mcp-oauth-is-geen-toolautorisatie/ ·

Begrippen

Bronnen

  1. Authorization, MCP specification 2026-07-28 · Model Context Protocol
  2. Tools, MCP specification 2026-07-28 · Model Context Protocol
  3. Streamable HTTP transport, MCP specification 2026-07-28 · Model Context Protocol
  4. How to build an MCP app on the 2026-07-28 spec with WorkOS AuthKit · WorkOS
  5. Permit MCP Gateway overview · Permit.io
  6. MCP Auth vs Tool-Call Authorization After the 2026-07-28 Spec · Permit.io
  7. MCP (begrip)
  8. MCP uitgelegd voor ondernemers
  9. De agentische codebase

Verder lezen

Markdown voor LLMs