"""Gemeinsame PDF-Verarbeitung fuer Files- und Mail-MCP. Text-PDFs -> extrahierter Text. Bildbasierte/gescannte PDFs -> Seiten als PNG gerendert (PyMuPDF) + OCR-Text (tesseract), damit das LLM sie per Vision liest UND durchsuchbaren Text bekommt. Verschluesselte/kaputte PDFs -> graceful. """ import base64 import io from mcp.types import TextContent, ImageContent, EmbeddedResource, BlobResourceContents DPI = 150 MAX_PAGES = 20 def _ocr(png_bytes): try: import pytesseract from PIL import Image return pytesseract.image_to_string(Image.open(io.BytesIO(png_bytes)), lang="deu+eng").strip() except Exception: return "" def pdf_to_content(content, label, ct="application/pdf", uri=None): uri = uri or f"file://{label}" # 1. Echten Text extrahieren try: import pdfplumber with pdfplumber.open(io.BytesIO(content)) as pdf: pages = [] for i, page in enumerate(pdf.pages, 1): t = page.extract_text() or "" if t.strip(): pages.append(f"--- Seite {i} ---\n{t}") text = "\n\n".join(pages) if text.strip(): return [TextContent(type="text", text=f"[PDF: {label}]\n\n{text[:200000]}")] except Exception: pass # weiter zum Rendern (z.B. verschluesselt -> faellt unten in except) # 2. Bildbasiert/gescannt -> Seiten rendern + OCR try: import fitz # PyMuPDF doc = fitz.open(stream=content, filetype="pdf") n = doc.page_count images, ocr_pages = [], [] for i in range(min(n, MAX_PAGES)): png = doc[i].get_pixmap(dpi=DPI).tobytes("png") images.append(ImageContent(type="image", data=base64.b64encode(png).decode(), mimeType="image/png")) t = _ocr(png) if t: ocr_pages.append(f"--- Seite {i + 1} (OCR) ---\n{t}") header = f"[PDF '{label}' ist bildbasiert/gescannt ({n} Seite(n)) — als Bilder gerendert" header += " (+ OCR-Text)" if ocr_pages else "" header += ":]" out = [TextContent(type="text", text=header)] if ocr_pages: out.append(TextContent(type="text", text="\n\n".join(ocr_pages)[:200000])) out += images if n > MAX_PAGES: out.append(TextContent(type="text", text=f"[... {n - MAX_PAGES} weitere Seiten ausgelassen (Limit {MAX_PAGES}).]")) return out except Exception as e: return [ TextContent(type="text", text=f"[PDF '{label}' konnte nicht verarbeitet werden ({e}). Rohdaten folgen.]"), EmbeddedResource(type="resource", resource=BlobResourceContents( uri=uri, blob=base64.b64encode(content).decode(), mimeType=ct)), ]