Cloudflare betreibt unter isitagentready.com einen Scanner, der eine Domain danach bewertet, wie bereit sie für KI-Agenten ist. Einundzwanzig Prüfungen in fünf Kategorien: Auffindbarkeit, Zugänglichkeit der Inhalte, Steuerung des Bot-Zugriffs, Protokoll-Discovery und Commerce.

Wir haben ihn auf spintax.net gerichtet, eine Doku-Seite, die eine Woche zuvor eine vollständige llms.txt-Oberfläche ausgeliefert hatte. Ein kuratierter Index, achtzehn saubere Markdown-Spiegel der Doku-Seiten und eine zusammengefügte llms-full.txt für einen einzigen Abruf. Das Ergebnis kam mit 21 von 100 zurück, Level 1, “Basic Web Presence”. Zugänglichkeit der Inhalte: null von eins.

Diese Lücke ist der interessante Teil. Der Wert misst nicht, wie gut Ihre Seite für ein Modell lesbar ist. Er misst, welche HTTP-Mechanismen Sie umsetzen. Das ist nicht dasselbe, und nur eines davon ist das, was die meisten Seiten zu kaufen glauben, wenn sie eine llms.txt veröffentlichen.

Hier steht, was das Audit tatsächlich misst, welche vier Prüfungen umzusetzen sich lohnte, und warum acht der Fehlschläge richtig waren.

Warum eine vollständige llms.txt bei der Zugänglichkeit null bekommt

Die Prüfung sucht keine Datei. Sie schickt eine Anfrage mit Accept: text/markdown und liest den Content-Type, der zurückkommt. Ist die Antwort text/html, fällt die Prüfung durch, ganz gleich, wie viel Markdown Sie unter anderen Adressen veröffentlichen.

Das ist HTTP-Content-Negotiation, und sie steht in der Spezifikation, seit lange bevor jemand Agenten baute. Eine URL, mehrere Repräsentationen, der Client nennt seine Präferenz in einem Header. llms.txt ist eine Konvention: eine Datei an einem bekannten Pfad, den ein Client vorher kennen muss. Content-Negotiation ist ein Mechanismus: jeder Client, der bereits HTTP spricht, bekommt das Markdown, indem er danach fragt, ohne zu wissen, dass es Ihre Seite gibt.

Wir hatten achtzehn Markdown-Dateien auf der Platte und keinen Weg für einen Client, sie zu erreichen, außer zuerst den Index zu lesen. Das Audit hatte recht.

Content-Negotiation auf Pages, im Free-Tarif

Cloudflare verkauft die einfache Variante davon. Eine Zonen-Einstellung namens Markdown for Agents fängt Antworten ab, wenn die Anfrage Accept: text/markdown trägt, und wandelt das HTML im Flug in Markdown. Ein Schalter in AI Crawl Control, oder ein PATCH auf die Zonen-Einstellung content_converter.

Verfügbar ist sie auf “Pro, Business and Enterprise plans, and SSL for SaaS customers at no cost”. Die Seite läuft auf Free.

Der Schalter wäre auch dann die schlechtere Option gewesen, wenn er verfügbar wäre. Unsere Spiegel werden zur Bauzeit aus demselben HTML erzeugt, über eine Turndown-Konfiguration mit eigenen Regeln für die Karten-Raster, die sonst in unlesbare, in Links verschachtelte Blöcke umgewandelt würden. Eine automatische Umwandlung am Edge kann davon nichts wissen. Handverlesene Ausgabe schlägt generische, wenn Sie sie ohnehin erzeugen.

Also: eine Pages Function. Beachten Sie, dass _headers und _redirects diese Aufgabe überhaupt nicht können, weil keines von beiden auf einen Anfrage-Header verzweigen kann. Content-Negotiation braucht Code.

// functions/_middleware.ts
export const onRequest = async (context) => {
  const { request, env, next } = context;
  const url = new URL(request.url);

  if (request.method === 'GET' && wantsMarkdown(request.headers.get('Accept'))) {
    const mirror = await env.ASSETS.fetch(
      new Request(new URL(mirrorFor(url.pathname), url.origin)),
    );
    if (mirror.ok) {
      return new Response(mirror.body, {
        status: 200,
        headers: {
          'Content-Type': 'text/markdown; charset=utf-8',
          'Vary': 'Accept',
          'X-Robots-Tag': 'noindex',
          'Link': `<${url.origin}${url.pathname}>; rel="canonical"`,
        },
      });
    }
  }

  const response = await next();
  const out = new Response(response.body, response);
  out.headers.append('Vary', 'Accept');
  return out;
};

Drei Details darin verdienen ihren Platz.

env.ASSETS.fetch liest die bereits ausgelieferte statische Datei. Die Function wandelt nichts um und hält keine Kopie des Inhalts.

Vary: Accept steht auf beiden Zweigen. Eine URL hat jetzt zwei Repräsentationen, und ein Cache, der den Header nicht in den Schlüssel nimmt, liefert einem Browser das Markdown aus.

Der Rückfall ist ein Fehlschlag auf den Spiegel, kein Fehler. Gibt env.ASSETS.fetch eine 404 zurück, fällt der Code auf das HTML durch. Eine Seite hinzuzufügen, die noch keinen Spiegel hat, verschlechtert sich still.

Die Präferenz zu erkennen verdient mehr als einen Teilstring-Vergleich. Browser schicken nie text/markdown, also genügt das Vorhandensein als Signal — aber ein Client kann Accept: text/markdown;q=0 schreiben und damit das Gegenteil meinen:

function wantsMarkdown(accept) {
  if (!accept) return false;
  for (const entry of accept.split(',')) {
    const [type, ...params] = entry.split(';');
    if (type.trim().toLowerCase() !== 'text/markdown') continue;
    const q = params.map(p => p.trim()).find(p => p.toLowerCase().startsWith('q='));
    return !q || Number.parseFloat(q.slice(2)) > 0;
  }
  return false;
}

Das Ergebnis lässt sich von jedem Terminal aus prüfen, und genau darum geht es beim Mechanismus statt der Konvention:

$ curl -sI -H 'Accept: text/markdown' https://spintax.net/
HTTP/1.1 200 OK
Content-Type: text/markdown; charset=utf-8
Link: <https://spintax.net/>; rel="canonical"
Vary: Accept
X-Robots-Tag: noindex

Eine statische Seite statisch halten

Ein _middleware im Wurzelverzeichnis greift standardmäßig bei jeder Anfrage. Auf einer Seite mit 116 Seiten plus Assets macht das aus einem vollständig statischen Deployment eines, bei dem jede Anfrage einen Worker aufruft, um auf neunzehn davon Markdown auszuliefern.

_routes.json in der Bauausgabe behebt das:

{
  "version": 1,
  "include": ["/", "/docs/", "/docs/syntax", "/docs/variables/"],
  "exclude": []
}

Wir erzeugen diese Datei aus derselben Liste, die die Spiegel erzeugt, damit die gerouteten und die verhandelbaren Pfade nicht auseinanderlaufen können. Siebenundneunzig lokalisierte Seiten, die 404 und jedes Asset kommen aus dem statischen Speicher und erreichen die Function nie.

Zwei Pages-Fallen auf dem Weg

Überlappende _headers-Regeln werden aneinandergehängt

Diese hat echte Zeit gekostet. Die Seite lieferte ihre Markdown-Spiegel bereits mit einer Wildcard aus:

/*.md
  Content-Type: text/plain; charset=utf-8
  X-Robots-Tag: noindex

Skill-Dateien unter /.well-known/agent-skills/ hinzuzufügen bedeutete, eine spezifischere Regel für sie hinzuzufügen, mit text/markdown. Beide Regeln greifen auf /.well-known/agent-skills/spintax-syntax/SKILL.md. Das Ergebnis:

Content-Type: text/plain; charset=utf-8, text/markdown; charset=utf-8

Pages hat die beiden Regeln zusammengeführt, indem es die Werte angehängt hat. Es hat die spezifischere nicht gewinnen lassen. Die Ausgabe ist kein gültiger Content-Type, und nichts warnt Sie.

Die Lösung war, die Wildcard zu löschen und aus dem Build eine explizite Regel je Spiegel zu erzeugen. Achtzehn Regeln statt eines Musters, deutlich unter dem Limit von 100. Wenn zwei _headers-Regeln denselben Pfad treffen und denselben Header-Namen setzen können, gehen Sie davon aus, dass beide gelten.

wrangler pages dev liest _headers nur einmal

Der lokale Dev-Server liest _headers beim Start und protokolliert, wie viele Regeln er geparst hat. Nach einem Rebuild liest er sie nicht erneut. Wir haben die Wildcard behoben, neu gebaut, erneut getestet und denselben kaputten Content-Type gesehen, weil der Server noch die Regeln von vor zwei Builds hielt.

Starten Sie den Dev-Server nach jeder Änderung an _headers neu. Die Regelzahl im Startlog ist die schnellste Bestätigung, dass er die neue Datei aufgenommen hat.

Die drei billigen Prüfungen

Content Signals. Eine Direktive in robots.txt, die angibt, wie der Inhalt von KI-Systemen genutzt werden darf. Sie steht in der User-agent-Gruppe, unter einer Präambel von contentsignals.org, die das rechtliche Gewicht trägt:

User-agent: *
Content-Signal: ai-train=yes, search=yes, ai-input=yes

Allow: /

Drei Signale, je drei Werte: search für die Indexierung, ai-input für Retrieval und Grounding, ai-train für das Modelltraining. Eine Doku-Seite, die Markdown-Spiegel für Maschinen veröffentlicht, hat auf alle drei eine naheliegende Antwort. Behalten Sie die Präambel. Sie ist es, die aus der Zeile statt einer Präferenz eine Rechtevorbehaltserklärung nach Artikel 4 der EU-Urheberrechtsrichtlinie macht.

Link-Antwortheader. RFC 8288, und das Audit akzeptiert nur registrierte Relationstypen. describedby für maschinenlesbare Beschreibungen, alternate für eine andere Repräsentation derselben Seite, service-doc für den Einstiegspunkt der Dokumentation:

Link: </llms.txt>; rel="describedby"; type="text/plain",
      </docs/variables.md>; rel="alternate"; type="text/markdown",
      </docs/>; rel="service-doc"; type="text/html"

Ein Agent, der auf irgendeiner Seite landet, findet den Rest über die Antwortheader, ohne HTML zu parsen und ohne von llms.txt zu wissen. Diese kostet zur Laufzeit nichts: sie ist statische _headers-Ausgabe, ohne Function.

Agent Skills. Ein Discovery-Index unter /.well-known/agent-skills/index.json, der Skill-Dokumente auflistet, die ein Agent installieren kann. Das ist die einzige der vier mit Produktnutzen statt Auditnutzen. Ein Skill ist ein verdichtetes, imperatives Dokument, gerichtet an eine Maschine, die gleich etwas tun wird — ein anderes Artefakt als eine Doku-Seite für Menschen.

Die Einträge des Index tragen einen SHA256-Digest jedes Artefakts. Erzeugen Sie den Index, schreiben Sie ihn nie von Hand. Ein von Hand geschriebener Digest ist falsch, sobald jemand einen Skill zum ersten Mal bearbeitet, und ein falscher Digest sagt einem Client, die Datei sei manipuliert worden. Wir lesen name und description aus dem Frontmatter jedes Skills und hashen die Bytes so, wie sie ausgeliefert werden — eine Quelle für die Metadaten, keine zweite Liste zu pflegen.

Acht Fehlschläge, die richtig waren

Die weiterhin fehlschlagenden Prüfungen sind DNS for AI Discovery, API Catalog, OAuth Discovery, OAuth Protected Resource, Auth.md, MCP Server Card, A2A Agent Card und WebMCP. Fünf Commerce-Prüfungen stehen daneben, und das Audit markiert sie als neutral statt als fehlgeschlagen, weil es erkennt, dass die Seite nichts verkauft.

Eine Doku-Seite hat keine API, keine Authentifizierung und nichts zu verkaufen. Ein leeres OAuth-Discovery-Dokument zu veröffentlichen, um einen Wert zu heben, bewirbt einen Autorisierungsserver, den es nicht gibt. Jedes dieser Dokumente ist ein Versprechen an einen Client darüber, was er vorfinden wird. Ein Versprechen, das Sie nicht halten können, ist schlimmer als eine fehlende Datei, weil der Client, der es liest, einen Roundtrip verschwendet und danach raten muss, ob Sie kaputt sind oder lügen.

Drei davon verdienen mehr Details, weil sich die Begründung verallgemeinern lässt.

Die MCP Server Card zeigt auf einen Pfad, den die Spezifikation verlassen hat

Das Audit prüft drei Orte auf eine MCP Server Card: /.well-known/mcp.json, /.well-known/mcp/server-cards.json und /.well-known/mcp/server-card.json.

Der zugrunde liegende Vorschlag, SEP-2127, ist offen und nicht gemergt, Status Draft, auf dem Extensions Track. Das normative Wire-Format liegt überhaupt nicht im Spezifikations-Repository. Es liegt in einem Repository namens experimental-ext-server-card. Der Discovery-Pfad ist mindestens dreimal umgezogen, und der aktuelle kanonische ist /.well-known/ai-catalog.json, mit dem Medientyp application/ai-catalog+json — ein Katalog, der Cards indexiert, statt selbst eine zu sein.

Keiner der drei Pfade, die das Audit prüft, ist der Pfad, den die Spezifikation heute nutzt. Diese Prüfung heute zu bestehen bedeutet, an eine Adresse zu veröffentlichen, die die Spec bereits verlassen hat.

DNS-AID: die Prüfung, die ihr eigener Autor nicht besteht

Diese ist am meisten wert, weil sie am saubersten zeigt, warum man die Fehlschläge liest, statt sie abzuarbeiten.

DNS for AI Discovery verlangt, SVCB-Einträge unter einem _agents-Namensraum zu veröffentlichen, damit Agenten Ihre Agent-Endpunkte über DNS finden. Es ist ein reiner DNS-Eintrag: kein Worker, keine Laufzeit, keine Kosten, und Cloudflare unterstützt SVCB im Free-Tarif. Nach der üblichen Rechnung ist es die billigste Prüfung der ganzen Liste.

Drei Dinge sprechen dagegen.

Die Spezifikation ist ein individueller Internet-Draft. Version 02, aktualisiert am 27. Mai 2026, von keiner Arbeitsgruppe angenommen, ohne formalen Stand im Standardisierungsweg, und sie läuft am 28. November 2026 ab. Das ist kein Grund, sie zu ignorieren. Es ist ein Grund zu wissen, worauf man baut.

Der Scanner prüft Labels, die der Draft nicht definiert. Er fragt _index._agents, _mcp._agents und _a2a._agents ab. Der Draft definiert den Einstiegspunkt bei _index._agents und wählt Protokolle über den Service-Parameter alpn, nicht über protokollspezifische Unterstrich-Labels. _mcp und _a2a gehören dem Prüfer, nicht der Spezifikation — dieselbe Diskrepanz wie oben bei der MCP Server Card, aus derselben Ursache.

Niemand veröffentlicht es. Kein rhetorisches Niemand. Am 22. Juli 2026 über DNS-over-HTTPS geprüft:

Domain _index._agents
isitagentready.com NXDOMAIN
cloudflare.com keine Einträge
agents.cloudflare.com keine Einträge

Die Audit-Seite selbst, und das Unternehmen, das das Audit geschrieben hat.

Und die Schlussfolgerung, die daraus folgt: richten Sie den Scanner auf sich selbst. isitagentready.com kommt mit Level 4, Agent-Integrated zurück, mit dnsAid unter den eigenen Fehlschlägen — dasselbe Level, das dieser Blog erreicht hat, von einem Werkzeug, das seine eigene Prüfung nicht besteht.

Dem Draft zuliebe: ein Einwand gegen das Veröffentlichen trägt nicht. Es liegt nahe zu sagen, wir hätten keine Agenten zu indexieren, aber der Draft stellt den Inhalt des Index-Endpunkts ausdrücklich außerhalb des Geltungsbereichs: es kann ein laufender Dienst oder ein statisches Dokument sein, und es gibt einen well-known-Service-Parameter, der auf Metadaten nach RFC 8615 zeigt. Wir könnten _index._agents ehrlich auf den Agent-Skills-Index richten, den wir ohnehin veröffentlichen.

Wir tun es nicht, und der Grund ist nicht Ehrlichkeit. Es ist, dass ein Eintrag, der eine Prüfung erfüllt, die ihr eigener Autor nicht umsetzt, gegen einen in vier Monaten ablaufenden Draft, in einem Namensraum, dessen Labels der Prüfer erfunden hat, eine Zahl kauft und sonst nichts.

Eine A2A Agent Card nennt einen laufenden Endpunkt

Die A2A Agent Card verlangt eine url für den A2A-Dienst. Sie beschreibt einen Agenten, dem andere Agenten Arbeit übergeben können. Wir betreiben keinen. Die Card zu veröffentlichen würde einen Dienst bewerben, der nichts beantwortet.

Das setzt auch dem Wert eine Decke auf. Level 5 verlangt Auth.md, eine MCP Server Card, eine A2A Agent Card und einen API Catalog. Eine Content-Seite kann davon ehrlich vielleicht eines erreichen. Level 4 ist die Obergrenze für eine Seite, die nur Dokumente veröffentlicht, und das ist eine Eigenschaft der Leiter, kein Mangel der Seite.

Der MCP-Server, den wir nicht gebaut haben, und die eine Zahl, die entschied

Der einzige Punkt auf der Liste mit echtem Wert für uns ist ein MCP-Server: einem Agenten ein validate-Werkzeug geben, das strukturierte Diagnosen mit Zeile und Spalte zurückgibt, und die Schleife von “Modell schreibt ein Template” zu “Modell repariert das Template” schließt sich ohne Menschen darin.

Zwei Funde haben ihn aus dieser Woche geschoben.

Das Protokoll ist mitten im Bruch. Die Revision 2026-07-28 macht Streamable HTTP zustandslos. Der Header Mcp-Session-Id “and the protocol-level session that came with it are also removed”, der initialize/initialized-Handshake ist entfernt, und der Transport “now requires Mcp-Method and Mcp-Name headers so load balancers, gateways, and rate-limiters can route on the operation”, mit einer neuen Methode server/discover für Fähigkeiten. Upstream nennt es die größte Revision des Protokolls seit dem Start. Gegen die aktuelle Form zu bauen heißt, in einer Woche neu zu schreiben.

Der zweite Fund ist eine Zahl. Cloudflares Dokumentation ist klar, dass ein zustandsloser MCP-Server keine Durable Objects und keinen bezahlten Tarif braucht, was klingt, als reiche der Free-Tarif. Dann sehen Sie nach, was der Free-Tarif gibt: 10 ms CPU pro Anfrage. Wir haben die Engine gemessen, die wir kapseln würden, auf Node — also als Größenordnung lesen, nicht als Messung des Ziels:

Arbeit CPU
400-Byte-Template, ein Render, warm 0,13 bis 0,28 ms
400-Byte-Template, zwanzig Varianten 3 bis 6 ms
Erster Aufruf auf kaltem Isolate etwa 12,5 ms
16-KB-Template, zwanzig Varianten etwa 85 ms

Allein das kalte Isolate liegt über dem Budget, bevor ein Template geparst ist. Ein 16-KB-Template überschreitet um den Faktor acht. Ein verlässlicher Server im Free-Tarif bräuchte Grenzen, die eng genug sind, um zu nerven — etwa 4 KB Template und fünf Varianten. Der Workers-Paid-Tarif für fünf Dollar im Monat hebt das Limit auf 30 Sekunden CPU und beseitigt die Frage.

Das ist eine Entwurfsentscheidung, die vor dem Code fällt, nicht danach.

Wo der Wert landete, und wo diese Seite steht

Drei bestehende Prüfungen wurden sieben, und das Level ging von 1 auf 4, “Agent-Integrated”. Sechs weitere sind als neutral markiert, weil sie nicht zutreffen, und die acht, die fehlschlagen, sind die acht, die es sollen.

Der nützliche Ertrag war nicht die Zahl. Es war die Auskunft, dass eine Woche Arbeit an llms.txt Dateien hervorgebracht hatte, für die kein Client verhandeln konnte — etwas Konkretes und Behebbares, das kein noch so gründliches Lesen der eigenen Dokumentation zutage gefördert hätte.

Fürs Protokoll: dieser Blog stand bei Level 1, als der Artikel geschrieben wurde, drei von einundzwanzig — genau dort, wo die andere Seite angefangen hat. Dieselben vier Prüfungen am selben Tag gegen ihn laufen zu lassen ergab eine andere Antwort, und der Unterschied ist mehr wert als der Wert.

Alle vier wurden umgesetzt, und er liest jetzt Level 4, dasselbe wie die Seite, um die es in diesem Artikel geht. Content Signals, Link-Header, ein Agent-Skills-Index und ein Markdown-Spiegel jedes Artikels, indexiert in llms.txt. Der interessante Teil ist der vierte, weil die Form der Seite ändert, was er kostet.

Diese Seite läuft nicht auf Pages. Sie ist ein Worker, der statische Assets ausliefert, und bis heute hatte sie überhaupt kein Skript — weshalb “requests to static assets are free and unlimited” jeden Seitenaufruf abdeckte. Negotiation braucht Code vor diesen Assets, und jede Seite, die diesen Code ausführt, wird zu einer gewöhnlichen Worker-Anfrage gegen die 100.000 pro Tag des Free-Tarifs.

Also läuft der Code auf so wenig wie möglich: run_worker_first listet die Apex-Domain und einsegmentige Pfade, sonst nichts. Stylesheets, Bilder, die Spiegel selbst, robots.txt und die Sitemap erreichen ihn nie und bleiben kostenlos.

Ein Detail ist es wert, übernommen zu werden. Cloudflares Cache-Schlüssel enthält Vary nicht, solange keine Cache Rule es hineinlegt, sodass eine URL mit zwei Repräsentationen einem Menschen Markdown aus dem Cache liefern kann. Die Lösung ist keine Zonen-Regel: es ist Cache-Control: no-store allein auf dem Markdown-Zweig. Agenten sind ein Rinnsal, das HTML behält sein normales Caching, und der Fehlerfall verschwindet.

Das andere ist peinlich und billig. Behandeln Sie HEAD, nicht nur GET. RFC 9110 sagt, ein HEAD muss mit den Headern antworten, die ein GET senden würde, und die erste Fassung davon tat es nicht — was curl -I sofort meldete, und genau so würde es jeder von Hand prüfen.

Dieselben vier Prüfungen, derselbe Tag, zwei verschiedene Seiten, und die Arbeit war auf jeder eine andere. Das ist die eigentliche Lehre aus einem Audit: die Prüfungen sind generisch, Ihre Infrastruktur ist es nicht.

Lassen Sie es gegen Ihre eigene Domain laufen. Lesen Sie dann die Fehlschläge und entscheiden Sie, welche davon eine Seite beschreiben, die Sie tatsächlich betreiben:

curl -sS -X POST https://isitagentready.com/api/scan \
  -H 'content-type: application/json' \
  -d '{"url":"https://example.com"}'

Der JSON-Endpunkt gibt jede Prüfung mit Status, Meldung und den gestellten Anfragen zurück, was schneller ist als der Browser und sich nach einem Deployment leichter vergleichen lässt. Er meldet das Level, nicht den Wert; die Zahl von 100 gibt es nur in der Weboberfläche.