Blog/Web-Engineering

WebMCP in der Praxis: eine Website agentenfertig machen mit deklarierten Tools

WebMCP im Code: deklarative Formular-Attribute, document.modelContext.registerTool, Tool-Annotationen, Sicherheits-Tore, lokales Testen und eine Checkliste.

··10 Min. Lesezeit

  • WebMCP
  • Chrome
  • AI agents
  • Origin trial
  • Permissions Policy
  • JSON Schema
Zwei Wege nebeneinander: ein Formular mit toolname- und tooldescription-Attributen, aus dem der Browser ein Schema baut, und ein JavaScript-Tool-Objekt für document.modelContext.registerTool

Das Wichtigste in Kürze

  • WebMCP ist ein vorgeschlagener Webstandard und ab Chrome 149 ein Intent to Experiment: Eine Seite registriert ihre Tools beim KI-Agenten, statt ihn das DOM lesen zu lassen.
  • Die deklarative API macht aus einem gewöhnlichen Formular ein Tool: toolname und tooldescription, dazu toolparamdescription pro Feld und toolautosubmit für das Absenden durch den Agenten.
  • Die imperative API ist ein Aufruf: document.modelContext.registerTool() mit Name, Description, einem inputSchema in JSON Schema und einer asynchronen execute-Funktion.
  • Alle vier Tool-Annotationen stehen standardmäßig auf false: readOnlyHint, untrustedContentHint, consequentialHint und ab Chrome 156 debugging.
  • WebMCP läuft nur in origin-isolierten Dokumenten und wird von der tools Permissions Policy abgesichert, die standardmäßig auf self steht und für ein Cross-Origin-iframe allow=tools verlangt.

WebMCP ist ein vorgeschlagener Webstandard, mit dem eine Seite ihre eigenen Tools für KI-Agenten im Browser deklarieren kann, statt sie das DOM lesen zu lassen und zu raten, wofür ein Button da ist. Chrome liefert ihn ab Chrome 149 als Origin Trial aus; Google hat ihn am 19. Mai 2026 auf der I/O angekündigt. Stand September 2026 ist er ein Intent to Experiment in der W3C Web Machine Learning Community Group und keine Recommendation – die Details unten sind deshalb jeweils mit der Chrome-Version gekennzeichnet, gegen die sie geprüft wurden.

Dieser Artikel geht beide WebMCP-APIs im Code durch: die deklarativen Attribute, die du an ein Formular hängst, den imperativen Aufruf document.modelContext.registerTool(), die Tool-Annotationen, die zwei Voraussetzungen, die dabei immer gelten müssen (origin isolation und die tools Permissions Policy), das lokale Testen und eine Checkliste. Das durchgerechnete Beispiel ist das Plugin auf dieser Seite; es registriert drei Seiten-Tools: get_page_content, get_contact_details und open_page.

Was ist WebMCP?

WebMCP erlaubt einer Webseite, benannte Tools zu registrieren, jedes mit einem JSON-Schema für seine Eingabe und einer Funktion, die der Browser aufrufen kann. Der Agent in diesem Browser sieht die Tool-Liste, entscheidet, welches Tool zur Aufgabe des Nutzers passt, füllt die Eingabe und liest das Ergebnis. Die drei Dinge, die er dadurch gewinnt, beschreibt die Chrome-Doku als Discovery (eine standardisierte Art, Tools wie checkout oder filter_results zu registrieren), JSON-Schemas für die Eingaben und State, damit der Agent weiß, was die aktuelle Seite anbietet.

Das Deployment-Modell ist wichtig. Diese Tools werden von der Seite registriert und von der Seite ausgeführt, in diesem Origin. Das ist etwas anderes als ein MCP-Server, den der Agent über das Netzwerk erreicht und der in deinem Backend läuft. Im Explainer unter webmachinelearning/webmcp wird das Design ausgehandelt, der ChromeStatus-Eintrag zeigt, wo die Umsetzung steht. Googles I/O-Beitrag kündigte an, Gemini in Chrome unterstütze die WebMCP-APIs „bald", und zeigte eine Wand an Marken, die damit arbeiten – darunter Expedia, Booking.com, Shopify, Etsy und Target.

Warum Scraping eine schlechte Schnittstelle für Agenten ist

Der übliche Weg, auf dem ein Agent eine Website nutzt, ist das, was die Chrome-Doku actuation nennt: „the act of an agent simulating manual mouse clicks and text input, as though it were the human user engaging with your website". Das ist die schlechteste Schnittstelle, die du anbieten kannst, denn jeder Schritt ist eine Auslegung, die der Agent selbst treffen muss. Klassen wechseln, ein Label sagt „Continue", wo „pay" gemeint ist, und ein Formular mit sechs Feldern hat sechs Chancen, danebenzuliegen.

Deklarierte Tools nehmen das Raten. Der Agent muss nicht herausfinden, dass ein Button mit der Aufschrift „Find flights" ein Suchformular abschickt; du hast es ihm in einem Schema gesagt, das er lesen kann. Die Dokumentation macht einen zweiten Punkt, der leicht übersehen wird: Tools „execute on your webpage visibly, so users gain trust that tasks are completed as expected", und deine Marke und deine menschenzentrierten Design-Entscheidungen bleiben erhalten, statt von einem Skript ersetzt zu werden, das durch einen abgemagerten Checkout klickt.

WebMCP ersetzt auch nicht dafür, deine Inhalte lesbar zu machen. Die Dokumentation nennt ihre erste Einschränkung selbst: „Clients and browsers must visit a site directly to know if it has callable tools." Ein Agent, der deine Seite nie lädt, braucht weiterhin eine Markdown-Kopie, eine llms.txt oder eine schlichte API – darum geht es in llms.txt versus Accept: text/markdown. Tools decken den Teil ab, den ein statisches Dokument nicht kann: Aktionen, State und die aktuelle Seite des Nutzers.

Wie funktioniert die deklarative API?

Die deklarative API braucht kein JavaScript. Du hängst Attribute an ein ganz gewöhnliches HTML-<form>, und der Browser leitet daraus ein Tool ab. Zwei Attribute am Formular sind Pflicht: toolname und tooldescription. Nimm eines davon weg, ist das Tool nicht registriert.

<form toolname="searchFlights"
      tooldescription="Search available flights between two airports for a date range."
      action="/flights">

  <label for="from">Departure airport</label>
  <input id="from" name="from" required>

  <label for="to">Arrival airport</label>
  <input id="to" name="to" required>

  <select name="cabin"
    toolparamdescription="Cabin class; economy is the default.">
    <option value="economy">Economy</option>
    <option value="premium_economy">Premium economy</option>
    <option value="business">Business</option>
  </select>

  <button type="submit">Search</button>
</form>

Die Formularfelder werden zu Tool-Parametern. Ein <select> wird zu einem enum, und der Text jedes <option> wird im erzeugten Schema zum Titel dieses Werts – der Agent weiß so, ob eine Auswahl „Return my purchase" oder „Where is my package" bedeutet; required an der Eingabe landet im required-Array des Schemas. Nimm toolparamdescription, sobald der Feldname allein nicht reicht. Ohne sie greift der Browser auf den Text des zugehörigen <label> zurück, dann auf aria-description – das ist eine dünne Beschreibung für einen Agenten, der deine Seite nie gesehen hat.

Ob abgeschickt wird, entscheidest du. Ohne toolautosubmit füllt der Agent die Felder, holt das Formular in den Fokus und überlässt den Submit-Button dem Menschen. Mit toolautosubmit schickt der Agent ebenfalls ab, und die Seite navigiert. In beiden Fällen bleibt das Formular sichtbar, während der Agent arbeitet, und zwei Window-Events sagen dir, was passiert ist: toolactivated feuert, sobald die Felder vorbefüllt sind, toolcancel feuert, wenn der Nutzer abbricht oder das Formular zurückgesetzt wird. Beide sind nicht abbrechbar und tragen ein toolName.

Wenn du doch ein Ergebnis zurückhaben willst, nimm respondWith() im Submit-Event. SubmitEvent bekommt ein Boolean agentInvoked, das dir sagt, dass ein Agent das Abschicken ausgelöst hat – derselbe Handler kann sich für einen Menschen also anders verhalten. Du musst zuerst preventDefault() aufrufen, und das Promise, das du übergibst, wird serialisiert und als Output des Tools an das Modell zurückgegeben.

form.addEventListener('submit', (event) => {
  event.preventDefault()
  if (event.agentInvoked) event.respondWith(runSearch())   // resolves to the tool output
})
Deklarativ und imperativ: WebMCP nebeneinanderLinks der deklarative Weg: ein Formular trägt die Attribute toolname und tooldescription, der Browser liest die Feldlabels und baut ein JSON Schema, und der Agent füllt danach das sichtbare Formular aus, damit der Nutzer die Werte sieht. Rechts der imperative Weg: ein JavaScript-Tool-Objekt mit Name, Description und Schema wird an document.modelContext.registerTool übergeben, und die eigene execute-Funktion der Seite läuft, die alles kann, was die Seite kann.DEKLARATIVFormular mit Attributentoolname, tooldescriptionBrowser liest Labelsund baut das SchemaAgent füllt das FormularIMPERATIVJavaScript-Tool-ObjektName, Description, Schemadocument.modelContextregisterTool(tool)dein execute() läuft
Deklarative Tools leitet der Browser aus dem Markup ab. Imperative Tools sind JavaScript-Objekte, die du registrierst, also können sie alles, was die Seite kann.
KriteriumDeklarative AttributeImperatives registerTool
Wo es liegtHTML-Attribute an einem FormularJavaScript, meist in einem Client-Plugin
Was du schreibstTool-Name, Description, Beschreibungen je FeldName, Description, JSON-Schema, execute-Funktion
Eingabe-SchemaAbgeleitet aus Labels, Optionen und requiredDeins, wortgleich
ErgebnisAbsenden und navigieren, oder respondWithWas execute zurückgibt
Gut fürEinfache Formulare, die auch ein Mensch ausfülltBerechnete, zustandsbehaftete Aktionen über Seiten hinweg
SchwächeNur, was ein Formular ausdrücken kannMehr Code, mehr Wege, es falsch zu machen

Wie funktioniert document.modelContext.registerTool()?

Die imperative API ist ein einziger Aufruf: Du übergibst ein Tool-Objekt mit name, description, einem inputSchema in JSON Schema und einer asynchronen execute-Funktion, und der Browser macht das Tool aufrufbar. Das ist das Plugin auf dieser Seite, gekürzt. target() löst Seitenname und Sprache zu einem Pfad und zu der Markdown-Kopie dieses Pfads auf.

const pageInput = {
  type: 'object',
  properties: {
    page: { type: 'string', enum: Object.keys(PAGES), description: 'home, about, references, blog, game …' },
    language: { type: 'string', enum: ['en', 'de', 'hu'], description: 'en, de or hu. Defaults to the shown language.' },
  },
  required: ['page'],
}

await document.modelContext.registerTool({
  name: 'get_page_content',
  description: 'Returns the full text of a page of this site as Markdown.',
  inputSchema: pageInput,
  annotations: { readOnlyHint: true },
  async execute(input) {
    const response = await fetch(target(input).markdown, { headers: { accept: 'text/markdown' } })
    if (!response.ok) throw new Error(`Could not load the page (${response.status}).`)
    return { content: [{ type: 'text', text: await response.text() }] }
  },
})

Drei Details in diesem Snippet machen daraus ein Tool und nicht bloß einen fetch-Wrapper. Erstens liefert das Tool Markdown statt HTML: Es fragt die .md-Kopie der Seite mit Accept: text/markdown an, der Agent bekommt also ein paar Kilobyte sauberen Text statt eines scriptlastigen Dokuments. Zweitens hält enum im Schema den Agenten davon ab, Seitennamen zu erfinden, und die Description sagt ihm, wofür jeder davon steht. Drittens nutzt open_page auf dieser Site den Nuxt Router statt navigateTo, weil Tools lange nach dem Setup laufen, außerhalb des Nuxt-Kontexts.

Das Zurücklesen ist symmetrisch. document.modelContext.getTools() liefert eine alphabetisch sortierte Liste dessen, was das aufrufende Dokument sehen darf, und executeTool(tool, input) führt eines aus und gibt null zurück, wenn das Tool statt eines Ergebnisses eine Navigation ausgelöst hat. Ein toolchange-Event auf document.modelContext sagt einem Frame, dass sich die Liste geändert hat. Tools lassen sich mit einem AbortSignal wieder abmelden, und ab Chrome 153 bricht das Abmelden laufende Ausführungen nicht mehr ab. Dein execute bekommt dieses Signal als zweites Argument – gib es an jeden fetch weiter, den es startet.

Ein WebMCP-Tool-Aufruf von Anfang bis EndeVier Beteiligte: der KI-Agent, der Browser, das Seiten-Tool und die Benutzeroberfläche. Der Agent fragt den Browser nach der Tool-Liste; der Browser antwortet mit dem Tool und seinem JSON-Schema; der Browser ruft execute mit der Eingabe auf; das Seiten-Tool aktualisiert die Seite, sodass der Nutzer es sieht; der Nutzer bestätigt; das Tool liefert sein Ergebnis an den Browser, und der Browser antwortet dem Agenten. Der execute-Aufruf ist in der Akzentfarbe dargestellt, alles andere in der neutralen Linienfarbe.KI-Agentim BrowserBrowsermodelContextSeiten-Toolexecute()UI des Nutzerssieht zugetTools()Tool + Schemaexecute(input)aktualisiert die SeitebestätigtErgebnis
Ein Tool-Aufruf: Auffinden, Schema, Ausführung auf der Seite, eine sichtbare Wirkung, die der Nutzer bestätigen kann, und ein Ergebnis zurück an den Agenten.

Was in execute läuft, ist eine gewöhnliche Schleife um einen Modellaufruf – dieselbe Schleife, die ein serverseitiger Agent fahren würde, nur dass der Zustand das DOM ist. Das ist in der Agentenschleife erklärt.

Was sagen die Tool-Annotationen dem Agenten?

Annotationen sind optionale Booleans im annotations-Objekt eines Tools. Alle stehen standardmäßig auf false, sie sind Hinweise und keine Sicherheitsgrenze, und sie existieren, damit ein Agent vor dem Aufruf entscheiden kann, was er tut.

AnnotationAuf true setzen, wennWas es dir bringt
readOnlyHintDas Tool nur liest und nichts ändertDer Agent darf es frei aufrufen, etwa um einen Katalog zu durchsuchen
untrustedContentHintDie Ausgabe enthält nutzergenerierte oder abgerufene DatenClients behandeln das Ergebnis als Daten, die zu bereinigen und abzugrenzen sind, nicht als Anweisungen
consequentialHintDas Ausführen hat echte, nicht umkehrbare FolgenAgenten und Browser können eine verpflichtende Bestätigung verlangen
debuggingDas Tool ist Entwicklerwerkzeug, nicht für Nutzer gedachtAllzweck-Agenten filtern es heraus; verfügbar ab Chrome 156

Das Plugin auf dieser Seite setzt readOnlyHint: true bei den zwei Tools, die Text zurückgeben, und lässt es bei open_page weg – denn Navigieren ist eine Zustandsänderung, auch wenn es nichts zerstört. Das ist die Messlatte, die ich anlegen würde: ehrlich annotieren, denn ein falsches readOnlyHint ist eine Lüge, auf die der Agent handelt, und ein Tool mit Folgen ohne Annotation ist ein Kauf, den ein Agent ohne Nachfrage tätigen kann. Alles, was von deinen Nutzern geschriebene Inhalte darstellt, sollte untrustedContentHint tragen; warum das eine Sicherheitsentscheidung und kein Etikett ist, steht in Prompt Injection als Architekturproblem.

Welche Sicherheitsvoraussetzungen hat WebMCP?

Zwei, und der Browser prüft beide, bevor dein Code läuft. Die erste ist origin isolation: WebMCP gibt es nur in origin-isolierten Dokumenten, damit der Origin des Dokuments für die Lebensdauer des Tools stabil bleibt. Ist document.domain aktiv, etwa weil eine Antwort Origin-Agent-Cluster: ?0 sendet, sind die WebMCP-APIs deaktiviert. Prüfe deine eigenen Header, bevor du einen Nachmittag an ein Tool verschwendest, das sich nie registriert.

Die zweite ist die toolsPermissions Policy, deren Default self ist. Top-Level- und Same-Origin-Dokumente dürfen Tools registrieren, Cross-Origin-iframes nicht – außer, die einbettende Seite ergänzt allow="tools". Die Registrierung ist außerdem getrennt von der Sichtbarkeit geregelt: Ein Tool muss in exposedTo stehen, um cross-origin sichtbar zu sein, und der Aufrufer muss es trotzdem noch in getTools({ fromOrigins }) anfordern.

Der Rest ist dein eigenes Design. Aktionen mit Folgen brauchen einen Menschen, und die API gibt dir zwei Wege, an einen zu kommen: den deklarativen Pfad, bei dem das Formular sichtbar bleibt und der Submit-Button beim Nutzer liegt, und consequentialHint, das den Browser eine Bestätigung verlangen lässt. Meine eigene Regel, und die wende ich auch bei Agenten an, die Code schreiben, gilt hier genauso: Das Tool bereitet es vor, der Mensch führt es aus. Nichts, was Geld ausgibt, eine Nachricht sendet oder sich nicht rückgängig machen lässt, sollte allein auf ein Modelldiktat hin durchlaufen.

Wie testet man WebMCP lokal?

Mit einem Flag. Das lokale Setup der Dokumentation ist chrome://flags/#enable-webmcp-testing: auf Enabled stellen, neu starten, und die APIs sind auf deinem Rechner verfügbar, ohne Enrollment. Für Tests auf den Rechnern echter Nutzer nimmst du am Origin Trial teil; das Chrome-Team beschreibt ihn als zeitlich begrenzten Early Access mit Nutzungslimits – das ist der Preis dafür, ein Experiment auf echten Traffic zu schicken.

Zum Inspizieren installierst du die Model Context Tool Inspector Extension. Sie zeigt, welche Tools eine Seite registriert hat, ruft sie von Hand auf, prüft, ob der Browser dein Eingabe-Schema parsen kann, und zeigt die strukturierte Ausgabe oder die Fehlermeldung – dort werden die meisten Schema-Fehler offensichtlich. Etwas anderes ist Gemini in Chrome.

Kenne die Grenzen, bevor du dich festlegst. Die Dokumentation sagt, die API sei „primarily designed for local browser workflows with a human in the loop" – Headless-Läufe sind also nicht das Ziel. Komplexe Interfaces müssen vielleicht refaktoriert werden, oder bekommen zusätzliches JavaScript, bevor ihr Zustand nach außen gegeben werden kann. Auffindbarkeit braucht einen Besuch. Und die Statuszeile der Dokumentation gilt auch: WebMCP „is under active discussion and subject to change in the future". Frameworks ziehen nach: Angular hat experimentelle Unterstützung, React ein usewebmcp-Paket.

Damit stellt sich die Frage, wann sich Warten lohnt. Sind deine Agenten Shopping-Agenten statt Browser-Agenten, sind die serverseitigen Protokolle weiter – und UCP, ACP, AP2 und WebMCP im Vergleich geht das durch. WebMCP verdient seinen Platz, wenn das, was ein Agent tun soll, die Seite braucht, auf der du gerade bist.

WebMCP-Checkliste

  1. Entscheide erst, ob du überhaupt Tools brauchst. Wenn ein Agent nur lesen muss, ist eine Markdown-Kopie der Seite billiger und funktioniert ohne Browser.
  2. Sichere zuerst den Zugang: das lokale Chrome-Flag für die Entwicklung, das Origin Trial für echte Nutzer.
  3. Attribute für einfache Formulare, registerTool() für alles andere. Nur die imperative API kommt an den Anwendungszustand.
  4. Beschreibe jeden Parameter mit toolparamdescription oder einem echten <label>, und nimm enums freien Strings vor.
  5. Entscheide bewusst, wie abgeschickt wird. Lass Submit beim Menschen, oder setze toolautosubmit und gib mit respondWith() ein Ergebnis zurück.
  6. Annotiere ehrlich: readOnlyHint nur, wenn sich nichts ändert, untrustedContentHint bei Nutzerinhalten, consequentialHint bei allem Unumkehrbaren.
  7. Prüfe deine Header. Halte origin isolation aufrecht, und bedenke, dass die tools-Policy das Tor für iframes ist.
  8. Halte Human in the Loop bei Geld, Nachrichten und Löschungen. Vorbereiten, dann fragen.
  9. Prüfe die API per Feature Detection und toleriere ihre Umbenennungen, damit ein Browser, der sie fallen lässt, dich nichts kostet.

Wenn du das in eine Anwendung statt in eine Marketing-Website baust, taucht dieselbe Art Arbeit noch einmal auf, in der Vue.js- und Nuxt-Entwicklung.

Quellen

  1. Chrome for Developers: WebMCP (Erste Schritte)
  2. Chrome for Developers: WebMCP Imperative API
  3. Chrome for Developers: WebMCP Declarative API
  4. Chrome for Developers: Join the WebMCP origin trial (9. Juni 2026)
  5. Chrome for Developers: 15 updates from Google I/O 2026 (19. Mai 2026)
  6. WebMCP-Explainer: webmachinelearning/webmcp
  7. ChromeStatus: WebMCP-Feature-Eintrag

Häufige Fragen

Was ist WebMCP?

WebMCP ist ein vorgeschlagener Webstandard, der in der W3C Web Machine Learning Community Group entstanden ist und einer Webseite erlaubt, ihre eigenen Tools bei einem KI-Agenten im Browser zu registrieren. Jedes Tool hat einen Namen, eine Description, ein JSON-Schema für seine Eingabe und eine ausführbare Funktion, damit der Agent es finden und aufrufen kann, statt das DOM zu scrapen und zu raten, welches Element was tut. Chrome bringt es ab Chrome 149 als Origin Trial.

Was ist der Unterschied zwischen navigator.modelContext und document.modelContext?

navigator.modelContext war die frühere Form, verwendet in den ersten Entwürfen und Blogbeiträgen. Die aktuelle imperative API hängt am Dokument, du rufst also document.modelContext.registerTool(), getTools() und executeTool() auf. Chrome hat den Einstiegspunkt umbenannt, während die API noch experimentell war, deshalb lesen produktive Stellen beides und fallen zurück. Eine browserexperimentelle API als harte Abhängigkeit zu behandeln, macht aus einer Umbenennung einen Ausfall.

Soll ich die deklarative oder die imperative WebMCP-API verwenden?

Nimm die deklarativen Attribute, wenn ein einfaches Formular die Arbeit schon erledigt: toolname und tooldescription am Formular, toolparamdescription auf Feldern, deren Bedeutung nicht offensichtlich ist, und toolautosubmit nur, wenn der Agent absenden soll. Nimm document.modelContext.registerTool() für alles, was berechnet, zustandsbehaftet oder seitenübergreifend ist, denn nur die imperative API erreicht den Anwendungszustand oder gibt ein Ergebnis zurück, ohne zu navigieren.

Ist WebMCP auf einer Produktionsseite sicher einsetzbar?

Das kann es sein, wenn man die Tore des Browsers respektiert. WebMCP läuft nur in origin-isolierten Dokumenten, jeder Header, der document.domain aktiviert, schaltet es also ab, und die tools Permissions Policy steht standardmäßig auf self, was für ein Cross-Origin-iframe allow=tools bedeutet. Darüber hinaus ist deine execute-Funktion die Grenze: markiere unumkehrbare Tools mit consequentialHint, damit eine Bestätigung verlangt werden kann, und halte Human in the Loop bei allem, was Geld ausgibt oder sich nicht rückgängig machen lässt.

Wie teste ich WebMCP-Tools lokal?

chrome://flags/#enable-webmcp-testing aktivieren, Chrome neu starten, und die APIs sind lokal ohne Enrollment verfügbar. Mit der Model Context Tool Inspector Extension siehst du, welche Tools eine Seite registriert hat, rufst sie von Hand auf und prüfst, ob der Browser dein Eingabe-Schema parsen kann. Für Tests mit echten Nutzern nimmst du am Origin Trial teil, einem zeitlich begrenzten Early Access mit Nutzungslimits.

Klingt nach dem, was du suchst?

Erzähl mir von deinem Projekt oder deiner Stelle – ich freue mich, von dir zu hören.