{"id":"6107810f-7a22-49b9-81ac-98bd381d1176","slug":"document-intelligence-platform","kind":"mcp","name":"document-intelligence-platform","summary":"Building your own secure document processing platform that: runs locally on a MacBook, uses n8n as an orchestrator, uses Python as the processing engine, anonymizes personal data, prepares data for an","owner":"pmackowka","repoUrl":"https://github.com/pmackowka/document-intelligence-platform","license":"MIT","licenseTier":"permissive","trust":"community","stars":1,"match":null,"fewStars":true,"format":"mcp","createdAt":"2026-07-17T11:05:06.000Z","addedAt":"2026-10-21T09:21:55.105Z","logoUrl":"https://avatars.githubusercontent.com/u/42898252?u=65e75e2de9cf69d069b19d252d49ffa236bf1593&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["files-documents","search","data-analytics"],"tags":["automation","document-processing","n8n","python","rag"],"runtime":"binary","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-26T07:21:48.639Z","sample":false,"url":"https://conduits.dev/tools/document-intelligence-platform","language":"Python","transports":["stdio"],"install":{},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# Document Intelligence Platform\n\nLokalna platforma do przetwarzania osobistej dokumentacji medycznej z analizą trendów w czasie. Projekt służy równolegle jako ścieżka nauki stacku AI Engineering.\n\n## Architektura — mapa wersji\n\n| Wersja | Co robi | Status |\n|---|---|---|\n| V1 | OCR → standaryzacja → magazyn NDJSON → raporty Markdown | zamknięta |\n| V2 | Serwer MCP (Tools) nad tym samym magazynem | zamknięta |\n| V3 | Przeniesienie do chmury | pominięta świadomie (backlog) |\n| V4 | Agent (Claude Agent SDK) orkiestrujący narzędzia MCP z V2 | zamknięta |\n| V5 | Resources i Prompts MCP nad tym samym serwerem (nadobowiązkowa) | zamknięta |\n\n### V1 — fundament: OCR → standaryzacja → magazyn → raport\n\n```mermaid\nflowchart TD\n    IN[\"Nowy dokument<br>upload ręczny do data/raw/structural\"] --> OCR[\"OCR: Docling<br>+ Apple Vision dla zdjęć\"]\n    OCR --> ST[\"Standaryzacja<br>aliases.yml → regex → units.yml\"]\n    ST --> AV[\"Weryfikacja nieznanych nazw<br>claude -p --model haiku\"]\n    AV --> PA[\"pending/*.yml<br>operator zatwierdza\"]\n    PA --> D[(\"Magazyn NDJSON<br>append-only, BigQuery-ready\")]\n    D --> AN[\"Analiza trendów<br>Claude Code w terminalu\"]\n    AN --> RP[\"Raport Markdown<br>na wizytę lekarską\"]\n```\n\n### V2 — serwer MCP nad magazynem z V1\n\n```mermaid\nflowchart LR\n    subgraph Klienty MCP\n        C1[Claude Code]\n        C2[Claude Desktop]\n        C3[Inne klienty]\n    end\n    C1 & C2 & C3 --> S[MCP Server<br>lokalnie na Macu]\n    S --> T1[get_marker_trend]\n    S --> T2[compare_markers]\n    T1 & T2 --> D[(NDJSON + Markdown<br>magazyn z V1)]\n```\n\n### V3 — przeniesienie do chmury (pominięta, backlog)\n\n```mermaid\nflowchart LR\n    T[\"Trigger<br>upload / cron\"] -->|HTTP request| API[\"FastAPI<br>w kontenerze Docker\"]\n    subgraph \"Cloud Run lub Mac mini — decyzja threat model\"\n        API --> P[\"Pipeline Python<br>moduły z V1, OCR do zamiany\"]\n    end\n    P --> D[(\"Magazyn NDJSON\")]\n    P --> R[\"Raporty Markdown\"]\n```\n\n### V4 — agent nad serwerem MCP z V2\n\n```mermaid\nflowchart TD\n    U[\"Użytkownik:<br>przygotuj mnie do wizyty u nefrologa\"] --> A[\"Agent<br>planowanie kroków\"]\n    A -->|krok 1| T1[\"MCP: get_marker_trend<br>kreatynina, eGFR\"]\n    A -->|krok 2| T2[\"MCP: compare_markers\"]\n    T1 & T2 --> S[Synteza wieloetapowa]\n    S --> O[\"Raport na wizytę<br>+ pytania do lekarza\"]\n```\n\n### V5 — Resources i Prompts MCP nad tym samym serwerem\n\n```mermaid\nflowchart LR\n    C[\"Inspector / Claude Desktop\"] --> S[MCP Server]\n    S --> R1[\"Resource: markers://catalog\"]\n    S --> R2[\"Resource: report://latest\"]\n    S --> P1[\"Prompt: porownaj_markery\"]\n    R1 & R2 --> CTX[Kontekst rozmowy]\n    P1 --> MSG[Gotowy tekst zapytania]\n```\n\nPełne uzasadnienie decyzji, ryzyka i kompetencje nauki dla każdej wersji: `CLAUDE.md` (status i decyzja o pominięciu V3) oraz `docs/v4/prd.md`, `docs/v5/prd.md`.\n\n## Dokumentacja\n\n- **`docs/v1/`** — Version 1 (OCR → magazyn → raporty), zamknięta — kontrakt `docs/v1/prd.md`, plan `00`–`04`\n- **`docs/v2/`** — Version 2 (serwer MCP), zamknięta — kontrakt `docs/v2/prd.md`, plan `00`–`03`\n- **`docs/v4/`** — Version 4 (agent nad MCP), zamknięta — kontrakt `docs/v4/prd.md`, plan `01`\n- **`docs/v5/`** — Version 5 (Resources/Prompts MCP, nadobowiązkowa), zamknięta — kontrakt `docs/v5/prd.md`, plan `01`\n- **`CLAUDE.md`** — konwencje projektu dla sesji Claude Code\n\n## Stack\n\nPython (uv) · Docling · Apple Vision · BigQuery · Claude (abonament Pro)\n\n## Uruchomienie\n\n```bash\nuv sync                 # odtworzenie środowiska z uv.lock\nuv run python <skrypt>  # uruchomienie kodu w środowisku projektu\n```\n\n## Codzienny flow — dodawanie nowych wyników\n\nDwa gotowe prompty do wklejenia w Claude Code — resztę (pipeline, aliasy, raport) robi już on, nie Ty ręcznie w terminalu. Mechanizm w tle: każdy przebieg pipeline'u tworzy migawkę-kandydata (`data/store/YYYY-MM-DD-structural.ndjson`), nigdy nie nadpisuje zaufanego magazynu (`data/store/structural-backfill.ndjson`) — pełne uzasadnienie: `docs/v1/prd.md` v1.3, `docs/v1/04-backfill.md`.\n\n**1. Wrzuciłeś nowy plik do `data/raw/structural/` — wklej prompt, pod nim dopisz ścieżkę pliku:**\n```\nWrzuciłem nowy plik do data/raw/structural/. Uruchom pipeline, obsłuż nieznane\nmarkery jeśli się pojawią (zapytaj mnie o zatwierdzenie każdego), wygeneruj\nraport z nowej migawki-kandydata i zaczekaj na moją weryfikację — nie scalaj\nz zaufanym magazynem.\n\nPlik:\n```\nSam prompt zostaje bez zmian za każdym razem — dopisujesz tylko ścieżkę pod linią „Plik:\", bez edycji tekstu nad nią.\n\nDostaniesz ścieżki: migawka `data/store/<data>-structural.ndjson`, raport `data/reports/<data>-trendy.md`. Sprawdź kolumnę **„Źródło\"** przy nowych wynikach względem oryginału.\n\n**2. Dane poprawne — wklej prompt, pod nim dopisz ścieżkę migawki z kroku 1:**\n```\nZweryfikowałem migawkę wskazaną poniżej — dane poprawne. Scal ją z zaufanym\nmagazynem data/store/structural-backfill.ndjson.\n\nMigawka:\n```\n\nDopiero teraz dane liczą się „na serio\" — BigQuery i dalsza analiza czytają wyłącznie zaufany magazyn. Procedura odświeżenia tabeli (zawsze replace, nigdy append): `docs/v1/03-storage-analysis.md`.\n\n**Przykład:** wracasz z laboratorium z nowym wynikiem TSH, kopiujesz plik do `data/raw/structural/`, wklejasz prompt 1. Marker już w słowniku → od razu dostajesz migawkę i raport do sprawdzenia. Nowy marker/lab → Claude Code najpierw zapyta o zatwierdzenie proponowanego aliasu, dopiero potem wygeneruje raport. Po sprawdzeniu kolumny „Źródło\" wklejasz prompt 2.\n\nDokument z nieczytelnym OCR (za mało wyciągniętego tekstu) nie wejdzie do wyników w ogóle — Claude Code to zgłosi w podsumowaniu; sprawdź skan ręcznie (kąt, ostrość, czy to na pewno badanie krwi/moczu).\n\n## MCP server (V2) — przykład działania\n\nVersion 2 udostępnia te same dane przez protokół MCP — Claude Desktop (i inni świadomie skonfigurowani klienci MCP) mogą pytać o trendy i porównania bez dostępu do plików, tak jak dotąd wyłącznie Claude Code w terminalu.\n\nRealne zapytanie w Claude Desktop — `compare_markers` wywołane bez żadnego ręcznego kroku pośredniego, dane z zaufanego magazynu:\n\n![Porównanie hemoglobiny i OB w czasie — odpowiedź Claude Desktop wywołująca compare_markers](docs/assets/v2-compare-markers-example.png)\n\nNarzędzie zwraca dwie w pełni niezależne serie (bez łączenia po dacie — różne markery bywają badane różnego dnia); to model po stronie odpowiedzi złożył je w jedną czytelną tabelę.\n\n## Pitch — projekt w 30 sekund\n\n> [!IMPORTANT]\n> Zbudowałem lokalny pipeline, który zamienia ~390 osobistych dokumentów medycznych — skany, PDF-y, zdjęcia — w kanoniczne dane strukturalne do analizy trendów w czasie. OCR opiera się na Docling z Apple Vision dla zdjęć, bo ten natywnie lepiej czyta polskie diakrytyki i jednostki niż modele ogólne. Cała wiedza domenowa — nazwy markerów, jednostki, zakresy referencyjne — żyje w YAML, nie w kodzie: dodanie nowego laboratorium to edycja configu, nie refaktor. Standaryzacja idzie w trzech warstwach: słownikowe mapowanie nazw, regexowa ekstrakcja wartości, a dla nieznanych nazw weryfikacja przez LLM. Model to wyłącznie Claude na abonamencie Pro (zero zakupu tokenów API), a trafia do niego sama nazwa markera — nigdy pełna treść dokumentu, więc prywatność wynika z architektury, nie z doklejonej warstwy. Człowiek jest w pętli obowiązkowo: propozycje aliasów czekają na zatwierdzenie, a zweryfikowane dane chroni bariera migawka-kandydat plus ręczne scalenie, żeby przyszła poprawka nie zmieniła po cichu już sprawdzonego rekordu. Świadomie odrzuciłem UI, orkiestrator i bazę danych — dla narzędzia jednego użytkownika płaski NDJSON gotowy pod BigQuery w zupełności wystarcza. Najtrudniejsza okazała się nie ścieżka happy-path, tylko integralność danych na skali: pomieszana kolejność OCR, PII wyciekające do pól wyniku — rozwiązane zasadą „nie zgaduj, sygnalizuj\" i przeglądem wiarygodności klinicznej, nie kolejnym regexem.\n"}