PullMD Teil 1: Strg+A und der Junk-Kontext
Ich sitze vor einem langen Reddit-Thread, in dem jemand ziemlich genau das Problem beschreibt, an dem ich gerade arbeite. Also wie immer: Strg+A, Strg+C, ab in den Chat. Am Handy ist es noch umständlicher. Alles markieren, kopieren, einfügen. „Mit Claude teilen“ funktioniert bei Reddit ja nicht, weil Reddit KI-Dienste blockiert.
Das Ergebnis ist meistens brauchbar. Der Weg dahin ist trotzdem ziemlich bescheuert.
Teilt man stattdessen einfach den Link mit dem Sprachmodell, muss es sich erst durch den ganzen Seitenaufbau kämpfen: Navigation, Cookie-Banner, Werbung, Empfehlungen und sonstigen Seitenmüll.
Und auch von dem Text, den ich selbst einfüge, besteht oft ein großer Teil aus genau diesem Zeug. Der eigentliche Beitrag steckt irgendwo dazwischen. Das Modell muss also erst jede Menge irrelevanten Inhalt verarbeiten, bevor es überhaupt bei dem ankommt, worum es mir eigentlich geht.
Das kostet Tokens, dauert länger und bläht den Kontext unnötig auf. Ich will ja nicht, dass der Chatbot Reddit analysiert. Ich will, dass er einfach nur den Thread liest.
Natürlich ist das keine bahnbrechende Erkenntnis. Mich hat nur irgendwann genervt, dass ich dieselben unnötigen Handgriffe ständig wiederhole. Und irgendwann war es einmal zu viel.
Die kurze Hoffnung: Cloudflare liefert Markdown
Cloudflare hat vor kurzem einen Markdown-Dienst vorgestellt: URL rein, sauberes Markdown raus. Ohne Navigation, Werbung und den ganzen Rest der Webseite.
Natürlich habe ich das direkt ausprobiert. Ein paar Wikipedia-Artikel, ein Tech-Blog – das Ergebnis sah wirklich gut aus. Sauberes Markdown, kaum Ballast, genau das, was ich gesucht hatte.
Man fordert die Seite einfach mit dem Accept-Header text/markdown an – quasi: „Ach übrigens, ich nehme auch Markdown, wenn du welches hast.“
curl "https://developers.cloudflare.com/fundamentals/reference/markdown-for-agents/" \
-H "Accept: text/markdown"
Der Haken: Das funktioniert nur bei Webseiten, die den Dienst aktiv unterstützen und überhaupt Cloudflare nutzen. Bei Reddit funktioniert es nicht, bei vielen Seiten, über die man so täglich stolpert, ebenfalls nicht.
Kurz hatte ich überlegt, dafür einen Skill zu bauen: unterstützte Seiten über Cloudflare abrufen, den Rest irgendwie anders behandeln. Aber ein Werkzeug, das ausgerechnet bei einem großen Teil meiner tatsächlichen Quellen nicht funktioniert, löst mein Problem nicht. Es macht es nur an einigen Stellen etwas kleiner.
Eigenbau – aber erst einmal klein
Damit war die Entscheidung eigentlich gefallen: Wenn es nichts Fertiges gibt, das zuverlässig funktioniert, baue ich es selbst.
Nicht gleich als universellen Web-Scraper für das halbe Internet, sondern erst einmal nur für Reddit. Dort nervt es mich am häufigsten, und es gibt einen angenehm einfachen Einstieg: Hängt man an eine Reddit-URL .json an, liefert Reddit die Rohdaten des Posts und der Kommentare zurück. Ohne API-Key, OAuth oder App-Registrierung.
Der Weg funktioniert seit Jahren und war für einen ersten Prototypen genau richtig.
Auf Android sollte der Dienst als PWA installierbar sein und die Web Share Target API unterstützen. Dann könnte ich in der Reddit-App einfach auf „Teilen“ drücken und den Link direkt an meinen Dienst schicken. Kein Markieren, kein Kopieren, kein zusätzlicher Browser-Tab.
Bevor ich Code schreiben lasse, wollte ich das Konzept sauber durchdenken. Also erst mal mit Claude auf claude.ai darüber philosophieren, um Architektur und Anforderungen festzuschreiben. Danach sollte Claude Code mit einer möglichst vollständigen Spezifikation loslegen.
Der Konzept-Chat vom 2. März
Der Chat begann direkt mit einem Tippfehler meiner Autokorrektur am Smartphone.
Claude antwortete auf „Tresor Posts“:
Gute Idee, lass uns das durchdenken! Ich nehme an, „Tresor“ war Autokorrektur und du meinst Reddit Posts – richtig?
Das war ganz passend. Das Modell verstand trotz des Fehlers, was ich meinte, weil der restliche Kontext eindeutig war. Genau darum geht es eigentlich immer: Dem Modell nicht einfach möglichst viel Text hinwerfen, sondern möglichst guten Kontext liefern.
Eine der ersten Architekturfragen war, ob es einen oder zwei Zugangswege geben sollte:
- Der eigentliche Kern ist ein HTTP-Endpoint. Den können CLI-Tools, Claude Code, n8n oder andere Automationen verwenden.
- Darüber liegt eine kleine Weboberfläche als PWA. Die ist hauptsächlich für Android gedacht und nimmt Links über das Share-Menü entgegen.
Beide Wege landen beim selben Endpoint. Die Verarbeitung bleibt also identisch, nur die Art der Nutzung ist unterschiedlich.
Auf die offizielle Reddit-API wollten wir bewusst verzichten. Der Zugang ist inzwischen unnötig umständlich und für so einen kleinen Dienst auch gar nicht erforderlich.
Stattdessen entstand eine zweistufige Strategie:
- Zuerst der
.json-Weg, mit einem normalen, realistischen User-Agent. - Falls Reddit das blockiert, als Fallback HTML-Scraping über
old.reddit.com.
Das alte Reddit lässt sich deutlich einfacher parsen als die heutige Oberfläche. Vor allem braucht man dafür keinen Headless-Browser, der das ganze Projekt sofort schwerer und wartungsintensiver gemacht hätte.
Auch beim Kommentarformat fiel eine Entscheidung, die mir direkt gefiel: Verschachtelte Kommentare werden mit jeweils zwei Leerzeichen eingerückt und nicht als immer längere Markdown-Blockquote-Ketten dargestellt.
Bei mehreren Ebenen sehen Blockquotes schnell so aus:
> > > > Antwort auf eine Antwort auf eine Antwort
Das ist spätestens beim Kopieren in einen Chat kaum noch vernünftig lesbar. Mit normaler Einrückung bleibt die Struktur sichtbar, ohne dass der eigentliche Inhalt unter einer Wand aus >-Zeichen verschwindet.
In diesem Chat entstand auch der erste Codename des Projekts. Unter diesem Namen habe ich die nächsten Wochen daran gebaut. Später musste er aus markenrechtlichen Gründen wieder verschwinden – dazu mehr in Teil 8.
Am Ende brauchte der Dienst noch eine Subdomain. Mein Heimnetz folgt einem festen Namensschema, also habe ich Claude einfach gebeten, einen passenden Namen dafür auszuwählen.
Web Share Target API – was sie macht
Über die Web Share Target API kann sich eine installierte PWA im Android-Share-Menü registrieren.
Wichtig ist dabei das Wort installiert. Es reicht nicht, die Seite nur im Browser zu öffnen oder als normales Lesezeichen zu speichern. Die PWA muss über „App installieren“ beziehungsweise „Zum Startbildschirm hinzufügen“ installiert werden.
Außerdem muss sie über HTTPS erreichbar sein. Da bei mir ohnehin Traefik als Reverse Proxy davorsteht, war das kein zusätzlicher Aufwand.
Der notwendige Manifest-Eintrag ist ziemlich überschaubar:
"share_target": {
"action": "/share",
"method": "GET",
"params": {
"url": "link"
}
}
Danach taucht die PWA im Android-Share-Menü neben WhatsApp, Signal und den anderen Apps auf.
Teile ich einen Reddit-Link mit ihr, öffnet sich die Oberfläche, und der Link ist bereits vorausgefüllt. Genau das wollte ich: Einen eigenen URL-Dienst auf Android verwenden, ohne dafür gleich eine native App bauen zu müssen.
Die Spezifikation
Am Ende des Chats stand eine vollständige Spezifikation:
- Dateistruktur
- API-Endpunkte
- Parameter und Rückgabeformate
- PWA-Manifest
- Docker-Compose-Datei
- Traefik-Labels
- Beispielausgabe mit eingerückten Kommentaren
Die offenen Architekturfragen waren damit geklärt. Noch keine Zeile Code – genau so war es gedacht.
Der Chat sollte das Projekt planen, nicht bereits nebenbei anfangen, halbfertige Dateien zu erzeugen. Das eigentliche Bauen sollte Claude Code übernehmen, mit der Spezifikation als Briefing.
Am selben Abend um 21:53 Uhr ging es los.
Teil 1 von 15 der PullMD Serie.