← Zurück zum Blog
pullmdmarkitdownarchitecturesecuritycode-reviewclaude-code16. Juni 2026 · 9 Min. Lesezeit

PullMD Teil 14: Microsofts MarkItDown integriert — und drei Teile wieder rausgerissen – Einfach erklärt

DOCS: SIDECAR · AUDIO/YT/VISION: OURS · NO SILENT EXTERNALSKI-generiertes Bild
Editorial-Diagramm: Dokumentformate (PDF, DOCX, PPTX, XLSX, EPUB) fließen in eine MarkItDown-Sidecar-Box, während drei eigene Pfade (Audio, YouTube, Vision) bewusst daran vorbei durch eigene Panels geführt werden; Warnschild 'No silent externals'Editorial-Diagramm: Dokumentformate (PDF, DOCX, PPTX, XLSX, EPUB) fließen in eine MarkItDown-Sidecar-Box, während drei eigene Pfade (Audio, YouTube, Vision) bewusst daran vorbei durch eigene Panels geführt werden; Warnschild 'No silent externals'

PullMD ist mein selbst gehostetes Tool, das Webseiten und Dateien in sauberen Text verwandelt. Die bisherigen Teile handelten vom Bauen, Härten und Veröffentlichen. Dieser Teil beginnt nicht mit einem Plan, sondern mit einem Juckreiz.

Der banale Juckreiz

Ich hatte ein paar HTML-Dateien auf der Festplatte, die ich Claude geben wollte. PullMD konnte schon Webseiten umwandeln. Warum also nicht auch lokale Dateien?

Die Lösung war ein kleines Feature: Datei hochladen, gleicher Ablauf wie bei einer Webseite. Die Schutzvorkehrungen drumherum kamen von Claude: keine Zwischenspeicherung für hochgeladene Dateien, der Dateiname so übertragen, dass er nicht in den Server-Protokollen auftaucht. Mein eigener Anteil war profaner: Ich habe schlicht keinen Sinn darin gesehen, eine Datei zu speichern, die man einmal per Drag-and-Drop umwandelt.

Beim Testen kam mir die naheliegende Frage:

„aber auf mobile könnte man ‚oder Datei öffnen’ daraus machen, oder? eine heruntergeladene html datei kann man auf mobile auch umwandeln wollen.“

Also wurde die Ablagefläche antippbar und öffnet seitdem einen Datei-Dialog. Desktop und Handy.

MarkItDown rein

Zwei Tage später stolperte ich über Microsofts MarkItDown. Eine Bibliothek unter der MIT-Lizenz (benannt nach der Hochschule, an der sie entstanden ist), die alle möglichen Dokumentformate in Text umwandelt: PDF, Word, PowerPoint, Excel, EPUB und mehr.

Wenn HTML-Dateien schon gehen, warum nicht auch diese Formate? Und warum nicht gleich YouTube-Transkripte? Wer einmal auf einer dieser überladenen Transkript-Seiten gelandet ist, weiß, warum man das lieber im eigenen Tool hätte.

Ich gab Claude das Projekt:

„Schau dir das folgende Repo an. Können wir es sinnvoll in PullMD integrieren?“

Der Plan leuchtete sofort ein. MarkItDown übernimmt nur die Dokumentformate, die PullMD noch nicht konnte. Für Webseiten wäre es ein Rückschritt gewesen.

PullMD ist ohnehin ein Verbund aus mehreren kleinen Hilfsdiensten. Seit April laufen Trafilatura (ein Werkzeug, das den eigentlichen Lesetext aus Webseiten herausfiltert) und Playwright (ein unsichtbarer Browser, der Seiten mit JavaScript-Inhalten vollständig aufbaut) als sogenannte Sidecars daneben. Ein Sidecar ist wie ein Beiwagen am Motorrad: er fährt mit, aber er lenkt nicht. Neu dazu kommt jetzt ein weiterer Sidecar für Dokumentformate, der einspringt, wenn ein PDF, eine Word-Datei oder etwas Ähnliches reinkommt.

So weit, so sauber. Eine fremde Bibliothek für die Fleißarbeit, denn Bürodokumente zu zerlegen ist niemandes Lieblingsbeschäftigung. Dann habe ich mir den Plan im Detail angesehen, und aus „rein damit“ wurde „rein, aber drei Teile davon nicht“.

Drei Teile wieder raus

Beim Lesen von Claudes Integrationsplan fielen mir zwei Dinge auf: Der YouTube-Teil fehlte kommentarlos, ausgerechnet der, der mich am meisten gereizt hatte. Und beim Audio stand „Privacy“ als Begründung, während der Plan trotzdem einen externen Dienst aufrief. Ich hakte nach:

„warum hast du YT-transcript weggelassen? … und was bedeutet ‚Privacy’, wenn du openai dennoch verwendest?“

Claudes Antwort ging die Gründe am Code entlang durch. Daraus wurde die eigentliche Arbeit dieses Releases: nicht das Einbauen, sondern das gezielte Wieder-Herausnehmen.

Audio. MarkItDowns Standard schickt Audiodateien still an Googles Spracherkennung im Internet. Bei einem selbst gehosteten Tool gehört diese Entscheidung dem Betreiber, nicht dem Standard. Also ein eigener Weg mit wählbarem Anbieter: lokal auf dem eigenen Rechner oder extern, je nachdem, was man bewusst einstellt.

YouTube. MarkItDowns YouTube-Baustein flog komplett raus. Claudes Blick in den Code fand: ein einziges gesperrtes Video reißt den ganzen Ablauf mit hoch. Man verliert sogar Titel und Zusatzinfos, obwohl die im Seitenquelltext längst dastanden. An die Stelle kam ein eigener YouTube-Handler, der bei Problemen sanft weitermacht statt alles mitzureißen.

Bild-Beschreibung. Statt MarkItDowns eigenen KI-Aufruf zu nutzen, läuft die Bilderkennung direkt im Hauptprogramm. Null Kopplung an MarkItDown für den KI-Teil: ein Update der Bibliothek kann daran nichts kaputtmachen.

Das Prinzip ging als Satz ins Projekt-Gedächtnis: MarkItDown nur für Dateitypen, fragile und externe Pfade bauen wir selbst.

Am nächsten Vormittag stellte ich die offene Frage:

„bauen wir unseren eigenen markitdown-Ersatz? wäre das schlechtes Design?“

Claudes Antwort war nein, und es war die richtige. Nicht den ganzen Zoo an Dokumentformat-Zerlegern nachbauen; den will niemand selbst pflegen. Aber die KI-Schicht gehörte heraus. Bilderkennung und Sprache-zu-Text wanderten komplett aus dem Sidecar ins Hauptprogramm. Meine Begründung:

„ich finde es nicht gut, wenn wir unsere LLM-Calls so von MarkItDown abhängig machen. Wenn jemand gar keine Dokumente braucht, muss er trotzdem MarkItDown mitlaufen lassen.“

LLM-Calls sind die Aufrufe an eine KI, zum Beispiel um Text zu verstehen oder Bilder zu beschreiben. Durch diese Trennung schrumpfte der Sidecar auf das, was er gut kann: Dokumente und YouTube.

Beinahe wäre noch ein viertes Stück dazugekommen: Docling, eine Tabellen-Engine von IBM, die lokal die besten Ergebnisse liefert. Ich war kurz begeistert, bis Claude die Zahlen nachreichte: riesiges Installationspaket, mehrere Gigabyte Arbeitsspeicher pro Anfrage, über eine Minute Anlaufzeit. Den Ausschlag gab dann keine technische Abwägung:

„wenn dann so ein ewig langer warmup kommt, bin ich frustriert und schiebe es auf ‚was für ein schlechtes projekt, dieses pullmd’.“

Docling ist jetzt nur als steckbarer Anknüpfungspunkt dokumentiert, nicht im Standardpaket. Der Leitsatz dahinter: leichte Tür für alle, schwere Tür nur für die, die sie bewusst aufmachen. Dieselbe Logik galt für die Tabellenerkennung in PDFs: ein externer KI-Dienst steht als Vorlage bereit, wird aber nur aktiv, wenn man ihn bewusst einschaltet.

Eine Anpassung kam im selben Umbau mit, hatte mit MarkItDown aber nichts zu tun. Sie blieb am Ende trotzdem der einzige echte Bruch mit der alten Version: Der umgewandelte Text enthält jetzt nur noch Titel und Inhalt. Quelle und Abrufdatum stehen ausschließlich im sogenannten YAML-Frontmatter. YAML ist ein einfaches Format für Metadaten, wie ein Etikett auf einem Aktenordner: Infos über den Inhalt, getrennt vom Inhalt selbst. Nichts ist mehr doppelt, KI-Assistenten lesen weniger. Wer das alte Format braucht, kann es per Einstellung zurückholen. Das machte aus dem Release v3.0.0.

Fable 5 Review

Am Abend, als v3 fertig war und der Release-Tag geplant, erschien Fable 5: Anthropics neues Spitzenmodell.

Ein brandneues Modell, dessen Klasse fürs Schwachstellen-Finden gebaut ist, am Abend vor einem Release, das gerade einen öffentlichen Upload-Pfad dazubekommen hatte: neue Angriffsfläche, die noch niemand mit kritischen Augen angesehen hatte. Erster Einsatz, direkt an den echten Änderungen:

„mach bitte ein code review und finde bugs und sicherheitsprobleme.“

Code-Review bedeutet: jemand (oder in diesem Fall etwas) liest den gesamten neuen Code und sucht nach Fehlern und Schwachstellen.

Sieben echte Befunde, ein falscher Alarm. Die zwei, die mir am meisten zu denken gaben, betrafen Sicherheit:

Eine Lücke in der Datenaufbereitung. Die Funktion, die Text für den Kopfbereich vorbereitet, übersah ein bestimmtes unsichtbares Zeichen. Das Format, in dem der Kopfbereich geschrieben ist, behandelt dieses Zeichen als Zeilenumbruch: ein Angreifer hätte über einen manipulierten Seitentitel eigene Daten einschleusen können. Der Fix war ein einzelnes Zeichen mehr in der Suchregel. Geschrieben hatte den Code Claude, und durch jedes Review davor war es trotzdem gerutscht.

Eine Überlastungs-Attacke auf den Upload-Pfad. Beliebige Dokumente jagten ungeschützt durch den Verarbeiter, und der Sidecar war intern nicht abgesichert. So eine Attacke nennt man DoS (Denial of Service, „Dienstverweigerung“): jemand schickt absichtlich so viel oder so Schwieriges, dass der Dienst für alle anderen aufhört zu funktionieren. Der Fix: jede Umwandlung läuft jetzt in einem Wegwerf-Prozess mit Zeitlimit und Speicherdeckel. Ein öffentlicher Upload-Pfad ohne solche Grenzen ist keine Sache, die man auf später verschiebt.

Dazu die Bugs: ein fehlender Schutz, der statt einer sauberen Fehlermeldung den ganzen Dienst zum Stolpern gebracht hätte. Ein Zwischenspeicher-Treffer, der bestimmte Zusatzinformationen verlor. Ein Werkzeug im MCP-Protokoll (Model Context Protocol, die Schnittstelle, über die KI-Assistenten das Tool direkt ansprechen können), das von der normalen Schnittstelle abgedriftet war. Und ein falscher Alarm: ein gemeldetes Datenleck, das keines war, weil die betroffenen Daten verworfen werden, bevor sie nach außen gelangen.

Auch das Lesen des Reviews bleibt Pflicht. Ein Befund ist eine Hypothese, kein Urteil.

Am nächsten Morgen ein einzelnes kaputtes Bild auf einer Teilen-Seite. Ich zog daran, und es kam ein ganzer Schwung mit. Bildadressen innerhalb einer Seite wurden nie gegen die Quellseite aufgelöst. Ein Zugangsschlüssel war seit einer früheren Version in keiner Konfigurationsdatei durchgeleitet. Und die Einstellung, mit der man den Formatbruch rückgängig machen konnte, war über keinen Weg erreichbar: ein Bruch ohne erreichbare Rückfalloption ist einfach nur ein Bruch.

Nach all dem standen 721 Tests plus 4 Sidecar-Schutztests auf Grün, von 644 beim Start des Arbeitsabschnitts.

Schaubild: Dokumentformate fließen links rein, das Hauptprogramm verteilt sie an verschiedene Hilfsdienste, rechts stehen optionale KI-Dienste, die nur aktiv werden, wenn der Betreiber sie bewusst einschaltet Schaubild: Dokumentformate fließen links rein, das Hauptprogramm verteilt sie an verschiedene Hilfsdienste, rechts stehen optionale KI-Dienste, die nur aktiv werden, wenn der Betreiber sie bewusst einschaltet
Die fertige v3-Architektur: alles in der Box bleibt in der Box. Die KI-Dienste rechts existieren nur, wenn der Betreiber sie bewusst einsteckt. KI-generiertes Bild

v3 draußen

Zusammenführen, Versionsnummer, Veröffentlichung. Minuten später der Beitrag auf Reddit. Kein Triumphton: das Ding ist draußen, fertig. Vor dem Hochladen lief noch ein Privatsphäre- und Geheimnisse-Scan über den ganzen Arbeitsabschnitt. Das war mir wichtiger als saubere Ästhetik, aus naheliegenden Gründen, die in dieser Serie schon einmal ein eigenes Thema waren.

Die Aufteilung war über den ganzen Bogen dieselbe: ich habe die Entscheidungen getroffen, Claude hat analysiert und gebaut. Was diesen Teil von den vorigen unterscheidet, ist, dass die wichtigsten Entscheidungen nicht das Einbauen waren, sondern das bewusste Wieder-Herausnehmen.

Eine fremde Bibliothek spart einem den ganzen Zoo an Dokumentformat-Zerlegern. Aber jeden Pfad, der entweder fragil ist oder still Daten nach außen schickt, übernimmt man besser nicht ungeprüft: den baut man selbst, oder man macht ihn wenigstens zur bewussten Wahl statt zum stillen Standard.


Teil 14 von 15 der PullMD Serie.

← PullMD Teil 13: Eine alte Regel | PullMD Teil 15: Was Fremde besser sehen →

Kommentare

Zum Kommentieren genügt eine E-Mail-Bestätigung oder ein GitHub-Login. Die Kommentare laufen auf einem eigenen Server, nicht bei einem Werbeanbieter — Details in der Datenschutzerklärung.