Skip to main content
Glama

VisualDoc

Visual-layout QA untuk dokumen — "Playwright, tapi untuk DOCX / Google Docs / Word." Satu MCP server yang dipakai identik oleh Claude Code dan OpenCode: render → ukur → cek → patch → diff → ulangi.

Status: Fase 0 + Fase 1 (+ penguatan) ✅ terverifikasi end-to-end lewat klien MCP stdio sungguhan, 47 pytest hijau. Loop tertutup: doc_autofix membawa dokumen contoh dari {critical:1}{critical:0} dalam 1 iterasi. Penguatan: anchor stabil w14:paraId, mode review/safe/full, regression guard (revisi yang memunculkan isu baru → rollback), dan guardrails input: validasi DOCX, deteksi halaman di luar rentang, serta error terstruktur {ok:false, error{code}} — menutup celah "laporan hijau padahal salah".

doc_render_report → 1 critical (table_overflow) + 1 warning (image_low_dpi)
doc_autofix       → baseline{critical:1} → final{critical:0}  accepted=True
                    iter1: set_table_width(el-4) → resolved [p1:table_overflow]
                    unresolved: [p1:image_low_dpi]   (butuh aset resolusi lebih tinggi)

Loop (Fase 1)

doc_get_structure ──► el-N (posisional) + anchor (w14:paraId, stabil) + version_hash
        │
doc_apply_patch(ops, expected_version_hash) ──► revisi + validasi round-trip
        │            target = el-N | anchor      (file asli tak disentuh)
doc_render_report(revisi) ──► ukur ulang
        │
doc_diff_versions ──► resolved / new(regresi) / delta
        ↺  (dibungkus jadi satu tool: doc_autofix — mode review|safe|full)
   berhenti saat tak ada yang bisa diperbaiki / max_iter / regression guard
   (revisi yang memunculkan warning baru → dibuang, versi sebelumnya dipertahankan)

Loop hidup sebagai satu tool (doc_autofix), bukan Claude Code hook — supaya portabel ke OpenCode (docs/01-compatibility.md). Agen juga bisa menyetir loop lebih halus sendiri via doc_get_structure + doc_apply_patch.

Struktur

namanya-nanti/
├── .mcp.json · pyproject.toml · README.md
├── docs/  00-concept · 01-compatibility · 02-preview-model (DOCX vs LaTeX)
├── src/visualdoc/
│   ├── server.py          # MCP server (FastMCP, stdio) — 7 tool
│   ├── core/              # FORMAT-AGNOSTIC (bekerja atas PDF apa pun)
│   │   ├── geometry.py    #   PDF -> bbox/font/DPI/tabel
│   │   ├── rules.py       #   rule engine (+ engine_sensitivity)
│   │   ├── report.py      #   analyze_pdf()
│   │   ├── diff.py        #   diff_reports() before/after
│   │   └── raster.py      #   PDF page -> PNG (kanal visual opsional)
│   └── adapters/docx/     # FORMAT-SPECIFIC
│       ├── render.py      #   DOCX -> PDF (LibreOffice)
│       ├── meta.py        #   margin/style OOXML
│       ├── patch.py       #   get_structure + ops aman + validasi round-trip
│       ├── autofix.py     #   loop deterministik (structure-driven, render-verified)
│       └── pipeline.py    #   build_report()
├── rules/general.yaml · configs/ · scripts/
└── workspace/  original/ revisions/ renders/ reports/

Tool (7)

Tool

Fungsi

doc_selftest()

Cek kesehatan (soffice/PyMuPDF/python-docx) + daftar patch_ops.

doc_render_report(document, pages?, rules?)

Render + ukur + cek → JSON (summary.headline, pages[]). Tanpa gambar.

doc_get_structure(document, assign_anchors?, output?)

Elemen ter-alamat: el-N + anchor (w14:paraId, stabil) + version_hash + text_column_mm. assign_anchorsnormalized_output.

doc_apply_patch(document, ops, expected_version_hash?, output?)

Op aman ke salinan (target el-N | anchor) + validasi round-trip.

doc_autofix(document, rules?, max_iter?, mode?)

Loop tertutup + regression guard → revisi final + history/diff. mode: review|safe|full.

doc_diff_versions(before, after, rules?)

Diff dua versi: resolved / new / delta.

doc_get_page_image(document, page?, dpi?)

Opsional: PNG satu halaman (mahal token; pakai hemat).

Op aman (doc_apply_patch): set_spacing, set_keep_with_next, set_page_break_before, set_paragraph_style, set_font, set_table_width. Tiap op melapor applied/error independen; op salah tak menggagalkan yang benar. target bisa el-N (posisional) atau anchor (w14:paraId — bertahan lintas-edit; doc_get_structure(assign_anchors=true) menuliskannya ke normalized_output).

Mode doc_autofix: review (rencana saja) · safe (invariant: fit tabel) · full (safe + heading keep_with_next + clamp spacing). Warning membawa engine_sensitivity (invariant benar di renderer manapun; dependent hanya valid untuk layout LibreOffice ini) — lihat docs/02-preview-model.md.

Kontrak error: tiap tool mengembalikan ok: true saat sukses, atau {ok: false, error: {code, message, detail?}} saat gagal — code ∈ {not_found, invalid_docx, invalid_rules, pages_out_of_range, stale_version, bad_argument, no_ops, no_ops_applied, render_unavailable, render_failed, internal}. Input DOCX & rule-pack divalidasi (bukan-DOCX / .doc lama / korup / YAML rusak ditolak), render yang gagal/timeout terbedakan dari input salah, dan halaman di luar rentang tak pernah menghasilkan laporan "hijau" palsu (semua di luar rentang → error; sebagian → di-flag). apply_patch yang 0-perubahan tidak menulis file (no_ops/no_ops_applied).

Keandalan tabel (#4): table_overflow dikoroborasi dengan lebar OOXML (patch._table_width_mm) — jumlah kolom di pesan direkonsiliasi ke angka OOXML yang otoritatif (mis. render 7 → OOXML 8), tiap tabel render dijembatani ke element_id, dan overflow yang tak terdeteksi renderer (mis. tabel tanpa border) tetap muncul sebagai isu struktural, bukan hilang diam-diam.

Prinsip desain

Semua analisis di server, dikembalikan sebagai teks/JSON — bukan gambar ke model. Rule engine jalan murni atas angka geometri (PyMuPDF), tanpa vision → murah token, deterministik, jalan di kedua klien bahkan dengan model text-only. Alasan: docs/01-compatibility.md.

Quickstart

# Prasyarat sistem (sekali):
sudo apt-get install -y --no-install-recommends libreoffice-writer python3-venv fonts-liberation
python3 -m venv .venv && .venv/bin/pip install -e .

# Coba:
.venv/bin/python scripts/make_sample.py    # -> workspace/original/sample.docx
.venv/bin/python scripts/mcp_smoke.py       # uji 7 tool end-to-end lewat protokol MCP
.venv/bin/mcp dev src/visualdoc/server.py   # (opsional) MCP Inspector interaktif

# Test (mengunci semua perilaku agar tak regres):
.venv/bin/pip install -e ".[test]" && .venv/bin/python -m pytest

Registrasi di klien

  • Claude Code.mcp.json sudah ada di root: /mcp → approve visualdoc (perlu restart sesi; server MCP tak hot-load).

  • OpenCode — salin configs/opencode.json ke opencode.json project Anda.

Contoh perintah ke agen: "jalankan doc_autofix pada workspace/original/sample.docx, ringkas apa yang diperbaiki dan apa yang tersisa."

Batasan (jujur, by design)

  • Anchor elemen stabil (w14:paraId) sudah ada; pemetaan elemen↔halaman presisi (korelasi render↔OOXML, mis. inject-marker) masih penyempurnaan lanjut.

  • Auto-fix: table overflow (safe) + heading keep_with_next & clamp spacing (full). image_low_dpi & orphan_heading dilaporkan, belum di-auto-fix (butuh aset/keputusan).

  • Satu engine (LibreOffice) — temuan dependent (page-break/orphan) perlu engine target (Word) untuk kepercayaan penuh. Multi-engine = Fase 4.

  • Belum ada preview-ui/ (viewer manusia) dan adapter non-DOCX. Roadmap → docs/00-concept.md.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Alvnvnc/visualdoc-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server